I have an MVC 5.2 application that takes advantage of ASP.Identity and the enhanced role services. All users are assigned a role that is prefixed as such: Corporate for internal users (e.g. "CorporateAdmin") and Client for external users (e.g. "ClientAdmin").
I have content that I want only to display in the view to external clients only that are authenticated. I have tried a variety of formats of the following:
#if (Request.IsAuthenticated && !Roles.GetRolesForUser().Contains("Corporate"))
{
*Content to be displayed*
}
The error message returned is "CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement.
I have also tried:
#if (Request.IsAuthenticated && Roles.GetRolesForUser().Contains("Corporate") == false)
{
*Content to be displayed*
}
and
#if (Request.IsAuthenticated && User.IsInRole("CorporateAdmin") == false)
{
*Content to be displayed*
}
all receiving the same error.
Any direction would be greatly appreciated.
I think, this errors means that GetRolesForUser returns an array of strings, not a list of strings. An array has not Contains method.
Try:
#if (Context.User.IsInRole("Corporate"))
{
"Content to be displayed"
}
Related
I am working with the below piece of code in Microsoft Bot Framework to access the list of regexes for global commands. This code is a part of botbuilder module:
if (typeof session.conversationData.globalCommands === "undefined") {
// An array which contains the list of all global commands
session.conversationData.globalCommands = [];
// Accessing the list of global commands
globalActions = session.library.actions.actions;
lenGlobalActions = Object.keys(globalActions).length;
// Assigning values to the above list
for (var i=0; i<lenGlobalActions; i++){
session.conversationData.globalCommands.push(globalActions[Object.keys(globalActions)[i]].options.matches);
}
}
// Checking if the incoming message from the user is a global command
var isGlobalCommand = session.conversationData.globalCommands.some(regex => regex.test(session.message.text));
The issue here is, the code runs fine for the first time and the values assigned to the variable session.conversationData.globalCommands are in the form given below:
However, after the first execution, the array converts to the below without any changes made in the code anywhere else:
Due to this, the line:
var isGlobalCommand = session.conversationData.globalCommands.some(regex => regex.test(session.message.text));
throws an exception "regex.test is not a function".
I am unable to understand why this should be happening and how do I solve this as I need this list separately for some processing.
I believe you cannot store complex types in the bot store (conversationData, etc). The object needs to be serializable to JSON and I don't believe a RegExp it is.
The workaround would be to store the regex as an string and then recreate the regex object using the constructor and the stored string expression.
Check the core-State sample to know more about the store capabilities.
I am using Orchard CMS v1.9 and want to display a custom registration page to accept the usual username/password/email and an additional token (invite token). The token will be used to match the user to some to custom data on the server.
I have walked through this blog Customizing User Registation With Dynamic Forms And Workflows. But in addition to what is achieved in this blog I want to force a registering user to enter a token. The token is used to lookup data on the server and create a link to the userpart.
Adding the token to the form is not the issue - its the querying and linking the entered token to the backend data and storing it in the userpart that im finding awkward.
Is this possible using just workflows - or do i need a custom module? I did not see a custom action that allowed me to match the token and link.
Is there a custom module already available that does something
similar?
Disclaimer: This approach is currently based on Orchard 1.10 but was initially developed on the 1.9.x branch. It does not rely on Dynamic Forms and Workflows, but I think you could achieve something similar with those modules.
Okay so I ended up building an example module with our approach to extended users / activation system. I stripped out a lot of code, but also let some juicy parts, which aren't directly related to your answer, in it.
First you should check out the UsersController it has the activate actions you are searching for. You may need to extend the orchard LogOn-View and include some GET & POST Actions accordingly.
[AllowAnonymous]
[HttpGet]
public ActionResult Activate(string activationCode)
{
// validation stuff....
var viewModel = new CustomUserActivate
{
// This is the activationCode you're looking for
ActivationCode = userFromActivationCode.ActivationCode,
UserName = userFromActivationCode.User.UserName,
WelcomeText = userFromActivationCode.WelcomeText,
Email = userFromActivationCode.User.Email
};
return this.View(viewModel);
}
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Activate(CustomUserActivate input)
{
if ( input == null )
{
this.ModelState.AddModelError("_form", this.T("The argument cannot be null").Text);
}
CustomUserPart customUserPart = null;
if ( this.ModelState.IsValid )
{
customUserPart = this.myService.GetCustomUserByActivationCode(input.ActivationCode);
if ( customUserPart == null || customUserPart.User == null || customUserPart.User.UserName != input.UserName )
{
this.notifier.Add(NotifyType.Error, this.T("The activation failed"));
}
if ( string.IsNullOrEmpty(input.Email) )
{
this.ModelState.AddModelError("Email", this.T("You must specify an email address.").Text);
}
else if ( input.Email.Length >= 255 )
{
this.ModelState.AddModelError("Email", this.T("The email address you provided is too long.").Text);
}
else if ( !Regex.IsMatch(input.Email, UserPart.EmailPattern, RegexOptions.IgnoreCase) )
{
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
this.ModelState.AddModelError("Email", this.T("You must specify a valid email address.").Text);
}
else if ( !this.myService.VerifyEmailUnicity(customUserPart.User.Id, input.Email) )
{
this.ModelState.AddModelError("Email", this.T("This email address is already in use.").Text);
}
}
if ( !this.ModelState.IsValid )
{
return this.View(input);
}
Debug.Assert(customUserPart != null, "customUserPart != null");
var user = customUserPart.User;
var userParams = new CreateUserParams(user.UserName, input.Password, input.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true);
this.myService.ActivateCustomUser(customUserPart.Id, userParams);
this.notifier.Add(NotifyType.Information, this.T("Your account was activated. You can now log in."));
return this.RedirectToAction("LogOn", "Account", new { area = "Orchard.Users" });
}
The interesting stuff happens in MyService.cs.
We designed the activation system so that you can still leverage all the features of the Orchard.User Module like Email-Verifcation.
For this we've implemented some CustomSettings, where you can decide if your user get's completely activated when an ActivationCode is used or if you trigger the normal Orchard mechanism.
I guess it's best to checkout the module and step through the code in Visual Studio.
Here a two screenshots of our activation views.
Step 1 - Enter your activation code
Step 2 - Fill in the remaining fields
Profit!
All the additional source is to make use of the CustomUser / ActivationCode in Workflows, Events, Tokens, etc. But I leave this for you to discover.
If you want more detailed descriptions of the source on GitHub let me know.
Hope this helps!
I just upgrade MvcSiteMapProvider from v3 to v4.6.3.
I see the upgrade note indicate:
In general, any reference to System.Web.SiteMap.Provider will need to be updated to MvcSiteMapProvider.SiteMaps.Current
I am trying to get the sitemap node by using:
SiteMaps.Current.FindSiteMapNode(rawUrl)
But it always return null
I looked into the code. In the sitemap it's actually calling the function:
protected virtual ISiteMapNode FindSiteMapNodeFromUrlMatch(IUrlKey urlToMatch)
{
if (this.urlTable.ContainsKey(urlToMatch))
{
return this.urlTable[urlToMatch];
}
return null;
}
It's trying to find a match in the urlTable.
I am using Default implementation of XmlSiteMapProvider .
It define var url = node.GetAttributeValue("url");
siteMapNode.Url = url;
siteMapNode.UrlResolver = node.GetAttributeValue("urlResolver");
So if I did not define url or urlResolver attribute in the .sitemap file. These variables a set to empty string, when generate the node.
And when this nodes are passed to AddNode function in SiteMap.
When adding the node
bool isMvcUrl = string.IsNullOrEmpty(node.UnresolvedUrl) && this.UsesDefaultUrlResolver(node);
this code will check if there is url or urlResolver
// Only store URLs if they are clickable and are configured using the Url
// property or provided by a custom URL resolver.
if (!isMvcUrl && node.Clickable)
{
url = this.siteMapChildStateFactory.CreateUrlKey(node);
// Check for duplicates (including matching or empty host names).
if (this.urlTable
.Where(k => string.Equals(k.Key.RootRelativeUrl, url.RootRelativeUrl, StringComparison.OrdinalIgnoreCase))
.Where(k => string.IsNullOrEmpty(k.Key.HostName) || string.IsNullOrEmpty(url.HostName) || string.Equals(k.Key.HostName, url.HostName, StringComparison.OrdinalIgnoreCase))
.Count() > 0)
{
var absoluteUrl = this.urlPath.ResolveUrl(node.UnresolvedUrl, string.IsNullOrEmpty(node.Protocol) ? Uri.UriSchemeHttp : node.Protocol, node.HostName);
throw new InvalidOperationException(string.Format(Resources.Messages.MultipleNodesWithIdenticalUrl, absoluteUrl));
}
}
// Add the URL
if (url != null)
{
this.urlTable[url] = node;
}
Finally no url is add to the urlTable, which result in FindSiteMapNode cannot find anything.
I am not sure if there needs to be specific configuration. Or should I implement custom XmlSiteMapProvider just add the url.
ISiteMapNodeProvider instances cannot use the FindSiteMapNode function for 2 reasons. The first you have already discovered is that finding by URL can only be done if you set the url attribute explicitly in the node configuration. The second reason is that the SiteMapBuilder doesn't add any of the nodes to the SiteMap until all of the ISiteMapNodeProvider instances have completed running, so it would be moot to add the URL to the URL table anyway.
It might help if you explain what you are trying to accomplish.
The ISiteMapNodeProvider classes have complete control over the data that is added to the SiteMapNode instances and they also have access to their parent SiteMapNode instance. This is generally all that is needed in order to populate the data. Looking up another SiteMapNode from the SiteMap object while populating the data is not supported. But as long as the node you are interested in is populated in the same ISiteMapNodeProvider instance, you can just get a reference to it later by storing it in a variable.
Update
Okay, I reread your question and your comment and it now just seems like you are looking in the wrong place. MvcSiteMapProvider v4 is no longer based on Microsoft's SiteMap provider model, so using XmlSiteMapProvider doesn't make sense, as it would sidestep the entire implementation. The only case where this might make sense is if you have a hybrid ASP.NET and ASP.NET MVC application that you want to keep a consitant menu structure between. See Upgrading from v3 to v4.
There are 2 stages to working with the data. The first stage (the ISiteMapBuilder and ISiteMapNodeProvider) loads the data from various sources (XML, .NET attributes, DynamicNodeProviders, and custom implementations of ISiteMapNodeProvider) and adds it to an object graph that starts at the SiteMap object. Much like Microsoft's model, this data is stored in a shared cache and only loaded when the cache expires. This is the stage you have been focusing on and it definitely doesn't make sense to lookup nodes here.
The second stage is when an individual request is made to access the data. This is where looking up data based on a URL might make sense, but there is already a built-in CurrentNode property that finds the node matching the current URL (or more likely the current route since we are dealing with MVC) which in most cases is the best approach to finding a node. Each node has a ParentNode and ChildNodes properties that can be used to walk up or down the tree from there.
In this second stage, you can access the SiteMap data at any point after the Application_Start event such as within a controller action, in one of the built in HTML helpers, an HTML helper template in the /Views/Shared/DisplayTemplates/ directory, or a custom HTML helper. This is the point in the application life cycle which you might call the lines SiteMaps.Current.FindSiteMapNode(rawUrl) or (more likely) SiteMaps.Current.CurrentNode to get an instance of the node so you can inspect its Attributes property (the custom attributes).
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
var currentNode = MvcSiteMapProvider.SiteMaps.Current.CurrentNode;
string permission = currentNode.Attributes.ContainsKey("permission") ? currentNode.Attributes["permission"].ToString() : string.Empty;
string programs = currentNode.Attributes.ContainsKey("programs") ? currentNode.Attributes["programs"].ToString() : string.Empty;
string agencies = currentNode.Attributes.ContainsKey("agencies") ? currentNode.Attributes["agencies"].ToString() : string.Empty;
// Do something with the custom attributes of the About page here
return View();
}
The most common usage of custom attributes is to use them from within a custom HTML helper template. Here is a custom version of the /Views/Shared/DisplayTemplates/SiteMapNodeModel.cshtml template that displays the custom attributes. Note that this template is called recursively by the Menu, SiteMapPath, and SiteMap HTML helpers. Have a look at this answer for more help if HTML helper customization is what you intend to do.
#model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
#using System.Web.Mvc.Html
#using MvcSiteMapProvider.Web.Html.Models
#if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") {
<text>#Model.Title</text>
} else if (Model.IsClickable) {
if (string.IsNullOrEmpty(Model.Description))
{
#Model.Title
}
else
{
#Model.Title
}
} else {
<text>#Model.Title</text>
}
#string permission = Model.Attributes.ContainsKey("permission") ? Model.Attributes["permission"].ToString() : string.Empty
#string programs = Model.Attributes.ContainsKey("programs") ? Model.Attributes["programs"].ToString() : string.Empty
#string agencies = Model.Attributes.ContainsKey("agencies") ? Model.Attributes["agencies"].ToString() : string.Empty
<div>#permission</div>
<div>#programs</div>
<div>#agencies</div>
I have an application which use to write contacts using ContactList.commit(). I am using this app on 2 different mobiles, one is Nokia X2-02 and other Samsung DUOS E2252.
When I use this code in Samsung DUOS, it goes inside if statement that means it supports these fields of Contact.TEL and Contact.ATTR_MOBILE, and probably also executes c.addString(...) statement. But when committing using c.commit() it throws PIMException and simply ignores the whole contact, while using the same application in Nokia phone it works fine. I wonder if Samsung doesn't supports ATTR_MOBILE field, it should simply ignore that filed but instead of that it throws exception and ignores the whole contact. What I want to do is simply ignore that particular field if it doesn't add that or whatever rather than ignoring the contact. My code for this portion is below
if (clist.isSupportedField(Contact.TEL)) {
if ((clist.isSupportedAttribute(Contact.TEL, Contact.ATTR_HOME)) && (contact.getPhoneHome() != null)) {
c.addString(Contact.TEL, Contact.ATTR_HOME, contact.getPhoneHome());
}
if ((clist.isSupportedAttribute(Contact.TEL, Contact.ATTR_PAGER)) && (contact.getPager() != null)) {
c.addString(Contact.TEL, Contact.ATTR_PAGER, contact.getPager());
}
if ((clist.isSupportedAttribute(Contact.TEL, Contact.ATTR_MOBILE)) && (contact.getPhoneMobile() != null)) {
c.addString(Contact.TEL, Contact.ATTR_MOBILE, contact.getPhoneMobile());
}
} c.commit();
This should be a very simple question, but I'm finding it surprisingly hard to find an answer.
I'm creating custom pages using hook_menu that have both static content and dynamic aspects, (mainly from sql queries and views embeds etc...). My question is, how does drupal's cache system treat these custom pages?
I'm concerned because as the traffic ramps up on some occasions, I feel like I need some kind of caching control over the display of these pages, but at this point, I have no idea if they're automatically being cached, or if I need to somehow specify to drupal that I do indeed want them cached.
Just to clarify, these pages are not admin pages, but are accessible by anyone.
The result of a menu callback is cached only when the menu callback returns the output; when the menu callback prints the output, it's not cached.
The code execute to bootstrap Drupal when a page is requested is the following:
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$return = menu_execute_active_handler();
// Menu status constants are integers; page content is a string.
if (is_int($return)) {
switch ($return) {
case MENU_NOT_FOUND:
drupal_not_found();
break;
case MENU_ACCESS_DENIED:
drupal_access_denied();
break;
case MENU_SITE_OFFLINE:
drupal_site_offline();
break;
}
}
elseif (isset($return)) {
// Print any value (including an empty string) except NULL or undefined:
print theme('page', $return);
}
drupal_page_footer();
drupal_page_footer() is the function that caches the result.
function drupal_page_footer() {
if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
page_set_cache();
}
module_invoke_all('exit');
}
page_set_cache() is the function that does the real work.
function page_set_cache() {
global $user, $base_root;
if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) {
// This will fail in some cases, see page_get_cache() for the explanation.
if ($data = ob_get_contents()) {
if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
$data = gzencode($data, 9, FORCE_GZIP);
}
ob_end_flush();
cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
}
}
}
The content is compressed (if the zlib is enabled), and saved in the cache.
If you want the cache the output of a custom menu callback, then you just need to return the output, instead of printing it directly.
function mymodule_callback() {
// …
return $output;
}
Instead of caching the output of the page, you can cache the data the module used to build its output. If, in example, the output data is obtained with an SQL query, you can cache the result of the query.