Working with Responsive Design Projects

refe-collage
Working as a software architect in projects where visual appearance, user interfaces and large scale of device support is as important as the right data and the functioning is very different than being just a back-end developer in a project where the UI just need to work with couple of different devices. It requires a lot more consideration of different kind of needs and point of views. It often requires some understanding and co-operation of:

(more…)

CSV Table Block Concept for Episerver

This is an option to avoid usage tables in WYSIWYG HTML editor. Tables in any rich text editor have always be problematic for responsive design, but data often exists in table format (example in Excel) so the usage of table tools is inevitable.

We are using CSV (comma separated values) as description of table because almost all systems and software (example Excel) can produce CSV data. Also because CSV is readable and editable for much larger group of people than example XML or JSON. PropertyList would be one good option to improve the editor’s experience but in this case the data came mostly from external sources. So the import feature was much more important.

CSV Table Block Example

(unfortunately, screenshots and URL examples are only in Finnish)

You can find live example of table block in Evira’s web page.

csv table block

(more…)

Scheduled Job Manager for cleaning Episerver background tasks

scheduled-job-manager-01

Episerver scheduled job manager for cleaning and maintaining scheduled jobs. Sometimes you may find old scheduled jobs in database if the scheduled job is renamed, moved or deleted. Old Scheduled jobs are not automatically deleted but with this tool you can easily and safely delete those useless scheduled jobs.

This is basically single file solution for developers and administrators which you can drop into any environment and start using it without any maintenance breaks (no application pool recycle).

Source codes can be found in github:
https://github.com/huilaaja/ScheduledJobManager

 

No parameterless constructor exception, Greediest Constructor and DefaultConstructor-attribute

You propably have pump into IoC framework (StructureMap) problem “No parameterless constructor” but are you familiar with StructureMap constructor selection, greediest constructor rule, default constructor attribute and good practices of implementing multiple constructors.

You probably have seen this YSOD-screen before and it actually says that my StartPageController-does not have parameterless constructor.  In this case the original problem had happen way before this MissingMethodException was thrown. The original exception was catch and written down into log-file and it hides the original exception. That’s definitely a problem but in this case there was a good reason for it and it’s not the original problem.

No parameterless constructor

Log file tells that something has broke the StructureMap and that’s why none of the future StructureMap requests can be processed.

In this case the problem was simple “Required primitive dependency is not explicitly defined“. So there was string parameter in the constructor which StructureMap try to use. StructureMap selects the constructor which had the most parameters (Greediest Constructor -rule).

One simple solution to primitive type constructor parameter is to mark constructor with DefaultConstructor-attribute and initial the default values in runtime. Note that the other constructor has it’s own purpose also.
DefaultConstructor

This way your you don’t have to mind what type of constructor parameter you have and you don’t have to define constructor and default values in your StructureMap configuration method.

 

Elasticsearch NEST 2 Upgrade Cheat Sheet (version 1.7 -> 2.0)

