Pages

April 7, 2014

Quick & Useful Site Permissions Report

Objective:
In SharePoint 2010/2013, when Site Admin wants to view all site contents of a particular site, then he/she would see all site contents (securable objects) with permission details, to provide quick and easy permission reporting.

Here is how it looks in SharePoint 2010:


When clicking the 'unique lists' link:


And in SharePoint 2013:


Similar Work:
I found a similar idea here. Its nice but it only shows a true/false value for permissions fields, and it missed the '!' (not) sign and will display opposite permissions results! So I decided to build another much more detailed version.


How To:
By adding custom code to the OTB View All Items (_layouts/viewlsts.aspx) page:
- Added a permissions field to show type of site perms in different colors
- The field also displays info about unique items/lists/webs (if available) inside each item
- Unique items and lists are clickable to show the contents in popup window


Steps:
1. From layouts folder, backup the original Viewlsts.aspx page, then copy and rename it for example: ViewlstsEx.aspx

2. Add couple namespace references to the page:
<%@ Import Namespace="System.Collections.ObjectModel" %>
<%@ Import Namespace="System.Collections.Generic" %>


3. Add new column in SharePoint:UIVersionedContent UIVersion="4" section
<th scope="col" class="ms-vh2-nofilter" style="white-space:nowrap; width:25%;"><SharePoint:EncodedLiteral runat="server" text="Permissions" EncodeMethod='HtmlEncode'/>
</th>


4. Add column content in the list's section (table cells after code if (!bShowSites)):
<td class="ms-vb2" width="25%" >
<%
string ListPermOutput = "";
if (!spList.HasUniqueRoleAssignments) 
{
ListPermOutput = "<span style='color: green'>Inherited</span>";
}
else 
{
ListPermOutput = "<span style='color: red'>Unique</span>";
}
  if (spList.GetItemsWithUniquePermissions().Count > 0)
{
string str1 = spList.ParentWeb.Url + "/_layouts/uniqperm.aspx?obj=" + spList.ID.ToString("B").ToUpper() + ",List&List=" + spList.ID.ToString("B").ToUpper();
string str2 = SPHttpUtility.EcmaScriptStringLiteralEncode(SPHttpUtility.UrlPathEncode(str1, false));
ListPermOutput += " with " + "<a onclick='" + "javascript:ShowPopupDialog(\"" + str2 + "\");' href='javascript:'>unique items</a>";
}
SPHttpUtility.NoEncode(ListPermOutput, Response.Output);
%>
</td>


5. Add column content in the web's section (table cells after code foreach (SPWeb webToDisplay in webs)):
<td class="ms-vb2" width="25%" >
<%
StringBuilder WebPermOutput = new StringBuilder();
if (!webToDisplay.HasUniqueRoleAssignments) 
{
WebPermOutput.Append("<span style='color: green'>Inherited</span>");
}
else 
{
WebPermOutput.Append("<span style='color: red'>Unique</span>");
}

