Quantcast
Channel: General .NET – Brian Pedersen's Sitecore and .NET Blog
Viewing all 167 articles
Browse latest View live

Sitecore check access and roles programatically

$
0
0

The Sitecore security model have changed over time, but the general API to check security access and roles have been stable for many many years.

CHECK IF USER HAS ACCESS TO AN ITEM:

To check if a user have access to a Sitecore item:

// Get Sitecore item
var item = Sitecore.Context.Database.GetItem("/sitecore/content/home/item");

// Check if the current context user 
// have read access to the item
bool canRead = item.Access.CanRead();

// Check if a named user have read access
// to an item
using (new Sitecore.Security.Accounts.UserSwitcher(@"extranet\username", true))
{
  bool canRead = item.Access.CanRead();
}

CHECK IF USER HAVE A CERTAIN ROLE:

The Sitecore User object has a Roles property, but this property does only list the immediate roles, not the roles obtained from roles-in-roles. To check for all roles, you need to use the IsInRole property:

using Sitecore.Security.Accounts;

// Get the role to check
// Remember to prefix the role name with 
// the domain name
Role role = Role.FromName(@"extranet\rolename");

// Check if user have the role
var isInRole = Sitecore.Context.User.IsInRole(role);

MORE TO READ:


c# Async fire and forget

$
0
0

Often in application development you want a process to call another thread and continue the process flow, without waiting for a response from the called thread. This pattern is called the “fire and forget” pattern.

I myself are oblivious to async coding, but these methods have proven to be functioning in very large scale systems.

THE OLD WAY: USING THREADPOOL 

The System.Threading.ThreadPool method have been around since .NET 1.0. The threadpool is a pool of threads that are available for you to use. The QueueUserWorkItem puts the method into a queue and executes the method when there is an available thread.

Please note that when the async method is called, the parameter is converted into an object, and you need to cast it back to the strong type before you can work with it:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
  public class MessageData
  {
    public string UserName { get; set; }
    public string IpAddress { get; set; }
    public string UserAgent { get; set; }
  }

  public class MyService
  {
    public static void DoWork(MessageData messageData)
    {
      ThreadPool.QueueUserWorkItem(new MyService().DoWorkAsync, messageData);
    }

    private void DoWorkAsync(object context)
    {
      try
      {
        MessageData messageData = (MessageData)context;        
        // Do stuff with the messageData object
      }
      catch (Exception ex)
      {
        // Remember that the Async code needs to handle its own
        // exceptions, as the "DoWork" method will never fail
      }
    }
  }
}

THE NEW WAY: TASK.FACTORY.STARTNEW

The Task class was introduced in .NET 4.something and is part of the new async/await keywords that makes async coding easier. The Task.Factory.StartNew() basically does the same as the TreadPool.QueueUserWorkItem but works with strong types:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
  public class MessageData
  {
    public string UserName { get; set; }
    public string IpAddress { get; set; }
    public string UserAgent { get; set; }
  }

  public class MyService
  {
    public static void DoWork(MessageData messageData)
    {
      Task.Factory.StartNew(() => DoWorkAsync(messageData));
    }

    private void DoWorkAsync(MessageData messageData)
    {
      try
      {
        // Do stuff with the messageData object
      }
      catch (Exception ex)
      {
        // Remember that the Async code needs to handle its own
        // exceptions, as the "DoWork" method will never fail
      }
    }
  }
}

MORE TO READ:

 

Azure Application Insights – Multiple services from same server

$
0
0

This trick enables you to have multiple services from the same server log to the same Application Insights instance, and at the same time distinguish between each service.

Application Insights

Application Insights Telemetry data

In the example above, I have 2 services from the same server. Each service have their own instance name. The @LTBP is a web server, and the @CloudDataloggerV1 is a windows console application.

To do this, you need to add a new TelemetryInitializer that sets the RoleInstance name.

STEP 1: CREATE NEW TELEMETRYINITIALIZER

This simple class adds the name of my service to the RoleInstance property:

internal class CloudRoleNameInitializer : ITelemetryInitializer
{
  private readonly string _roleName;

  public CloudRoleNameInitializer()
  {
    _roleName = "CloudDataLoggerV1";
  }

  public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
  {
    telemetry.Context.Cloud.RoleInstance = _roleName;
  }
}

STEP 2: ADD THE TELEMETRYINITIALIZER TO THE APPLICATIONINSIGHTS INSTANCE

You can now add the CloudRoleNameInitializer to your ApplicationInsights initializing code. This example is taken from the console application’s setup code:

public static class ApplicationInsights
{
  private static TelemetryClient _telemetryClient;

  public static void Initialize()
  {
    TelemetryConfiguration configuration = TelemetryConfiguration.Active;
    configuration.InstrumentationKey = Settings.ApplicationInsights.InstrumentationKey;
    configuration.TelemetryInitializers.Add(new CloudRoleNameInitializer());
    QuickPulseTelemetryProcessor processor = null;
    configuration.TelemetryProcessorChainBuilder
      .Use((next) =>
      {
        processor = new QuickPulseTelemetryProcessor(next);
        return processor;
      })
      .Build();

    var QuickPulse = new QuickPulseTelemetryModule();
    QuickPulse.Initialize(configuration);
    QuickPulse.RegisterTelemetryProcessor(processor);

    _telemetryClient = new TelemetryClient(configuration);
    Telemetry = _telemetryClient;
  }
  public static TelemetryClient Telemetry { get; private set; }

}

