一提到sharepoint 我相信權限控制一定會是一個很重要的話題,尤其是對列表的權限控制,創建、修改、查看權限。其實網上這方面的資料已經很多了,尤其是以下2篇文章,后來索性把CodeArt_PermissionEx的源代碼下載下來,然后仔細閱讀了以下。
利用開源SharePoint Permission Extension插件對SharePoint 的列表進行權限控制!
要想完全明白源碼設計的原理 建議大家先閱讀再議WSS RenderingTemplate 對sharepoint模板覆蓋有一個簡單的了解。我把CodeArt_PermissionEx仔細閱讀了一下並且修改了里面的一些代碼(個人覺得原作者C#基本功一般,sharepoint方面還是不錯,我這里修改的是VS2013版的源碼)。修改后部署到sharepoint2013上如圖:
內容內型權限設計如圖:
這里的PersonType是一個自定義內容內型,每一個內型這里有一個人員和組的權限控制,主要控制user是否可以新建記錄,比如user不在PersonType內容內型人員和組里面,那么這個user將不能創建記錄,沒有權限的user進入后看到的結果如圖:
看到這里的新建項目變為不可用了。
這里說說查找權限的方式,首先我們需要的我們的設置保存起來,
1)根據我們的類容類型來找權限設置,如果沒有任何權限設置那么我們就直接返回true;
2)如果當前用戶是管理員用戶直接返回true;
3)在指定的人員里面查找當前user是否存在,存在返回true;
4)指定的組里面是否包含當前user,存在返回true;否則返回false
相關代碼如下:

public bool CheckRight(SPUser user, string contentTypeName) { ContentTypeCreateSetting set = this.GetContentTypeCreateSetting(contentTypeName); if (set == null) return true; return set.CanCreate(user); } public bool CanCreate(SPUser currentUser) { if (currentUser != null && currentUser.IsSiteAdmin) return true; if (String.IsNullOrEmpty(this.SpecialAccounts) && String.IsNullOrEmpty(this.SpecialGroups)) return true; if (currentUser == null) { return false; } bool inAccounts = this.IsInSpecialAccounts(currentUser.LoginName); if (inAccounts) return true; return this.IsInSpecialGroups(currentUser); } }
主要原理是新建列表數據默認用到的是NewMenu類,這里我們開發一個帶有權限控制的子類NewMenuWithPermission,主要代碼如下:

public class NewMenuWithPermission : NewMenu { private ListContentTypesCreateSetting _setting; bool _settingExist = true ; bool UserHaveRight(string cName) { if (!_settingExist) return true; if (_setting == null) { _setting = ListContentTypesCreateSetting.GeSetting(base.List); _settingExist = _setting != null; } if (_setting == null) return true ; return _setting.CheckRight(SPContext.Current.Web.CurrentUser, cName); //return true; } public override MenuItemTemplate AddMenuItem(string id, string displayName, string imageUrl, string description, string navigateUrl, string onClickScript) { MenuItemTemplate m = base.AddMenuItem(id, displayName, imageUrl, description, navigateUrl, onClickScript); m.Visible = this.UserHaveRight(displayName); return m; } }
如何讓sharepoint使用我們這個NewMenuWithPermission類,這里我們需要覆蓋sharepoint的DocumentLibraryViewToolBar 和 ViewToolBar模板,相關代碼如下:

<SharePoint:RenderingTemplate ID="DocumentLibraryViewToolBar" runat="server"> <Template> <wssuc:ToolBar CssClass="ms-menutoolbar" EnableViewState="false" id="toolBarTbl" ButtonSeparator="<img src='/_layouts/15/images/blank.gif' alt=''>" RightButtonSeparator=" " runat="server"> <Template_Buttons> <codeart:NewMenuWithPermission runat="server" AccessKey="<%$Resources:wss,tb_NewMenu_AK%>"/> <%--<SharePoint:NewMenu ID="NewMenu1" Visible="false" AccessKey="<%$Resources:wss,tb_NewMenu_AK%>" runat="server"/>--%> <SharePoint:UploadMenu ID="UploadMenu1" AccessKey="<%$Resources:wss,tb_UploadMenu_AK%>" runat="server"/> <SharePoint:ActionsMenu ID="ActionsMenu1" AccessKey="<%$Resources:wss,tb_ActionsMenu_AK%>" runat="server"/> <SharePoint:SettingsMenu ID="SettingsMenu1" AccessKey="<%$Resources:wss,tb_SettingsMenu_AK%>" runat="server"/> </Template_Buttons> <Template_RightButtons> <SharePoint:PagingButton ID="PagingButton1" runat="server"/> <SharePoint:ListViewSelector ID="ListViewSelector1" runat="server"/> </Template_RightButtons> </wssuc:ToolBar> </Template> </SharePoint:RenderingTemplate> <SharePoint:RenderingTemplate ID="ViewToolBar" runat="server"> <Template> <wssuc:ToolBar CssClass="ms-menutoolbar" EnableViewState="false" id="toolBarTbl" ButtonSeparator="<img src='/_layouts/15/images/blank.gif' alt=''>" RightButtonSeparator=" " runat="server"> <Template_Buttons> <codeart:NewMenuWithPermission runat="server" AccessKey="<%$Resources:wss,tb_NewMenu_AK%>"/> <%--<SharePoint:NewMenu AccessKey="<%$Resources:wss,tb_NewMenu_AK%>" runat="server" />--%> <SharePoint:ActionsMenu AccessKey="<%$Resources:wss,tb_ActionsMenu_AK%>" runat="server" /> <SharePoint:SettingsMenu AccessKey="<%$Resources:wss,tb_SettingsMenu_AK%>" runat="server" /> </Template_Buttons> <Template_RightButtons> <SharePoint:PagingButton runat="server"/> <SharePoint:ListViewSelector runat="server"/> </Template_RightButtons> </wssuc:ToolBar> </Template> </SharePoint:RenderingTemplate>
現在我們來查看view的權限設計
沒有權限的人訪問時運行結果如下:
這里的view權限查找方式和控制方式與上面內容內型權限控制都是一樣的,下面提到的字段權限設計也是一樣的原理。只是這里我們需要開發一個PermissionListViewSelector類來擴展默認的ViewSelectorMenu類,實現代碼如下:

public class PermissionListViewSelector : ViewSelectorMenu // Microsoft.SharePoint.WebControls.ListViewSelector { protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); //if (!this.Context.User.Identity.IsAuthenticated)//for anonymous access //{ // return; //} var currentUser = base.Web.CurrentUser; if (currentUser != null && currentUser.IsSiteAdmin) { return; } SPList curList = SPContext.Current.List; ListViewPermissionSetting listSetting = ListViewPermissionSetting.GetListSetting(curList); if (listSetting == null) { return; } if (SPContext.Current.ViewContext == null || SPContext.Current.ViewContext.View == null) return; SPView currentView = SPContext.Current.ViewContext.View; ViewPermission viewSetting = listSetting.GetByViewID(currentView.ID); if (viewSetting == null) return; if (( viewSetting!= null) && (!(viewSetting.CanDisplay(currentUser)))) { //如果用戶沒有權限訪問當前視圖,那么需要計算其有權限訪問的試圖 if (currentView.DefaultView) //如果訪問默認視圖而沒有權限時,自動轉向一個有權限的試圖 { Guid id = listSetting.GetCanDisplayView(currentUser); if (id == Guid.Empty) { SPUtility.TransferToErrorPage(Util.GetResource("Msg_NoViewRight")); return; } else { SPView view = curList.Views[id]; SPUtility.Redirect( base.Web.Url +"/"+ view.Url, SPRedirectFlags.Default, this.Context); return; } } SPUtility.TransferToErrorPage(Util.GetResource("Msg_NoViewRight")); } //NND, 在2010的07UI模式下這樣設置不起作用。 foreach (Control item in base.MenuTemplateControl.Controls) { if ((item is MenuItemTemplate)) { MenuItemTemplate menuItem = (MenuItemTemplate)item; if (menuItem.PermissionsString != "ViewListItems") //修改視圖和創建視圖菜單 { menuItem.Visible = false; continue; } try { SPView tempView = curList.Views[menuItem.Text]; viewSetting = listSetting.GetByViewName(menuItem.Text); if (viewSetting == null) continue; item.Visible = viewSetting.CanDisplay(currentUser); } catch (ArgumentException) { } } } } }
如何讓sharepoint默認使用我們這個PermissionListViewSelector類了,同樣我們需要修改ViewSelector模板

<SharePoint:RenderingTemplate ID="ViewSelector" runat="server"> <Template> <table border=0 cellpadding=0 cellspacing=0 style='margin-right: 4px'> <tr> <td nowrap class="ms-listheaderlabel"><SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="<%$Resources:wss,view_selector_view%>" EncodeMethod='HtmlEncode'/> </td> <td nowrap class="ms-viewselector" id="onetViewSelector" onmouseover="this.className='ms-viewselectorhover'" onmouseout="this.className='ms-viewselector'" runat="server"> <codeArt:PermissionListViewSelector MenuAlignment="Right" AlignToParent="true" runat="server" id="ViewSelectorMenu" /> </td> </tr> </table> </Template> </SharePoint:RenderingTemplate>
最后我們來看看字段權限設置
這里權限設置分為編輯和查看權限,沒有編輯權限的用戶如下圖:
結合權限設置的那張圖看一看,這里只有Middle Name字段可以編輯,其他所有字段都不能編輯,Middle Name是允許”ALL User“編輯的,所以這里user可以編輯。查看權限也是一樣的。
這里我們需要開發一個EditControlListFieldIterator類來擴展默認的ListFieldIterator類,如何讓sharepoint來使用我們的這些類,我們需要修改sharepoint的ListForm和FileFormFields模板,相關代碼如下:

public class EditControlListFieldIterator : Microsoft.SharePoint.WebControls.ListFieldIterator { protected override void CreateChildControls() { //if (this.ControlMode == SPControlMode.Display) //{ // base.CreateChildControls(); // return; //} SPUser currentUser = SPContext.Current.Web.CurrentUser; if (currentUser != null && currentUser.IsSiteAdmin) { base.CreateChildControls(); return; } ListFieldPermissionSetting listSetting = ListFieldPermissionSetting.GetListSetting(this.List); if (listSetting == null || listSetting.Count == 0) { base.CreateChildControls(); return; } //base.CreateChildControls(); this.Controls.Clear(); if (this.ControlTemplate == null) { throw new ArgumentException("Could not find ListFieldIterator control template."); } Type t = typeof(TemplateContainer); PropertyInfo ControlModeProp = t.GetProperty("ControlMode", BindingFlags.Instance | BindingFlags.NonPublic); PropertyInfo FieldNameProp = t.GetProperty("FieldName", BindingFlags.Instance | BindingFlags.NonPublic); SPUser author = null; if (this.ControlMode == SPControlMode.New) { author = base.Web.CurrentUser; for (int i = 0; i < base.Fields.Count; i++) { SPField field = base.Fields[i]; if (!this.IsFieldExcluded(field)) { FieldPermission set = listSetting.GetByFieldName(field.InternalName); if (set != null && !set.CanEdit(currentUser, author)) { continue; } TemplateContainer child = new TemplateContainer(); this.Controls.Add(child); FieldNameProp.SetValue(child, field.InternalName, null); this.ControlTemplate.InstantiateIn(child); } } } else { SPFieldUserValue authorFieldValue = new SPFieldUserValue(base.Web, "" + this.ListItem["Author"]); author = authorFieldValue.User; for (int i = 0; i < base.Fields.Count; i++) { SPField field = base.Fields[i]; if (!this.IsFieldExcluded(field)) { SPControlMode thisMode = this.ControlMode; FieldPermission set = listSetting.GetByFieldName(field.InternalName); if (set != null ) { if (!set.CanEdit(currentUser, author)) { if (set.CanDisplay(currentUser, author)) thisMode = SPControlMode.Display; else continue; } } TemplateContainer child = new TemplateContainer(); this.Controls.Add(child); FieldNameProp.SetValue(child, field.InternalName, null); ControlModeProp.SetValue(child, thisMode , null); this.ControlTemplate.InstantiateIn(child); } } } } }

<SharePoint:RenderingTemplate ID="ListForm" runat="server"> <Template> <SPAN id='part1'> <SharePoint:InformationBar runat="server"/> <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator=" " runat="server"> <Template_RightButtons> <SharePoint:NextPageButton runat="server"/> <SharePoint:SaveButton runat="server"/> <SharePoint:GoBackButton runat="server"/> </Template_RightButtons> </wssuc:ToolBar> <SharePoint:FormToolBar runat="server"/> <TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0 cellspacing=0 width=100%> <SharePoint:ChangeContentType runat="server"/> <SharePoint:FolderFormFields runat="server"/> <codeArt:EditControlListFieldIterator runat="server"/> <SharePoint:ApprovalStatus runat="server"/> <SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/> </TABLE> <table cellpadding=0 cellspacing=0 width=100%><tr><td class="ms-formline"><IMG SRC="/_layouts/15/images/blank.gif" width=1 height=1 alt=""></td></tr></table> <TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px"><tr><td width=100%> <SharePoint:ItemHiddenVersion runat="server"/> <SharePoint:ParentInformationField runat="server"/> <SharePoint:InitContentType runat="server"/> <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator=" " runat="server"> <Template_Buttons> <SharePoint:CreatedModifiedInfo runat="server"/> </Template_Buttons> <Template_RightButtons> <SharePoint:SaveButton runat="server"/> <SharePoint:GoBackButton runat="server"/> </Template_RightButtons> </wssuc:ToolBar> </td></tr></TABLE> </SPAN> <SharePoint:AttachmentUpload runat="server"/> <hr/> </Template> </SharePoint:RenderingTemplate> <SharePoint:RenderingTemplate ID="FileFormFields" runat="server"> <Template> <codeArt:EditControlListFieldIterator runat="server"/> </Template> </SharePoint:RenderingTemplate>
這里我們的權限設置都保存到哪里去了?這里默認是把權限設置的object 序列化為字符串然后保存到一個特定的文檔庫里面去了的。
看到這個圖,我想大家應該知道這里的權限設置時怎么保存的了吧,每個list內容內型權限設置、試圖權限設置、字段權限設置分別對應一個文件。
為了回報大家,這里特地分享一個知識點;大家在安裝sharepoint2013 和 VS2013是 一定要保持語言版本一致,我以前遇到一個 站點模板創建后無法識別的issue,原因就是這2個語言不一致。中文版的sharepoint只能識別2052下的自定義模板,而英文版的VS2013 默認會發布到一個1033的文件夾下,所以中文版的sharepoint2013 默認是不能識別英文版VS2013創建的站點模板。