Collection<SPWebListInfo> UniqueObjects = webToDisplay.GetWebsAndListsWithUniquePermissions();
if (UniqueObjects.Count > 0)
{
List<string> UniqueSubWebs = new List<string>();
int UniqueSubWebsCount = 0;
int UniqueSubListsCount = 0;
foreach (SPWebListInfo item in UniqueObjects)
{
if (item.Type == Microsoft.SharePoint.SPObjectType.Web && item.HasUniqueRoleAssignments)
{
UniqueSubWebs.Add(item.WebId.ToString());
UniqueSubWebsCount++;
}
else if (item.Type == Microsoft.SharePoint.SPObjectType.List  && item.HasUniqueRoleAssignments)
{
UniqueSubListsCount++;
}
}

UniqueSubWebsCount--;
string SubWebsResult = "";
if (UniqueSubWebsCount > 0)
{
 SubWebsResult = " unique webs" ;
}

string SubListsResult = "";
if (UniqueSubListsCount > 0)
{
string str1 = webToDisplay.Url + "/_layouts/uniqperm.aspx";
string str2 = SPHttpUtility.EcmaScriptStringLiteralEncode(SPHttpUtility.UrlPathEncode(str1, false));
SubListsResult += "<a onclick='" + "javascript:ShowPopupDialog(\"" + str2 + "\");' href='javascript:'>unique items</a>";
}

if (SubListsResult != "" && SubWebsResult == "")
{
WebPermOutput.Append(" with "); WebPermOutput.Append(SubListsResult);
}

else if (SubListsResult == "" && SubWebsResult != "")
{
WebPermOutput.Append(" with "); WebPermOutput.Append(SubWebsResult);
}

else if (SubListsResult != "" && SubWebsResult != "")
{
WebPermOutput.Append(" with "); WebPermOutput.Append(SubListsResult);
WebPermOutput.Append(" and "); WebPermOutput.Append(SubWebsResult);
}
}

SPHttpUtility.NoEncode(WebPermOutput, Response.Output);
%>
</td>


The page is tested for SharePoint 2010, and SharePoint 2013.
Click here to download an example page viewlstsEx.aspx.

Have a nice SharePoint Administration!

Ibraheem


February 18, 2014

Comparing Ways to Counting Items in SPList


This is a quick comparison between several ways to count the number of items in SPList:

  • SPList.Items
  • SPList.GetItems(SPQuery).Count
  • SPList.ItemCount

First, keep in mind that SPList.Items.Count is a special case of the SPList.GetItems(SPQuery).Count.
Here is how it constructed behind the scenes inside Microsoft.SharePoint.dll:

public SPListItemCollection Items
        {
            [ClientCallableExceptionConstraint(FixedId="c", ErrorType=typeof(SPQueryThrottledException), Condition="There is a throttle failure.", ErrorCode=-2147024860)]
            get
            {
                SPQuery query = new SPQuery {
                    ViewAttributes = "Scope=\"Recursive\""
                };
                return this.GetItems(query);
            }

        }


So here is technical & usage comparison:




SPList.Items.Count


SPList.GetItems(SPQuery).Count

SPList.ItemCount
In doc library
Includes all Files in all Folders
Except: folders and checked-out files

Depending on query scope. Can include all folders + all Files in all Folders
Except: checked-out files
Includes all files, folders and subfolders + Checked out files
In list

All list items
All list items
All list items
Based on View

Yes (Default All Items view)
Yes (Default All Items view)
No
User Security Trimming 

Yes
Yes
No
Retrieval Mechanism

Based on list query
Based on list query

Based on List Metadata properties
Depth Scopes



Recursive: Includes all Files in all Folders but not Folders themselves


4 supported scopes:
·     Default: Includes all Files and Folders in current Folder
·     Recursive: Includes all Files in all Folders but not Folders themselves
·     RecursiveAll: Includes all folders and all Files in all Folders
·     FilesOnly: Includes all Files in all Folders in current Folder

N/A
Resource Throttling

Yes
Yes. Can be overridden
No
Accuracy

Not Guaranteed
Guaranteed
Not Guaranteed
Performance

Low
Good
Very High
When to use



Not recommended
In planning & displaying items of small to medium list/library
In planning; To asses size of very large list/library


I hope you find it useful & handy, cheers!

January 30, 2014

Content Slider Web Part For SharePoint 2010


A friend of mine found this cool content slider web part for SharePoint 2007 & 2010 (Original posts can be found here & here). The developer did a nice job illustrating the coding and list details, but I decided to write a more straight forward procedure to setup and customize, so here we go..

Required Files:

Optional File:

Steps:
  1. Download the files above: Contentslider.css, contentslider.js and the list template
  2. To adjust animation time: change the number from ‘50’ to ‘150’ in the following two lines inside contentslider.js file:
    • setting.autorotate[1]=setting.autorotate[1]+(1/setting.enablefade[1]*50) //add time to run fade animation (roughly) to delay between rotation
    • window["fcsfade"+setting.id]=setTimeout(function(){featuredcontentslider.fadeup(setting)}, 50)
  3. To adjust the UI edit the contentslider.css classes:
    • To set a fixed picture size: change the “img” class of the “contentdiv” element:
      .contentdiv img {box-shadow: 2px 2px 2px #ccc; -web-kit-box-shadow: 2px 2px 2px #ccc; -moz-box-shadow: 2px 2px 2px #ccc; margin-right:10px; width: 200px; height: 150px;}
    • To set a fixed slider box size:
      .sliderContainer {position:relative; width:700px; height: 290px;}
  4. Navigate to “SiteAssets” folder of your site or create a doc library, for example “SiteAssets1”
  5. Upload the files “contentslider.css” and “contentslider.js” into the SiteAssets library or folder
  6. The List Template is based on announcements list, but you can create your own list. Rename the extension of List Template into “.stp” and upload it into List Templates gallery
  7. Create a list based on this template, for example: “Test Content Slider List”, and insert items into the list
  8. Create a page and edit it using SharePoint Designer 2010
  9. Insert the Data View Web Part -> select Empty Data View from drop down
  10. Click on “click here to select a data source” -> then select any fields from right panel, such as Title, body and image
  11. Switch to code view, and find the first <xsl> tag which is after the closing </DatasSources> tag
  12. Right click on the <xsl> tag and choose “Select Tag”. Press delete
  13. Copy the code from 2nd source link (for SP2010) and paste into the page where you deleted the code
  14. Save the page and that’s it, now you can enjoy the slider!



December 18, 2013

Expand SPTreeView Node Automatically When Selected


Before:
After:


The standard behavior of SharePoint tree view control (SPTreeView) is that when you select a folder node it will not expand its children nodes. You must click the expand icon [+] in order to show the sub folders. Also, that is not exclusive, so when you click the expand [+] icons of another node folder, the tree will show it's children without collapsing the previous node folder.


The objective:

I wanted to customize the tree view navigation to combine the functionality of clicking the node title and clicking the expand icon so that user can expand nodes with less hassle. The user should only click a node folder title and the tree will automatically expand to show child nodes while other nodes will collapse.


How:

I'll use JQuery. The idea is when a node title is clicked then a post back occurs and there will be a RootFolder parameter in page Url indicating selected folder node, so we will call the javascript function associated with its exapnd [+] icon.

This is the markup for the SPTreeView in the page:

<SharePoint:SPHierarchyDataSourceControl id="doclibDataSource" runat="server" RootListId="a65a6e37-de92-496a-b896-5b976dff7b61"
RootWebId="277d98b7-be33-451f-9ed8-6691a5a72736" ShowFolderChildren="true" EnableViewState="false">

</SharePoint:SPHierarchyDataSourceControl>
<SharePoint:SPRememberScroll runat="server" id="TreeViewRememberScrollV4" onscroll="javascript:_spRecordScrollPositions(this);" style="overflow: auto">
<SharePoint:SPTreeView ID="doclibtreeview" runat="server" DataSourceID="doclibDataSource" EnableViewState="false" ExpandDepth="1" SelectedNodeStyle-CssClass="SelectedItemStyle">
</SharePoint:SPTreeView>
</SharePoint:SPRememberScroll>


Inject the following script in the master page or in CEWP. I added the SelectedItemStyle CSS style to make selected nodes more visible.


<style>
.SelectedItemStyle {
BORDER-TOP: 1px dotted; BORDER-RIGHT: 1px dotted; BORDER-BOTTOM: 1px dotted; BORDER-LEFT: 1px dotted; BACKGROUND-COLOR: #dddddd
}</style>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<script>

_spBodyOnLoadFunctionNames.push("customIBRTreeNav");

function customIBRTreeNav(){
var SelectedFolder = unescape(getUrlParameter('RootFolder'));
var ItemTitle = SelectedFolder.substring(SelectedFolder.lastIndexOf("/") + 1);
var LinkItem = jQuery('a[title="' + ItemTitle + '"]:not(:has(img))');
var IconItem = LinkItem.closest('td').prev('td').prev('td').find('a');// [+] & [-] icon
var IconItemHref = IconItem.attr("href");
var CustomTreeViewPopulate = new Function(IconItemHref); // create the function from string
CustomTreeViewPopulate.call(this);
}

function getUrlParameter(name)
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}</script>