MORE TO READ:

 

Sitecore delete old item versions

$
0
0

Are your Sitecore editors version-trigger-happy? Is your master database items drowning in old versions that will never be used – or is just exact replicas of the previous version?

Many versions of the same item

Many versions of the same item

Fear not, you can in fact – with a little bit of code of course – recycle versions of items.

This tiny class will recursively run through items from the root of your choice, and recycle versions, except those you wish to keep:

namespace MyCode
{
  private class Cleanup
  {
    public void CleanUpItemVersions(Item item, int maxVersionCount)
    {
      int latestVersionNumber = item.Versions.GetLatestVersion().Version.Number;
      if (latestVersionNumber > maxVersionCount)
      {
        int latestVersionToKeep = latestVersionNumber - maxVersionCount;
        RecycleVersions(item, latestVersionToKeep);
      }

      foreach (Item child in item.Children)
      {
        CleanUpItemVersions(child, maxVersionCount);
      }
    }

    private static void RecycleVersions(Item item, int latestVersionToKeep)
    {
      Item[] versions = item.Versions.GetVersions();
      foreach (Item version in versions)
      {
        if (version.Version.Number <= latestVersionToKeep)
        {
          version.RecycleVersion();
        }
      }
    }
  }
}

USAGE:

This method will clean all but the latest 3 version from all english items below /sitecore/content/home:

publicvoid CleanUpItemVersions()
{
  Database master = Database.GetDatabase("master");
  Item rootItem = master.GetItem("(sitecore/content/home", LanguageManager.GetLanguage("en"));
  var cleanup = new Cleanup();
  cleanup.CleanUpItemVersions(rootItem, 3);
}

MORE TO READ:

 

Sitecore disable automatic lock on create items

$
0
0

From Sitecore 9, Sitecore changed the default behavior when creating items in the content editor. As per default, any item is locked when created, and will only be unlocked if the user unlocks it, or the user saves the item, and the AutomaticUnlockOnSaved config property is true.

Item is always locked on create

Item is always locked on create

Unfortunately, Sitecore forgot to include a DoNotLockOnCreate property, so it is impossible to get rid of this feature – unless we do some coding.

This piece of code is a collaboration between Sitecore Support and myself, and will work ONLY WHEN CREATING ITEMS FROM TEMPLATES. Items created based on a branch will stay locked.

STEP 1: EXTEND THE addFromTemplate pipeline

The addFromTemplate pipeline runs every time an item is created. We add a new processor that will create the new item, and then call the item:checkin command to unlock the item.

using Sitecore;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.ItemProvider.AddFromTemplate;
using System;

namespace MyCode
{
  public class UnlockItem : AddFromTemplateProcessor
  {
    public override void Process(AddFromTemplateArgs args)
    {
      Assert.IsNotNull(args.FallbackProvider, "FallbackProvider is null");

      try
      {
        var item = args.FallbackProvider.AddFromTemplate(args.ItemName, args.TemplateId, args.Destination, args.NewId);
        if (item == null)
          return;
        Context.ClientPage.SendMessage(this, "item:checkin");
        args.ProcessorItem = args.Result = item;
      }
      catch (Exception ex)
      {
        Log.Error(this.GetType() + " failed. Removing item " + args.NewId, ex, this);
        var item = args.Destination.Database.GetItem(args.NewId);
        item?.Delete();
        args.AbortPipeline();
      }
    }
  }
}

STEP 2: ADD THE PROCESSOR TO THE addFromTemplate pipeline

<sitecore>
  <pipelines>
    <group>
      <pipelines>
        <addFromTemplate>
          <processor type="MyCode.UnlockItem, MyDll"/>
        </addFromTemplate>
      </pipelines>
    </group>
  </pipelines>
</sitecore>

The code will now run every time you create a new item, unlocking the item immediately after. As stated before, this hack will only work when creating new items based on templates. Items created from a branch will keep a lock on the top branch item.

MORE TO READ:

Your session ID is being reused and there is nothing you can do about it

$
0
0

In many years we have been used to the fact that the ASP.Net session ID uniquely identifies one session, and one session only. Back in 2006, Margaret Rouse from TechTarget even wrote in the definition of a session ID that:

Every time an Internet user visits a specific Web site, a new session ID is assigned. Closing a browser and then reopening and visiting the site again generates a new session ID. However, the same session ID is sometimes maintained as long as the browser is open, even if the user leaves the site in question and returns. In some cases, Web servers terminate a session and assign a new session ID after a few minutes of inactivity.

Margaret Rouse, TechTarget

But a new feature in modern browsers changes this paradigm. If you in the Google Chrome browser selects the “Continue where you left off” feature:

Google Chrome Setting

Google Chrome Setting

… the browser will remember the session ID forever, even when a new session is started.

EXAMPLE:

I have enabled the “Continue where you left off” feature in my Google Chrome.
This is my current session ID.

Current Session ID

Current Session ID

For the sake of this test, my session will expire in 1 minute. I am using a SQL server as my session provider, so I can find my session:

Session in SQL

Session in SQL

My ASP.Net Global.asax Session_Start() event is fired when my session is created:

protected void Session_Start(object sender, EventArgs e)
{
  // do stuff
}

I now close my browser, and wait 1 minute. After 1 minute of inactivity, my session expires, and the session is removed from the Session state provider:

Session Provider is empty

Session Provider is empty

When reopen my browser, I would have expected my session ID to be recreated. But no, a new session with the same session ID is created:

New session created with same ID

New session created with same ID

