2014-10-14 16:07:00 -07:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2016-05-27 17:08:03 +01:00
using System.Text.RegularExpressions ;
2014-10-14 16:07:00 -07:00
using Orchard.DisplayManagement ;
using Orchard.DisplayManagement.Descriptors ;
using Orchard.Environment ;
using Orchard.Environment.Extensions ;
using Orchard.Layouts.Elements ;
using Orchard.Layouts.Framework.Display ;
2015-11-19 17:39:52 +01:00
using Orchard.Layouts.Framework.Drivers ;
2014-10-14 16:07:00 -07:00
using Orchard.Layouts.Framework.Elements ;
using Orchard.Layouts.Framework.Harvesters ;
2015-11-19 17:39:52 +01:00
using Orchard.Layouts.Helpers ;
using Orchard.Layouts.Models ;
2014-10-14 16:07:00 -07:00
using Orchard.Layouts.Services ;
2015-11-19 17:39:52 +01:00
using Orchard.Layouts.Shapes ;
using Orchard.Layouts.ViewModels ;
using Orchard.Localization ;
2014-10-14 16:07:00 -07:00
using Orchard.Themes.Services ;
2015-11-19 17:39:52 +01:00
using Orchard.Tokens ;
2014-10-14 16:07:00 -07:00
using Orchard.Utility.Extensions ;
2016-04-12 21:53:12 +02:00
using YamlDotNet.Dynamic ;
2014-10-14 16:07:00 -07:00
namespace Orchard.Layouts.Providers {
[OrchardFeature("Orchard.Layouts.Snippets")]
2016-04-12 01:08:44 +02:00
public class SnippetElementHarvester : IElementHarvester {
2014-10-14 16:07:00 -07:00
private const string SnippetShapeSuffix = "Snippet" ;
private readonly Work < IShapeFactory > _shapeFactory ;
private readonly Work < ISiteThemeService > _siteThemeService ;
private readonly Work < IShapeTableLocator > _shapeTableLocator ;
private readonly Work < IElementFactory > _elementFactory ;
2015-11-19 17:39:52 +01:00
private readonly Work < IShapeDisplay > _shapeDisplay ;
private readonly Work < ICurrentThemeShapeBindingResolver > _currentThemeShapeBindingResolver ;
private readonly Work < ITokenizer > _tokenizer ;
2016-02-20 17:44:31 +01:00
private readonly IWorkContextAccessor _wca ;
2014-10-14 16:07:00 -07:00
public SnippetElementHarvester (
IWorkContextAccessor workContextAccessor ,
Work < IShapeFactory > shapeFactory ,
Work < ISiteThemeService > siteThemeService ,
2016-02-20 17:44:31 +01:00
Work < IShapeTableLocator > shapeTableLocator ,
2015-11-19 17:39:52 +01:00
Work < IElementFactory > elementFactory ,
Work < IShapeDisplay > shapeDisplay ,
Work < ITokenizer > tokenizer ,
2016-04-12 21:53:12 +02:00
Work < ICurrentThemeShapeBindingResolver > currentThemeShapeBindingResolver ) {
2014-10-14 16:07:00 -07:00
_shapeFactory = shapeFactory ;
_siteThemeService = siteThemeService ;
_shapeTableLocator = shapeTableLocator ;
_elementFactory = elementFactory ;
2015-11-19 17:39:52 +01:00
_shapeDisplay = shapeDisplay ;
_tokenizer = tokenizer ;
_currentThemeShapeBindingResolver = currentThemeShapeBindingResolver ;
2016-02-20 17:44:31 +01:00
_wca = workContextAccessor ;
2014-10-14 16:07:00 -07:00
}
public IEnumerable < ElementDescriptor > HarvestElements ( HarvestElementsContext context ) {
var currentThemeName = _siteThemeService . Value . GetCurrentThemeName ( ) ;
var shapeTable = _shapeTableLocator . Value . Lookup ( currentThemeName ) ;
2015-03-12 17:27:00 +01:00
var shapeDescriptors = shapeTable . Bindings . Where ( x = > ! String . Equals ( x . Key , "Elements_Snippet" , StringComparison . OrdinalIgnoreCase ) & & x . Key . EndsWith ( SnippetShapeSuffix , StringComparison . OrdinalIgnoreCase ) ) . ToDictionary ( x = > x . Key , x = > x . Value . ShapeDescriptor ) ;
2016-02-20 17:44:31 +01:00
var elementType = typeof ( Snippet ) ;
2015-11-19 17:39:52 +01:00
var snippetElement = ( Snippet ) _elementFactory . Value . Activate ( elementType ) ;
2014-10-14 16:07:00 -07:00
foreach ( var shapeDescriptor in shapeDescriptors ) {
2016-05-27 17:08:03 +01:00
var snippetManifest = ParseSnippetManifest ( shapeDescriptor . Value . BindingSource ) ;
2014-10-14 16:07:00 -07:00
var shapeType = shapeDescriptor . Value . ShapeType ;
2016-05-27 17:08:03 +01:00
var elementName = GetDisplayName ( snippetManifest , shapeDescriptor . Value . BindingSource ) ;
var toolboxIcon = GetToolboxIcon ( snippetManifest , snippetElement ) ;
var description = GetDescription ( snippetManifest , shapeType ) ;
var category = GetCategory ( snippetManifest , snippetElement ) ;
2014-10-14 16:07:00 -07:00
var closureDescriptor = shapeDescriptor ;
2016-05-27 17:08:03 +01:00
var snippetDescriptor = ParseSnippetDescriptor ( snippetManifest ) ;
yield return new ElementDescriptor ( elementType , shapeType , new LocalizedString ( elementName ) , description , category ) {
2016-04-12 01:08:44 +02:00
Displaying = displayContext = > Displaying ( displayContext , closureDescriptor . Value , snippetDescriptor ) ,
2016-05-27 17:08:03 +01:00
ToolboxIcon = toolboxIcon ,
2016-04-12 01:08:44 +02:00
EnableEditorDialog = snippetDescriptor ! = null | | HasSnippetFields ( shapeDescriptor . Value ) ,
Editor = ctx = > Editor ( snippetDescriptor ? ? DescribeSnippet ( shapeType , snippetElement ) , ctx ) ,
UpdateEditor = ctx = > UpdateEditor ( snippetDescriptor ? ? DescribeSnippet ( shapeType , snippetElement ) , ctx )
2014-10-14 16:07:00 -07:00
} ;
}
}
2015-11-19 17:39:52 +01:00
private void Editor ( SnippetDescriptor descriptor , ElementEditorContext context ) {
UpdateEditor ( descriptor , context ) ;
}
private void UpdateEditor ( SnippetDescriptor descriptor , ElementEditorContext context ) {
var viewModel = new SnippetViewModel {
Descriptor = descriptor
} ;
2016-02-20 17:44:31 +01:00
2015-11-19 17:39:52 +01:00
if ( context . Updater ! = null ) {
foreach ( var fieldDescriptor in descriptor . Fields ) {
var name = fieldDescriptor . Name ;
var result = context . ValueProvider . GetValue ( name ) ;
if ( result = = null )
continue ;
context . Element . Data [ name ] = result . AttemptedValue ;
}
}
viewModel . FieldEditors = descriptor . Fields . Select ( x = > {
var fieldEditorTemplateName = String . Format ( "Elements.Snippet.Field.{0}" , x . Type ? ? "Text" ) ;
var fieldDescriptorViewModel = new SnippetFieldViewModel {
Descriptor = x ,
Value = context . Element . Data . Get ( x . Name )
} ;
var fieldEditor = context . ShapeFactory . EditorTemplate ( TemplateName : fieldEditorTemplateName , Model : fieldDescriptorViewModel , Prefix : context . Prefix ) ;
return fieldEditor ;
} ) . ToList ( ) ;
var snippetEditorShape = context . ShapeFactory . EditorTemplate ( TemplateName : "Elements.Snippet" , Model : viewModel , Prefix : context . Prefix ) ;
snippetEditorShape . Metadata . Position = "Fields:0" ;
2016-02-20 17:44:31 +01:00
2015-11-19 17:39:52 +01:00
context . EditorResult . Add ( snippetEditorShape ) ;
}
2016-04-12 01:08:44 +02:00
private void Displaying ( ElementDisplayingContext context , ShapeDescriptor shapeDescriptor , SnippetDescriptor snippetDescriptor ) {
2014-10-14 16:07:00 -07:00
var shapeType = shapeDescriptor . ShapeType ;
2015-11-19 17:39:52 +01:00
var shape = ( dynamic ) _shapeFactory . Value . Create ( shapeType ) ;
shape . Element = context . Element ;
2017-02-14 06:44:36 +02:00
shape . SnippetDescriptor = snippetDescriptor ;
2015-11-19 17:39:52 +01:00
2016-04-12 01:08:44 +02:00
if ( snippetDescriptor ! = null ) {
foreach ( var fieldDescriptor in snippetDescriptor . Fields ) {
var value = context . Element . Data . Get ( fieldDescriptor . Name ) ;
shape . Properties [ fieldDescriptor . Name ] = value ;
}
}
2015-11-19 17:39:52 +01:00
ElementShapes . AddTokenizers ( shape , _tokenizer . Value ) ;
2014-10-14 16:07:00 -07:00
context . ElementShape . Snippet = shape ;
}
2016-05-27 17:08:03 +01:00
private dynamic ParseSnippetManifest ( string bindingSource ) {
2016-04-12 01:08:44 +02:00
var physicalSourcePath = _wca . GetContext ( ) . HttpContext . Server . MapPath ( bindingSource ) ;
var paramsFileName = Path . Combine ( Path . GetDirectoryName ( physicalSourcePath ) ? ? "" , Path . GetFileNameWithoutExtension ( physicalSourcePath ) + ".txt" ) ;
if ( ! File . Exists ( paramsFileName ) )
return null ;
var yaml = File . ReadAllText ( paramsFileName ) ;
2016-04-12 21:53:12 +02:00
var snippetConfig = Deserialize ( yaml ) ;
2016-05-27 17:08:03 +01:00
return snippetConfig ;
}
private string GetDisplayName ( dynamic snippetManifest , string bindingSource ) {
if ( snippetManifest ! = null & & ( string ) snippetManifest . DisplayName ! = null ) {
return ( string ) snippetManifest . DisplayName ;
}
var fileName = Path . GetFileNameWithoutExtension ( bindingSource ) ? ? "" ;
var lastIndex = fileName . IndexOf ( SnippetShapeSuffix , StringComparison . OrdinalIgnoreCase ) ;
return fileName . Substring ( 0 , lastIndex ) . CamelFriendly ( ) ;
}
private string GetToolboxIcon ( dynamic snippetManifest , Snippet snippetElement ) {
if ( snippetManifest ! = null & & ( string ) snippetManifest . ToolboxIcon ! = null ) {
return Regex . Unescape ( ( string ) snippetManifest . ToolboxIcon ) ;
}
return snippetElement . ToolboxIcon ;
}
private LocalizedString GetDescription ( dynamic snippetManifest , string shapeType ) {
if ( snippetManifest ! = null & & ( string ) snippetManifest . Description ! = null ) {
return new LocalizedString ( ( string ) snippetManifest . Description ) ;
}
return new LocalizedString ( String . Format ( "An element that renders the {0} shape." , shapeType ) ) ;
}
private string GetCategory ( dynamic snippetManifest , Snippet snippetElement ) {
if ( snippetManifest ! = null & & ( string ) snippetManifest . Category ! = null ) {
return ( string ) snippetManifest . Category ;
}
return snippetElement . Category ;
}
private SnippetDescriptor ParseSnippetDescriptor ( dynamic snippetManifest ) {
if ( snippetManifest = = null | | snippetManifest . Fields . Count = = 0 ) {
return null ;
}
var fieldsConfig = snippetManifest . Fields . Children ;
2016-04-12 01:08:44 +02:00
var descriptor = new SnippetDescriptor ( ) ;
foreach ( var fieldConfig in fieldsConfig ) {
descriptor . Fields . Add ( new SnippetFieldDescriptor {
Name = ( string ) fieldConfig . Name ,
Type = ( string ) fieldConfig . Type ,
DisplayName = new LocalizedString ( ( string ) fieldConfig . DisplayName ) ,
Description = new LocalizedString ( ( string ) fieldConfig . Description )
} ) ;
}
return descriptor ;
}
2015-11-19 17:39:52 +01:00
private SnippetDescriptor DescribeSnippet ( string shapeType , Snippet element ) {
var shape = ( dynamic ) _shapeFactory . Value . Create ( shapeType ) ;
shape . Element = element ;
return DescribeSnippet ( shape ) ;
}
private SnippetDescriptor DescribeSnippet ( dynamic shape ) {
// Execute the shape and intercept calls to the Html.SnippetField method.
var descriptor = new SnippetDescriptor ( ) ;
2016-02-20 17:44:31 +01:00
shape . DescriptorRegistrationCallback = ( Action < SnippetFieldDescriptor > ) ( fieldDescriptor = > {
2015-11-24 23:19:06 +01:00
var existingDescriptor = descriptor . Fields . SingleOrDefault ( x = > x . Name = = fieldDescriptor . Name ) ; // Not using Dictionary, as that will break rendering the view for some obscure reason.
2016-02-20 17:44:31 +01:00
2015-11-24 23:19:06 +01:00
if ( existingDescriptor = = null )
descriptor . Fields . Add ( fieldDescriptor ) ;
2015-11-19 17:39:52 +01:00
if ( fieldDescriptor . DisplayName = = null )
fieldDescriptor . DisplayName = new LocalizedString ( fieldDescriptor . Name ) ;
} ) ;
using ( _currentThemeShapeBindingResolver . Value . Enable ( ) ) {
_shapeDisplay . Value . Display ( shape ) ;
}
shape . SnippetDescriptor = descriptor ;
return descriptor ;
}
2016-02-20 17:44:31 +01:00
private bool HasSnippetFields ( ShapeDescriptor shapeDescriptor ) {
var bindingSource = shapeDescriptor . BindingSource ;
var localFileName = _wca . GetContext ( ) . HttpContext . Server . MapPath ( bindingSource ) ;
if ( ! File . Exists ( localFileName ) )
return false ;
var markup = File . ReadAllText ( localFileName ) ;
return markup . Contains ( "@Html.SnippetField" ) ;
}
2016-04-12 21:53:12 +02:00
private dynamic Deserialize ( string yaml ) {
return new DynamicYaml ( yaml ) ;
}
2014-10-14 16:07:00 -07:00
}
}