Note: remember to set the ExpandDepth parameter to value of '1' in the SPTreeView control. 
This is necessary to get the intended functionality. I didn't have time to implement the logic when ExpandDepth is 2 or more.. maybe later!


December 6, 2013

Issue: Starting a service crashes SharePoint site with unknown error


Issue:

After you stop a service (such as User Profile Synchronization or Search service) and try to start it again in Central Administration, the SharePoint 2010 site pages will crash with an unknown error.



Analysis:

Prior to the issue, the password was changed for the services account in SharePoint. However all services were working.

Searching the cascading numerous errors in ULS to find the root cause kept my head spinning for hours. I knew that issue was because of the password change in services account but couldn't spot where is the start point to fix.
Some of the error messages I found in IIS log:
- "Application pool 15f65d1adf954fa68aee81f7bcb9cec8 has been disabled. Windows Process Activation Service (WAS) encountered a failure when it started a worker process to serve the application pool."
- Another message is that the account may need batch logon permissions.

 then I had a clear image of what happened:

  1. The service (User Profile Synchronization service or Search service) tries to start
  2. Service fails to start because its application pool in IIS could not login 
  3. Since its application pool is down, it will bring down all the services that are running on it. If User Profile service is working on this application pool then it will stop. Also if Metadata service is working on this application pool then it will stop, and bring down the Profile service as well since User Profile depends on Metadata.
  4. All content on SharePoint pages (such as pages, web parts,..) requires a valid user login to display according to user permissions. Since User Profile service is down, current user cannot be verified and SharePoint shows unknown error.

I already made sure that that the service account has the necessary permissions and that services and service applications are setup correctly on CA. Also the application pool account is using the correct password.
Well, it didn't work! Every time I try to start the application pool in IIS it stops after couple of seconds. Agrrrrrr...

It appeared that the faulty application pool "15f65d1adf954fa68aee81f7bcb9cec8" in IIS becomes corrupted object and will not work.



Solution:

Relocate applications that are on corrupted application pool into another one with exact same settings.
First, make a backup for WFE and APP servers.
Depending on your current configurations, make the following changes in IIS in WFE and APP servers:

  1.      Create new AppPool “MetadataCustomAppPool” as copy of the corrupt AppPool but using new password  for service account
  2.      Move “metadata” and “Profile” applications from corrupt AppPool to   “MetadataCustomAppPool” AppPool.  This is useful when other service is down (and     subsequently its AppPool is down too), it will not affect the Metadata/User Profile and the   SharePoint site will continue to work.
  3.     If other service applications are in corrupt AppPool, then you can relocate them into MetaDataCustomAppPool or create a new AppPool for them as appropriate.
  4.      Restart IIS



November 14, 2013

How to make SPTreeView work on a custom page with 2 or more list views


The issue

I was reading the other day an article here on how to add an SPTreeView control to a list view page using SharePoint Designer 2010. This works fine on a library view page (for example: AllItems.aspx).
However, if you add another list view web part into the same list view page, or if you want to use the SPTreeView in a custom page then an issue appears. When user clicks on folder node the SPTreeView control will NOT stay in the same page but will redirect into the library root page (AllItems.apsx).



Analysis

