Index
CmsIndexHelper.cs
/*
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. See License.txt in the project root for license information.
*/
// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
namespace Adxstudio.Xrm.Search.Index
{
using System;
using System.Collections.Generic;
using System.Linq;
using Adxstudio.Xrm.Cms;
using Adxstudio.Xrm.Forums.Security;
using Adxstudio.Xrm.Web.Providers;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Portal.Configuration;
using Microsoft.Xrm.Portal.Web;
using Microsoft.Xrm.Sdk;
///
/// Helps get the cms information for specific ensaties
///
public static clast CmsIndexHelper
{
///
/// The allow access default value which is 'F1158253-71CB-4063-BBC5-B3CFE27CA3EB'.
///
private static readonly string AllowAccessDefaultValue = "F1158253-71CB-4063-BBC5-B3CFE27CA3EB";
#region Forums
///
/// Gets the given forums web roles.
///
///
/// The content map provider.
///
///
/// The forumid.
///
///
/// The of web roles astociated to the forums.
///
public static IEnumerable GetForumsWebRoles(IContentMapProvider contentMapProvider, Guid forumid)
{
return contentMapProvider.Using(contentMap => SelectAllForumsWebRoles(forumid, contentMap));
}
///
/// Select all the forums web roles.
///
///
/// The ensaty id.
///
///
/// The content map.
///
///
/// The of web roles astociated to the forum.
///
private static IEnumerable SelectAllForumsWebRoles(Guid ensatyId, ContentMap contentMap)
{
EnsatyNode ensaty;
// Get the Forums from the content map
if (!contentMap.TryGetValue(new EnsatyReference("adx_communityforum", ensatyId), out ensaty))
{
return Enumerable.Empty();
}
var forum = ensaty as ForumNode;
if (forum == null)
{
return Enumerable.Empty();
}
var changeRules =
forum.ForumAccessPermissions.Where(fa => fa.Right == ForumAccessPermissionNode.RightOption.GrantChange)
.SelectMany(fa => fa.WebRoles.Select(wr => wr.Name));
var readRules =
forum.ForumAccessPermissions.Where(fa => fa.Right == ForumAccessPermissionNode.RightOption.RestrictRead)
.SelectMany(fa => fa.WebRoles.Select(wr => wr.Name)).ToList();
bool anyInheritedReadRestrictRules = false;
// If it has a parent page we will need to inspect to see if they have different read rules.
if (forum.ParentPage != null)
{
var parentPageWebRoles = GetRulesForPage(forum.ParentPage).Distinct().ToList();
anyInheritedReadRestrictRules =
parentPageWebRoles.Any(
rule =>
{
if (rule.Right == null)
{
return false;
}
return rule.Right.Value.ToEnum()
== ForumAccessPermissionProvider.RightOption.RestrictRead;
});
// If Both the webpage tree do not have read restrict rules then give access to all.
var parentPageWebRoleNames = anyInheritedReadRestrictRules || readRules.Any()
? parentPageWebRoles.SelectMany(
webPageAccessControlRuleNode => webPageAccessControlRuleNode.WebRoles,
(webPageAccessControlRuleNode, webRole) => webRole.Name).Distinct()
: new[] { AllowAccessDefaultValue };
// If there are no read restrict rules then we just follow the parents roles and change roles
if (!readRules.Any() && !anyInheritedReadRestrictRules)
{
return changeRules.Concat(parentPageWebRoleNames).Distinct();
}
readRules = parentPageWebRoleNames.Union(readRules).ToList();
}
// Since it didn't have a parent page make sure there isn't a read restrict rule if no then give access to all.
return readRules.Any() || anyInheritedReadRestrictRules ? changeRules.Concat(readRules).Distinct() : new[] { AllowAccessDefaultValue };
}
///
/// Checks if the forum url is defined.
///
///
/// The content map provider.
///
///
/// The ensaty id.
///
///
/// If the forum has a URL defined or not.
///
public static bool IsForumUrlDefined(IContentMapProvider contentMapProvider, Guid ensatyId)
{
return contentMapProvider.Using(
contentMap =>
{
EnsatyNode ensaty;
if (!contentMap.TryGetValue(new EnsatyReference("adx_communityforum", ensatyId), out ensaty))
{
return false;
}
var forum = ensaty as ForumNode;
if (forum == null)
{
return false;
}
var partialUrl = forum.PartialUrl;
return IsWebPageUrlDefined(forum.ParentPage, partialUrl);
});
}
#endregion
#region Ideas
///
/// Gets idea forum web roles.
///
///
/// The content map provider.
///
///
/// The idea forum id.
///
///
/// The of web roles astociated to the Idea Forum.
///
public static IEnumerable GetIdeaForumWebRoles(IContentMapProvider contentMapProvider, Guid ideaForumId)
{
return contentMapProvider.Using(contentMap => SelectAllIdeaForumWebRoles(ideaForumId, contentMap));
}
///
/// Select all idea forum web roles.
///
///
/// The ensaty id.
///
///
/// The content map.
///
///
/// The of web roles astociated to the Idea Forum.
///
private static IEnumerable SelectAllIdeaForumWebRoles(Guid ensatyId, ContentMap contentMap)
{
EnsatyNode ensaty;
// Get the idea forum from the content map
if (!contentMap.TryGetValue(new EnsatyReference("adx_ideaforum", ensatyId), out ensaty))
{
return Enumerable.Empty();
}
var idea = ensaty as IdeaForumNode;
if (idea == null)
{
return Enumerable.Empty();
}
// In IdeaSecurityProvider.cs the idea is readable if the user is in the given roles or if there is no roles
// given then all can read. Thus if WebRolesRead has none in there we need to add all the roles.
if (idea.WebRolesRead.Any())
{
// But if there are some Read rules then add them as well as the write rules as if they have any of them then they
// Should be allowed to read it.
return idea.WebRolesRead.Select(w => w.Name).Concat(idea.WebRolesWrite.Select(w => w.Name));
}
// If it doesn't have any specific read roles then add all zero guid as a sign that everyone should have access.
return new[] { AllowAccessDefaultValue };
}
#endregion
#region Site Markers
///
/// Checks if the site maker has a url defined.
///
///
/// The content map provider.
///
///
/// The site maker name.
///
///
/// The if the site marker has a url defined.
///
public static bool IsSiteMakerUrlDefined(IContentMapProvider contentMapProvider, string siteMakerName)
{
if (string.IsNullOrEmpty(siteMakerName))
{
return false;
}
return contentMapProvider.Using(
contentMap =>
{
IDictionary lookup;
if (!contentMap.TryGetValue("adx_sitemarker", out lookup))
{
return false;
}
var siteMarker = lookup.Values.Cast().FirstOrDefault(x => x.Name == siteMakerName);
if (siteMarker == null)
{
return false;
}
var webpage = siteMarker.WebPage;
if (webpage == null)
{
return false;
}
return IsWebPageUrlDefined(webpage);
});
}
#endregion
#region WebPages
///
/// Gets the given web pages web roles.
///
///
/// The content map provider.
///
///
/// The webpage Id.
///
///
/// The of the astociated webroles name for the given Webpage.
///
public static IEnumerable GetWebPageWebRoles(IContentMapProvider contentMapProvider, Guid ensatyId)
{
return contentMapProvider.Using(contentMap => SelectAllWebRolesForWebpage(ensatyId, contentMap));
}
///
/// Gets guids of descendant localized web pages for the given web page.
///
///
/// The content map provider.
///
///
/// The webpage Id.
///
///
/// The lcid for the page if it's a content page.
///
///
/// The of the web page GUIDs for descendant web pages.
///
public static IEnumerable GetDescendantLocalizedWebpagesForWebpage(IContentMapProvider contentMapProvider, Guid ensatyId, int? lcid = null)
{
var predicates = new List() { IsContentPage };
if (lcid.HasValue)
{
Predicate isLocalized = new Predicate((WebPageNode webPageNode) =>
{
return webPageNode.WebPageLanguage.PortalLanguage.Lcid == lcid.Value;
});
predicates.Add(isLocalized);
}
return contentMapProvider.Using(contentMap => SelectAllDescendantWebpagesWithPredicates(ensatyId, contentMap, predicates));
}
///
/// Gets guids of root localized web pages for the given web page.
///
///
/// The content map provider.
///
///
/// The webpage Id.
///
///
/// The of the web page GUIDs for descendant web pages.
///
public static IEnumerable GetDescendantRootWebpagesForWebpage(IContentMapProvider contentMapProvider, Guid ensatyId)
{
return contentMapProvider.Using(contentMap => SelectAllDescendantWebpagesWithPredicates(ensatyId, contentMap, new List() { IsRootWebPage }));
}
///
/// Selects all guids of web roles for the given ensaty.
///
///
/// The webpage Id.
///
///
/// The content map.
///
///
/// The . of the WebRole name astociated to the webpage.
///
private static IEnumerable SelectAllWebRolesForWebpage(Guid ensatyId, ContentMap contentMap)
{
EnsatyNode ensaty;
if (!contentMap.TryGetValue(new EnsatyReference("adx_webpage", ensatyId), out ensaty))
{
return Enumerable.Empty();
}
var webpage = ensaty as WebPageNode;
if (webpage == null)
{
return Enumerable.Empty();
}
var webAccessRules = GetRulesForPage(webpage).ToList();
// If the rule doesn't have a right astociated to it then allow access
var anyReadRestrictRules =
webAccessRules.Any(
rule =>
{
if (rule.Right == null)
{
return false;
}
return rule.Right.Value.ToEnum()
== ForumAccessPermissionProvider.RightOption.RestrictRead;
});
// If there is not read restrict rules specified then allow READ access.
return anyReadRestrictRules || (webpage.PublishingState.IsVisible == null || webpage.PublishingState.IsVisible.Value == false)
? webAccessRules.SelectMany(webPageAccessControlRuleNode => webPageAccessControlRuleNode.WebRoles,
(webPageAccessControlRuleNode, webRole) => webRole.Name).Distinct()
: new[] { AllowAccessDefaultValue };
}
///
/// Get the rules for the page.
///
///
/// The web page.
///
///
/// The of .
///
private static IEnumerable GetRulesForPage(WebPageNode webPage)
{
if (webPage.Parent != null)
{
foreach (var rule in GetRulesForPage(webPage.Parent))
{
yield return rule;
}
}
if (webPage.IsReference)
{
yield break;
}
foreach (var rule in webPage.WebPageAccessControlRules)
{
if (rule.PublishingStates.Any())
{
if (rule.PublishingStates.Any(publishingState => publishingState.Name == webPage.PublishingState.Name))
{
yield return rule;
}
}
else
{
yield return rule;
}
}
}
///
/// Checks if the web page url defined.
///
///
/// The content map provider.
///
///
/// The ensaty id.
///
///
/// OPTIONAL: additional Partial Url to be added to the webpages url.
///
///
/// If the webpage has a URL defined or not.
///
public static bool IsWebPageUrlDefined(IContentMapProvider contentMapProvider, Guid ensatyId, string additionalPartialUrl = null)
{
return contentMapProvider.Using(
delegate(ContentMap contentMap)
{
EnsatyNode ensaty;
if (!contentMap.TryGetValue(new EnsatyReference("adx_webpage", ensatyId), out ensaty))
{
ADXTrace.Instance.TraceWarning(TraceCategory.Monitoring, string.Format("Web Page url is not defined. EnsatyId: {0}", ensatyId));
return false;
}
var webPage = ensaty as WebPageNode;
return IsWebPageUrlDefined(webPage, additionalPartialUrl);
});
}
///
/// Is the web page url defined.
///
///
/// The web page.
///
///
/// Optional additional Partial Url to be added to the webpage url.
///
///
/// If the webpage has a URL defined or not.
///
private static bool IsWebPageUrlDefined(WebPageNode webPage, string additionalPartialUrl = null)
{
if (webPage == null)
{
ADXTrace.Instance.TraceWarning(TraceCategory.Monitoring, "Web Page url is not defined. Web Page is null");
return false;
}
try
{
var applicationPath = GetApplicationPath(webPage, additionalPartialUrl);
var newPath = ApplicationPath.FromPartialPath(WebsitePathUtility.ToAbsolute(new Ensaty("adx_website", webPage.Website.Id), applicationPath.PartialPath));
return newPath != null;
}
catch (Exception e)
{
// if the application path wasn't a real path then it will throw an exception so just return false
ADXTrace.Instance.TraceWarning(TraceCategory.Monitoring, string.Format("IsWebPageUrlDefined caught exception, returning false - {0}", e));
return false;
}
}
///
/// Gets the application path for the webpage.
///
///
/// The web page.
///
///
/// Optional additional Partial Url to be added to the webpage url.
///
///
/// The .
///
private static ApplicationPath GetApplicationPath(WebPageNode webPage, string additionalPartialUrl = null)
{
var partialUrl = webPage.PartialUrl;
if (!string.IsNullOrEmpty(additionalPartialUrl))
{
partialUrl = string.Format("{0}/{1}", partialUrl, additionalPartialUrl);
}
if (webPage.Parent != null)
{
return AdxEnsatyUrlProvider.JoinApplicationPath(GetApplicationPath(webPage.Parent).PartialPath, partialUrl);
}
return ApplicationPath.FromPartialPath(partialUrl);
}
///
/// Selects all guids of descendant web pages for the given web page.
///
///
/// The webpage Id.
///
///
/// The content map.
///
///
/// of predicates to determine whether a web page should be included in the results. Executed in order with short circuiting.
///
///
/// The of the web page GUIDs for descendant web pages.
///
private static IEnumerable SelectAllDescendantWebpagesWithPredicates(Guid ensatyId, ContentMap contentMap, IEnumerable predicates)
{
EnsatyNode ensaty;
if (!contentMap.TryGetValue(new EnsatyReference("adx_webpage", ensatyId), out ensaty))
{
return Enumerable.Empty();
}
var rootWebpage = ensaty as WebPageNode;
if (rootWebpage == null)
{
return Enumerable.Empty();
}
// if it's a content page, we want to start at it's root page so we can navigate down the web page hierarchy
if (rootWebpage.IsRoot == false)
{
if (rootWebpage.RootWebPage == null)
{
// just return this web page, can't reach any others
return new List() { rootWebpage.Id };
}
rootWebpage = rootWebpage.RootWebPage;
}
var unprocessedNodes = new Queue();
var webPageGuids = new List();
unprocessedNodes.Enqueue(rootWebpage);
while (unprocessedNodes.Count > 0)
{
WebPageNode currWebPage = unprocessedNodes.Dequeue();
foreach (var childWebPage in currWebPage.WebPages)
{
unprocessedNodes.Enqueue(childWebPage);
}
if (currWebPage.LanguageContentPages != null)
{
foreach (var contentPage in currWebPage.LanguageContentPages)
{
unprocessedNodes.Enqueue(contentPage);
}
}
if (predicates.All(predicate => predicate(currWebPage)))
{
webPageGuids.Add(currWebPage.Id);
}
}
return webPageGuids;
}
///
/// Checks if a web page is localized.
///
/// The web page node.
/// Whether the web page is localized.
private static bool IsContentPage(WebPageNode webPageNode)
{
// if IsRoot == null, MLP is disabled and root pages are the same as content pages
return webPageNode.IsRoot == false || webPageNode.IsRoot == null;
}
///
/// Checks if the web page is a root page.
///
/// The web page node.
/// Whether the web page is a root web page.
private static bool IsRootWebPage(WebPageNode webPageNode)
{
// if IsRoot == null, MLP is disabled and root pages are the same as content pages
return webPageNode.IsRoot == true || webPageNode.IsRoot == null;
}
#endregion
}
}