Sharepoint 2013 FBA Error:Could not retrieve the IIS Settings.Parameter name: context - sharepoint-2013

(The original post is here:https://social.technet.microsoft.com/Forums/sharepoint/en-US/6b02dfe8-5594-4d25-991a-51ac9a0528b7/sharepoint-2013-fba-errorcould-not-retrieve-the-iis-settingsparameter-name-context?forum=sharepointadminprevious)
I am trying to use windows live to login sharepoint 2013. From some posts, I could get windows live token(aouth 2.0) and windows live user profile. It will redirect to my sharepoint site.
I follow some articles to develop my custom login page:
also I defined my membership provider and role provider
public class LiveMembershipProvider : MembershipProvider
{
private MembershipUserCollection employees;
private void generateUsers()
{
//Mock Data
employees = new MembershipUserCollection();
employees.Add(new MembershipUser(this.Name, "Jack Chen", "JackChen", "Jack#Chen.com", "What your Name?", "I am Jack", true, false, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today));
employees.Add(new MembershipUser(this.Name, "Bruce Li", "BruceLi", "BruceLi#Li.com", "How are u?", "How old are u", true, false, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today));
employees.Add(new MembershipUser(this.Name, "Eyes Wang", "EyesWang", "EyesWang#Mintcode.com", "What the hell?", "what the fuck", true, false, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today, DateTime.Today));
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
if (employees == null) generateUsers();
MembershipUserCollection returnFoundUsers = new MembershipUserCollection();
(employees.Cast<MembershipUser>().
Where(membershipUser => membershipUser.UserName.ToLowerInvariant().Contains(usernameToMatch.ToLowerInvariant())))
.ToList().ForEach(returnFoundUsers.Add);
totalRecords = returnFoundUsers.Count;
return returnFoundUsers;
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
if (employees == null) generateUsers();
totalRecords = employees.Count;
return employees;
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (employees == null) generateUsers();
IEnumerable<MembershipUser> usersFound = employees.Cast<MembershipUser>().Where(membershipUser => membershipUser.UserName == username);
return usersFound.FirstOrDefault();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
if (employees == null) generateUsers();
IEnumerable<MembershipUser> usersFound = employees.Cast<MembershipUser>().Where(membershipUser => membershipUser.ProviderUserKey.ToString() == providerUserKey.ToString());
return usersFound.FirstOrDefault();
}
public override string GetUserNameByEmail(string email)
{
if (employees == null) generateUsers();
IEnumerable<MembershipUser> usersFound = employees.Cast<MembershipUser>().Where(membershipUser => membershipUser.Email.ToLowerInvariant() == email.ToLowerInvariant());
MembershipUser user = usersFound.FirstOrDefault();
if (user != null)
return user.UserName;
else
return null;
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
if (employees == null) generateUsers();
MembershipUserCollection returnFoundUsers = new MembershipUserCollection();
(employees.Cast<MembershipUser>().
Where(membershipUser => membershipUser.Email.ToLowerInvariant().Contains(emailToMatch.ToLowerInvariant())))
.ToList().ForEach(returnFoundUsers.Add);
totalRecords = returnFoundUsers.Count;
return returnFoundUsers;
}
public override bool ValidateUser(string username, string password)
{
//return true;
if (employees == null) generateUsers();
IEnumerable<MembershipUser> usersFound = employees.Cast<MembershipUser>().Where(membershipUser => membershipUser.UserName == username);
MembershipUser user = usersFound.FirstOrDefault();
if (user != null)
{
if (string.IsNullOrEmpty(password))
{
return false;
}
else
{
return true;
}
}
else
return false;
}
}
public class LiveRoleProvider : RoleProvider
{
public override string ApplicationName { get; set; }
private string[] m_AllRoles = { "Vendor" };
private string[,] m_RolesForUser = new string[,] {
{"Eyes Wang", "Vendor"},
{"Bruce Li","Vendor"},
{"Jack Chen","Vendor"}
};
public override string[] GetAllRoles()
{
return m_AllRoles;
}
public override string[] GetRolesForUser(string username)
{
List<string> roles = new List<string>();
for (int i = 0; i <= m_RolesForUser.GetUpperBound(0); i++)
{
if (m_RolesForUser[i, 0] == username)
{
roles = m_RolesForUser[i, 1].Split(',').ToList<string>();
}
}
return roles.ToArray();
}
public override string[] GetUsersInRole(string rolename)
{
List<string> users = new List<string>();
for (int i = 0; i <= m_RolesForUser.GetUpperBound(0); i++)
{
List<string> userRoles = m_RolesForUser[i, 1].Split(',').ToList<string>();
if (userRoles.Where(userRole => userRole == rolename).Count() > 0)
{
users.Add(m_RolesForUser[i, 0]);
}
}
return users.ToArray();
}
public override bool IsUserInRole(string username, string rolename)
{
List<string> usersForRole = GetUsersInRole(rolename).ToList();
if (usersForRole.Where(userName => userName == username).Count() > 0)
{
return true;
}
else
{
return false;
}
}
public override bool RoleExists(string rolename)
{
bool roleExsists = m_AllRoles.ToList().Where(roleName => roleName == rolename).Count() > 0;
return roleExsists;
}
public override string[] FindUsersInRole(string rolename, string usernameToMatch)
{
List<string> users = GetUsersInRole(rolename).ToList<string>();
List<string> foundUsers = users.Where(userName => userName.ToLowerInvariant().Contains(usernameToMatch.ToLowerInvariant())).ToList<string>();
return foundUsers.ToArray();
}
}
In the Central Application web.config
I add
<roleManager>
<providers>
<add name="LiveRoleProvider" type="SPLiveWebForm.LiveRoleProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</roleManager>
<membership>
<providers>
<add name="LiveMembershipProvider" type="SPLiveWebForm.LiveMembershipProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</membership>
<PeoplePickerWildcards>
<clear />
<add key="AspNetSqlMembershipProvider" value="%" />
<add key="LiveMembershipProvider" value="%" />
</PeoplePickerWildcards>
In the WA web.config
<membership defaultProvider="i">
<providers>
<add name="i" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthMembershipProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="LiveMembershipProvider" type="SPLiveWebForm.LiveMembershipProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</membership>
<roleManager defaultProvider="c" enabled="true" cacheRolesInCookie="false">
<providers>
<add name="c" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthRoleProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="LiveRoleProvider" type="SPLiveWebForm.LiveRoleProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</roleManager>
<PeoplePickerWildcards>
<clear />
<add key="AspNetSqlMembershipProvider" value="%" />
<add key="LiveMembershipProvider" value="%" />
</PeoplePickerWildcards>
In the STS web.config
<system.web>
<membership defaultProvider="i">
<providers>
<add name="i" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthMembershipProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="LiveMembershipProvider" type="SPLiveWebForm.LiveMembershipProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</membership>
<roleManager defaultProvider="c" enabled="true" cacheRolesInCookie="false">
<providers>
<add name="c" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthRoleProvider, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="LiveRoleProvider" type="SPLiveWebForm.LiveRoleProvider,SPLiveWebForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34a64026791bbfa4" />
</providers>
</roleManager>
</system.web>
I could successfully login to share point by using the default login page (http://www.akmii.com/_forms/default.aspx)
But using my customize page (when I get windows live user profile then try to use the follow method to login sharepoint)
private void SPUserLogin(string usrName)
{
string userProviderName = string.Empty;
string roleProviderName = string.Empty;
foreach (MembershipProvider p in Membership.Providers)
{
if (p.GetType().Equals(typeof(LiveMembershipProvider)))
{
userProviderName = p.Name;
break;
}
}
foreach (RoleProvider rp in System.Web.Security.Roles.Providers)
{
if (rp.GetType().Equals(typeof(LiveRoleProvider)))
{
roleProviderName = rp.Name;
break;
}
}
SecurityToken tk = null;
try
{
tk = SPSecurityContext.SecurityTokenForFormsAuthentication(
new Uri(SPContext.Current.Web.Url), userProviderName, roleProviderName,
"Jack Chen", "123", SPFormsAuthenticationOption.PersistentSignInRequest);
}
catch (Exception e)
{
Response.Write(e.Message);
}
if (tk != null)
{
//try setting the authentication cookie
SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current;
fam.SetPrincipalAndWriteSessionToken(tk);
//look for the Source query string parameter and use that as the redirection
//string src = Request.QueryString["Source"];
string src = "http://www.akmii.com/_layouts/15/start.aspx#/SitePages/Home.aspx";
if (!string.IsNullOrEmpty(src))
Response.Redirect(src);
}
else
{
}
}
}
There is an exception throwing out in the catch section:
Could not retrieve the IIS Settings.Parameter name: context
The error stack as follows:
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.ReadResponse(Message response)
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst, RequestSecurityTokenResponse& rstr)
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst)
at Microsoft.SharePoint.SPSecurityContext.SecurityTokenForContext(Uri context, Boolean bearerToken, SecurityToken onBehalfOf, SecurityToken actAs, SecurityToken delegateTo, SPRequestSecurityTokenProperties properties)
at Microsoft.SharePoint.SPSecurityContext.SecurityTokenForFormsAuthentication(Uri context, String membershipProviderName, String roleProviderName, String username, String password, SPFormsAuthenticationOption options)
at SPLiveWebForm.Layouts.SPLiveWebForm.Login.SPUserLogin(String usrName)
If I use
bool status = SPClaimsUtility.AuthenticateFormsUser(
new Uri(SPContext.Current.Web.Url),
usrName,
"123");
The same exception throw out.
at Microsoft.SharePoint.IdentityModel.SPFormsOriginalIssuerBuilder.GetFormsAuthenticationProviderFromContext(Uri context)
at Microsoft.SharePoint.IdentityModel.SPFormsOriginalIssuerBuilder.ValidateFormsAuthProviderNames(Uri context, String membershipProvider, String roleProvider)
at Microsoft.SharePoint.IdentityModel.SPFormsOriginalIssuerBuilder.SetProviderNames()
at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.SPRequestInfo.InitializeForForms(SPRequestSecurityToken request)
at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.GetTokenLifetime(Lifetime requestLifetime)
at Microsoft.IdentityModel.SecurityTokenService.SecurityTokenService.Issue(IClaimsPrincipal principal, RequestSecurityToken request)
at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.Issue(IClaimsPrincipal principal, RequestSecurityToken request)
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.DispatchRequest(DispatchContext dispatchContext)
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.ProcessCore(Message requestMessage, WSTrustRequestSerializer requestSerializer, WSTrustResponseSerializer responseSerializer, String requestAction, String responseAction, String trustNamespace)
at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract.ProcessTrust13Issue(Message message)
at SyncInvokeProcessTrust13Issue(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
I spend whole day to dig this issue but failed.
Is there any solution to resolve this?
Many Thanks!
Vincent

I had the same problem.
In my case I had to go to the Central Administartion => System Settings => Configure alternate access mappings.
Click on "Edit Public URLs", select Your app in "Alternate access mappings collection" and then fill up "Internet" textbox.
I am not sure what value is correct, but I filled it with my sharepoint URL with default (80) port.
Hope it help You.

Related

Custom Provider for AWS SSM using Microsoft.Configuration.ConfigurationBuilders

I seem to be stuck at developing a custom Key/Value pair provider for Amazon's System Manager Parameter Store (SSM) using NETFramework 4.7.1 that utilizes Microsoft.Configuration.ConfigurationBuilders.
The implementation:
using System;
using System.Collections.Generic;
using Amazon.SimpleSystemsManagement;
using Amazon.SimpleSystemsManagement.Model;
using Microsoft.Configuration.ConfigurationBuilders;
using System.Linq;
using System.Diagnostics;
using System.Collections.Specialized;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using System.Configuration;
using System.Threading.Tasks;
namespace AXS.Configurations
{
public class ParameterStoreConfigBuilder : KeyValueConfigBuilder
{
public const string envTag = "Environment";
public const string appNameTag = "AppName";
private IAmazonSimpleSystemsManagement client;
/// <summary>
/// Gets or sets an environment (dev|qa|staging|production)
/// </summary>
public string Environment { get; set; }
/// <summary>
/// Gets or sets a AppName
/// </summary>
public string AppName { get; set; }
public ParameterStoreConfigBuilder(IAmazonSimpleSystemsManagement client,
string appName,
string environment)
{
this.client = client;
Environment = environment.ToLower();
AppName = appName;
}
public ParameterStoreConfigBuilder()
{
client = new AmazonSimpleSystemsManagementClient();
}
public override string Description => "Parameter Store";
public override string Name => "SSM";
protected override void LazyInitialize(string name, NameValueCollection config)
{
Optional = false;
base.LazyInitialize(name, config);
string env = UpdateConfigSettingWithAppSettings(envTag);
if (string.IsNullOrWhiteSpace(env))
throw new ArgumentException($"environment must be specified with the '{envTag}' attribute.");
Environment = env;
string appName = UpdateConfigSettingWithAppSettings(appNameTag);
if (string.IsNullOrWhiteSpace(appName))
throw new ArgumentException($"appName must be specified with the '{appNameTag}' attribute.");
AppName = appName;
client = new AmazonSimpleSystemsManagementClient("","", Amazon.RegionEndpoint.USWest2);
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
Trace.TraceInformation($"return values prefix {prefix}");
if (client == null)
return null;
var parameters = new List<Parameter>();
string nextToken = null;
do
{
var response = client.GetParametersByPath(new GetParametersByPathRequest { Path = prefix, Recursive = true, WithDecryption = true, NextToken = nextToken });
nextToken = response.NextToken;
parameters.AddRange(response.Parameters);
} while (!string.IsNullOrEmpty(nextToken));
return parameters.Select(p => new
{
Key = p.Name,
p.Value
}).ToDictionary(parameter => parameter.Key, parameter => parameter.Value, StringComparer.OrdinalIgnoreCase);
}
public override string GetValue(string key)
{
return Task.Run(async () => { return await GetValueAsync(key); }).Result;
}
private async Task<string> GetValueAsync(string key)
{
var name = $"/{Environment}/{AppName}/{key.Replace(':', '/')}";
Trace.WriteLine($"get value async:{name}");
if (client == null)
return null;
try
{
Trace.TraceInformation($"fetch key {name}");
var request = new GetParameterRequest
{
Name = name,
WithDecryption = true
};
var response = await client.GetParameterAsync(request);
var parameter = response.Parameter;
var value = parameter.Type == ParameterType.SecureString ? "*****" : parameter.Value;
Trace.TraceInformation($"fetched name={name} value={value}");
return value;
}
catch (Exception e) when (Optional && ((e.InnerException is System.Net.Http.HttpRequestException) || (e.InnerException is UnauthorizedAccessException))) { }
return null;
}
}
}
The problem seems to be that AWS SSM client never gets created.
If I change the code and try to instantiate in the constructor I get a stack overflow exception due to recursion.
Any ideas on how to force to get AmazonSimpleSystemsManagementClient created?
The code uses guidance from https://github.com/aspnet/MicrosoftConfigurationBuilders
The App.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection,
System.Configuration, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
restartOnExternalChanges="false"
requirePermission="true" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" Environment="development" AppName="myAppNameforParmStore" type="AXS.Configurations.ParameterStoreConfigBuilder, AXS.Configurations" />
<add name="Env" prefix="appsettings_" stripPrefix="true" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=2.0.0.0, Culture=neutral" />
</builders>
</configBuilders>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
</startup>
<appSettings configBuilders="Env,ParameterStore">
<add key="Url" value="URL Value for from paramter Store" />
<add key="Secret" value="Some Secret value decrypted" />
</appSettings>
</configuration>
Thanks
UPDATE
I posted an updated version of the AwsSsmConfigurationBuilder, and a sample ASP.NET Web Forms project that uses it, on my GitHub:
https://github.com/Kirkaiya/AwsSsmConfigBuilderPoC/
Disclaimer: This is a proof-of-concept (POC) for a custom ConfigurationBuilder for ASP.NET 4.7.1 or higher (running on .NET Framework obviously). It's a POC, so it doesn't do anything besides allow you store Configuration AppSettings in AWS Parameter Store (a feature of Simple Systems Manager). So, clearly, don't use this in production without productizing and testing it!
Prerequisites:
Your project must target .NET Framework 4.7.1 or higher
Include NuGet package Microsoft.Configuration.ConfigurationBuilders.Base
Have parameters in AWS SSM Parameter Store that have the same name (not counting the prefix) as parameters in your web.config file, and vice-versa.
Notes
In order to avoid recursively calling a concrete constructor or Initialize, I used a static constructor to instantiate the AmazonSimpleSystemsManagementClient, which is held in a static member.
Web.Config additions
Note: change the assembly/class-name of your builder to match yours, etc.
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" ssmPrefix="/padnugapp/ApiKeys" type="Microsoft.Configuration.ConfigurationBuilders.AwsSsmConfigBuilder, AspNetWebFormsSample" />
</builders>
</configBuilders>
<appSettings configBuilders="ParameterStore">
<add key="TestKey" value="TestKey Value from web.config" />
<add key="TwitterKey" value="TwitterKey value from web.config" />
</appSettings>
And the AwsSsmConfigBuilder.cs file:
namespace Microsoft.Configuration.ConfigurationBuilders
{
public class AwsSsmConfigBuilder : KeyValueConfigBuilder
{
private string BaseParameterPath = "/padnugapp/ApiKeys";
private static IAmazonSimpleSystemsManagement _client;
static AwsSsmConfigBuilder()
{
_client = new AmazonSimpleSystemsManagementClient();
}
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
if (config["ssmPrefix"] == null)
return;
BaseParameterPath = config["ssmPrefix"];
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
if (_client == null)
return null;
var request = new GetParametersByPathRequest
{
Path = $"{BaseParameterPath}/{prefix}",
WithDecryption = true,
};
var response = _client.GetParametersByPathAsync(request).Result;
var result = response.Parameters.ToDictionary(param => param.Name, param => param.Value, StringComparer.OrdinalIgnoreCase);
return result;
}
public override string GetValue(string key)
{
if (_client == null)
return null;
var request = new GetParameterRequest
{
Name = $"{BaseParameterPath}/{key}",
WithDecryption = true,
};
var response = _client.GetParameterAsync(request).Result;
return response.Parameter.Value;
}
}
}
The code I put into a web-forms (.aspx) page that renders the two appSettings items in HTML:
TestKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TestKey"]) %>
<br />
TwitterKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TwitterKey"]) %>
I can't stress enough that this is just for a demo I'm doing, and not tested in any way, shape or form except on my laptop ;-)