I found very little info about this in the Internet, so decided to dig this up. I'll show here the details with an example. If you are interested keep reading, or you can go directly to solution section below.

The example setup:
  • Suppose we want a custom page to show 2 list views of the same library using different filters, and we want a tree view navigation to work for both of them whenever we click a folder.
  • I created a document library named 'Test Library Doc' and put some files and folders.


  • Created 2 list views for 'Test Doc Lib':
  1. Local view: Content Type is not Folder, and the Scope field is equal to Global
  2. Global view: Content Type is not Folder, and the Scope field is equal to Local
  • Added a custom web part page 'TestTreeView.aspx', added an SPTreeView control with its SPHierarchyDataScourceControl to the page using SPD2010. Make sure the RootListID and RootWebId are relevant.



Tests: (both on custom page TestTreeView.aspx)

Case 1:
Using SPTreeView with only 1 list view web part. Upon user click, each folder node will redirect to same page TestTreeView.aspx with corresponding folder level. Here is how 'Folder A' node is rendered:

<a title="Folder A" class="ctl00_PlaceHolderMain_doclibtreeview_0" id="ctl00_PlaceHolderMain_doclibtreeviewt1" href="javascript:_spNavigateHierarchy(this,'doclibDataSource','30:FolderNode:ef072b87-28a5-4db4-b82f-097c7ba329d3','\u002fTest Library Doc\u002fFolder A',true,'FolderNode')">

Case 2:
Using SPTreeView with 2 or more list view web parts. Upon user click, each folder node will redirect to the default page of library (example: AllItems.aspx) instead of our TestTreeView.aspx. Here is how 'Folder A'node is rendered:

<a title="Folder A" class="ctl00_PlaceHolderMain_doclibtreeview_0" id="ctl00_PlaceHolderMain_doclibtreeviewt1" href="javascript:_spNavigateHierarchy(this,'doclibDataSource','30:FolderNode:ef072b87-28a5-4db4-b82f-097c7ba329d3','\u002fTest Library Doc\u002fFolder A',false,'FolderNode')">



I tried to override _spNavigateHierarchy() method & inserted our custom page TestTreeView.aspx as the url parameter to force the redirect..but it didn't work. This is due to how the tree view nodes internally works as you will see below.


Digging on the front-end:


One of the arguments (highlighted above in test cases 1 & 2) caught my attention, it's called 'listInContext'. The _spNavigateHierarchy() definition is inserted by the SPHierarchyDataSourceControl in its onLoad event:

<script type=\"text/javascript\"> \r\n//<![CDATA[\r\nfunction _spNavigateHierarchy(nodeDiv, dataSourceId, dataPath, url, listInContext, type)  {\r\n   CoreInvoke('ProcessDefaultNavigateHierarchy', nodeDiv, dataSourceId, dataPath, url, listInContext, type, document.forms." + this.Page.Form.ClientID + ", \"" + SPEncode.ScriptEncode(this.ConstructSamePageQueryString()) + "\", \"" + SPEncode.ScriptEncode(str) + "\");\r\n\r\n}\r\n//]]>\r\n </script>

When we click a folder node,  the following call sequence starts:
_spNavigateHierarchy() -> CoreInvoke() -> ProcessDefaultNavigateHierarchy() in Core.js file

