I've been working with a client on a WSS site deployment, and one of our big sticking points has been with the out-of-the-box WSS navigation. The client set the following requirements for the navigation: 1. Must be security trimmed – so if you don't have access to a site, you don't see it in the nav 2. Must allow for the addition of custom navigation items 3. Must have flyouts (drop-downs) that go at least 2 or 3 levels deep Out of the box, we get #1 & #2, but since we're not using MOSS, we can't just modify the master page to get #3 to work. That's where my buddy the SharePoint Cowboy, Eric Shupps, found a nice way to add the drop-down menus to WSS. The problem is that this approach switches the data source, and you lose the ability to specify what appears in the navigation. So while you gain #3, you lose #2. Talk about one step ahead, one step back. So after some trial and error, some searching and enlisting the help of Josh Carlisle for a few lines of code, I have a solution… The solution involves the following: 1. Creating a WSS list that will manage you navigation. 2. Implementing the Cascading Navigation web part from CodePlex. 3. Adding a couple lines to your master page. 4. Go wild! So, in detail, here is what you need to do: #1 – Setup your Navigation List The first thing you will want to do is create your navigation list. It should be a Custom List, and I named mine 'WSSNavigation', but feel free to call yours whatever you would like. I also do not display mine in the Quick Launch. Once the base custom list is created, then create the following fields: Note that for Item Level, your choices should only be Level 1, Level 2 or Level 3 (include spaces). And for Display0 (make sure there is a zero!) the choices should be Yes or No. Do not use a Yes/No field. Now, go ahead and add a temporary line or two into the list. Make sure that your Link ID is unique for each line… think of it as your primary key for each navigation item (it should just be an incrememental number… start at 1 and keep incrementing). And if there is no parent of the item you are adding, keep that field blank. #2 – Install the Navigation Download the Cascading Nav WSP from CodePlex here: http://www.codeplex.com/sharepointnavigation/Release/ProjectReleases.aspx?ReleaseId=9461 Install & deploy the solution package as you normally would. Then, dump the web part on to a page & in the web part Miscellaneous properties, put in the name of the SharePoint list holding the navigation information under the Admin List field. In my case above, I would input WSSNavigation. If the navigation renders properly, then you are good to move on to step 3. If it doesn't, make sure you've put the in the correct name of the navigation list, and that each of the fields is set up properly. #3 – Modify the Master Page So you've got the navigation working inside of a site in a web part zone – great. Now, let's replace the not so great out-of-the-box nav with our really cool nav. Crack open the master page for the site, and insert the following line under the other lines that look the same (they will be at the top of the page and start with <%@ Register TagPrefix=): <%@ Register TagPrefix="customnav" assembly="CascadingNav, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5″ namespace="CascadingNav" %> Then, let's get rid of the old navigation by commenting it out. Look for the following: <asp:ContentPlaceHolder id="PlaceHolderHorizontalNav" runat="server"> Just before it, add <!- – Hide the original horizontal nav and after it, add - -> Then, on the next line after the line where you put the – ->, put in the following: <customnav:CascadingNav runat="server" id="customNav" Set_AdminList="WSSNavigation" __WebPartId="{89DFF3CB-0E4A-4623-B69B-DFB818FBF6DB}"/> Note that under Set_AdminList= make sure you input the name of your WSS List you created in Step 1 here. #4 – Go Wild & Create your Menu Your site should now be rendering the menu along with the navigational elements specified in the list. Now, head back over to your list and build out your navigation. As you add items to the list, your navigation will be updated, so you can quickly check and make sure things are looking good. And remember – since the navigational items are essentially list items, you can set permissions on them individually. Therefore, you can hide links from users that shouldn't see them. Limitations:- It is working fine for the URL Like: http://servername/sitename We are getting invalidurl error message for the URL Like : http://servername/sites/sitename
<SharePoint:AspMenu
ID="TopNavigationMenu"
Runat="server"
DataSourceID="topSiteMap"
EnableViewState="false"
AccessKey="<%$Resources:wss,navigation_accesskey%>"
Orientation="Horizontal"
StaticDisplayLevels="2″
MaximumDynamicDisplayLevels="2″
DynamicHorizontalOffset="0″
StaticPopoutImageUrl="/_layouts/images/menudark.gif"
StaticPopoutImageTextFormatString=""
DynamicHoverStyle-BackColor="#CBE3F0″
SkipLinkText=""
StaticSubMenuIndent="0″
CssClass="ms-topNavContainer">
<StaticMenuStyle/>
<StaticMenuItemStyle CssClass="ms-topnav" ItemSpacing="0px"/>
<StaticSelectedStyle CssClass="ms-topnavselected" />
<StaticHoverStyle CssClass="ms-topNavHover" />
<DynamicMenuStyle BackColor="#F2F3F4″ BorderColor="#A7B4CE" BorderWidth="1px"/>
<DynamicMenuItemStyle CssClass="ms-topNavFlyOuts"/>
<DynamicHoverStyle CssClass="ms-topNavFlyOutsHover"/>
<DynamicSelectedStyle CssClass="ms-topNavFlyOutsSelected"/>
</SharePoint:AspMenu>
Wednesday, February 10, 2010
WSS Navigation – Flyouts, Security Trimming & Custom Nav Items
Posted by Rami Reddy Annapu Reddy at 1:22 AM 0 comments
Thursday, January 28, 2010
Using Cookies with SharePoint's Data View
In my previous post I've explained how to pass parameters from URL to use in a Data View. There is another very useful way: - using Cookies. The process is very similar, using SharePoint Designer: 1. To read the cookie value and use it as a parameter: Open the properties pane of the XSLT Data View and select Parameters Add a parameter, name it as you wish, and in the Parameter Source select Cookie. In the next field enter the name of the cookie which to read and again you can enter a default value. And you can use the parameter value as you wish - for filtering, conditional formatting, as content in the dataview, etc. 2. But how to set or manipulate Cookies? The easiest way to set a cookie value is with functions I've found on QuirksMode. I've contacted the author but got no reply so I'll dare to publish the JavaScripts here. I repeat again: these are the property of QuirksMode. I use the functions below to set, change value or delete cookies function createCookie(name,value,days) { Delete cookie: function eraseCookie(name) { To put the above example into practice. In a DataView I'd be reading a cookie named ShoeSize and use it as parameter $Size in a data view. To set the value of a cookie for example to M I'd use a function: <script type="text/javascript">createCookie('ShoeSize','M')</script> If the third parameter is not supplied the cookie is active only as long as the browser is open. To change a cookie value to S and to be valid for 3 days, I'd use <script type="text/javascript">createCookie('ShoeSize','S',3)</script> When I don't need the cookie anymore, I just use <script type="text/javascript">eraseCookie('ShoeSize')</script>. Using cookies to pass parameters between pages has its benefits, like - you don't complicate with codepages, long URLs, you can pass the parameter between more pages than one, etc. But you can't pass the parameter with cookie between sites. For this I'd recomend QueryString.
Set or change value of a Cookie:
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else
var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
createCookie(name,"",-1);
}
Posted by Rami Reddy Annapu Reddy at 4:10 AM 0 comments
How to bind SharePoint event receivers to a custom list definition
In most cases you want to create your own list definition with your own event receiver that sould only react on events of your custom list. SharePoint ships with alot of list definitions with unique list type numbers: 100: Generic list You can create event receivers that react on events of these list types. To create a custom list definition you should use the visual studio template "List Definition" shipped with the VSeWSS 1.1. If you create a custom list with base type "document library" its id will also be 101. It's just a copy of the template you selected. First line of schema.xml after creating the custom list definition project: XML: So what you should do ist change the Type value to a non-used number: XML: You also have to modify the ListDefinition.xml XML: and the ItemEvntReceiver.xml XML: Now your event receiver will be called only by MyDocumentLibrary lists and no longer by all document libraries.
101: Document library
104: Announcements list
and many more.
Id="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc"
xmlns="http://schemas.microsoft.com/sharepoint/">
Name="BskhDocumentLibrary"
/>
Id="faca01fe-8478-4116-8b72-1e243bda6268"
xmlns="http://schemas.microsoft.com/sharepoint/">
ListTemplateOwner="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc"
ListTemplateId="999">
Posted by Rami Reddy Annapu Reddy at 4:10 AM 0 comments
How SharePoint stores field names
In my one of my previous posts I've received a good comment from Mike that got me talking quite a lot about SharePoint List / Document Library Field Names. So it's better to dedicate a post to it. First I'll explain some exceptions and then show how to quickly get a sharepoint field name. You can't change SharePoint field name Once you create a field, you can't change the "SharePoint" name for it. Im using this sometimes when creating lists. I'll create a field called FirstName and then rename it to First Name. Like that I have no complications with spaces or any unstandard characters. So now (compared to previous post) I can filter with FilterField1=FirstName. Title field No matter which list you create (except survey), there will always be one Title field no matter how we have it named. We can recognize this title field as the one that is linked to the item (with edit menu) No matter what you rename this field to, this field will always be "SharePoint" named Title. Field names for Out-of-the-box lists Lists that are already included in the templates (like for example Contacts) have different "SharePoint" names than "Display" names. For example: In a Contacts list we can see a field called Fax Number, but "SharePoint" name for it is WorkFax. How to discover real names? Skip to the end of this post and read more. Spaces and special characters in field names Spaces in field names get converted to _x0020_. 0020 represents the Unicode character code. If you'd like to know any other, I've found this very good web character convertion tool. Just enter character into Characters field and you'll find the code in Hexadecimal code points but make it a 4 digit number (for example 20 for space should be 0020). When using those special characters in URL, the underscore (_) gets converted to %5F So how the heck to know SharePoint field names? You COULD mess around with all the specialities and translations and conversions that I've mentioned untill now, but there's an easier way:(two actually): 1. Sort the List / Document Library and check in URL for SortField parameter value. 2. Go to List / Document Library Settings, click on the name of the column to modify it and in URL find the last parameter value And ofcourse if you need to use the field name in other places than URL (like in SharePoint designer), don't forget to convert %5F to underscore. P.S. - Yes, I know there is also a way to see SharePoint Field Names in SharePoint Designer (mouseover a field name in Data Source Details), but in this post I wanted to focus on work without it.
Posted by Rami Reddy Annapu Reddy at 3:54 AM 0 comments
Navigating through SharePoint Site using URL
I like to access lists, document libraries directly instead of visiting site or subsite and then clicking my way around. SharePoint has very logically structured URLs. Here are some of the most common: To access: Type url http://[URL]+ Lists /lists/[List name]* List New Item form [list URL] + NewForm.aspx List item details (display) [list URL] + DispForm.aspx?ID=[item ID] List item edit [list URL] + EditForm.aspx?ID=[item ID] Document Libraries /[Document Library Name]* Document library upload [DocLib url] + /Forms/Upload.aspx Document library item details [DocLib url] + /Forms/DispForm.aspx?ID=[item ID] Document library item edit properties [DocLib url] + /Forms/EditForm.aspx?ID=[item ID] Special SharePoint pages http://[URL]+/_layouts+ View all site content viewlsts.aspx Recycle Bin... recyclebin.aspx Site Collection's recycle bin adminrecyclebin.aspx Create create.aspx Site Settings settings.aspx New Subweb newsbweb.aspx * in cases of Lists and document libraries only spaces get preserved (and translated in %20 in url). Special characters are excluded (for example: list Special ÄŒheck url would be http://[sute url]/lists/Special%20heck/ These things are quite obvious. What I'm trying to point out is if you're accessing certain lists/document libraries/settings often, it's not a bad idea to start paying attention to URLs. Oznake ponudnika Technorati:
Posted by Rami Reddy Annapu Reddy at 3:45 AM 0 comments
Wednesday, January 6, 2010
Cascading Drop Down Lists
Below find a few screenshots of configuring and using the custom field -
I have a list called 'Continents':
A second list 'Countries':
And a third list 'Cities':
In my fourth list 'My Location' in which I would want to have the three dropdowns displayed, I create three columns : Continent, Country and City using my custom field 'Cascading Drop Down List (With Filter)'.
The three columns created are -
Configuring the columns and using the filter -
When adding a 'New Item' to the 'My Location' list, the first dropdown will have "Please select an Item' (which is also something I added) as the first item in the dropdown.
Cascading Drop Down List (With Filter) in action:
According to the filter set, the list of continents that appear are only the ones that contain 'America'.
Posted by Rami Reddy Annapu Reddy at 3:14 AM 0 comments
Thursday, December 24, 2009
Custom Auditing In SharePoint
Code download available at: OfficeSpace2008_09a.exe (631 KB)
Browse the Code Online
Contents
The AuditingDemo Project
Creating a Page to Support Auditing Configuration
Extending the Site Actions Menu
Viewing Audit Log Entries
Viewing Logs on a Per-Item Basis
Many people using SharePoint® technologies don't realize that there is auditing support built directly into the Windows® SharePoint Services (WSS) 3.0 platform. The primary reason this is not well-known is that WSS auditing support is turned off by default, and it cannot be enabled using anything supplied by WSS out of the box. Taking advantage of the WSS auditing infrastructure requires extra code that is not included with WSS.
One simple way to take advantage of WSS auditing support is to use Microsoft® Office SharePoint Server (MOSS) 2007. MOSS provides a user interface (under Site Settings > Configure Audit Settings) that allows a site-collection owner to enable and configure automatic activity event logging. MOSS also provides an audit-reporting facility that reads audit log entries and reports on which users have been engaging in activities, such as reading and writing content, within a specific site collection.
A second approach to using WSS auditing support will appeal to you as a developer. You can build a custom SharePoint solution with Visual Studio® and create a custom application page that allows a site-collection administrator to enable and configure WSS activity event logging. But it's not enough just to turn on automatic activity event logging. It requires more than that. A custom auditing solution targeting WSS must also include some type of user interface so that users can view or report on audit entries that have been added to the WSS audit log.
Two years ago, not long after Microsoft released the first public beta for MOSS 2007, Joanna Bichsel and I published a white paper titled "Item-Level Auditing with SharePoint Server 2007" (msdn.microsoft.com/library/bb397403), along with an accompanying code sample. In this white paper, we discussed many important details about how to program auditing support in WSS. We also covered how MOSS leverages and extends the WSS auditing infrastructure using custom policies and audit report generation.
In this month's column I am going to walk through a new SharePoint solution I recently created with a Visual Studio project named AuditingDemo. The project contains some of the same auditing management code I covered in the white paper, such as the code that enables auditing support through the WSS object model. However, the AuditingDemo project has been totally redesigned and is a much better starting point than the sample code I wrote two years ago. For example, the AuditingDemo project has been created using the STSDEV utility, and I have incorporated many best practices in areas such as solution-package deployment, security programming, and security trimming.
My goal with this month's column is to explain these updated best practices and SharePoint development techniques. It is my intention to complement the original white paper rather than overlap its content. If you have not read the original white paper, I recommend taking a look at it first so that you have a proper context for this month's column.
The AuditingDemo Project
The purpose of the AuditingDemo project is to provide a custom SharePoint solution that can take advantage of the auditing support built into the WSS platform. I designed the project around a scenario wherein a site-collection owner (or another user in the role of Audit Manager) can enable, view, and modify audit settings for the current site collection. The AuditingDemo project also provides viewing capabilities so that a user in the role of an auditor can see what's in the audit log to determine what activity has occurred on a site-collection-wide basis and also on a per-item or per-document basis.
As I mentioned earlier, this project was built using the STSDEV utility, which you can download from codeplex.com/stsdev. You can read about it in my March 2008 Office Space column (msdn.microsoft.com/magazine/cc337895).
The AuditingDemo project has been designed with a central feature (also named AuditingDemo) that is scoped to the level of the site collection. To use the AuditingDemo, you simply need to activate the feature once per site collection.
The AuditingDemo feature includes a feature receiver class with a FeatureActivated event handler (see Figure 1). The code inside FeatureActivated executes during feature activation, and it accomplishes two important things. First, it fully enables audit logging for the entire site collection. Second, it creates a few security objects specific to the AuditingDemo solution. In particular, the code inside FeatureActivated creates two new SharePoint groups and two new permission levels that allow the site-collection administrator to add users into the roles of Auditor and Audit Manager.
Figure 1 The FeatureActivated Event Handler
Copy Code
public override void FeatureActivated(SPFeatureReceiverProperties
properties) {
using (SPSite siteCollection = (SPSite)properties.Feature.Parent) {
SPWeb TopLevelSite = siteCollection.RootWeb;
// Turn on auditing flags.
siteCollection.Audit.AuditFlags = SPAuditMaskType.All;
siteCollection.Audit.Update();
// create permission levels
SPRoleDefinition AuditorPermissions, AuditManagerPermissions;
AuditorPermissions = CreatePermissionLevel(
"Auditor Permissions",
"Can view audit logs",
"Read");
AuditManagerPermissions = CreatePermissionLevel(
"Audit Manager Permissions",
"Can configure auditing support",
"Design",
SPBasePermissions.ManageWeb);
// create Auditors group
SPGroup Auditors = CreateGroup(
"Auditors",
"for users who need to audit user activity");
SPRoleAssignment AuditorRoleAssignment = new SPRoleAssignment(Auditors);
AuditorRoleAssignment.RoleDefinitionBindings.Add(AuditorPermissions);
TopLevelSite.RoleAssignments.Add(AuditorRoleAssignment);
// create Audit Managers group
SPGroup AuditManagers = CreateGroup(
"Audit Managers",
"for users who configure WSS auditing support");
SPRoleAssignment AuditManagerRoleAssignment =
new SPRoleAssignment(AuditManagers);
AuditManagerRoleAssignment.RoleDefinitionBindings.Add(
AuditManagerPermissions);
TopLevelSite.RoleAssignments.Add(AuditManagersRoleAssignment);
}
Note that the code in the FeatureActivated event handler calls the two utility methods named CreatePermissionLevel and CreateGroup. The reason I wrote these was to ensure that any preexisting security objects with the same name as those being created were first deleted. Each method then creates the requested security object (permission level or group), adds it to the appropriate collection within the top-level site, and returns a strongly typed security object to the caller. The CreatePermissionLevel method returns an SPRoleDefinition object that represents the new permission level, and the CreateGroup method returns an SPGroup.
Note that I designed the CreatePermissionLevel method with several overloaded implementations that make the third and fourth parameters optional. The third parameter is for passing the name of an existing permission level that is used to make a copy for the new permission level. For example, the AuditorPermissions permission level is created by making a copy of the built-in Read permission level. And the AuditManagerPermissions permission level is created by making a copy of the built-in Design permission level.
The fourth parameter to the CreatePermissionLevel method is of the SPBasePermissions type, which is used to pass one or more extra permissions that are then used to initialize the new permission level. For example, the call to CreatePermissionLevel that creates the SPRoleDefinition object AuditManagerPermissions passes a value of ManageWeb for the fourth parameter. This means that the new permission level is created with the same permissions as the built-in Design permission level plus the ManageWeb permission.
The ManageWeb permission is important because it's required for users who need to adjust audit settings. I will cover two security trimming techniques later in this column that can be used to display menu items to just those users that have this permission.
The CreateGroup method does a little more than add a new group to the top-level site. It also adds the new group to the associated group's collection and adds the ID of the new group into a site-level property named vti_associatemembergroup. This extra code is important if you want the new group to show up in the group's QuickLaunch bar and inside the combobox of the Add Users page, which makes it far more intuitive for the site-collection owner to add users to the new group.
Creating a Page to Support Auditing Configuration
The AuditingDemo project provides three custom app pages—AuditConfig.aspx, AuditLogViewer.aspx, and ItemAudit.aspx—that were developed using best-practice techniques. For example, these application pages are deployed within a solution-specific directory named AuditingDemo inside the LAYOUTS directory. The code that provides the behavior for these pages has been written in codebehind files (such as AuditConfig.cs). The codebehind files are then compiled into the project's main assembly, AuditingDemo.dll, which is installed and loaded from the Global Assembly Cache (GAC).
The AuditConfig.aspx application page provides a user interface for configuring WSS auditing support. I added a CustomAction to the AuditingDemo feature so that users with the ManageWeb permission will be presented with a link on a site-settings page that allows them to navigate to AuditConfig.aspx:
Copy Code
<!-- Add Link to Site Setting Page -->
<CustomAction
Id="SiteActionsToolbar"
GroupId="SiteCollectionAdmin"
Location="Microsoft.SharePoint.SiteSettings"
Sequence="0"
Rights="ManageWebs"
Title="Audit Management" >
<UrlAction Url="~sitecollection/_layouts/AuditingDemo/AuditConfig.aspx"/>
</CustomAction>
Note that I created this custom action with a GroupId attribute value of SiteCollectionAdmin and a Sequence attribute value of 0. This is what makes the link show up first in the Site Collection Administration column of the Site Settings page.
The Rights attribute, which I have included with a value of ManageWebs, is used by WSS to provide declarative security trimming. Therefore, only users who have the ManageWebs permission will see this link. This will be the case for site-collection owners as well as any users that the site-collection owner has added to the role of Audit Manager.
When users navigate to AuditConfig.aspx, they see the UI in Figure 2. As you can see, this application page provides radio buttons to fully enable or disable auditing. You will also find a radio button for entering a selective mode where the user can choose exactly what activities are recorded with an audit log entry.
Figure 2 AuditConfig.aspx Lets Users Configure Audit Logging
The code that is behind the OK button of AuditConfig.aspx is pretty simple. It reads which radio button has been selected by the user and sets the audit flags for the current site collection to the appropriate value. Then it calls Update on the Audit property to record the changes into the Content Database:
Copy Code
SPSite siteCollection = this.Site;
if (radAuditingOff.Checked) {
siteCollection.Audit.AuditFlags = SPAuditMaskType.None;
}
if (radAuditingOnFull.Checked) {
siteCollection.Audit.AuditFlags = SPAuditMaskType.All;
}
if (radAuditingOnSelective.Checked) {
siteCollection.Audit.AuditFlags = GetSelectiveAuditingFlags();
}
siteCollection.Audit.Update();
If the user has chosen the option for selective auditing configuration, there is a call to the GetSelectiveAuditingFlags utility method. It inspects all those checkboxes and uses bitwise OR operations to return an SPAuditTypeMask value that represents a combination of all the types of activities that the user has selected for auditing:
Copy Code
private SPAuditMaskType GetSelectiveAuditingFlags() {
SPAuditMaskType AuditFlags = SPAuditMaskType.None;
if (chkAuditView.Checked) {
AuditFlags |= SPAuditMaskType.View;
}
if (chkAuditUpdate.Checked) {
AuditFlags |= SPAuditMaskType.Update;
}
// Repeat for Copy, Move, Delete, Undelete, CheckIn, CheckOut,
// Search, Workflow, SecurityChange, ProfileChange, SchemaChange
return AuditFlags;
}
Extending the Site Actions Menu
I've provided navigation support so that users can get to the AuditLogViewer.aspx application page. This is a page designed for users in the role of auditor who want to see what activity has occurred on the site. I decided against putting another link on the Site Settings page because many of the users in the role of auditor may not necessarily be administrators and therefore may never go to the Site Settings page for any other reason. Therefore, adding navigation support using the custom Site Actions menu items seemed more intuitive. It also gives me a chance to show you how to create a flyout menu with programmatic security trimming.
When you want to build a menu item dynamically, you need to create a custom control class and also add a declarative CustomAction element to instantiate an instance of the control class in the correct place. Note how the following CustomAction element differs from the one I showed earlier:
Copy Code
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Add Command to Site Actions Dropdown -->
<CustomAction Id="AuditingSupport"
GroupId="SiteActions"
Location="Microsoft.SharePoint.StandardMenu"
Sequence="1000"
ControlClass="AuditingDemo.SiteActionsCustomSubMenu"
ControlAssembly="AuditingDemo, [4-part name]"
Title="Auditing Support"
Description="Support for configuring and viewing auditing" />
</Elements>
The main difference with this CustomAction element is that it has been declared using a ControlClass attribute and a ControlAssembly attribute instead of a UrlAction element. This makes it possible to generate menu items dynamically using a custom control class such as the SiteActionsCustomSubMenu, which is used in the AuditingDemo project and is shown in the code in Figure 3. Note that a custom control class must inherit from the WebControl class supplied by the ASP.NET programming model.
Figure 3 SiteActionsCustomSubMenu
Copy Code
public class SiteActionsCustomSubMenu : WebControl {
protected override void OnLoad(EventArgs e) {
this.EnsureChildControls();
base.OnLoad(e);
}
protected override void CreateChildControls() {
SPSite siteCollection = SPContext.Current.Site;
SPWeb site = SPContext.Current.Web;
SPUser user = site.CurrentUser;
// provide security trimming
if (IsCurrentUserInGroup("Auditors") ||
IsCurrentUserInGroup("Audit Managers") ||
user.IsSiteAdmin) {
string siteCollectionPath = siteCollection.Url;
if (!siteCollectionPath.EndsWith(@"/"))
siteCollectionPath += "/";
SubMenuTemplate smt = new SubMenuTemplate();
smt.Text = "Auditing Support";
smt.ID = "mnuAuditingSupport";
smt.Description = "Configure and View Auditing";
smt.ImageUrl = siteCollectionPath +
@"_layouts/images/AuditingDemo/AfricanPith32.gif";
smt.Sequence = 400;
MenuItemTemplate mit1 = new MenuItemTemplate();
mit1.ID = "mnuAuditLog";
mit1.Text = "Audit Log";
mit1.Description = "Inspect Audit Entries";
mit1.Sequence = 401;
mit1.ClientOnClickNavigateUrl = siteCollectionPath +
"_layouts/AuditingDemo/AuditLogViewer.aspx";
mit1.ImageUrl = siteCollectionPath +
@"_layouts/images/AuditingDemo/Binoculars32.gif";
// add menu item to Controls collection
smt.Controls.Add(mit1);
// perform extra security trimming for menu for AuditConfig.aspx
if (IsCurrentUserInGroup("Audit Managers") || user.IsSiteAdmin) {
MenuItemTemplate mit2 = new MenuItemTemplate();
mit2.ID = "mnuAuditingConfiguration";
mit2.Text = "Auditing Configuration";
mit2.Description = "Enable/Disable Auditing";
mit2.Sequence = 402;
mit2.ClientOnClickNavigateUrl = siteCollectionPath +
"_layouts/AuditingDemo/AuditConfig.aspx";
mit2.ImageUrl = siteCollectionPath +
@"_layouts/images/AuditingDemo/Compass32.gif";
smt.Controls.Add(mit2);
}
this.Controls.Add(smt);
}
}
private bool IsCurrentUserInGroup(string GroupName) {
SPWeb site = SPContext.Current.Web;
foreach (SPGroup group in site.SiteGroups) {
if (group.Name.Equals(GroupName)) {
return group.ContainsCurrentUser;
}
}
throw new ApplicationException("There is no group named " +
GroupName);
}
}
The CreateChildControls method provides dynamic security trimming. Note that the entire flyout menu is only shown to users who are either site-collection owners or who have been added to the Auditors or Audit Manager groups. The second menu item, which allows a user to navigate to the AuditConfig.aspx page, is further trimmed to exclude users in the Auditors group. This menu is only displayed to site-collection owners or users who have been added to the Audit Manager group.
There is one more important point I want to make about creating a dynamic menu with a custom control class. Like a Web Part, a custom control class requires a SafeControl entry in the web.config file of the hosting Web application. Here, the STSDEV utility provides the support for adding this SafeControl. It adds the appropriate elements into the manifest.xml file, and, as a result, the required SafeControl entry is automatically added to one or more web.config files whenever the AuditingDemo.wsp solution package is deployed within a farm.
Viewing Audit Log Entries
The AuditingDemo solution provides the AuditLogViewer.aspx custom application page shown in Figure 4. This page provides the users with a view of all the audit entries for the entire site collection. There is also a button on this page with the caption Clear Audit Log. This button is security trimmed so it is only shown to site-collection owners. The code behind this button deletes all entries from the audit log, which you might find to be helpful while testing a custom auditing solution.
Figure 4 AuditLogViewer.aspx Displays Audit Log Entries
The code behind AuditLogViewer.aspx uses an SPAuditQuery object to query the audit log of the current site collection. A query is run by calling the GetEntries method on the Audit property of an SPSite object. A call to GetEntries returns an SPAuditEntryCollection object that can be enumerated to inspect each audit entry as an SPAuditEntry object:
Copy Code
SPSite SiteCollection = SPContext.Current.Site;
SPAuditQuery wssQuery = new SPAuditQuery(SiteCollection);
SPAuditEntryCollection auditCol =
SiteCollection.Audit.GetEntries(wssQuery);
foreach (SPAuditEntry entry in auditCol) {
// enumererate through each audit entry
}
There are many ways in which you can enumerate through an SPAuditEntryCollection to create a display. I used a technique where I dynamically create an ADO.NET DataTable object and then populate it with one row of information per audit entry. By creating a DataTable, it becomes relatively simple to display the audit information by binding it to an SPGridView control.
The code behind the AuditLogViewer.aspx page must deal with a special security concern. If you examine the code that queries the audit log, you will see that it first makes a call to RunWithElevatedPrivileges so that it can run under the identity of the privileged SHAREPOINT\System account. Using this technique is required because querying the audit log is a privileged operation.
There will likely be scenarios where a user is added into the role of Auditor, but this user will not be granted the permissions that are necessary in order to query the audit log. Therefore, a call to RunWithElevatedPrivileges allows your code to query the audit log, even if the current user does not have the required permissions to do so.
Viewing Logs on a Per-Item Basis
The third application page in the AuditingDemo project is ItemAudit.aspx. This page shows audit entries for one particular item or document. Navigation to this page is provided by adding a CustomAction element that adds a new EditControlBlock (ECB) menu item to the built-in Item content type:
Copy Code
<CustomAction Id="ItemAuditing.ECBItemMenu"
RegistrationType="ContentType"
RegistrationId="0x01"
ImageUrl="/_layouts/images/GORTL.GIF"
Location="EditControlBlock"
Sequence="300"
Title="View Audit History">
<UrlAction Url=
"~site/_layouts/AuditingDemo/ItemAudit.aspx?ItemId=
{ItemId}&ListId={ListId}"/>
</CustomAction>
A CustomAction configured for the Item content type will also apply to any content types that inherit from Item. And since all content types inherit from the Item content type, this technique will effectively add an ECB menu item to every item and document across the entire site collection.
You can test this technique out by downloading the AuditingDemo sample code and then deploying it within the farm on a SharePoint development machine. Once you have completed activation of the AuditingDemo feature in a particular site collection, you will see that every item and document now has an ECB menu item with a View Audit History title. Whenever a user selects that ECB menu item, he is redirected to the ItemAudit.aspx page, as shown in Figure 5.
Figure 5 ItemAudit.aspx Displays Audit Entries for a Particular Item
Like the AuditLogViewer.aspx page, the code behind ItemAudit.aspx uses an SPAuditQuery object to retrieve audit log data as well as a DataTable that is populated with audit log entry data and then bound to an SPGridView control. The main difference is that the code behind ItemAudit.aspx makes a call to the RestrictToListItem method of the SPAuditQuery object before running the query to filter the results so that they only pertain to the item or document in question.
It's important to note that the SPAuditQuery class also provides two additional filtering methods, RestrictToList and RestrictToUser, so that you can see audit entries for a particular list or a particular user. Also note that you can use the RestrictToUser together with either the RestrictToListItem method or the RestrictToList method to determine what a specific user has been doing with either a list or a list item.
Send your questions and comments for Ted to mmoffice@microsoft.com.
Ted Pattison is an author, trainer, and SharePoint MVP who lives in Tampa, Florida, and has just completed his book Inside Windows SharePoint Services 3.0 for Microsoft Press. He also delivers advanced SharePoint training to professional developers through his company, Ted Pattison Group (www.TedPattison.net).
Posted by Rami Reddy Annapu Reddy at 4:41 AM 0 comments