ASP.Net will fire the Global.asax Session_Start() event as expected, since a new session have been created.

WHY IS THIS IMPORTANT TO KNOW?

This behavior means that you cannot use the session ID to identify one user session. Any data collection cannot rely on the session ID as the unique identifier. Collecting anonymous user statistics based on session ID will become skewed because you will have multiple session_start events for one session ID.

As an edge case, we risk that the session ID will no longer be considered as anonymous. The GDPR rules clearly states that any user-identifiable data cannot be collected without the consent of the user. When the session ID does not change, one could argue that the session ID is user identifiable just as the IP address or the user name, because it will live on the user machine forever.

WHAT CAN YOU DO ABOUT IT?

It is possible to generate a new session ID, but it requires to do some postbacks, so this solution is only feasible as a method to generate a new session ID when you log in, or when a user have finished a shopping transaction.

This StackOverflow thread discusses the possibilities of creating a new session ID.

If you plan on using the session ID as a data collection key, don’t. Instead, create your own key, and use that. For ASP.Net solutions that have a Global.asax file, you can create a new “session key” every time a new session starts:

protected void Session_Start(object sender, EventArgs e)
{
  HttpContext.Current.Session.Add("s_id", Guid.NewGuid().ToString());
}

In your data collection methods, replace SessionID with the custom property:

public static void Collect(HttpContext context, string behavior)
{
  if (context != null && context.Session != null)
  {
    // Don't do this
    context.Session.SessionID;
    // Use this instead
    context.Session["s_id"].ToString()
  }
}

MORE TO READ:

 

Sitecore Pipelines – The great walkthrough

$
0
0

I have previously stated my enthusiasm for the Sitecore Pipeline concept. This article describes how to create your own, how to add parameters, and which properties to use.

WHAT IS A SITECORE PIPELINE?

It is best described as a simple form of dependency injection pattern, or maybe more of a config-file-configurable method chaining pattern. The idea is, that if you have a task to perform, and this task requires several independent sub-tasks to run in sequence, you can split these sub-tasks into separate methods (called processors), and then run them in sequence using the same parameters for each method.

The best known Sitecore pipeline is the httpRequestBegin pipeline that runs for every request:

<httpRequestBegin>
  <processor type="Sitecore.Pipelines.PreprocessRequest.CheckIgnoreFlag, Sitecore.Kernel" />
  ..
  <processor type="Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel" />
  <processor type="Sitecore.Pipelines.HttpRequest.UserResolver, Sitecore.Kernel" />
  ...
  <processor type="Sitecore.Pipelines.HttpRequest.ExecuteRequest, Sitecore.Kernel" />
</httpRequestBegin>

WHY NOT JUST MAKE ONE BIG METHOD? OR ONE CLASS THAT USES DEPENDENCY INJECTION?

Pipelines give you the following advantages:

  • Extensibility: You can easily add another processor to any pipeline, thus adding to the functionality of that task.
  • Replaceability: You can easily replace any processor with one of your own, replacing the way Sitecore works.
  • Testability: Pipelines allow you to break your code into very small processors. Any change of one processor does not interfere with any of the other processors, making testing easier.
  • It’s easy: It’s really easy to work with. It’s standard .NET code.

ENOUGH SMALL TALK, LETS MAKE A PIPELINE:

A pipeline consists of a class or Arguments, inheriting from Sitecore.Pipelines.PipelineArgs, and a configuration section that describes which tasks (processors) to execute in which sequence.

The arguments class is usually a POCO class with getters and setters:

public class MyArgs : PipelineArgs
{
  public string Argument1
  {
    get; set;  
  }

  public int Argument2
  {
    get; set;
  }
}

The configuration section determines the pipeline name, and which processors to run:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore>
    <pipelines>
      <my_pipeline>
        <processor type="MyCode.MyPipeline.MyProcessor1, MyDll"/>
        <processor type="MyCode.MyPipeline.MyProcessor2, MyDll"/>
        <processor type="MyCode.MyPipeline.MyProcessor3, MyDll"/>
      </my_pipeline>
    </pipelines>
  </sitecore>
</configuration>

HOW DOES THE PROCESSOR LOOK LIKE?

The processor needs a method called “Process” that takes one argument, “args” of the “MyArgs” type:

namespace MyCode.MyPipeline
{
  public class MyProcessor1
  {
    public void Process(MyArgs args)
    {
      try
      {
        DoSomething(args.Argument1);
        args.Argument2 = DoAnotherThing();
      }
      catch (Exception ex)
      {
        args.AddMessage("An error occurred");
        args.AbortPipeline();
      }
    }
  }
}

HOW TO EXECUTE A PIPELINE:

The CorePipeline.Run() method executes a pipeline:

MyArgs args = new MyArgs();
args.Argument1 = "my string";
args.Argument2 = 42;

CorePipeline.Run("my_pipeline", args);
if (args.Aborted)
{
  foreach (var message in args.GetMessages())
    Log.Error(message.Text, this);
}

HOW TO ADD PARAMETERS TO A PIPELINE:

If you add a subitem to the configuration:

<processor type="MyCode.MyPipeline.MyProcessor1, MyDll">
  <MyParameter>value</MyParameter>
</processor

This subitem will by magic appear in any property of the same name in the processor:

namespace MyCode.MyPipeline
{
  public class MyProcessor1
  {
    public string MyParameter { get; set; }
	
	public void Process(MyArgs args)
    {
	   // value of MyParameter is "value" 
    }
  }
}