function ProcessDefaultNavigateHierarchy(g,e,d,b,c,j,i,h,f){
    ULSrLq:;
    if(typeof _spCustomNavigateHierarchy=="function")_spCustomNavigateHierarchy(g,e,d,b,c,j);
    else if(c==false)top.location=b;
    else{
        var a=document.createElement("INPUT");
        a.type="hidden";
        a.name="_spTreeNodeClicked";
        a.value=d;
        i.appendChild(a);
        var k="?RootFolder="+escapeProperly(b)+h+"&"+g_AdditionalNavigateHierarchyQString;
        _SubmitFormPost(f+k);
        return false
    }


Inside ProcessDefaultNavigateHierarchy(), the logic is as this:
If the 'islistInContent' flag is false then set the page location to the root page of current library and eventually redirect to the library root page (i.e AllItems.aspx). Otherwise, if the islistInContent flag is true, then eventually redirect to our custom page (TestTreeView.aspx).

Now we need to find out why listInContext parameter was set to false in test case 1.


Digging more on the back-end:


Let's have a look on how the SPTreeView is being internally rendered inside Microsoft.SharePoint.dll
In Microsoft.SharePoint.Navigation.SPHierarchyNode.cs I found NavigateUrl() method representing a tree node click. The highlighted portion is the important part.

public string NavigateUrl

        {

            {
                string str = "javascript:_spNavigateHierarchy(this,";
                return (((((str + "'" + this.DataSourceControlName + "','") + this.Path + "','") +
SPHttpUtility.EcmaScriptStringLiteralEncode("/" + this.m_url)) + "'," + ((this.m_ds.IsSingleListViewPage &&
this.IsContextListNode) ? "true" : "false")) + ",'" + this.m_type + "')");
            }
        }



According to internal code, 'IsSingleListViewPage' member returns True when there is only 1 web part (either data view or xslt list view) in current page. 'IsContextListNode' property returns True only within a list context and there is a valid list ID.
So, as long as we use 2 list view web parts or use a custom page, then listInContext parameter in NavigateUrl() will always get the value of 'False'.



Solution:

I Wrote a JQuery script to force change the listInContext flag value from 'False' to 'True' for every rendered tree view folder node, then insert this script into a CEWP in our custom TestTreeView.aspx page:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<script>

_spBodyOnLoadFunctionNames.push("IbrTreeView");

function IbrTreeView() {

$("a[href*='javascript:_spNavigateHierarchy']")
.each(function()
{
this.href = this.href.replace("false", "true");
});
}

</script>


And it works!
When user clicks 'Folder A' folder node, the 2 view web parts will show underlying filtered contents on the same TestTreeView.aspx page. No more redirecting to AllItems.aspx.




Note:
Remember your page should only contain list view web parts of the same library. Otherwise user will witness inconsistencies/errors when clicking on tree nodes because SPTreeView may redirect to a folder that exist in library of 1st web part but does not exist in library of the 2nd web part.


I hope people find this useful :)

Ibraheem

November 6, 2013

Styling The Folders In Doc Lib Using JQuery and CSS


Objective:

In your SharePoint site, sometimes you need to stylize how the folders look like so that they no longer reflect the typical folder hierarchy layout. You can use a new image, color and font style in a specific document library view or in a web part page 


For example:
  • We want to replace folder icon with a custom image
  • We want the title font to be bold and with larger size


How to:


First let's prepare url to the new folder icon file. I uploaded "fact.gif" into Images library:
        http://{YourServerName}/publishingImages/fact.gif



Since we don't want to affect the entire SharePoint farm, we will use JQuery script to target specific page. You can use the script by registering the script file using ScriptLink control, or by putting the follwoing script inside a Content Editor Web part (CEWP).



1. To change the folder type icon only:


<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<script>
_spBodyOnLoadFunctionNames.push("changeFolderUI");

function changeFolderUI() {
jQuery("a[href*='?RootFolder=']").find("img").attr("src", "http://{YourServerName}/publishingImages/fact.gif");
}

</script>





2. To change the folder name and add icon to it:

(This can be useful when hiding the Type field, allowing only folders to have icons in Name field)

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script><script>

_spBodyOnLoadFunctionNames.push("changeFolderUI");

function changeFolderUI() {
jQuery("a[href*='?RootFolder=']:not(:has(img))")
.css("font-weight", "bold")
.css("font-size", "16px")
.before("<img alt='arrow' src='{YourServerName}/publishingImages/fact.gif'/>");

}</script>




You can also step forward and apply multiple custom styling into folders depending on a folder name.
Have fun!