500 Custom Error Pages

I want to implement 500 error page for multi-sites/multi-languages, i am following this article.
But Application_Error in Global.asax is not firing. Here is my code:
<%# Application Language='C#' Inherits="Sitecore.ContentSearch.SolrProvider.CastleWindsorIntegration.WindsorApplication" %>
<script RunAt="server">
private void Application_Error(object sender, EventArgs e)
{
var customErrorsSection = (System.Web.Configuration.CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
var lastException = Server.GetLastError();
if (customErrorsSection.Mode != System.Web.Configuration.CustomErrorsMode.Off)
{
try
{
// Log.Error( "There was an error in the application", lastException);
Server.ClearError();
HttpContext.Current.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
Server.Transfer(string.Format("/Error/{0}_500.html", GetSafeLanguage()));
}
catch
{
}
}
}
private string GetSafeLanguage()
{
try
{
return Sitecore.Context.Language.CultureInfo.TwoLetterISOLanguageName;
}
catch
{
}
return string.Empty;
}
</script>
Since I am using SOLR, It needs WindsorApplication in Global.asax and this class not inherited from System.Web.HttpApplication, i follow these links to handle 500 page error :
https://sitecorecommerce.wordpress.com/2015/10/15/500-error-page-in-your-sitecore-application/
http://www.partechit.nl/en/blog/2014/01/sitecore-mvc-applications-and-the-application-error-event
Connfiguation :
<pipelines>
<mvc.exception>
<processor type="Sitecore.Mvc.Pipelines.MvcEvents.Exception.ShowAspNetErrorMessage, Sitecore.Mvc">
<patch:attribute name="type">MyWebsite.Web.Pipelines.HandleMvcException, MyWebsite.Web</patch:attribute>
</processor>
</mvc.exception>
</pipelines>
My Class
public class HandleMvcException : ExceptionProcessor
{
public override void Process(ExceptionArgs args)
{
var customErrorsSection = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
var context = args.ExceptionContext;
var httpContext = context.HttpContext;
var exception = context.Exception;
if (customErrorsSection.Mode != CustomErrorsMode.Off)
{
if (context.ExceptionHandled || httpContext == null || exception == null)
{
return;
}
// Create a report with exception details.
string exceptionInfo = this.GetExceptionInfo(httpContext, exception);
// Store the report in a session variable so we can access it from the custom error page.
Log.Error(string.Format("There was an error in {0} : {1}", Sitecore.Context.Site.Name, exceptionInfo),this);
// Return a 500 status code and execute the custom error page.
httpContext.Server.ClearError();
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
httpContext.Server.Execute((Sitecore.Context.Site.ErrorPage(GetSafeLanguage())));
}
}
private string GetExceptionInfo(HttpContextBase httpContext, Exception exception)
{
// Generate an error report.
var errorInfo = new StringBuilder();
errorInfo.AppendLine(string.Concat("URL: ", httpContext.Request.Url));
/* Snipped additional lines of report generation */
errorInfo.AppendLine(string.Concat("Source: ", exception.Source));
errorInfo.AppendLine(string.Concat("Message: ", exception.Message));
errorInfo.AppendLine(string.Concat("Stacktrace: ", exception.StackTrace));
return errorInfo.ToString();
}
private string GetSafeLanguage()
{
try
{
return Sitecore.Context.Language.CultureInfo.TwoLetterISOLanguageName;
}
catch
{
}
return string.Empty;
}
}
and in site definition i added custom attribute for error page :
<site name="mywebsite" patch:before="site[#name='website']" xdt:Transform="Replace" xdt:Locator="Match(name)"
hostName ="mywebsite.local"
virtualFolder="/"
physicalFolder="/"
rootPath="/sitecore/content/mywebsite"
startItem="/home"
database="web"
domain="extranet"
allowDebug="true"
cacheHtml="true"
htmlCacheSize="50MB"
enablePreview="true"
enableWebEdit="true"
enableDebugger="true"
disableClientData="false"
errorPage="/assets/error/mywebsite/{0}_500.html"/>
and I created extended method to read the errorPage attribute :
public static class SiteExtension
{
/// <summary>
/// Retrun the site unique ID
/// </summary>
/// <returns></returns>
public static string ErrorPage(this SiteContext site, string language)
{
try
{
string errorPage = site.Properties["errorPage"];
if (!String.IsNullOrEmpty(errorPage))
return string.Format(errorPage, language);
else
return string.Empty;
}
catch (Exception)
{
return string.Empty;
}
}
}

Power user access to Sitecore Recycle Bin

Does anyone know of a solution to allow users in a certain role to view all items in the Sitecore Recycle Bin?
Currently, only admins can see all deleted items. Users can only see items they have deleted.
There isn't a way out of the box, the SqlArchive.GetEntries checks against user.IsAdministrator to show all entries in the archive.
You would need to implement a custom Archive provider and override the GetEntries method to work from a role.
Example:
public class CustomSqlArchive : SqlArchive
{
public CustomSqlArchive(string name, Database database)
: base(name, database)
{
}
protected override IEnumerable<ArchiveEntry> GetEntries(User user, int pageIndex, int pageSize, ID archivalId)
{
Assert.IsNotNull(archivalId, "archivalId");
var arrayList = new ArrayList(new[] { "archiveName", this.Name });
var str1 = "SELECT * FROM \r\n (SELECT {0}Archive{1}.{0}ArchivalId{1}, {0}Archive{1}.{0}ItemId{1}, {0}ParentId{1}, {0}Name{1}, {0}OriginalLocation{1}, \r\n {0}ArchiveDate{1}, {0}ArchivedBy{1}, ROW_NUMBER() OVER(ORDER BY {0}ArchiveDate{1} DESC, {0}ArchivalId{1}) as {0}RowNumber{1}\r\n FROM {0}Archive{1} \r\n WHERE {0}ArchiveName{1} = {2}archiveName{3}";
var showAllItems = user.IsInRole("Super User Role") || user.IsAdministrator;
if (user != null && !showAllItems)
{
str1 = str1 + " AND {0}ArchivalId{1} IN (SELECT {0}ArchivalId{1}\r\n FROM {0}ArchivedVersions{1} WHERE {0}ArchivedBy{1} = {2}archivedBy{3}) ";
arrayList.AddRange(new[] { "archivedBy", user.Name });
}
if (archivalId != ID.Null)
{
str1 = str1 + " AND {0}ArchivalId{1} = {2}archivalId{3}";
arrayList.Add("archivalId");
arrayList.Add(archivalId);
}
var str2 = str1 + ") {0}ArchiveWithRowNumbers{1}";
if (pageSize != int.MaxValue)
{
str2 = str2 + " WHERE {0}RowNumber{1} BETWEEN {2}firstRow{3} AND {2}lastRow{3}";
var num1 = (pageIndex * pageSize) + 1;
int num2 = pageSize == int.MaxValue ? int.MaxValue : (pageIndex + 1) * pageSize;
arrayList.AddRange(new[] { "firstRow", num1.ToString(), "lastRow", num2.ToString() });
}
return this.GetEntries(str2 + " ORDER BY {0}ArchiveDate{1} DESC, {0}ArchivalId{1}", arrayList.ToArray());
}
}
You would then need to add your custom provider to the config:
<archives defaultProvider="custom" enabled="true">
<providers>
<clear />
<add name="custom" type="Sitecore.Data.Archiving.SqlArchiveProvider, Sitecore.Kernel" database="*" />
<add name="sql" type="Sitecore.Data.Archiving.SqlArchiveProvider, Sitecore.Kernel" database="*" />
<add name="switcher" type="Sitecore.Data.Archiving.SwitchingArchiveProvider, Sitecore.Kernel" />
</providers>
</archives>
Then add a role called Super User Role and put any users you want to have that access as members.
** note - code is untested **
Below is a similar approach to Richard's answer, but instead of copying all of the logic within GetEntries(), it spoofs the admin user. You will also need to implement a SqlArchiveProvider in addition to the CustomSqlArchive itself.
SQL Archive
public class CustomSqlArchive : SqlArchive
{
private const string PowerUserRole = #"sitecore\Power User";
private const string AdminUser = #"sitecore\Admin";
public AvidSqlArchive(string name, Database database) : base(name, database) { }
protected override IEnumerable<ArchiveEntry> GetEntries(User user, int pageIndex, int pageSize, ID archivalId)
{
if (user != null && Role.Exists(PowerUserRole) && user.IsInRole(PowerUserRole))
{
User admin = User.FromName(AdminUser, true);
return base.GetEntries(admin, pageIndex, pageSize, archivalId);
}
return base.GetEntries(user, pageIndex, pageSize, archivalId);
}
}
SQL Archive Provider
public class CustomSqlArchiveProvider : SqlArchiveProvider
{
protected override Sitecore.Data.Archiving.Archive GetArchive(XmlNode configNode, Database database)
{
string attribute = XmlUtil.GetAttribute("name", configNode);
return !string.IsNullOrEmpty(attribute) ?
new CustomSqlArchive(attribute, database) :
null;
}
}
Configuration Patch
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<!-- Use custom archive that allows users in the "Power User" role to see other user's items by spoofing the admin user -->
<archives defaultProvider="sql" enabled="true">
<patch:attribute name="defaultProvider">custom</patch:attribute>
<providers>
<clear />
<add name="custom" type="Example.CustomSqlArchiveProvider, Example" database="*" />
<add name="sql" type="Sitecore.Data.Archiving.SqlArchiveProvider, Sitecore.Kernel" database="*" />
<add name="switcher" type="Sitecore.Data.Archiving.SwitchingArchiveProvider, Sitecore.Kernel" />
</providers>
</archives>
</sitecore>
</configuration>

Workflow XAML Custom Activity Parsing issue?

I've created a custom XAML Activity inherited from NativeActivity and IActivityTemplateFactory
The Designer shows the correct look and feel inside the Library I've defined.
However when I drop it on the Flow Surface of a workflow Console Application I don't see it rendered. The CPU pegs at 50% and if I run procmon.exe I see BUFFEROVERFLOW on the .XAML file and a NOT REPARSE error on the exe itself.
I do have the typeof Designer defined as the attribute of the class.
I've turned on Exceptions in the VS debug to see if an exception is being thrown but nothing ever comes out.
--CODE-- CS
[Designer(typeof(ITCRetryActivityDesigner))]
public sealed class ITCRetryActivity : NativeActivity, IActivityTemplateFactory
{
private static readonly TimeSpan DefaultRetryInterval = new TimeSpan(0, 0, 0, 1);
private readonly Variable<Int32> _attemptCount = new Variable<Int32>();
private readonly Variable<TimeSpan> _delayDuration = new Variable<TimeSpan>();
private readonly Delay _internalDelay;
public ITCRetryActivity()
{
_internalDelay = new Delay
{
Duration = new InArgument<TimeSpan>(_delayDuration)
};
Body = new ActivityAction();
MaxAttempts = 5;
ExceptionType = typeof(TimeoutException);
RetryInterval = DefaultRetryInterval;
}
[DebuggerNonUserCode]
public Activity Create(DependencyObject target)
{
return new ITCRetryActivity
{
Body =
{
Handler = new Sequence()
}
};
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddDelegate(Body);
metadata.AddImplementationChild(_internalDelay);
metadata.AddImplementationVariable(_attemptCount);
metadata.AddImplementationVariable(_delayDuration);
RuntimeArgument maxAttemptsArgument = new RuntimeArgument("MaxAttempts", typeof(Int32), ArgumentDirection.In, true);
RuntimeArgument retryIntervalArgument = new RuntimeArgument("RetryInterval", typeof(TimeSpan), ArgumentDirection.In, true);
metadata.Bind(MaxAttempts, maxAttemptsArgument);
metadata.Bind(RetryInterval, retryIntervalArgument);
Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>
{
maxAttemptsArgument,
retryIntervalArgument
};
metadata.SetArgumentsCollection(arguments);
ValidationError validationError;
if (Body == null)
{
validationError = new ValidationError("No Children are defined in this Retry Activity", true, "Body");
metadata.AddValidationError(validationError);
}
if (typeof (Exception).IsAssignableFrom(ExceptionType) != false) return;
validationError = new ValidationError("Exception type does not match", false, "ExceptionType");
metadata.AddValidationError(validationError);
}
protected override void Execute(NativeActivityContext context)
{
ExecuteAttempt(context);
}
private static Boolean ShouldRetryAction(Type exceptionType, Exception thrownException)
{
return exceptionType != null && exceptionType.IsInstanceOfType(thrownException);
}
private void ActionFailed(NativeActivityFaultContext faultcontext, Exception propagatedexception, ActivityInstance propagatedfrom)
{
Int32 currentAttemptCount = _attemptCount.Get(faultcontext);
currentAttemptCount++;
_attemptCount.Set(faultcontext, currentAttemptCount);
Int32 maxAttempts = MaxAttempts.Get(faultcontext);
if (currentAttemptCount >= maxAttempts)
{
// There are no further attempts to make
return;
}
if (ShouldRetryAction(ExceptionType, propagatedexception) == false)
{
return;
}
faultcontext.CancelChild(propagatedfrom);
faultcontext.HandleFault();
TimeSpan retryInterval = RetryInterval.Get(faultcontext);
if (retryInterval == TimeSpan.Zero)
{
ExecuteAttempt(faultcontext);
}
else
{
// We are going to wait before trying again
_delayDuration.Set(faultcontext, retryInterval);
faultcontext.ScheduleActivity(_internalDelay, DelayCompleted);
}
}
private void DelayCompleted(NativeActivityContext context, ActivityInstance completedinstance)
{
ExecuteAttempt(context);
}
private void ExecuteAttempt(NativeActivityContext context)
{
if (Body == null)
{
return;
}
context.ScheduleAction(Body, null, ActionFailed);
}
[Browsable(false)]
public ActivityAction Body
{
get;
set;
}
[DefaultValue(typeof(TimeoutException))]
public Type ExceptionType
{
get;
set;
}
public InArgument<Int32> MaxAttempts
{
get;
set;
}
public InArgument<TimeSpan> RetryInterval
{
get;
set;
}
}
}
--XAML--
<sap:ActivityDesigner x:Class="ITC.Common.Workflow.ITCRetryActivityDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
xmlns:conv="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation">
<sap:ActivityDesigner.Icon>
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0"
Size="16,16">
</Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="d-metal-reload-arrows.jpg"></BitmapImage>
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</sap:ActivityDesigner.Icon>
<sap:ActivityDesigner.Resources>
<conv:ModelToObjectValueConverter x:Key="ModelItemConverter"
x:Uid="sadm:ModelToObjectValueConverter_1" />
<DataTemplate x:Key="Collapsed">
<TextBlock HorizontalAlignment="Center"
FontStyle="Italic"
Foreground="Gray">
Double-Click to View
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="Expanded">
<StackPanel Orientation="Vertical">
<Grid Name="contentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center">
Exception Type:
</TextBlock>
<sapv:TypePresenter HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="6"
Grid.Row="0"
Grid.Column="1"
Filter="ExceptionTypeFilter"
AllowNull="false"
BrowseTypeDirectly="false"
Label="Exception Type"
Type="{Binding Path=ModelItem.ExceptionType, Mode=TwoWay, Converter={StaticResource ModelItemConverter}}"
Context="{Binding Context}" />
</Grid>
<sap:WorkflowItemPresenter Item="{Binding ModelItem.Body.Handler}"
HintText="Drop Activity"
Margin="6" />
</StackPanel>
</DataTemplate>
<Style x:Key="ExpandOrCollapsedStyle"
TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate"
Value="{DynamicResource Collapsed}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowExpanded}"
Value="true">
<Setter Property="ContentTemplate"
Value="{DynamicResource Expanded}" />
</DataTrigger>
</Style.Triggers>
</Style>
</sap:ActivityDesigner.Resources>
<Grid>
<ContentPresenter Style="{DynamicResource ExpandOrCollapsedStyle}"
Content="{Binding}" Height="16" VerticalAlignment="Top" />
</Grid>
How can I debug this situation?
Thanks.
I resolved my issue by removing the Exception type as stated in comments and not having the icon graphic (using the default since it's not all that important.

Google Glass Live Card not inserting

Glass GDK here. Trying to insert a livecard using remote views from service. I'm launching service via voice invocation. The voice command works, however it appears my service is not starting(no entries in log). Service is in android manifest. Below is code:
public class PatientLiveCardService extends Service {
private static final String LIVE_CARD_ID = "timer";
#Override
public void onCreate() {
Log.warn("oncreate");
super.onCreate();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
publishCard(this);
return START_STICKY;
}
#Override
public void onDestroy() {
unpublishCard(this);
super.onDestroy();
}
private void publishCard(Context context) {
Log.info("inserting live card");
if (mLiveCard == null) {
String cardId = "my_card";
TimelineManager tm = TimelineManager.from(context);
mLiveCard = tm.getLiveCard(cardId);
mLiveCard.setViews(new RemoteViews(context.getPackageName(),
R.layout.activity_vitals));
Intent intent = new Intent(context, MyActivity.class);
mLiveCard.setAction(PendingIntent
.getActivity(context, 0, intent, 0));
mLiveCard.publish();
} else {
// Card is already published.
return;
}
}
private void unpublishCard(Context context) {
if (mLiveCard != null) {
mLiveCard.unpublish();
mLiveCard = null;
}
}
}
Here is AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO" >
</uses-permission>
<application
android:name="com.myApp"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.myApp.MyActivity"
android:label="#string/app_name"
android:screenOrientation="landscape" >
</activity>
<service android:name="com.myApp.services.MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
</intent-filter>
<meta-data
android:name="com.google.android.glass.VoiceTrigger"
android:resource="#xml/voice_trigger_get_patient" />
</service>
</application>
This is a bug with XE11: the service is not started after the speech recognizer is complete.
As a workaround, you can have your voice trigger start an Activity which:
Processes the recognized speech in onResume.
Once the speech is processed, starts your Service with startService.
Calls finish to jump to the published LiveCard.