In fact the same advanced configuration rules apply to pipelines as all other Sitecore items.

PROCESSOR ATTRIBUTES:

You can decorate your processors with attributes:

<processor type="MyCode.MyPipeline.MyProcessor1, MyDll" method="MyMethod" name="MyName" runIfAborted="false" resolve="false" reusable="false"/>
  • type: The class to run
  • method: The method name to run. If not included, you use the name “Process”
  • name: Just a descriptive name
  • runIfAborted: Processors with this attribute set to true will run even if the pipeline is aborted. This is useful as a cleanup property if something went wrong.
  • resolve: Set this property to true to allow the Sitecore Dependency Injection engine to resolve the type name to run.
  • reusable: This property allows your processor to be run in a transient scope.

GOOD PRACTISES:

  • If you throw an exception in any processor, the pipeline will stop its execution, and the CorePipeline.Run() also throws an exception. This means that your “runIfAborted=true” processor will not run. You should therefore catch any exception in the processor, and use the args.AbortPipeline() method to control execution flow.
  • Make your processors as atomic as possible. Some of my best processors consists of one line of code. Your processor should do one task and one task only.
  • You can debug the performance of your processors, but this slows down the performance of all pipelines, so use this with caution.
  • Processors are only created once and kept alive for the entire application lifetime. Any constructors are only called once and local variables are not reset, so treat your processors as a stateless piece of code.
    This is true unless you use the reusable=”false” property, but beware that object construction takes time and this feature will slow down your pipeline.
  • Any configuration error will result in an exception when CorePipeline.Run() is called. If the method or class is not found for one processor, the whole pipeline is not executed.

MORE TO READ:

Deserialize XML array to string[] and how to allow JSON to be deserialized into the same POCO class

$
0
0

How do you deserialize the classic XML array to an string[] type?

The XML representation of an array is a list of elements:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <MyXml>
      <Id>657467</Id>
	  <MyArray>
	    <element>A</element>
	    <element>B</element>
	    <element>C</element>
	  </MyArray>
   </MyXml>
</root>

And the MyArray would normally be deserialized to an annoying class:

// I don't want this class
[XmlRoot(ElementName="MyArray")]
public class MyArray 
{
  [XmlElement(ElementName="element")]
  public List<string> Element { get; set; }
}

// I Just want a string array:
public string[] MyArray { get; set; }

The solution is to use the XmlArray and XmlArrayItem attributes. These properties specifies that the XmlSerializer must serialize the XML structure as an array:

[XmlArray(ElementName = "MyArray", IsNullable = true)]
[XmlArrayItem(ElementName = "element", IsNullable = true)]
public string[] Myarray 
{
  get; set;
}

The XmlArray attribute determines that the MyArray XML element is an array, and the XmlArrayItem attribute determines that each array element is called element in the XML.

Now, the XmlSerializer will serialize the element to an array:

string contents = "this is my xml string";

XmlSerializer serializer = new XmlSerializer(typeof(MyXml));
using (TextReader reader = new StringReader(contents))
{
  var myclass = (MyXml)serializer.Deserialize(reader);
}

SO WHY IS THIS IMPORTANT?

This is important if you import XML and JSON, because you would rather not have to define this JSON just to cope with the XML array format:

"MyArray": {
  "element": [
    "A",
    "B",
    "C"
  ]

Instead, if you just decorate your POCO class with both XML and JSON properties:

[JsonProperty(PropertyName = "MyArray")]
[XmlArray(ElementName = "MyArray", IsNullable = true)]
[XmlArrayItem(ElementName = "element", IsNullable = true)]
public string[] Myarray 
{
  get; set;
}

You can use the proper JSON array notation:

"MyArray": [    
    "A",
    "B",
    "C"
]

MORE TO READ:


Sitecore get Context from fully qualified URL

$
0
0

One of the basic Sitecore concepts is the concept of a context. The context is resolved from the HTTP Context to Sitecore and determines the database, language, user domain, content start item etc. The context is defined in the App_Config/Sitecore.config configuration file in the <sites> section:

<sites>
  <site name="shell" ... rootPath="/sitecore/content" startItem="/home" language="en" contentLanguage="en" database="core" domain="sitecore" ... />
  ...
  <site name="website_en" ... hostName="www.site.com|www.site.eu|www.site.co.uk" language="en" database="web" domain="extranet" ... />
  <site name="website_da" ... hostName="www.site.dk" language="da" database="web" domain="extranet" ... />
</sites>

The context is resolved from a first-match algorithm where Sitecore goes through the list of sites and finds the first site that matches the criteria.

You can go through the sites list yourself using the Sitecore.Configuration.Factory.GetSiteInfoList() method. So with a little guesswork you can get the context from any URL, assuming that:

  • The url must be fully qualified (i.e. https://www.site.com/url)
  • The site section contains the URL in the hostName property
using Sitecore.Configuration;
using Sitecore.Sites;
using Sitecore.Web;

public static SiteContext GetSiteContext(Uri url)
{
  string hostName = url.Host;

  foreach (SiteInfo site in Factory.GetSiteInfoList())
  {
    foreach(string sitecoreHostName in site.HostName.Split('|'))
    {
      if (string.Equals(sitecoreHostName, hostName, StringComparison.InvariantCultureIgnoreCase))
      {
        return new SiteContext(site);
      }
    }
  }

  return null;
}

WHAT IS THE HostName VS TargetHostName?

Sitecore uses 2 definitions to determine URL’s:

  • The hostName is the incoming URL. You can define a list of incoming URL’s in a pipe separated list, and each URL will be resolved to that site context.
  • The targetHostName is the outgoing URL and determines which URL to create when asking for a fully qualified URL.

MORE TO READ:

Sitecore start scheduled task manually

$
0
0

Sitecore scheduled tasks are background tasks running on a schedule. But never when you need it, especially when developing the tasks. You can trigger these tasks manually if you please.

THE EASY WAY: INSTALL SITECORE POWERSHELL

The Sitecore PowerShell module have a built in task runner. When installed, you can right click the task you wish to run and click “Run Task Schedule

Powrshell Run Task

Powrshell Run Task

THE FUN WAY: PROGRAM YOUR OWN TASK STARTER:

You can, of course, write your own task runner, create a nice scheduled task page, and display all possible tasks where you can start them individually:

Scheduled Tasks List

Scheduled Tasks List

To get a list of all scheduled tasks:

private const string SCHEDULED_PATH = "/sitecore/system/Tasks//*[@@templatename='Schedule']";
  
foreach (Item item in Sitecore.Context.Database
  .SelectItems(SCHEDULED_PATH)
  .OrderBy(t => t.Parent.Name)
  .ThenBy(t => t.Name))
{
  ScheduleItem scheduleItem = new ScheduleItem(item);
  // Do stuff with the scheduleItem
}

To start a scheduled task:

Sitecore.Tasks.ScheduleItem scheduleItem = new Sitecore.Tasks.ScheduleItem(item);
scheduleItem.Execute();

MORE TO READ:

Change date format using .NET Regular Expressions

$
0
0

You can use Regex.Replace to change the format of a date string. For example if your input string is YYYY/MM/DD but you need to change it to YYYY-MM-DD, you can use this small method:

public static string ReformatDate(string input) 
{
   try {
      return Regex.Replace(input, 
            "\\b(?<year>\\d{2,4})/(?<month>\\d{1,2})/(?<day>\\d{1,2})\\b",
            "${year}-${month}-${day}", 
            RegexOptions.IgnoreCase,
            TimeSpan.FromMilliseconds(1000));
   }         
   catch (RegexMatchTimeoutException) {
      return input;
   }
}

Regular expressions are unreadable as usual, but the trick here lies in the way the year, month and day parts are grabbed and stored in “variables” to be used in the replacement part of the Regex.Replace:

  • Pattern: (?\\d{2,4}) grabs 2-4 digits and places them in a variable called “year”. The same goes for month and day.
  • Replacement: ${year} places the value grabbed from the input and places it into the output of the replace function.

With this in mind we can create lots of different search/replace patters:

  • Input: DD/MM/YYYY Output: YYYY-MM-DD
    Pattern: \\b(?<day>\\d{1,2})/(??<month>\\d{1,2})/(??<year>\\d{2,4})\\b
    Replacement: ${year}-${month}-${day}
  • Input: YYYY-MM-DD Output: MM/DD/YYYY
    Patern: \\b(??<year>\\d{2,4})-(??<month>\\d{1,2})-(??<day>\\d{1,2})\\b
    Replacement: ${month}/${day}/${year}

Would you like to change multiple dates from your input? Add RegexOptions.Multiline to the options:

public static string ReformatDate(string input) 
{
   try {
      return Regex.Replace(input, 
            "\\b(?<year>\\d{2,4})/(?<month>\\d{1,2})/(?<day>\\d{1,2})\\b",
            "${year}-${month}-${day}", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase,
            TimeSpan.FromMilliseconds(1000));
   }         
   catch (RegexMatchTimeoutException) {
      return input;
   }
}

Or how about changing dates from within an XML document?

<item>
 <start_date>2019/06/11</start_date>
 <expiration_date>2019/06/17</g:expiration_date>
</item>

Add the < > to the pattern and replacement variables:

public static string ReformatDate(string input) 
{
   try {
      return Regex.Replace(input, 
            ">\\b(?<year>\\d{2,4})/(?<month>\\d{1,2})/(?<day>\\d{1,2})\\b<", 
            ">${year}-${month}-${day}<", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase,
            TimeSpan.FromMilliseconds(1000));
   }         
   catch (RegexMatchTimeoutException) {
      return input;
   }
}

MORE TO READ:

 

200 OK vs 202 Accepted – Whats the difference?

$
0
0

When working with Azure cloud services like Azure Logic Apps or Azure Automation (Runbooks), you often come across that service endpoints return 202 Accepted instead of 200 OK on success. But why do they do that, and what is the difference between the two?

202 Accepted

202 Accepted

200 OK means that the request has succeeded and the processing of our request is done. The response is the final payload and the service will not take further actions.

202 Accepted on the other hand means that the request have been accepted for processing, and the service will now start. This does not mean that the processing itself is a success, nor that the process have ended.

The 202 accepted is therefor the equivalent of a fire-and-forget, and all you know is that the fire was a success. With 200 OK you should expect the processing not only to be done but also be successful.

MORE TO READ:

IdentityServer use IdentityModel to get user token and user info

$
0
0

Using IdentityServer have been made easier with the IdentityModel helper library. The library implements extension methods that maps the input and output to classes.

GET ACCESS TOKEN:

Use the PasswordTokenRequest and RequestPasswordTokenAsync to get the access token. replace your_domain, your_clientid and your_secret with the values from your Identity Server:

using IdentityModel.Client;

public string LoginUsingIdentityServer(string username, string password)
{
  var client = new HttpClient();
  PasswordTokenRequest tokenRequest = new PasswordTokenRequest() 
  { 
    Address = "http://your_domain/connect/token",
    ClientId = "your_clientid",
    ClientSecret = "your_secret",
    UserName = username,
    Password = password
  };
  
  var response = client.RequestPasswordTokenAsync(tokenRequest).Result;
  if (!response.IsError)
  {
     response.AccessToken;
  }
  else
  {
    throw new Exception("Invalid username or password");
  }
}

GET USERINFO:

First you need to ensure that the Identity Resource in Identity Server can return the claims from the user (claims is the fancy word for properties). If you use the Identity Server Admin interface, you can choose OPENID from the Resources/Identity Resources menu and select the claims visually:

Identity Server Claims

Identity Server Claims

Use the UserInfoRequest and pass your access token to GetUserInfoAsync to get the user claims:

using IdentityModel.Client;
using Newtonsoft.Json;
using System.Collections;
using System.Net.Http;

public void GetUserInfo(string accessToken)
{
  var client = new HttpClient();
  var userInfoRequest = new UserInfoRequest()
  {
    Address = "http://your_server/connect/userinfo",
    Token = accessToken
  };

  var response = client.GetUserInfoAsync(userInfoRequest).Result;
  if (response.IsError)
    throw new Exception("Invalid accessToken");

  dynamic responseObject = JsonConvert.DeserializeObject(response.Raw);
  if (responseObject.given_name != null && responseObject.family_name != null)
  {
    // do something with the name
    string name = responseObject.given_name.ToString() 
	              + " " 
				  + responseObject.family_name.ToString();
    }

    // If there is one role, the role property is a string.
	// There there is more roles, the role property is an array
	// To handle this, I use this clumsy statement:
    if (responseObject.role.Type == Newtonsoft.Json.Linq.JTokenType.String)
	{
      // do something with the roles
	string role = responseObject.role.ToString();
	}	
    else
    {
      foreach (var role in responseObject.role)
      {
        // do something with the roles
        string role = role.ToString();
      }
    }
  }
}

FOR SITECORE USERS:

If you use Sitecore, you can create a Virtual User based on the user info, and log into Sitecore using this virtual user.

using IdentityModel.Client;
using Newtonsoft.Json;
using Sitecore.Security.Authentication;
using System.Collections;
using System.Net.Http;

namespace MyCode
{
  public class VirtualUserFactory
  {
    public static Sitecore.Security.Accounts.User Create(string identityServerAccessToken)
    {
      var client = new HttpClient();
      var userInfoRequest = new UserInfoRequest()
      {
        Address = "http://your_server/connect/userinfo",
        Token = identityServerAccessToken
      };

      var response = client.GetUserInfoAsync(userInfoRequest).Result;
      if (response.IsError)
        throw new Sitecore.Exceptions.AccessDeniedException();

      dynamic responseObject = JsonConvert.DeserializeObject(response.Raw);

      Sitecore.Security.Accounts.User virtualUser = AuthenticationManager.BuildVirtualUser("extranet\\" + responseObject.preferred_username.ToString(), true);

      if (responseObject.given_name != null && responseObject.family_name != null)
        virtualUser.Profile.FullName = responseObject.given_name.ToString() + " " + responseObject.family_name.ToString();

      if (responseObject.email != null)
        virtualUser.Profile.Email = responseObject.email.ToString();

      if (responseObject.role.Type == Newtonsoft.Json.Linq.JTokenType.String)
        virtualUser.Roles.Add(Sitecore.Security.Accounts.Role.FromName(responseObject.role.ToString()));
      else
      {
        foreach (var role in responseObject.role)
        {
          virtualUser.Roles.Add(Sitecore.Security.Accounts.Role.FromName(role.ToString()));
        }
      }

      if (responseObject.sub != null)
        virtualUser.Profile.ProfileItemId = responseObject.sub.ToString();

      return virtualUser;
    }
  }
}

Logging into Sitecore uisng a virtual user is simple:

// Login the virtual user
Sitecore.Security.Authentication.AuthenticationManager.LoginVirtualUser(virtualUser);

MORE TO READ:

Which of my old Sitecore posts are still valid in Sitecore 9?

$
0
0

I have been writing Sitecore blog posts since April 2006. The first ones were for Sitecore 4.2. Now, 13 years later, some of the old posts are still valid, while others are obsolete as the Sitecore API have changed.

But which ones will still work on a Sitecore 9 installation?

I have skipped articles on XSLT and very specific WebForms articles. Although XSLT and WebForms are still around and working, no new projects should be based on these technologies.

First the shortlist. These articles are top 10 reads:

  1. LinkManager – Working with URL’s in Sitecore 6
  2. Sitecore.Links.LinkManager and the context
  3. Sitecore: Setting up Security on Languages
  4. Multiple languages in Sitecore
  5. Run Sitecore scheduled task at the same time every day
  6. Sitecore Image Parameters
  7. Create and publish items in Sitecore
  8. Sitecore Links with LinkManager and MediaManager
  9. Sitecore Virtual Users – authenticate users from external systems
  10. Sitecore Scheduled Tasks – Run on certain server instance

This is the list of all articles not specifically written for Sitecore 9 but the code still works. There are almost 60 articles not Sitecore 9 based, but still relevant:

2006

2008

2009

2010

2011

2012

2013

2014

2015

2016

2017

From 2017, Sitecore 9 was the standard, and I started writing articles on Sitecore 9. All articles from 2017 and forward will help you in Sitecore 9 development.

Newtonsoft serialize property that is an array and a string

$
0
0

IdentityServer4 has a strange way of returning roles. If there is only one role, the property is a string:

{
    "sub": "55568182-9273-4c6f-8d61-6555f7f02551",
    "role": "role001"
}

But if there is more than one role, the property is an array of strings:

{
    "sub": "55568182-9273-4c6f-8d61-6555f7f02551",
    "role": [
        "role001",
        "role002"
    ]
}

If the property is a string, and you try to serialize this into an IEnumerable<string>, you get the following error:

Could not cast or convert from System.String to System.Collections.Generic.IEnumerable`1[System.String].

JsonSerializationException: Error converting value “xxx” to type ‘System.Collections.Generic.IEnumerable`1[System.String]

Or

Could not cast or convert from System.String to System.String[].

JsonSerializationException: Error converting value “xxx” to type ‘System.String[]’

To serialize this into a model class, you will need to implement a JsonConverter, which is a custom converter that, in this case, checks if the property is one object or several before serializing.

STEP 1: IMPLEMENT THE CUSTOM CONVERTER

This JsonConverter checks the property before serializing to see if the property is an array or not. If not, it will output the single item as an array:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace MyCode
{
  internal class CustomArrayConverter<T> : JsonConverter
  {
    public override bool CanConvert(Type objectType)
    {
      return (objectType == typeof(List<T>));
    }

    public override object ReadJson(
      JsonReader reader,
      Type objectType,
      object existingValue,
      JsonSerializer serializer)
    {
      JToken token = JToken.Load(reader);
      if (token.Type == JTokenType.Array)
        return token.ToObject<List<T>>();
      return new List<T> { token.ToObjec<T>() };
    }

    public override bool CanWrite
    {
      get
      {
        return false;
      }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
      throw new NotImplementedException();
    }
  }
}

STEP 2: USE THE CUSTOM CONVERTER IN THE MODEL CLASS:

The custom converter is used as a property on the attribute:

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace MyCode
{
  [Serializable]
  public class UserInfoModel
  {
    [JsonProperty("sub")]
    public string ID { get; set; }

    [JsonProperty("role")]
    [JsonConverter(typeof(CustomArrayConverter<string>))]
    public IEnumerable<string> Roles { get; set; }
  }
}

The CustomArrayConverter will work on any type.

MORE TO READ: 


HttpClient POST or PUT Json with content type application/json

$
0
0

The HttpClient is a nifty tool for getting and sending data to a URL, but it works differently from the old fashioned WebRequest class.

The content type is added to the post data instead of added as a header parameter. So if you with to PUT or POST Json data, and you need to set the content type to application/json, specify the content type in the parameters of the StringContent that you POST:

string json;

var content = new StringContent(
  json, 
  System.Text.Encoding.UTF8, 
  "application/json"
  );

An example of a complete POST method that can take an object and POST it as Json could look like this:

using System.Net.Http;
using Newtonsoft.Json;

private static HttpClient _httpClient = new HttpClient();

public bool POSTData(object json, string url)
{
  using (var content = new StringContent(JsonConvert.SerializeObject(json), System.Text.Encoding.UTF8, "application/json"))
  {
    HttpResponseMessage result = _httpClient.PostAsync(url, content).Result;
    if (result.StatusCode == System.Net.HttpStatusCode.Created)
      return true;
    string returnValue = result.Content.ReadAsStringAsync().Result;
    throw new Exception($"Failed to POST data: ({result.StatusCode}): {returnValue}");
  }
}

This method is not utilizing the async properties of the HttpClient class. Click here to see how to use HttpClient with async.

MORE TO READ:

ID or Id? Naming conventions in code

$
0
0

This topic is not as toxic than the tabs-vs-spaces or 2 vs 4 indents discussion, but it’s still something every coder I have met have an opinion about, no matter the programming language:

Which is correct:

  • OrderId
  • OrderID

Lets solve this ancient mystery like any programming problem is solved: We Google it.

THE MERRIAM-WEBSTER DICTIONARY

According to Merriam-Webster, ID is short for identification, spelled with 2 capital letters. This matches the intention of OrderID as “Order identification“.

ID wins

WIKIPEDIA

In Wikipedia we can learn that Id is part of the Freudian psyche structural model, and is used to describe the Id, ego and super-ego. When searching for id however, the 2 capital letters “ID” leads to an article on Identity Document and Identifier, the intended description of the ID in OrderID.

ID wins

MICROSOFT .NET CAPITALIZATION CONVENTIONS

Microsoft does not agree with Merriam-Webster, and clearly states that Id should be written with one capital letter, as OrderId.

Id wins

COLLINS DICTIONARY

The Collins dictionary also states that ID is a noun and means identity or identification, while Id is short for Idaho.

ID wins

CAMEL CASING

You would think that camel casing would vote for Id, but no, when you have two letters, both should be upper case. When you have three, only the first one should be upper case.

ID wins

GOOGLE DEVELOPER DOCUMENTATION STYLE GUIDE

Google is on the ID team, unless its in string literals and enums:

Not Id or id, except in string literals or enums. In some contexts, best to spell out as identifier or identification.

ID wins, but since Id is allowed in some contexts, I will say this is a draw.

CONCLUSION

Well, ID is the winner over Id with a 5-2 victory. This does not make you a bad programmer should you choose to use Id instead of ID. As long as you do it consistently. And if in doubt, you could always waste a few more bytes and spell out the entire ID:

  • OrderIdentity

But tell me what do you think? OrderID or OrderId?

OrderID or OrderId

To orderID or orderId, that’s the question 

MORE TO READ:

Sitecore Publish Items using the PublishManager

$
0
0

Sitecore have two entries to publishing:

You should use the PublishManager. Sitecore introduced the Publishing Service which is an optional external publisher that is much faster than the built in publishing. It comes with a small price as there are differences to integration points, and publishing behavior. But if you use the PublishManager you will be ready to switch in case you need the improved performance and are willing to manage yet another external service.

Using the Publisher class will only publish through the built in publisher.

The PublishManager is very simple to use:

using Sitecore.Data.Items;
using System;
using Sitecore.Data;
using Sitecore.Publishing;

PublishOptions publishOptions = new PublishOptions(item.Database, Database.GetDatabase("web"), publishMode, item.Language, DateTime.Now);
publishOptions.RootItem = item;
publishOptions.Deep = deepPublish;
publishOptions.PublishRelatedItems = publishRelatedItems;
publishOptions.CompareRevisions = compareRevisions;

var handle = PublishManager.Publish(new PublishOptions[] { publishOptions });
PublishManager.WaitFor(handle);

The WaitFor() call is only needed if you wish to wait for the publish to end before continuing.

You can create a simple extension method that can publish an item like this:

using Sitecore.Data.Items;
using System;
using System.Globalization;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Publishing;

namespace MyCode
{
  public static class ItemExtensions
  {
    public static void PublishItem(this Item item, PublishMode publishMode, bool publishAsync = false, bool deepPublish = false, bool publishRelatedItems = false, bool compareRevisions = false)
    {
      
      if (item == null)
        return;

      PublishOptions publishOptions = new PublishOptions(item.Database, Database.GetDatabase("web"), publishMode, item.Language, DateTime.Now);
      publishOptions.RootItem = item;
      publishOptions.Deep = deepPublish;
      publishOptions.PublishRelatedItems = publishRelatedItems;
      publishOptions.CompareRevisions = compareRevisions;

      var handle = PublishManager.Publish(new PublishOptions[] { publishOptions });
      if (publishAsync)
        return;
      PublishManager.WaitFor(handle);
    }
  }
}

And you can use the extension method like this:

Item dbItem = Context.ContentDatabase.GetItem(xxx,xxx,xxx);
dbItem.PublishItem(PublishMode.SingleItem, false, false, false, false);

Thanks to Stephen Pope for reminding me of the existence of the PublishingManager class.

MORE TO READ:

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ‘await’ operator to the result of the call

$
0
0

This C# warning occurs if you call an async method from your non-async code.

Imagine you have this imaginary async method:

internal class QueueRepository
{
  public async Task AddMessage<T>(T message)
  {
    await _mock.AddMessageAsync(message);
  }
}

And you call the method from this imaginary non-async method:

QueueRepository rep = new QueueRepository();
rep.AddMessage("hello world");

The compiler will warn you with the following message:

Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ‘await’ operator to the result of the call.

In this case, the compiler is just being nice, telling you that the async code will run in its own thread, and the code calling the method is continuing to run alongside.

But if you follow the compiler’s suggestion, and add the ‘await’ operator, you get a compiler error:

Error CS4032 The ‘await’ operator can only be used within an async method. Consider marking this method with the ‘async’ modifier and changing its return type to ‘Task<IActionResult>’.

So what can you do?

SOLUTION 1: WAIT FOR THE METHOD

If you wish to wait for the method to finish before continuing, you can call .Wait():

QueueRepository rep = new QueueRepository();
rep.AddMessage("hello world").Wait();

SOLUION 2: FIRE AND FORGET

You are the grown up here, you would actually like to have the method run asynchronously. You can ignore the warning, or add the secret discards ‘_ = ‘ return variable:

QueueRepository rep = new QueueRepository();
_ = rep.AddMessage("hello world");

With the _ = return variable, you tell the compiler to leave you alone you know what you are doing. Please note that you need C# 7.0 for the discards to work.

MORE TO READ:

Sitecore use global and environment variables in config files

$
0
0

The Sitecore config files are a complex machine and it requires a healthy mind to work with it. Fortunately Sitecore have implemented a few tricks to ease our pain. This article focuses on 2 parts: global variable replacement and environment variable replacement.

GLOBAL VARIABLES:

Global variables are defined once and can then be reused in all your config settings.

Maybe you have seen these variables:

<sitecore>
  <sc.variable name="dataFolder" value="/App_Data" />
  <sc.variable name="mediaFolder" value="/upload" />
  <sc.variable name="tempFolder" value="/temp" />
</sitecore>

The sc.variable element defines the variable. The variables can then be used where you need them by writing $(variable_name):

<file value="$(dataFolder)/logs/myfile.log"/>
<setting name="IndexFolder" value="$(dataFolder)/indexes" />

The above variables are just the built in ones. You can create your own variables and use them too.

ENVIRONMENT VARIABLES:

Since Sitecore 8.2 we have been blessed with environment variable replacements too.

Let’s say you have an environment variable on your machine:

Global Environment Variables

Global Environment Variables

You can add the environment variable to the config by writing $(env:variable_name):

<setting name="Environment" value="$(env:ASPNETCORE_ENVIRONMENT)" />

This is especially nifty as you can deploy the same config file to different environments and control your application per server.

MORE TO READ:

 

Viewing all 167 articles
Browse latest View live