Problem
There is an issue with OOTB SharePoint logic.
Scenario
- 2 types of Users
- User1 is Site Administrator
- User2 is a simple user with Read, Limited Access.
- Issue comes under blog site.
How to reproduce? Follow the below given test case.
- Create a site collection. In my case I have created it on "http://pc92".
- I create a blog site with name "TestBlog" using User1. In my case it is "http://pc92/TestBlog/".
- Navigate to default blog created on site and add 11 comments. Right now the URL will be "http://pc92/TestBlog/Lists/Posts/Post.aspx?ID=1". This will enable the paging at the comments webpart.
- Now login with User2(Read, Limited Access User) and navigate to the URL "http://pc92/TestBlog/Lists/Posts/Post.aspx?ID=1". The page will show normal. The only difference here is you won't see the "Add Comment" box at the bottom of the screen.
- Click the next button. You will see the error.
Cause
The cause of the whole act is when you login as a User2(Read, Limited Access) the Comment Box(ListFormWebPart) is not available for this users. On page load everything is fine. But when you do a postback like paging event it bombs out. This might have got out of the site of MicroSoft Team.
Solution
I created 2 features, one is a site based feature(BlogFixSite) and another one web based feature(BlogFixWeb). On the BlogFixSite activation we check all the web which doesnot have BlogFixWeb activated and the template is a Blog template. So if the feature is activated I assume that the site is already having a fix. In the BlogFixWeb I go to the "Posts" folder and copy "Post.aspx" content and create "Post1.aspx". In the "Post1.aspx" i will remove the "ListFormWebPart" programatically. In "Post.aspx" at the bottom i will place a jquery which will check if the "Add Comment" box is missing than take the user to "Post1.aspx". Repeat the above given test cases and you should be all good.
BlogFixSite - C#
#region System
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace BlogFix.Features.BlogFixSite
{
[Guid("63c859cc-77ff-436e-a79e-b4619a67c7c3")]
public class BlogFixSiteEventReceiver : SPFeatureReceiver
{
#region Events
/// <summary>
/// Activating
/// </summary>
/// <param name="properties"></param>
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
base.FeatureActivated(properties);
try
{
//Site object
SPSite _site = (SPSite)properties.Feature.Parent;
if (_site != null)
{
//Feature ID - BlogFixWeb
Guid _featureWeGuid = new Guid("c7178a85-ed45-4f27-bd4a-db2ec3eed95b");
//Web List
IList<SPWeb> _webList = (from _web in _site.AllWebs.Cast<SPWeb>()
where string.Compare(_web.WebTemplate, "Blog", true) == 0
&& _web.Features[_featureWeGuid] == null
select _web).ToList();
//Change the post page
foreach (SPWeb _web in _webList)
{
//Enables the feature - BlogFixWeb
if (_web.Features[_featureWeGuid] == null)
{
_web.Features.Add(_featureWeGuid);
}
}
}
}
catch (Exception Exc)
{
SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("BlogFixSiteEventReceiver - Activated", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, Exc.Message, Exc.StackTrace);
}
}
#endregion
}
}
BlogFixWeb - C#
#region System
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace BlogFix.Features.BlogFixWeb
{
[Guid("564b0ad9-90a0-45fc-a672-901ec1fd111b")]
public class BlogFixWebEventReceiver : SPFeatureReceiver
{
#region Events
/// <summary>
/// Activate the web based feautre
/// </summary>
/// <param name="properties"></param>
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
base.FeatureActivated(properties);
try
{
SPWeb _web = (SPWeb)properties.Feature.Parent;
//All the subfolders
SPFolderCollection _folderCollection = _web.Folders["Lists"].SubFolders;
//Finds the folder containing Post.aspx
IList<SPFolder> _folderPostList = (from _folderX in _folderCollection.Cast<SPFolder>()
let _fName = _folderX.Name
let _fCount = (from _fileX in _folderX.Files.Cast<SPFile>()
where string.Compare(_fileX.Name, "post.aspx", true) == 0
select _fileX).Count()
where _fCount > 0
select _folderX).ToList();
//Loop it if there are more than one
if (_folderPostList != null)
{
foreach (SPFolder _folderPost in _folderPostList)
{
//Get the Post file
SPFile _filePost = (from _fileX in _folderPost.Files.Cast<SPFile>()
where string.Compare(_fileX.Name, "post.aspx", true) == 0
select _fileX).FirstOrDefault();
//The file post to be copied
if (_filePost != null)
{
//New Post1.aspx URL
string _newPostUrl = string.Format("{0}/{1}/{2}",
_web.Url,
_folderPost.Url,
"Post1.aspx");
//Copy the file
_filePost.CopyTo(_newPostUrl, true);
//Update the folder
_folderPost.Update();
//Remove the control once the page is copied
Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager _webPartCollection = _web.GetLimitedWebPartManager(_newPostUrl, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
//Retrive the webpart and remove
Microsoft.SharePoint.WebPartPages.WebPart _listFormWebPart = (from _wp in _webPartCollection.WebParts.Cast<Microsoft.SharePoint.WebPartPages.WebPart>()
where _wp.GetType().UnderlyingSystemType.Name == "ListFormWebPart"
select _wp).FirstOrDefault();
//Microsoft.SharePoint.WebPartPages.ListFormWebPart _XlistFormWebPart = (Microsoft.SharePoint.WebPartPages.ListFormWebPart)_listFormWebPart;
//string _webPartTitle = _XlistFormWebPart.Title
//return;
if (_listFormWebPart != null)
{
//Remove the webpart
_webPartCollection.DeleteWebPart(_listFormWebPart);
//Update
_web.Update();
}
//Modify Post1.aspx and add the javascript tag
if (_filePost.RequiresCheckout)
{
_filePost.CheckOut();
}
//Edit the file
byte[] _htmlByte = _filePost.OpenBinary();
string _html = System.Text.Encoding.ASCII.GetString(_htmlByte);
string _topHtml = _html.Substring(0, _html.LastIndexOf("</asp:Content>"));
string _stript = "\n<script language=\"javascript\" type=\"text/javascript\">!window.jQuery && document.write('<script src=\"http://code.jquery.com/jquery-1.4.2.min.js\"><\\/script>');</script>\n<script language='javascript'>\n\r$(document).ready(function(){\n\r\rif($(\"h3[class='ms-CommentHeader']\").length == 1){\n\r\r\r\rwindow.location.href = window.location.href.replace(\".aspx?\" , \"1.aspx?\");\n\r\r}\n\r});</script>";
_html = string.Format("{0}{1}</asp:Content>", _topHtml, _stript);
_filePost.SaveBinary(System.Text.Encoding.ASCII.GetBytes(_html));
//Check In
if (_filePost.RequiresCheckout)
{
_filePost.CheckIn(string.Empty);
}
}
}
}
}
catch (Exception Exc)
{
SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("BlogFixWebEventReceiver - Activated", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, Exc.Message, Exc.StackTrace);
}
}
#endregion
}
}
JavaScript added on Post.aspx
There are 2 "<h3></h3>" tags on the post.aspx page. (1) is "Comments" and (2) is "Add Comments". So if a user dont have access he/she wont see "Add Comment" box. If that box doesnot exists on the page I simply send the user to Post1.aspx.
<script> !window.jQuery && document.write('<script src="http://code.jquery.com/jquery-1.4.2.min.js"><\/script>');</script>
<script language='javascript'>
$(document).ready(function () {
if ($("h3[class='ms-CommentHeader']").length == 1) {
window.location.href = window.location.href.replace(".aspx?", "1.aspx?");
}
});</script>
Final Comments
To make this feature available for all the webs just create a feature stapler which activates the web based feature just like its done in "BlogFixSite".
I know many people wont agree with my solution but right now it is what it is. Happy Coding. Do let me know if this helped you.