NEST version 2 official documentations:

 
NEST 1.7
NEST 2.0
.OnFields()
.Fields()
.OnField()
.Field()
.ConnectionStatus
.CallDetails
.ConnectionStatus.Request
.CallDetails.RequestBodyInBytes
 .Types(new[] {
     typeof(PageIndexObj),
     typeof(NewsIndexObj),
     typeof(PersonIndexObj)
   })
)
.Type(“pageindexobj,newsindexobj,personindexobj”)
OR
.Type(
  string.Join(“,” ,
     new[] {
        typeof(PageIndexObj),
        typeof(NewsIndexObj),
        typeof(PersonIndexObj)
     }.Select(t => t.Name.ToLower())
  )
)
OR
.AllTypes()
.SortAscending(o => o. Time)
.Sort(sd => sd.Ascending(o => o. Time))
.Search<T>(s => s
   .Filter(m => m
      .Term(t => t.Field(f => f.TypeId).Value(5)))
   )
)
Direct conversion:
.Search<T>(s => s
   .PostFilter(m => m
      .Term(t => t.Field(f => f.TypeId).Value(5)))
   )
)
In most cases is better to use Query level filters:
.Search<T>(s => s
   .Query(q => q
      .Bool(b => b
         .Filter(m => m
            .Term(t => t.Field(o => o.TypeId).Value(5))
         )
      )
   )
)
FilterContainer
QueryContainer
KeyValuePair<string, Highlight>
IHighlightHit
.Highlights.FirstOrDefault(kvp => kvp.Key == “name” )
o.Highlights[“name”]
.MapFromAttributes()
.AutoMap()
.MapFromAttributes().IgnoreConflicts()
.AutoMap()
.PutTemplate()
.PutIndexTemplate().
.PutIndexTemplate( EsSettings .IndexNameBase + “-template” , t => t
  .Template(EsSettings.IndexNameBase + “*”)
  .Settings(s => s
     .Add( “index.number_of_shards” , 1)
     .Add( “index.number_of_replicas” , 0)
  )
  . AddMapping <_default_ >(ms => ms.Dynamic (DynamicMappingOption.Ignore))
);
.PutIndexTemplate(“my-index-template” , t => t
  .Template(“my-index-name-prefix*”)
  .Settings(s => s
     .NumberOfShards(1)
     .NumberOfReplicas(0)
  )
  .Mappings(mds => mds.Map< _default_>(s => s.Dynamic(DynamicMapping.Ignore)))
);
IElasticClient.Raw
IElasticClient.LowLevel
.Aggs.Terms(“aggCategoryIds”).Items
.Aggs.Terms(“aggCategoryIds”).Buckets
TermsAgregation.Items
TermsAgregation.Aggregations
.Greater(0)
.GreaterThan(0)
.LowerOrEquals(0)
.LessThanOrEquals(0)
.SuggestTerm()
.Suggest(s => s.Term())
  .OnFieldsWithBoost (d => d
     .Add (entry => entry.Name , 2)
  )
  .Fields(pf => pf
     .Field(entry => entry.Name, 2)
  )
 .ScriptScore(sc => sc.Script (SearchScoreScript))
.Functions(sc => sc.ScriptScore(sf => sf.Script(f => f.Inline(SearchScoreScript))))
.QueryString($”{query}*”)
.Query(qc => qc.QueryString(q => q.Query($”{query}*”)))
.AliasExists(aliasName).Exists
.AliasExists(s => s.Index(aliasName)).Exists
.CreateIndex(id => id.Index(“index_name” ))
.CreateIndex( “index_name”, id => id)
[ElasticProperty(Type = FieldType.String, Index = FieldIndexOption .NotAnalyzed)]
[ElasticProperty(Type = FieldType.Object)]
[ElasticProperty(Type = FieldType.Nested)]
[StringAttribute(Index = FieldIndexOption .NotAnalyzed)]
[ObjectAttribute()]
[Nested()]
[ElasticType()]
[ElasticsearchType()]
.Range()
.Range()
OR
.DateRange()
OR
.TermRange()
.DeleteByQuery()
NEST supports DeleteByQuery() OOTB, but elastic search requires Delete By Query -pluginSo the way to do it is with separate requests:
var deletedObjects =  elasticClient.Search<ObjectType>(i => i
   .AllTypes()
   .Query(f => f.Term(e => e.ContentId, contentId))
);
var deleteResults = deletedObjects .Hits.Select(h =>
     elasticClient.Delete(new DocumentPath<object>(h.Id).Type(h.Type), s => s)
);
.Filter(f => f.Terms(o => o. ContentId, items))
.Filter(f => f.Terms(t => t.Field(o => o.ContentId).Terms(items)))
.GetAliases()
.GetAlias()
.Facets()
.Aggregates()

(more…)

EPiServer Content Area Renderer With Row Support

EPiServer Display options is a really nice feature but it’s lacking a row rendering option for Bootstrap Grid system.

content-area-with-rows-01

Without row-elements your grid will got broken quite easily and you need to create “dirty CSS hacks” to fix it.

content-area-with-rows-09

To create rich content with various types of blocks and content sizes you need to separate the rows with <div class=”row”> -element.

content-area-with-rows-07

Luckily EPiServer is so extendable that you can add the feature quite easily in your own project. So after adding the following codes you are able to get these rows and your HMTL is properly formatted for Bootstrap grid or some other grid system.

