Wednesday, 16 October 2013

Orchard CMS Taxonomy Term Autoroute Token Slug

Edit 17/10/2013:
Refer to https://orchard.codeplex.com/workitem/20215 on updates regarding this feature.

Edit 11/11/2013:
Since writing this article I've discovered a better method which allows a full taxonomy term path to be slugified and returned into a token result: http://sheltonial.blogspot.com.au/2013/11/orchard-cms-taxonomy-term-autoroute.html

Recently (as of 17/10/2013) the Orchard CMS team has fixed a bug which was preventing taxonomy tokens from returning a value.

Currently I am working off changeset a62f0281ebdac780a1d3a583dbc5821ca8fbc6ce on version 1.7.1 and can confirm that this is working.

An example of an autoroute pattern that works is {Content.Fields.BlogPost.Category.Terms:0}, where [BlogPost] is the name of your content type, and [Category] is the name of the field within our content type, not the taxonomy or term.

This means we can start injecting taxonomy terms into our autoroute patterns and enjoy structuring a url hierarchy based on taxonomy terms and being able to interact with this new url data within queries and layer rules. This is fine if your taxonomy is a single word containing no spaces or special characters, but once you introduce other characters these are also injected into the URL.

The following code adds a new token called TermSlug which allows a taxonomy term to be slugified:

  • File to modify: \src\Orchard.Web\Modules\Orchard.Taxonomies\Tokens\TaxonomyTokens.cs
  • Usage: {Content.Fields.BlogPost.Category.TermsSlug:0} (refer to example above)
// Comment
using System;
using System.Linq;
using Orchard.Taxonomies.Fields;
using Orchard.Localization;
using Orchard.Tokens;
using Orchard.Autoroute.Services;

namespace Orchard.Taxonomies.Tokens {
    public class TaxonomyTokens : ITokenProvider {
        private readonly ISlugService _slugService;

        public TaxonomyTokens(ISlugService slugService)
        {
            T = NullLocalizer.Instance;
            _slugService = slugService;
        }

        public Localizer T { get; set; }

        public void Describe(DescribeContext context) {
            // Usage:
            // Content.Fields.Article.Categories.Terms -> 'Science, Sports, Arts'
            // Content.Fields.Article.Categories.Terms:0 -> 'Science'

            // When used with an indexer, it can be chained with Content tokens
            // Content.Fields.Article.Categories.Terms:0.DisplayUrl -> http://...

            context.For("TaxonomyField", T("Taxonomy Field"), T("Tokens for Taxonomy Fields"))
                   .Token("Terms", T("Terms"), T("The terms (Content) associated with field."))
                   .Token("Terms[:*]", T("Terms"), T("A term by its index. Can be chained with Content tokens."))
                   .Token("TermsSlug[:*]", T("Terms"), T("A term slug by its index. Can be chained with Content tokens."))
                   ;
        }

        public void Evaluate(EvaluateContext context) {

            context.For("TaxonomyField")
                   .Token("Terms", field => String.Join(", ", field.Terms.Select(t => t.Name).ToArray()))
                   .Token(
                       token => token.StartsWith("Terms:", StringComparison.OrdinalIgnoreCase) ? token.Substring("Terms:".Length) : null,
                       (token, t) => {
                           var index = Convert.ToInt32(token);
                           return index + 1 > t.Terms.Count() ? null : t.Terms.ElementAt(index).Name;
                       })
                    .Token(
                       token => token.StartsWith("TermsSlug:", StringComparison.OrdinalIgnoreCase) ? token.Substring("TermsSlug:".Length) : null,
                       (token, t) =>
                       {
                           var index = Convert.ToInt32(token);
                           return index + 1 > t.Terms.Count() ? null :  _slugService.Slugify(t.Terms.ElementAt(index).Name);
                       })
                // todo: extend Chain() in order to accept a filter like in Token() so that we can chain on an expression
                   .Chain("Terms:0", "Content", t => t.Terms.ElementAt(0))
                   .Chain("Terms:1", "Content", t => t.Terms.ElementAt(1))
                   .Chain("Terms:2", "Content", t => t.Terms.ElementAt(2))
                   .Chain("Terms:3", "Content", t => t.Terms.ElementAt(3))
                   ;
        }
    }
}

Hope this helps!

Monday, 21 February 2011

Windows 2008 R2 Terminal Server Error: The task you are trying to do can't be completed because the Remote Desktop Services is currently busy. Please try again in a few minutes. Other users should still be able to log on. Solution/Fix/Remedy

The following is not a permanent solution or fix, but the steps should help logging users back on the terminal server without having to reboot and inconvenience other users logged on.

  1. Log onto the terminal server and open the Remote Desktop Services Manager.
  2. Select the terminal server you are logged onto in the left hand pane. (if you are logged on remotely you may not have sufficient permissions to end processes).
  3. Get the Session ID of the hung session(s)
    1. Click on the Sessions tab. Look for sessions which display the following characteristics (as the image above):
       - Session: Disconnected
       - User: [BLANK]
       - State: Disconnected
       - ClientName: [BLANK]
       - LogOnTime: Unknown
    2. Take note of the ID field. In the screen shot above this is "6".
    3. NOT REQUIRED: This step is not required but can be useful: If you right click on the session, click "Connect", enter a domain administrators password and click ok, you can connect to the session and can see at what stage the login has hung.
  4. Free the hung session(s)
    1. Click on the Processes tab, click on the ID column to sort the processes by ID. Scroll down through the list and find processes matching the ID gathered from the previous step (in this example "6")
    2. Commonly with this particular error, only 3 processes will be visible - LogonUI.exe, winlogon.exe, and csrss.exe
    3. Right click on winlogon.exe and click End Process. Once you end this process the other 2 should disappear. WARNING: do not end csrss.exe, this will crash and reboot your terminal server.
  5. Now get the user who was having difficulties logging on to try again.
Cheers