content-area-with-rows-04

Solution

Adding rows in content area renderer is actually pretty simple job. You can make it much more fail safe and improved but this is the simple version.



    public class ContentAreaRendererWithRows : ContentAreaRenderer
    {
	    // Wraps every 12 cells with row divs if the tags are used.
        protected override void RenderContentAreaItems(HtmlHelper htmlHelper, IEnumerable contentAreaItems)
        {
            var items = contentAreaItems.ToArray();
            int rowWidthState = 0;
            var itemInfos = items.Select(item =>
            {
                var tag = GetContentAreaItemTemplateTag(htmlHelper, item);
                var columnWidth = GetColumnWidth(tag);
                rowWidthState += columnWidth;
                return new
                {
                    ContentAreaItem = item,
                    Tag = tag,
                    ColumnWidth = columnWidth,
                    RowWidthState = rowWidthState,
                    RowNumber = rowWidthState % 12 == 0 ? rowWidthState/12 - 1 : rowWidthState / 12,
                };
            }).ToArray();
            
            //if tags exists wrap items with row or not then use the deafault rendering.
            bool tagExists = itemInfos.Any(ii => !string.IsNullOrEmpty(ii.Tag));
            if(!tagExists)
            {
                base.RenderContentAreaItems(htmlHelper, items);
                return;
            }

            var rows = itemInfos.GroupBy(a => a.RowNumber, a => a.ContentAreaItem);
            foreach (var row in rows)
            {
                htmlHelper.ViewContext.Writer.Write("<div class=\"row row"+row.Key+"\">");
                base.RenderContentAreaItems(htmlHelper, row);
                htmlHelper.ViewContext.Writer.Write("</div>");
            }
        }
        
        public static int GetColumnWidth(string tag)
        {
            switch (tag)
            {
                case Settings.ContentAreaTags.FullWidth:
                    return 12;
                case Settings.ContentAreaTags.TwoThirdsWidth:
                    return 8;
                case Settings.ContentAreaTags.HalfWidth:
                    return 6;
                case Settings.ContentAreaTags.OneThirdWidth:
                    return 4;
                default:
                    return 12;
            }
        }

        protected override string GetContentAreaItemCssClass(HtmlHelper htmlHelper, ContentAreaItem contentAreaItem)
        {
            var tag = GetContentAreaItemTemplateTag(htmlHelper, contentAreaItem);
            return string.Format("block {0} {1}", GetTypeSpecificCssClasses(contentAreaItem, ContentRepository), tag);
        }

        private static string GetTypeSpecificCssClasses(ContentAreaItem contentAreaItem, IContentRepository contentRepository)
        {
            var content = contentAreaItem.GetContent(contentRepository);
            var cssClass = content == null ? String.Empty : content.GetOriginalType().Name.ToLowerInvariant();
            return cssClass;
        }
    }

Initialization:


    public class StructureMapConfig
    {
        public static void ConfigureContainer(IContainer container)
        {
            container.Configure(x =>
            {
                x.For<ContentAreaRenderer>().Use<ContentAreaRendererWithRows>();
            });
        }
    }

Settings:



    public static class Settings
    {
        public static class ContentAreaTags
        {
            public const string FullWidth = "col-sm-12";
            public const string TwoThirdsWidth = "col-sm-8";
            public const string HalfWidth = "col-sm-6";
            public const string OneThirdWidth = "col-sm-4";
        }
    }

 

And Bob’s your uncle!!!

ContentReferenceList property in EPiServer CMS 8.6.0

EPiServer just released a excellent new property type ContentReferenceList or in code it’s written IList<ContentReference>.

In Edit view ContentReferenceList property makes it easy to drag & drop content and it clearly says which content types are allowed.

content-reference-list-v04

Selection view makes it easy to recognize which pages  are allowed.

content-reference-list-v02

And the code is the prettiest part of all


public class BlogPage : BaseContentPage
{
    [Display(
        Name = "Writers",
        GroupName = SystemTabNames.Content,
        Order = 150)]
    [AllowedTypes(typeof(WriterPage))]
    public virtual IList Writers { get; set; }

}

 

More info of the ContentReferenceListProperty and restricted content types.