Manifest refactoring
This commit is contained in:
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
Func<List<GridEditor>> getResult = () =>
|
||||
{
|
||||
var parser = new ManifestParser(_logger, _appPlugins, _runtimeCache);
|
||||
// fixme - should use the common one somehow! + ignoring _appPlugins here!
|
||||
var parser = new ManifestParser(_runtimeCache, _logger);
|
||||
|
||||
var editors = new List<GridEditor>();
|
||||
var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js");
|
||||
@@ -40,10 +41,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
try
|
||||
{
|
||||
var arr = JArray.Parse(File.ReadAllText(gridConfig));
|
||||
//ensure the contents parse correctly to objects
|
||||
var parsed = parser.GetGridEditors(arr);
|
||||
editors.AddRange(parsed);
|
||||
editors.AddRange(parser.ParseGridEditors(File.ReadAllText(gridConfig)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -51,16 +49,13 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var builder = new ManifestBuilder(_runtimeCache, parser);
|
||||
foreach (var gridEditor in builder.GridEditors)
|
||||
// add manifest editors, skip duplicates
|
||||
foreach (var gridEditor in parser.Manifest.GridEditors)
|
||||
{
|
||||
//no duplicates! (based on alias)
|
||||
if (editors.Contains(gridEditor) == false)
|
||||
{
|
||||
editors.Add(gridEditor);
|
||||
}
|
||||
}
|
||||
|
||||
return editors;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ namespace Umbraco.Core.Configuration
|
||||
|
||||
private static readonly Lazy<UmbracoConfig> Lazy = new Lazy<UmbracoConfig>(() => new UmbracoConfig());
|
||||
|
||||
public static UmbracoConfig For
|
||||
{
|
||||
get { return Lazy.Value; }
|
||||
}
|
||||
public static UmbracoConfig For => Lazy.Value;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -44,6 +44,12 @@ namespace Umbraco.Core.IO
|
||||
return retval;
|
||||
}
|
||||
|
||||
public static string ResolveVirtualUrl(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) return path;
|
||||
return path.StartsWith("~/") ? ResolveUrl(path) : path;
|
||||
}
|
||||
|
||||
//Replaces tildes with the root dir
|
||||
public static string ResolveUrl(string virtualPath)
|
||||
{
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures that virtual paths are taken care of
|
||||
/// </summary>
|
||||
internal class GridEditorConverter : JsonCreationConverter<GridEditor>
|
||||
{
|
||||
protected override GridEditor Create(Type objectType, JObject jObject)
|
||||
{
|
||||
return new GridEditor();
|
||||
}
|
||||
|
||||
protected override void Deserialize(JObject jObject, GridEditor target, JsonSerializer serializer)
|
||||
{
|
||||
base.Deserialize(jObject, target, serializer);
|
||||
|
||||
if (target.View.IsNullOrWhiteSpace() == false && target.View.StartsWith("~/"))
|
||||
{
|
||||
target.View = IOHelper.ResolveUrl(target.View);
|
||||
}
|
||||
|
||||
if (target.Render.IsNullOrWhiteSpace() == false && target.Render.StartsWith("~/"))
|
||||
{
|
||||
target.Render = IOHelper.ResolveUrl(target.Render);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// This reads in the manifests and stores some definitions in memory so we can look them on the server side
|
||||
/// </summary>
|
||||
internal class ManifestBuilder
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
private readonly ManifestParser _parser;
|
||||
|
||||
public ManifestBuilder(IRuntimeCacheProvider cache, ManifestParser parser)
|
||||
{
|
||||
_cache = cache;
|
||||
_parser = parser;
|
||||
}
|
||||
|
||||
private const string GridEditorsKey = "grideditors";
|
||||
private const string PropertyEditorsKey = "propertyeditors";
|
||||
private const string ParameterEditorsKey = "parametereditors";
|
||||
|
||||
/// <summary>
|
||||
/// Returns all grid editors found in the manfifests
|
||||
/// </summary>
|
||||
internal IEnumerable<GridEditor> GridEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cache.GetCacheItem<IEnumerable<GridEditor>>(
|
||||
typeof (ManifestBuilder) + GridEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<GridEditor>();
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
if (manifest.GridEditors != null)
|
||||
{
|
||||
editors.AddRange(_parser.GetGridEditors(manifest.GridEditors));
|
||||
}
|
||||
|
||||
}
|
||||
return editors;
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all property editors found in the manfifests
|
||||
/// </summary>
|
||||
internal IEnumerable<PropertyEditor> PropertyEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cache.GetCacheItem<IEnumerable<PropertyEditor>>(
|
||||
typeof(ManifestBuilder) + PropertyEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<PropertyEditor>();
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
if (manifest.PropertyEditors != null)
|
||||
{
|
||||
editors.AddRange(_parser.GetPropertyEditors(manifest.PropertyEditors));
|
||||
}
|
||||
|
||||
}
|
||||
return editors;
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all parameter editors found in the manfifests and all property editors that are flagged to be parameter editors
|
||||
/// </summary>
|
||||
internal IEnumerable<ParameterEditor> ParameterEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cache.GetCacheItem<IEnumerable<ParameterEditor>>(
|
||||
typeof (ManifestBuilder) + ParameterEditorsKey,
|
||||
() =>
|
||||
{
|
||||
var editors = new List<ParameterEditor>();
|
||||
foreach (var manifest in _parser.GetManifests())
|
||||
{
|
||||
if (manifest.ParameterEditors != null)
|
||||
{
|
||||
editors.AddRange(_parser.GetParameterEditors(manifest.ParameterEditors));
|
||||
}
|
||||
}
|
||||
return editors;
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -16,345 +15,139 @@ namespace Umbraco.Core.Manifest
|
||||
/// <summary>
|
||||
/// Parses the Main.js file and replaces all tokens accordingly.
|
||||
/// </summary>
|
||||
internal class ManifestParser
|
||||
public class ManifestParser
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
private readonly string _path;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly DirectoryInfo _pluginsDir;
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
private static readonly string Utf8Preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
|
||||
|
||||
//used to strip comments
|
||||
private static readonly Regex CommentsSurround = new Regex(@"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", RegexOptions.Compiled);
|
||||
private static readonly Regex CommentsLine = new Regex(@"^\s*//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
public ManifestParser(IRuntimeCacheProvider cache, ILogger logger) // fixme is LightInject going to pick that one?
|
||||
: this(cache, "~/App_Plugins", logger)
|
||||
{ }
|
||||
|
||||
public ManifestParser(ILogger logger, DirectoryInfo pluginsDir, IRuntimeCacheProvider cache)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
public ManifestParser(IRuntimeCacheProvider cache, string path, ILogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
if (pluginsDir == null) throw new ArgumentNullException("pluginsDir");
|
||||
_pluginsDir = pluginsDir;
|
||||
_logger = logger;
|
||||
_cache = cache;
|
||||
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullOrEmptyException(nameof(path));
|
||||
_path = path.StartsWith("~/") ? IOHelper.MapPath(path) : path;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the grid editors from the json array
|
||||
/// </summary>
|
||||
/// <param name="jsonEditors"></param>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<GridEditor> GetGridEditors(JArray jsonEditors)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<IEnumerable<GridEditor>>(
|
||||
jsonEditors.ToString(),
|
||||
new GridEditorConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the property editors from the json array
|
||||
/// </summary>
|
||||
/// <param name="jsonEditors"></param>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<PropertyEditor> GetPropertyEditors(JArray jsonEditors)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<IEnumerable<PropertyEditor>>(
|
||||
jsonEditors.ToString(),
|
||||
new PropertyEditorConverter(_logger),
|
||||
new PreValueFieldConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the property editors from the json array
|
||||
/// </summary>
|
||||
/// <param name="jsonEditors"></param>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<ParameterEditor> GetParameterEditors(JArray jsonEditors)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<IEnumerable<ParameterEditor>>(
|
||||
jsonEditors.ToString(),
|
||||
new ParameterEditorConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all registered manifests
|
||||
/// Gets all manifests, merged into a single manifest object.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This ensures that we only build and look for all manifests once per Web app (based on the IRuntimeCache)
|
||||
/// </remarks>
|
||||
public IEnumerable<PackageManifest> GetManifests()
|
||||
{
|
||||
return _cache.GetCacheItem<IEnumerable<PackageManifest>>(typeof (ManifestParser) + "GetManifests", () =>
|
||||
public PackageManifest Manifest
|
||||
=> _cache.GetCacheItem<PackageManifest>("Umbraco.Core.Manifest.ManifestParser::Manifests", () =>
|
||||
{
|
||||
//get all Manifest.js files in the appropriate folders
|
||||
var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
|
||||
return CreateManifests(manifestFileContents.ToArray());
|
||||
}, new TimeSpan(0, 10, 0));
|
||||
}
|
||||
var manifests = GetManifests();
|
||||
return MergeManifests(manifests);
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Get the file contents from all declared manifest files
|
||||
/// Gets all manifests.
|
||||
/// </summary>
|
||||
/// <param name="currDir"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<string> GetAllManifestFileContents(DirectoryInfo currDir)
|
||||
private IEnumerable<PackageManifest> GetManifests()
|
||||
{
|
||||
var depth = FolderDepth(_pluginsDir, currDir);
|
||||
var manifests = new List<PackageManifest>();
|
||||
|
||||
if (depth < 1)
|
||||
foreach (var path in GetManifestFiles())
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (currDir.Exists)
|
||||
{
|
||||
var dirs = currDir.GetDirectories();
|
||||
|
||||
foreach (var d in dirs)
|
||||
{
|
||||
result.AddRange(GetAllManifestFileContents(d));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//look for files here
|
||||
return currDir.GetFiles("Package.manifest")
|
||||
.Select(f => File.ReadAllText(f.FullName))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the folder depth compared to the base folder
|
||||
/// </summary>
|
||||
/// <param name="baseDir"></param>
|
||||
/// <param name="currDir"></param>
|
||||
/// <returns></returns>
|
||||
internal int FolderDepth(DirectoryInfo baseDir, DirectoryInfo currDir)
|
||||
{
|
||||
var removed = currDir.FullName.Remove(0, baseDir.FullName.Length).TrimStart('\\').TrimEnd('\\');
|
||||
return removed.Split(new char[] {'\\'}, StringSplitOptions.RemoveEmptyEntries).Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of PropertyEditorManifest from the file contents of each manifest file
|
||||
/// </summary>
|
||||
/// <param name="manifestFileContents"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This ensures that comments are removed (but they have to be /* */ style comments
|
||||
/// and ensures that virtual paths are replaced with real ones
|
||||
/// </remarks>
|
||||
internal IEnumerable<PackageManifest> CreateManifests(params string[] manifestFileContents)
|
||||
{
|
||||
var result = new List<PackageManifest>();
|
||||
foreach (var m in manifestFileContents)
|
||||
{
|
||||
var manifestContent = m;
|
||||
|
||||
if (manifestContent.IsNullOrWhiteSpace()) continue;
|
||||
|
||||
// Strip byte object marker, JSON.NET does not like it
|
||||
var preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
|
||||
|
||||
// Strangely StartsWith(preamble) would always return true
|
||||
if (manifestContent.Substring(0, 1) == preamble)
|
||||
manifestContent = manifestContent.Remove(0, preamble.Length);
|
||||
|
||||
if (manifestContent.IsNullOrWhiteSpace()) continue;
|
||||
|
||||
//remove any comments first
|
||||
var replaced = CommentsSurround.Replace(manifestContent, match => " ");
|
||||
replaced = CommentsLine.Replace(replaced, match => "");
|
||||
|
||||
JObject deserialized;
|
||||
try
|
||||
{
|
||||
deserialized = JsonConvert.DeserializeObject<JObject>(replaced);
|
||||
var text = File.ReadAllText(path);
|
||||
text = TrimPreamble(text);
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
continue;
|
||||
var manifest = ParseManifest(text);
|
||||
manifests.Add(manifest);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error<ManifestParser>("An error occurred parsing manifest with contents: " + m, ex);
|
||||
continue;
|
||||
_logger.Error<ManifestParser>($"Failed to parse manifest at \"{path}\", ignoring.", e);
|
||||
}
|
||||
|
||||
//validate the javascript
|
||||
var init = deserialized.Properties().Where(x => x.Name == "javascript").ToArray();
|
||||
if (init.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'javascript' element");
|
||||
}
|
||||
|
||||
//validate the css
|
||||
var cssinit = deserialized.Properties().Where(x => x.Name == "css").ToArray();
|
||||
if (cssinit.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'css' element");
|
||||
}
|
||||
|
||||
//validate the property editors section
|
||||
var propEditors = deserialized.Properties().Where(x => x.Name == "propertyEditors").ToArray();
|
||||
if (propEditors.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'propertyEditors' element");
|
||||
}
|
||||
|
||||
//validate the parameterEditors section
|
||||
var paramEditors = deserialized.Properties().Where(x => x.Name == "parameterEditors").ToArray();
|
||||
if (paramEditors.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'parameterEditors' element");
|
||||
}
|
||||
|
||||
//validate the gridEditors section
|
||||
var gridEditors = deserialized.Properties().Where(x => x.Name == "gridEditors").ToArray();
|
||||
if (gridEditors.Length > 1)
|
||||
{
|
||||
throw new FormatException("The manifest is not formatted correctly contains more than one 'gridEditors' element");
|
||||
}
|
||||
|
||||
var jConfig = init.Any() ? (JArray)deserialized["javascript"] : new JArray();
|
||||
ReplaceVirtualPaths(jConfig);
|
||||
|
||||
var cssConfig = cssinit.Any() ? (JArray)deserialized["css"] : new JArray();
|
||||
ReplaceVirtualPaths(cssConfig);
|
||||
|
||||
//replace virtual paths for each property editor
|
||||
if (deserialized["propertyEditors"] != null)
|
||||
{
|
||||
foreach (JObject p in deserialized["propertyEditors"])
|
||||
{
|
||||
if (p["editor"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths((JObject) p["editor"]);
|
||||
}
|
||||
if (p["preValues"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths((JObject)p["preValues"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//replace virtual paths for each property editor
|
||||
if (deserialized["gridEditors"] != null)
|
||||
{
|
||||
foreach (JObject p in deserialized["gridEditors"])
|
||||
{
|
||||
if (p["view"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths(p["view"]);
|
||||
}
|
||||
if (p["render"] != null)
|
||||
{
|
||||
ReplaceVirtualPaths(p["render"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var manifest = new PackageManifest()
|
||||
{
|
||||
JavaScriptInitialize = jConfig,
|
||||
StylesheetInitialize = cssConfig,
|
||||
PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(),
|
||||
ParameterEditors = paramEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(),
|
||||
GridEditors = gridEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray()
|
||||
};
|
||||
result.Add(manifest);
|
||||
}
|
||||
return result;
|
||||
|
||||
return manifests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces any virtual paths found in properties
|
||||
/// Merges all manifests into one.
|
||||
/// </summary>
|
||||
/// <param name="jarr"></param>
|
||||
private void ReplaceVirtualPaths(JArray jarr)
|
||||
private static PackageManifest MergeManifests(IEnumerable<PackageManifest> manifests)
|
||||
{
|
||||
foreach (var i in jarr)
|
||||
var scripts = new HashSet<string>();
|
||||
var stylesheets = new HashSet<string>();
|
||||
var propertyEditors = new List<PropertyEditor>();
|
||||
var parameterEditors = new List<ParameterEditor>();
|
||||
var gridEditors = new List<GridEditor>();
|
||||
|
||||
foreach (var manifest in manifests)
|
||||
{
|
||||
ReplaceVirtualPaths(i);
|
||||
if (manifest.Scripts != null) foreach (var script in manifest.Scripts) scripts.Add(script);
|
||||
if (manifest.Stylesheets != null) foreach (var stylesheet in manifest.Stylesheets) stylesheets.Add(stylesheet);
|
||||
if (manifest.PropertyEditors != null) propertyEditors.AddRange(manifest.PropertyEditors);
|
||||
if (manifest.ParameterEditors != null) parameterEditors.AddRange(manifest.ParameterEditors);
|
||||
if (manifest.GridEditors != null) gridEditors.AddRange(manifest.GridEditors);
|
||||
}
|
||||
|
||||
return new PackageManifest
|
||||
{
|
||||
Scripts = scripts.ToArray(),
|
||||
Stylesheets = stylesheets.ToArray(),
|
||||
PropertyEditors = propertyEditors.ToArray(),
|
||||
ParameterEditors = parameterEditors.ToArray(),
|
||||
GridEditors = gridEditors.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
// gets all manifest files (recursively)
|
||||
private IEnumerable<string> GetManifestFiles()
|
||||
=> Directory.GetFiles(_path, "package.manifest", SearchOption.AllDirectories);
|
||||
|
||||
private static string TrimPreamble(string text)
|
||||
{
|
||||
// strangely StartsWith(preamble) would always return true
|
||||
if (text.Substring(0, 1) == Utf8Preamble)
|
||||
text = text.Remove(0, Utf8Preamble.Length);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces any virtual paths found in properties
|
||||
/// Parses a manifest.
|
||||
/// </summary>
|
||||
/// <param name="jToken"></param>
|
||||
private void ReplaceVirtualPaths(JToken jToken)
|
||||
internal PackageManifest ParseManifest(string text)
|
||||
{
|
||||
if (jToken.Type == JTokenType.Object)
|
||||
{
|
||||
//recurse
|
||||
ReplaceVirtualPaths((JObject)jToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = jToken as JValue;
|
||||
if (value != null)
|
||||
{
|
||||
if (value.Type == JTokenType.String)
|
||||
{
|
||||
if (value.Value<string>().StartsWith("~/"))
|
||||
{
|
||||
//replace the virtual path
|
||||
value.Value = IOHelper.ResolveUrl(value.Value<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentNullOrEmptyException(nameof(text));
|
||||
|
||||
var manifest = JsonConvert.DeserializeObject<PackageManifest>(text,
|
||||
new PropertyEditorConverter(_logger),
|
||||
new ParameterEditorConverter(),
|
||||
new ManifestValidatorConverter());
|
||||
|
||||
// scripts and stylesheets are raw string, must process here
|
||||
for (var i = 0; i < manifest.Scripts.Length; i++)
|
||||
manifest.Scripts[i] = IOHelper.ResolveVirtualUrl(manifest.Scripts[i]);
|
||||
for (var i = 0; i < manifest.Stylesheets.Length; i++)
|
||||
manifest.Stylesheets[i] = IOHelper.ResolveVirtualUrl(manifest.Stylesheets[i]);
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces any virtual paths found in properties
|
||||
/// </summary>
|
||||
/// <param name="jObj"></param>
|
||||
private void ReplaceVirtualPaths(JObject jObj)
|
||||
// purely for tests
|
||||
internal IEnumerable<GridEditor> ParseGridEditors(string text)
|
||||
{
|
||||
foreach (var p in jObj.Properties().Select(x => x.Value))
|
||||
{
|
||||
ReplaceVirtualPaths(p);
|
||||
}
|
||||
return JsonConvert.DeserializeObject<IEnumerable<GridEditor>>(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges two json objects together
|
||||
/// </summary>
|
||||
/// <param name="receiver"></param>
|
||||
/// <param name="donor"></param>
|
||||
/// <param name="keepOriginal">set to true if we will keep the receiver value if the proeprty already exists</param>
|
||||
/// <remarks>
|
||||
/// taken from
|
||||
/// http://stackoverflow.com/questions/4002508/does-c-sharp-have-a-library-for-parsing-multi-level-cascading-json/4002550#4002550
|
||||
/// </remarks>
|
||||
internal static void MergeJObjects(JObject receiver, JObject donor, bool keepOriginal = false)
|
||||
{
|
||||
foreach (var property in donor)
|
||||
{
|
||||
var receiverValue = receiver[property.Key] as JObject;
|
||||
var donorValue = property.Value as JObject;
|
||||
if (receiverValue != null && donorValue != null)
|
||||
{
|
||||
MergeJObjects(receiverValue, donorValue);
|
||||
}
|
||||
else if (receiver[property.Key] == null || !keepOriginal)
|
||||
{
|
||||
receiver[property.Key] = property.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the donor array values into the receiver array
|
||||
/// </summary>
|
||||
/// <param name="receiver"></param>
|
||||
/// <param name="donor"></param>
|
||||
internal static void MergeJArrays(JArray receiver, JArray donor)
|
||||
{
|
||||
foreach (var item in donor)
|
||||
{
|
||||
if (!receiver.Any(x => x.Equals(item)))
|
||||
{
|
||||
receiver.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ using Umbraco.Core.Serialization;
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Used when deserialing the validation collection, any serialized property editors are from a manifest and thus the
|
||||
/// validators are manifest validators.
|
||||
/// Implements a json read converter for <see cref="IPropertyValidator"/>.
|
||||
/// </summary>
|
||||
internal class ManifestValidatorConverter : JsonCreationConverter<IPropertyValidator>
|
||||
internal class ManifestValidatorConverter : JsonReadConverter<IPropertyValidator>
|
||||
{
|
||||
protected override IPropertyValidator Create(Type objectType, JObject jObject)
|
||||
{
|
||||
// all validators coming from manifests are ManifestPropertyValidator instances
|
||||
return new ManifestPropertyValidator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,27 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a manifest file for packages
|
||||
/// Represents the content of a package manifest.
|
||||
/// </summary>
|
||||
internal class PackageManifest
|
||||
public class PackageManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// The json array used to initialize the application with the JS dependencies required
|
||||
/// </summary>
|
||||
public JArray JavaScriptInitialize { get; set; }
|
||||
[JsonProperty("javascript")]
|
||||
public string[] Scripts { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The json array used to initialize the application with the CSS dependencies required
|
||||
/// </summary>
|
||||
public JArray StylesheetInitialize { get; set; }
|
||||
[JsonProperty("css")]
|
||||
public string[] Stylesheets { get; set; }= Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The json array of property editors
|
||||
/// </summary>
|
||||
public JArray PropertyEditors { get; set; }
|
||||
[JsonProperty("propertyEditors")]
|
||||
public PropertyEditor[] PropertyEditors { get; set; } = Array.Empty<PropertyEditor>();
|
||||
|
||||
/// <summary>
|
||||
/// The json array of parameter editors
|
||||
/// </summary>
|
||||
public JArray ParameterEditors { get; set; }
|
||||
[JsonProperty("parameterEditors")]
|
||||
public ParameterEditor[] ParameterEditors { get; set; } = Array.Empty<ParameterEditor>();
|
||||
|
||||
/// <summary>
|
||||
/// The json array of grid editors
|
||||
/// </summary>
|
||||
public JArray GridEditors { get; set; }
|
||||
[JsonProperty("gridEditors")]
|
||||
public GridEditor[] GridEditors { get; set; } = Array.Empty<GridEditor>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,25 +7,33 @@ using Umbraco.Core.Serialization;
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to convert a parameter editor manifest to a property editor object
|
||||
/// Implements a json read converter for <see cref="ParameterEditor"/>.
|
||||
/// </summary>
|
||||
internal class ParameterEditorConverter : JsonCreationConverter<ParameterEditor>
|
||||
internal class ParameterEditorConverter : JsonReadConverter<ParameterEditor>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override ParameterEditor Create(Type objectType, JObject jObject)
|
||||
{
|
||||
return new ParameterEditor();
|
||||
|
||||
}
|
||||
|
||||
protected override void Deserialize(JObject jObject, ParameterEditor target, JsonSerializer serializer)
|
||||
/// <inheritdoc />
|
||||
protected override void Deserialize(JObject jobject, ParameterEditor target, JsonSerializer serializer)
|
||||
{
|
||||
//since it's a manifest editor, we need to create it's instance.
|
||||
//we need to specify the view value for the editor here otherwise we'll get an exception.
|
||||
target.ManifestDefinedParameterValueEditor = new ParameterValueEditor
|
||||
if (jobject.Property("view") != null)
|
||||
{
|
||||
View = jObject["view"].ToString()
|
||||
};
|
||||
// the deserializer will first try to get the property, and that would throw since
|
||||
// the editor would try to create a new value editor, so we have to set a
|
||||
// value editor by ourselves, which will then be populated by the deserializer.
|
||||
target.ValueEditor = new ParameterValueEditor();
|
||||
|
||||
base.Deserialize(jObject, target, serializer);
|
||||
// the 'view' property in the manifest is at top-level, and needs to be moved
|
||||
// down one level to the actual value editor.
|
||||
jobject["editor"] = new JObject { ["view"] = jobject["view"] };
|
||||
jobject.Property("view").Remove();
|
||||
}
|
||||
|
||||
base.Deserialize(jobject, target, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to convert a pre-value field manifest to a real pre value field
|
||||
/// </summary>
|
||||
internal class PreValueFieldConverter : JsonCreationConverter<PreValueField>
|
||||
{
|
||||
protected override PreValueField Create(Type objectType, JObject jObject)
|
||||
{
|
||||
return new PreValueField();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -10,89 +8,74 @@ using Umbraco.Core.Serialization;
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to convert a property editor manifest to a property editor object
|
||||
/// Implements a json read converter for <see cref="PropertyEditor"/>.
|
||||
/// </summary>
|
||||
internal class PropertyEditorConverter : JsonCreationConverter<PropertyEditor>
|
||||
internal class PropertyEditorConverter : JsonReadConverter<PropertyEditor>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyEditorConverter"/> class.
|
||||
/// </summary>
|
||||
public PropertyEditorConverter(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override PropertyEditor Create(Type objectType, JObject jObject)
|
||||
{
|
||||
return new PropertyEditor(_logger);
|
||||
}
|
||||
|
||||
protected override void Deserialize(JObject jObject, PropertyEditor target, JsonSerializer serializer)
|
||||
/// <inheritdoc />
|
||||
protected override void Deserialize(JObject jobject, PropertyEditor target, JsonSerializer serializer)
|
||||
{
|
||||
if (jObject["editor"] != null)
|
||||
if (jobject["editor"] != null)
|
||||
{
|
||||
//since it's a manifest editor, we need to create it's instance.
|
||||
//we need to specify the view value for the editor here otherwise we'll get an exception.
|
||||
target.ManifestDefinedPropertyValueEditor = new PropertyValueEditor
|
||||
{
|
||||
View = jObject["editor"]["view"].ToString()
|
||||
};
|
||||
|
||||
//the manifest JSON is a simplified json for the validators which is actually a dictionary, however, the
|
||||
//c# model requires an array of validators not a dictionary so we need to change the json to an array
|
||||
//to deserialize properly.
|
||||
JArray converted;
|
||||
if (TryConvertValidatorDictionaryToArray(jObject["editor"]["validation"] as JObject, out converted))
|
||||
{
|
||||
jObject["editor"]["validation"] = converted;
|
||||
}
|
||||
// the deserializer will first try to get the property, and that would throw since
|
||||
// the editor would try to create a new value editor, so we have to set a
|
||||
// value editor by ourselves, which will then be populated by the deserializer.
|
||||
target.ValueEditor = new PropertyValueEditor();
|
||||
|
||||
// in the manifest, validators are a simple dictionary eg
|
||||
// {
|
||||
// required: true,
|
||||
// regex: '\\d*'
|
||||
// }
|
||||
// and we need to turn this into a list of IPropertyValidator
|
||||
// so, rewrite the json structure accordingly
|
||||
if (jobject["editor"]["validation"] is JObject validation)
|
||||
jobject["editor"]["validation"] = RewriteValidators(validation);
|
||||
}
|
||||
if (jObject["prevalues"] != null)
|
||||
{
|
||||
target.ManifestDefinedPreValueEditor = new PreValueEditor();
|
||||
|
||||
//the manifest JSON is a simplified json for the validators which is actually a dictionary, however, the
|
||||
//c# model requires an array of validators not a dictionary so we need to change the json to an array
|
||||
//to deserialize properly.
|
||||
var fields = jObject["prevalues"]["fields"] as JArray;
|
||||
if (fields != null)
|
||||
// see note about validators, above - same applies to field validators
|
||||
if (jobject["prevalues"]?["fields"] is JArray jarray)
|
||||
{
|
||||
foreach (var field in jarray)
|
||||
{
|
||||
foreach (var f in fields)
|
||||
{
|
||||
JArray converted;
|
||||
if (TryConvertValidatorDictionaryToArray(f["validation"] as JObject, out converted))
|
||||
{
|
||||
f["validation"] = converted;
|
||||
}
|
||||
}
|
||||
// see note above, for editor
|
||||
if (field["validation"] is JObject validation)
|
||||
field["validation"] = RewriteValidators(validation);
|
||||
}
|
||||
}
|
||||
|
||||
base.Deserialize(jObject, target, serializer);
|
||||
base.Deserialize(jobject, target, serializer);
|
||||
}
|
||||
|
||||
private bool TryConvertValidatorDictionaryToArray(JObject validation, out JArray result)
|
||||
private static JArray RewriteValidators(JObject validation)
|
||||
{
|
||||
if (validation == null)
|
||||
var jarray = new JArray();
|
||||
|
||||
foreach (var v in validation)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
var key = v.Key;
|
||||
var val = v.Value?.Type == JTokenType.Boolean ? string.Empty : v.Value;
|
||||
var jo = new JObject { { "type", key }, { "config", val } };
|
||||
jarray.Add(jo);
|
||||
}
|
||||
|
||||
result = new JArray();
|
||||
foreach (var entry in validation)
|
||||
{
|
||||
//in a special case if the value is simply 'true' (boolean) this just indicates that the
|
||||
// validator is enabled, the config should just be empty.
|
||||
var formattedItem = JObject.FromObject(new { type = entry.Key, config = entry.Value });
|
||||
if (entry.Value.Type == JTokenType.Boolean)
|
||||
{
|
||||
formattedItem["config"] = "";
|
||||
}
|
||||
|
||||
result.Add(formattedItem);
|
||||
}
|
||||
return true;
|
||||
return jarray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration.Grid;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
internal class GridEditor : IGridEditorConfig
|
||||
public class GridEditor : IGridEditorConfig
|
||||
{
|
||||
private string _view;
|
||||
private string _render;
|
||||
|
||||
public GridEditor()
|
||||
{
|
||||
Config = new Dictionary<string, object>();
|
||||
@@ -18,10 +22,18 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public string Alias { get; set; }
|
||||
|
||||
[JsonProperty("view", Required = Required.Always)]
|
||||
public string View { get; set; }
|
||||
public string View
|
||||
{
|
||||
get => _view;
|
||||
set => _view = IOHelper.ResolveVirtualUrl(value);
|
||||
}
|
||||
|
||||
[JsonProperty("render")]
|
||||
public string Render { get; set; }
|
||||
public string Render
|
||||
{
|
||||
get => _render;
|
||||
set => _render = IOHelper.ResolveVirtualUrl(value);
|
||||
}
|
||||
|
||||
[JsonProperty("icon", Required = Required.Always)]
|
||||
public string Icon { get; set; }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
@@ -10,30 +9,25 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
public class ParameterEditor : IParameterEditor
|
||||
{
|
||||
|
||||
private readonly ParameterEditorAttribute _attribute;
|
||||
|
||||
private ParameterValueEditor _valueEditor;
|
||||
private ParameterValueEditor _valueEditorAssigned;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor will setup the property editor based on the attribute if one is found
|
||||
/// </summary>
|
||||
public ParameterEditor()
|
||||
{
|
||||
Configuration = new Dictionary<string, object>();
|
||||
//assign properties based on the attribute if it is found
|
||||
_attribute = GetType().GetCustomAttribute<ParameterEditorAttribute>(false);
|
||||
if (_attribute != null)
|
||||
{
|
||||
//set the id/name from the attribute
|
||||
Alias = _attribute.Alias;
|
||||
Name = _attribute.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These are assigned by default normally based on parameter editor attributes or manifest definitions,
|
||||
/// developers have the chance to override CreateValueEditor if they don't want to use the pre-defined instance
|
||||
/// </summary>
|
||||
internal ParameterValueEditor ManifestDefinedParameterValueEditor = null;
|
||||
// assign properties based on the attribute, if it is found
|
||||
_attribute = GetType().GetCustomAttribute<ParameterEditorAttribute>(false);
|
||||
if (_attribute == null) return;
|
||||
|
||||
Alias = _attribute.Alias;
|
||||
Name = _attribute.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The id of the property editor
|
||||
@@ -53,17 +47,19 @@ namespace Umbraco.Core.PropertyEditors
|
||||
[JsonProperty("config")]
|
||||
public IDictionary<string, object> Configuration { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[JsonProperty("editor")]
|
||||
public ParameterValueEditor ValueEditor
|
||||
{
|
||||
get { return CreateValueEditor(); }
|
||||
get => _valueEditor ?? (_valueEditor = CreateValueEditor());
|
||||
set
|
||||
{
|
||||
_valueEditorAssigned = value;
|
||||
_valueEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
IValueEditor IParameterEditor.ValueEditor
|
||||
{
|
||||
get { return ValueEditor; }
|
||||
}
|
||||
IValueEditor IParameterEditor.ValueEditor => ValueEditor; // fixme - because we must, but - bah
|
||||
|
||||
/// <summary>
|
||||
/// Creates a value editor instance
|
||||
@@ -71,25 +67,18 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <returns></returns>
|
||||
protected virtual ParameterValueEditor CreateValueEditor()
|
||||
{
|
||||
if (ManifestDefinedParameterValueEditor != null)
|
||||
{
|
||||
//detect if the view is a virtual path (in most cases, yes) then convert it
|
||||
if (ManifestDefinedParameterValueEditor.View.StartsWith("~/"))
|
||||
{
|
||||
ManifestDefinedParameterValueEditor.View = IOHelper.ResolveUrl(ManifestDefinedParameterValueEditor.View);
|
||||
}
|
||||
return ManifestDefinedParameterValueEditor;
|
||||
}
|
||||
// handle assigned editor
|
||||
if (_valueEditorAssigned != null)
|
||||
return _valueEditorAssigned;
|
||||
|
||||
//create a new editor
|
||||
// create a new editor
|
||||
var editor = new ParameterValueEditor();
|
||||
|
||||
if (_attribute.EditorView.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new NotImplementedException("This method must be implemented if a view is not explicitly set");
|
||||
}
|
||||
var view = _attribute?.EditorView;
|
||||
if (string.IsNullOrWhiteSpace(view))
|
||||
throw new InvalidOperationException("The editor does not specify a view.");
|
||||
editor.View = view;
|
||||
|
||||
editor.View = _attribute.EditorView;
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
internal class ParameterEditorCollectionBuilder : LazyCollectionBuilderBase<ParameterEditorCollectionBuilder, ParameterEditorCollection, IParameterEditor>
|
||||
public class ParameterEditorCollectionBuilder : LazyCollectionBuilderBase<ParameterEditorCollectionBuilder, ParameterEditorCollection, IParameterEditor>
|
||||
{
|
||||
private readonly ManifestBuilder _manifestBuilder;
|
||||
private readonly ManifestParser _manifestParser;
|
||||
|
||||
public ParameterEditorCollectionBuilder(IServiceContainer container, ManifestBuilder manifestBuilder)
|
||||
public ParameterEditorCollectionBuilder(IServiceContainer container, ManifestParser manifestParser)
|
||||
: base(container)
|
||||
{
|
||||
_manifestBuilder = manifestBuilder;
|
||||
_manifestParser = manifestParser;
|
||||
}
|
||||
|
||||
protected override ParameterEditorCollectionBuilder This => this;
|
||||
@@ -33,8 +33,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
return base.CreateItems(args)
|
||||
.Where(x => (x is PropertyEditor) == false || ((PropertyEditor) x).IsParameterEditor)
|
||||
.Union(_manifestBuilder.ParameterEditors)
|
||||
.Union(_manifestBuilder.PropertyEditors.Where(x => x.IsParameterEditor));
|
||||
.Union(_manifestParser.Manifest.ParameterEditors)
|
||||
.Union(_manifestParser.Manifest.PropertyEditors.Where(x => x.IsParameterEditor))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
// fixme - can we kill this and use "ValueEditor" for both macro and all?
|
||||
|
||||
@@ -7,6 +9,8 @@
|
||||
/// </summary>
|
||||
public class ParameterValueEditor : IValueEditor
|
||||
{
|
||||
private string _view;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParameterValueEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -25,6 +29,10 @@
|
||||
/// <summary>
|
||||
/// Gets or sets the editor view.
|
||||
/// </summary>
|
||||
public string View { get; set; }
|
||||
public string View
|
||||
{
|
||||
get => _view;
|
||||
set => _view = IOHelper.ResolveVirtualUrl(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// If fields are specified then the master View and Validators will be ignored
|
||||
/// </remarks>
|
||||
[JsonProperty("fields")]
|
||||
public List<PreValueField> Fields { get; private set; }
|
||||
public List<PreValueField> Fields { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// A method to format the posted values from the editor to the values to be persisted
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
@@ -9,6 +10,8 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
public class PreValueField
|
||||
{
|
||||
private string _view;
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
@@ -73,7 +76,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// * a simple view name which will map to the views/prevalueeditors/{view}.html
|
||||
/// </summary>
|
||||
[JsonProperty("view", Required = Required.Always)]
|
||||
public string View { get; set; }
|
||||
public string View
|
||||
{
|
||||
get => _view;
|
||||
set => _view = IOHelper.ResolveVirtualUrl(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection of validators for the pre value field
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -21,6 +20,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
private readonly PropertyEditorAttribute _attribute;
|
||||
|
||||
private PropertyValueEditor _valueEditor;
|
||||
private PropertyValueEditor _valueEditorAssigned;
|
||||
private PreValueEditor _preValueEditor;
|
||||
private PreValueEditor _preValueEditorAssigned;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -49,23 +53,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// These are assigned by default normally based on property editor attributes or manifest definitions,
|
||||
/// developers have the chance to override CreateValueEditor if they don't want to use the pre-defined instance
|
||||
/// </summary>
|
||||
internal PropertyValueEditor ManifestDefinedPropertyValueEditor = null;
|
||||
|
||||
/// <summary>
|
||||
/// These are assigned by default normally based on property editor attributes or manifest definitions,
|
||||
/// developers have the chance to override CreatePreValueEditor if they don't want to use the pre-defined instance
|
||||
/// </summary>
|
||||
internal PreValueEditor ManifestDefinedPreValueEditor = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this editor can be used as a parameter editor.
|
||||
/// </summary>
|
||||
[JsonProperty("isParameterEditor")]
|
||||
public bool IsParameterEditor { get; internal set; }
|
||||
public bool IsParameterEditor { get; internal set; } // fixme understand + explain
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique alias of the property editor.
|
||||
@@ -95,50 +87,61 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// Gets or sets a value indicating whether the property editor is deprecated.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool IsDeprecated { get; internal set; } // fixme kill it all in v8
|
||||
public bool IsDeprecated { get; internal set; } // fixme - kill it all in v8
|
||||
|
||||
[JsonProperty("editor", Required = Required.Always)]
|
||||
public PropertyValueEditor ValueEditor => CreateValueEditor();
|
||||
public PropertyValueEditor ValueEditor
|
||||
{
|
||||
get => _valueEditor ?? (_valueEditor = CreateValueEditor());
|
||||
set
|
||||
{
|
||||
_valueEditorAssigned = value;
|
||||
_valueEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
IValueEditor IParameterEditor.ValueEditor => ValueEditor;
|
||||
IValueEditor IParameterEditor.ValueEditor => ValueEditor; // fixme - because we must, but - bah
|
||||
|
||||
[JsonProperty("prevalues")]
|
||||
public PreValueEditor PreValueEditor => CreatePreValueEditor();
|
||||
public PreValueEditor PreValueEditor
|
||||
{
|
||||
get => _preValueEditor ?? (_preValueEditor = CreatePreValueEditor());
|
||||
set
|
||||
{
|
||||
_preValueEditorAssigned = value;
|
||||
_preValueEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("defaultConfig")]
|
||||
public virtual IDictionary<string, object> DefaultPreValues { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
IDictionary<string, object> IParameterEditor.Configuration => DefaultPreValues;
|
||||
IDictionary<string, object> IParameterEditor.Configuration => DefaultPreValues; // fixme - because we must, but - bah
|
||||
|
||||
/// <summary>
|
||||
/// Creates a value editor instance
|
||||
/// Creates a value editor instance.
|
||||
/// </summary>
|
||||
protected virtual PropertyValueEditor CreateValueEditor()
|
||||
{
|
||||
// handle manifest-defined editors
|
||||
if (ManifestDefinedPropertyValueEditor != null)
|
||||
{
|
||||
// map view path if virtual
|
||||
if (ManifestDefinedPropertyValueEditor.View.StartsWith("~/"))
|
||||
ManifestDefinedPropertyValueEditor.View = IOHelper.ResolveUrl(ManifestDefinedPropertyValueEditor.View);
|
||||
return ManifestDefinedPropertyValueEditor;
|
||||
}
|
||||
// handle assigned editor
|
||||
if (_valueEditorAssigned != null)
|
||||
return _valueEditorAssigned;
|
||||
|
||||
// create a new editor
|
||||
var editor = new PropertyValueEditor();
|
||||
|
||||
var view = _attribute?.EditorView;
|
||||
if (string.IsNullOrWhiteSpace(view))
|
||||
throw new InvalidOperationException("The editor does not specify a view.");
|
||||
|
||||
if (view.StartsWith("~/"))
|
||||
view = IOHelper.ResolveUrl(view);
|
||||
editor.View = view;
|
||||
|
||||
editor.ValueType = _attribute.ValueType;
|
||||
editor.HideLabel = _attribute.HideLabel;
|
||||
return editor;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -146,17 +149,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
protected virtual PreValueEditor CreatePreValueEditor()
|
||||
{
|
||||
// handle manifest-defined editors
|
||||
if (ManifestDefinedPreValueEditor != null)
|
||||
{
|
||||
foreach (var field in ManifestDefinedPreValueEditor.Fields)
|
||||
{
|
||||
// map view path if virtual
|
||||
if (field.View.StartsWith("~/"))
|
||||
field.View = IOHelper.ResolveUrl(field.View);
|
||||
}
|
||||
return ManifestDefinedPreValueEditor;
|
||||
}
|
||||
// handle assigned editor
|
||||
if (_preValueEditorAssigned != null)
|
||||
return _preValueEditorAssigned;
|
||||
|
||||
// else return an empty one
|
||||
return new PreValueEditor();
|
||||
@@ -216,17 +211,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
// fixme clear that one! breaking everything!
|
||||
//public class PropertyEditor<TConfiguration> : PropertyEditor
|
||||
//{
|
||||
// public PropertyEditor(ILogger logger)
|
||||
// : base(logger)
|
||||
// { }
|
||||
|
||||
// public virtual TConfiguration MapConfiguration(string configuration)
|
||||
// {
|
||||
// return JsonConvert.DeserializeObject<TConfiguration>(configuration);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,19 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public class PropertyEditorCollectionBuilder : LazyCollectionBuilderBase<PropertyEditorCollectionBuilder, PropertyEditorCollection, PropertyEditor>
|
||||
{
|
||||
public PropertyEditorCollectionBuilder(IServiceContainer container)
|
||||
: base(container)
|
||||
{ }
|
||||
private readonly ManifestParser _manifestParser;
|
||||
|
||||
// have to property-inject that one as it is internal & the builder is public
|
||||
[Inject]
|
||||
internal ManifestBuilder ManifestBuilder { get; set; }
|
||||
public PropertyEditorCollectionBuilder(IServiceContainer container, ManifestParser manifestParser)
|
||||
: base(container)
|
||||
{
|
||||
_manifestParser = manifestParser;
|
||||
}
|
||||
|
||||
protected override PropertyEditorCollectionBuilder This => this;
|
||||
|
||||
protected override IEnumerable<PropertyEditor> CreateItems(params object[] args)
|
||||
{
|
||||
return base.CreateItems(args).Union(ManifestBuilder.PropertyEditors);
|
||||
return base.CreateItems(args).Union(_manifestParser.Manifest.PropertyEditors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -18,6 +17,8 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
public class PropertyValueEditor : IValueEditor
|
||||
{
|
||||
private string _view;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyValueEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -61,8 +62,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </remarks>
|
||||
public virtual void ConfigureForDisplay(PreValueCollection preValues)
|
||||
{
|
||||
if (preValues == null) throw new ArgumentNullException("preValues");
|
||||
_preVals = preValues;
|
||||
_preVals = preValues ?? throw new ArgumentNullException(nameof(preValues));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,7 +73,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// folder, or (3) a view name which maps to views/propertyeditors/{view}/{view}.html.</para>
|
||||
/// </remarks>
|
||||
[JsonProperty("view", Required = Required.Always)]
|
||||
public string View { get; set; }
|
||||
public string View
|
||||
{
|
||||
get => _view;
|
||||
set => _view = IOHelper.ResolveVirtualUrl(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value type which reflects how it is validated and stored in the database
|
||||
@@ -84,9 +88,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <summary>
|
||||
/// A collection of validators for the pre value editor
|
||||
/// </summary>
|
||||
[JsonProperty("validation", ItemConverterType = typeof(ManifestValidatorConverter))]
|
||||
[JsonProperty("validation")]
|
||||
public List<IPropertyValidator> Validators { get; private set; }
|
||||
|
||||
// fixme - need to explain and understand these two + what is "overridable pre-values"
|
||||
|
||||
/// <summary>
|
||||
/// Returns the validator used for the required field validation which is specified on the PropertyType
|
||||
/// </summary>
|
||||
@@ -96,10 +102,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// The default validator used is the RequiredValueValidator but this can be overridden by property editors
|
||||
/// if they need to do some custom validation, or if the value being validated is a json object.
|
||||
/// </remarks>
|
||||
public virtual ManifestValueValidator RequiredValidator
|
||||
{
|
||||
get { return new RequiredManifestValueValidator(); }
|
||||
}
|
||||
public virtual ManifestValueValidator RequiredValidator => new RequiredManifestValueValidator();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the validator used for the regular expression field validation which is specified on the PropertyType
|
||||
@@ -110,10 +113,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// The default validator used is the RegexValueValidator but this can be overridden by property editors
|
||||
/// if they need to do some custom validation, or if the value being validated is a json object.
|
||||
/// </remarks>
|
||||
public virtual ManifestValueValidator RegexValidator
|
||||
{
|
||||
get { return new RegexValidator(); }
|
||||
}
|
||||
public virtual ManifestValueValidator RegexValidator => new RegexValidator();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the true DataTypeDatabaseType from the string representation ValueType.
|
||||
@@ -144,7 +144,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
case PropertyEditorValueTypes.Time:
|
||||
return DataTypeDatabaseType.Date;
|
||||
default:
|
||||
throw new ArgumentException("Not a valid value type.", "valueType");
|
||||
throw new ArgumentException("Not a valid value type.", nameof(valueType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,10 +157,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <summary>
|
||||
/// Set this to true if the property editor is for display purposes only
|
||||
/// </summary>
|
||||
public virtual bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public virtual bool IsReadOnly => false;
|
||||
|
||||
/// <summary>
|
||||
/// Used to try to convert the string value to the correct CLR type based on the DatabaseDataType specified for this value editor
|
||||
@@ -170,14 +167,8 @@ namespace Umbraco.Core.PropertyEditors
|
||||
internal Attempt<object> TryConvertValueToCrlType(object value)
|
||||
{
|
||||
//this is a custom check to avoid any errors, if it's a string and it's empty just make it null
|
||||
var s = value as string;
|
||||
if (s != null)
|
||||
{
|
||||
if (s.IsNullOrWhiteSpace())
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
if (value is string s && string.IsNullOrWhiteSpace(s))
|
||||
value = null;
|
||||
|
||||
Type valueType;
|
||||
//convert the string to a known type
|
||||
|
||||
@@ -55,9 +55,7 @@ namespace Umbraco.Core.Runtime
|
||||
composition.Container.RegisterSingleton(factory => factory.GetInstance<FileSystems>().XsltFileSystem, Constants.Composing.FileSystems.XsltFileSystem);
|
||||
|
||||
// register manifest builder, will be injected in eg PropertyEditorCollectionBuilder
|
||||
composition.Container.RegisterSingleton(factory
|
||||
=> new ManifestParser(factory.GetInstance<ILogger>(), new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), factory.GetInstance<IRuntimeCacheProvider>()));
|
||||
composition.Container.RegisterSingleton<ManifestBuilder>();
|
||||
composition.Container.RegisterSingleton<ManifestParser>();
|
||||
|
||||
composition.Container.RegisterCollectionBuilder<PropertyEditorCollectionBuilder>()
|
||||
.Add(factory => factory.GetInstance<TypeLoader>().GetPropertyEditors());
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Umbraco.Core.Serialization
|
||||
{
|
||||
|
||||
internal abstract class JsonCreationConverter<T> : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of objectType, based properties in the JSON object
|
||||
/// </summary>
|
||||
/// <param name="objectType">type of object expected</param>
|
||||
/// <param name="jObject">contents of JSON object that will be deserialized</param>
|
||||
/// <returns></returns>
|
||||
protected abstract T Create(Type objectType, JObject jObject);
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
// Load JObject from stream
|
||||
var jObject = JObject.Load(reader);
|
||||
|
||||
// Create target object based on JObject
|
||||
var target = Create(objectType, jObject);
|
||||
|
||||
Deserialize(jObject, target, serializer);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
protected virtual void Deserialize(JObject jObject, T target, JsonSerializer serializer)
|
||||
{
|
||||
// Populate the object properties
|
||||
serializer.Populate(jObject.CreateReader(), target);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Umbraco.Core.Serialization
|
||||
{
|
||||
|
||||
internal abstract class JsonReadConverter<T> : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of objectType, based properties in the JSON object
|
||||
/// </summary>
|
||||
/// <param name="objectType">type of object expected</param>
|
||||
/// <param name="jObject">contents of JSON object that will be deserialized</param>
|
||||
/// <returns></returns>
|
||||
protected abstract T Create(Type objectType, JObject jObject);
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
// Load JObject from stream
|
||||
var jObject = JObject.Load(reader);
|
||||
|
||||
// Create target object based on JObject
|
||||
var target = Create(objectType, jObject);
|
||||
|
||||
Deserialize(jObject, target, serializer);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
protected virtual void Deserialize(JObject jobject, T target, JsonSerializer serializer)
|
||||
{
|
||||
// Populate the object properties
|
||||
serializer.Populate(jobject.CreateReader(), target);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,14 +489,11 @@
|
||||
<Compile Include="Macros\XsltExtensionCollection.cs" />
|
||||
<Compile Include="Macros\XsltExtensionCollectionBuilder.cs" />
|
||||
<Compile Include="MainDom.cs" />
|
||||
<Compile Include="Manifest\GridEditorConverter.cs" />
|
||||
<Compile Include="Manifest\ManifestBuilder.cs" />
|
||||
<Compile Include="Manifest\ManifestParser.cs" />
|
||||
<Compile Include="Manifest\ManifestValidatorConverter.cs" />
|
||||
<Compile Include="Manifest\ManifestWatcher.cs" />
|
||||
<Compile Include="Manifest\PackageManifest.cs" />
|
||||
<Compile Include="Manifest\ParameterEditorConverter.cs" />
|
||||
<Compile Include="Manifest\PreValueFieldConverter.cs" />
|
||||
<Compile Include="Manifest\PropertyEditorConverter.cs" />
|
||||
<Compile Include="Media\Exif\BitConverterEx.cs" />
|
||||
<Compile Include="Media\Exif\ExifBitConverter.cs" />
|
||||
@@ -1286,7 +1283,7 @@
|
||||
<Compile Include="Serialization\IFormatter.cs" />
|
||||
<Compile Include="Serialization\ISerializer.cs" />
|
||||
<Compile Include="Serialization\IStreamedResult.cs" />
|
||||
<Compile Include="Serialization\JsonCreationConverter.cs" />
|
||||
<Compile Include="Serialization\JsonReadConverter.cs" />
|
||||
<Compile Include="Serialization\JsonNetSerializer.cs" />
|
||||
<Compile Include="Serialization\JsonToStringConverter.cs" />
|
||||
<Compile Include="Serialization\KnownTypeUdiJsonConverter.cs" />
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using System.Text;
|
||||
@@ -7,11 +6,9 @@ using NUnit.Framework;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
|
||||
namespace Umbraco.Tests.Manifest
|
||||
{
|
||||
@@ -24,20 +21,105 @@ namespace Umbraco.Tests.Manifest
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_parser = new ManifestParser(Mock.Of<ILogger>(), new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), NullCacheProvider.Instance);
|
||||
_parser = new ManifestParser(NullCacheProvider.Instance, Mock.Of<ILogger>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Property_Editors_With_Pre_Vals()
|
||||
public void CanParseComments()
|
||||
{
|
||||
|
||||
var a = JsonConvert.DeserializeObject<JArray>(@"[
|
||||
const string json1 = @"
|
||||
// this is a single-line comment
|
||||
{
|
||||
""x"": 2, // this is an end-of-line comment
|
||||
""y"": 3, /* this is a single line comment block
|
||||
/* comment */ ""z"": /* comment */ 4,
|
||||
""t"": ""this is /* comment */ a string"",
|
||||
""u"": ""this is // more comment in a string""
|
||||
}
|
||||
";
|
||||
|
||||
var jobject = (JObject) JsonConvert.DeserializeObject(json1);
|
||||
Assert.AreEqual("2", jobject.Property("x").Value.ToString());
|
||||
Assert.AreEqual("3", jobject.Property("y").Value.ToString());
|
||||
Assert.AreEqual("4", jobject.Property("z").Value.ToString());
|
||||
Assert.AreEqual("this is /* comment */ a string", jobject.Property("t").Value.ToString());
|
||||
Assert.AreEqual("this is // more comment in a string", jobject.Property("u").Value.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ThrowOnJsonError()
|
||||
{
|
||||
// invalid json, missing the final ']' on javascript
|
||||
const string json = @"{
|
||||
propertyEditors: []/*we have empty property editors**/,
|
||||
javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2.js' }";
|
||||
|
||||
// parsing fails
|
||||
Assert.Throws<JsonReaderException>(() => _parser.ParseManifest(json));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanParseManifest_ScriptsAndStylesheets()
|
||||
{
|
||||
var json = "{}";
|
||||
var manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(0, manifest.Scripts.Length);
|
||||
|
||||
json = "{javascript: []}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(0, manifest.Scripts.Length);
|
||||
|
||||
json = "{javascript: ['~/test.js', '~/test2.js']}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.Scripts.Length);
|
||||
|
||||
json = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.Scripts.Length);
|
||||
|
||||
Assert.AreEqual("/test.js", manifest.Scripts[0]);
|
||||
Assert.AreEqual("/test2.js", manifest.Scripts[1]);
|
||||
|
||||
// kludge is gone - must filter before parsing
|
||||
json = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()) + "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
|
||||
Assert.Throws<JsonReaderException>(() => _parser.ParseManifest(json));
|
||||
|
||||
json = "{}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(0, manifest.Stylesheets.Length);
|
||||
|
||||
json = "{css: []}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(0, manifest.Stylesheets.Length);
|
||||
|
||||
json = "{css: ['~/style.css', '~/folder-name/sdsdsd/stylesheet.css']}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.Stylesheets.Length);
|
||||
|
||||
json = "{propertyEditors: [], css: ['~/stylesheet.css', '~/random-long-name.css']}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.Stylesheets.Length);
|
||||
|
||||
|
||||
|
||||
json = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js'], css: ['~/stylesheet.css', '~/random-long-name.css']}";
|
||||
manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.Scripts.Length);
|
||||
Assert.AreEqual(2, manifest.Stylesheets.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanParseManifest_PropertyEditors()
|
||||
{
|
||||
const string json = @"{'propertyEditors': [
|
||||
{
|
||||
alias: 'Test.Test1',
|
||||
name: 'Test 1',
|
||||
editor: {
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
|
||||
valueType: 'int',
|
||||
hideLabel: true,
|
||||
validation: {
|
||||
'required': true,
|
||||
'Regex': '\\d*'
|
||||
@@ -60,147 +142,10 @@ namespace Umbraco.Tests.Manifest
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]");
|
||||
var parser = _parser.GetPropertyEditors(a);
|
||||
|
||||
Assert.AreEqual(1, parser.Count());
|
||||
Assert.AreEqual(2, parser.ElementAt(0).PreValueEditor.Fields.Count);
|
||||
Assert.AreEqual("key1", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Key);
|
||||
Assert.AreEqual("Some config 1", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).View);
|
||||
Assert.AreEqual(1, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Validators.Count);
|
||||
|
||||
Assert.AreEqual("key2", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Key);
|
||||
Assert.AreEqual("Some config 2", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).View);
|
||||
Assert.AreEqual(0, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Validators.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Grid_Editors()
|
||||
{
|
||||
var a = JsonConvert.DeserializeObject<JArray>(@"[
|
||||
{
|
||||
alias: 'Test.Test1',
|
||||
name: 'Test 1',
|
||||
view: 'blah',
|
||||
icon: 'hello'
|
||||
},
|
||||
{
|
||||
alias: 'Test.Test2',
|
||||
name: 'Test 2',
|
||||
config: { key1: 'some default val' },
|
||||
view: '~/hello/world.cshtml',
|
||||
icon: 'helloworld'
|
||||
},
|
||||
{
|
||||
alias: 'Test.Test3',
|
||||
name: 'Test 3',
|
||||
config: { key1: 'some default val' },
|
||||
view: '/hello/world.html',
|
||||
render: '~/hello/world.cshtml',
|
||||
icon: 'helloworld'
|
||||
}
|
||||
]");
|
||||
var parser = _parser.GetGridEditors(a).ToArray();
|
||||
|
||||
Assert.AreEqual(3, parser.Count());
|
||||
|
||||
Assert.AreEqual("Test.Test1", parser.ElementAt(0).Alias);
|
||||
Assert.AreEqual("Test 1", parser.ElementAt(0).Name);
|
||||
Assert.AreEqual("blah", parser.ElementAt(0).View);
|
||||
Assert.AreEqual("hello", parser.ElementAt(0).Icon);
|
||||
Assert.IsNull(parser.ElementAt(0).Render);
|
||||
Assert.AreEqual(0, parser.ElementAt(0).Config.Count);
|
||||
|
||||
Assert.AreEqual("Test.Test2", parser.ElementAt(1).Alias);
|
||||
Assert.AreEqual("Test 2", parser.ElementAt(1).Name);
|
||||
Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(1).View);
|
||||
Assert.AreEqual("helloworld", parser.ElementAt(1).Icon);
|
||||
Assert.IsNull(parser.ElementAt(1).Render);
|
||||
Assert.AreEqual(1, parser.ElementAt(1).Config.Count);
|
||||
Assert.AreEqual("some default val", parser.ElementAt(1).Config["key1"]);
|
||||
|
||||
Assert.AreEqual("Test.Test3", parser.ElementAt(2).Alias);
|
||||
Assert.AreEqual("Test 3", parser.ElementAt(2).Name);
|
||||
Assert.AreEqual("/hello/world.html", parser.ElementAt(2).View);
|
||||
Assert.AreEqual("helloworld", parser.ElementAt(2).Icon);
|
||||
Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(2).Render);
|
||||
Assert.AreEqual(1, parser.ElementAt(2).Config.Count);
|
||||
Assert.AreEqual("some default val", parser.ElementAt(2).Config["key1"]);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Property_Editors()
|
||||
{
|
||||
|
||||
var a = JsonConvert.DeserializeObject<JArray>(@"[
|
||||
{
|
||||
alias: 'Test.Test1',
|
||||
name: 'Test 1',
|
||||
icon: 'icon-war',
|
||||
editor: {
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
|
||||
valueType: 'int',
|
||||
validation: {
|
||||
required : true,
|
||||
regex : '\\d*'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
alias: 'Test.Test2',
|
||||
name: 'Test 2',
|
||||
group: 'customgroup',
|
||||
defaultConfig: { key1: 'some default pre val' },
|
||||
editor: {
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html',
|
||||
hideLabel: true
|
||||
}
|
||||
}
|
||||
]");
|
||||
var parser = _parser.GetPropertyEditors(a);
|
||||
|
||||
Assert.AreEqual(2, parser.Count());
|
||||
|
||||
Assert.AreEqual(false, parser.ElementAt(0).ValueEditor.HideLabel);
|
||||
Assert.AreEqual("Test.Test1", parser.ElementAt(0).Alias);
|
||||
Assert.AreEqual("Test 1", parser.ElementAt(0).Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html", parser.ElementAt(0).ValueEditor.View);
|
||||
Assert.AreEqual("int", parser.ElementAt(0).ValueEditor.ValueType);
|
||||
Assert.AreEqual(2, parser.ElementAt(0).ValueEditor.Validators.Count());
|
||||
var manifestValidator1 = parser.ElementAt(0).ValueEditor.Validators.ElementAt(0) as ManifestPropertyValidator;
|
||||
Assert.IsNotNull(manifestValidator1);
|
||||
Assert.AreEqual("required", manifestValidator1.Type);
|
||||
var manifestValidator2 = parser.ElementAt(0).ValueEditor.Validators.ElementAt(1) as ManifestPropertyValidator;
|
||||
Assert.IsNotNull(manifestValidator2);
|
||||
Assert.AreEqual("regex", manifestValidator2.Type);
|
||||
|
||||
//groups and icons
|
||||
Assert.AreEqual("common", parser.ElementAt(0).Group);
|
||||
Assert.AreEqual("customgroup", parser.ElementAt(1).Group);
|
||||
|
||||
Assert.AreEqual("icon-war", parser.ElementAt(0).Icon);
|
||||
Assert.AreEqual("icon-autofill", parser.ElementAt(1).Icon);
|
||||
|
||||
|
||||
Assert.AreEqual(true, parser.ElementAt(1).ValueEditor.HideLabel);
|
||||
Assert.AreEqual("Test.Test2", parser.ElementAt(1).Alias);
|
||||
Assert.AreEqual("Test 2", parser.ElementAt(1).Name);
|
||||
Assert.IsTrue(parser.ElementAt(1).DefaultPreValues.ContainsKey("key1"));
|
||||
Assert.AreEqual("some default pre val", parser.ElementAt(1).DefaultPreValues["key1"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Property_Editors_Can_Be_Parameter_Editor()
|
||||
{
|
||||
|
||||
var a = JsonConvert.DeserializeObject<JArray>(@"[
|
||||
{
|
||||
alias: 'Test.Test1',
|
||||
name: 'Test 1',
|
||||
isParameterEditor: true,
|
||||
defaultConfig: { key1: 'some default val' },
|
||||
editor: {
|
||||
@@ -211,31 +156,76 @@ namespace Umbraco.Tests.Manifest
|
||||
regex : '\\d*'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
alias: 'Test.Test2',
|
||||
name: 'Test 2',
|
||||
defaultConfig: { key1: 'some default pre val' },
|
||||
editor: {
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
|
||||
}
|
||||
}
|
||||
]");
|
||||
var parser = _parser.GetPropertyEditors(a);
|
||||
]}";
|
||||
|
||||
Assert.AreEqual(1, parser.Count(x => x.IsParameterEditor));
|
||||
var manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.PropertyEditors.Length);
|
||||
|
||||
IParameterEditor parameterEditor = parser.First();
|
||||
Assert.AreEqual(1, parameterEditor.Configuration.Count);
|
||||
Assert.IsTrue(parameterEditor.Configuration.ContainsKey("key1"));
|
||||
Assert.AreEqual("some default val", parameterEditor.Configuration["key1"]);
|
||||
var editor = manifest.PropertyEditors[1];
|
||||
Assert.IsTrue(editor.IsParameterEditor);
|
||||
|
||||
editor = manifest.PropertyEditors[0];
|
||||
Assert.AreEqual("Test.Test1", editor.Alias);
|
||||
Assert.AreEqual("Test 1", editor.Name);
|
||||
Assert.IsFalse(editor.IsParameterEditor);
|
||||
|
||||
var valueEditor = editor.ValueEditor;
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html", valueEditor.View);
|
||||
Assert.AreEqual("int", valueEditor.ValueType);
|
||||
Assert.IsTrue(valueEditor.HideLabel);
|
||||
|
||||
// these two don't make much sense here
|
||||
// valueEditor.RegexValidator;
|
||||
// valueEditor.RequiredValidator;
|
||||
|
||||
var validators = valueEditor.Validators;
|
||||
Assert.AreEqual(2, validators.Count);
|
||||
var validator = validators[0];
|
||||
var v = validator as ManifestPropertyValidator;
|
||||
Assert.IsNotNull(v);
|
||||
Assert.AreEqual("required", v.Type);
|
||||
Assert.AreEqual("", v.Config);
|
||||
validator = validators[1];
|
||||
v = validator as ManifestPropertyValidator;
|
||||
Assert.IsNotNull(v);
|
||||
Assert.AreEqual("Regex", v.Type);
|
||||
Assert.AreEqual("\\d*", v.Config);
|
||||
|
||||
// this is not part of the manifest
|
||||
var preValues = editor.DefaultPreValues;
|
||||
Assert.IsNull(preValues);
|
||||
|
||||
var preValueEditor = editor.PreValueEditor;
|
||||
Assert.IsNotNull(preValueEditor);
|
||||
Assert.IsNotNull(preValueEditor.Fields);
|
||||
Assert.AreEqual(2, preValueEditor.Fields.Count);
|
||||
|
||||
var f = preValueEditor.Fields[0];
|
||||
Assert.AreEqual("key1", f.Key);
|
||||
Assert.AreEqual("Some config 1", f.Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html", f.View);
|
||||
var fvalidators = f.Validators;
|
||||
Assert.IsNotNull(fvalidators);
|
||||
Assert.AreEqual(1, fvalidators.Count);
|
||||
var fv = fvalidators[0] as ManifestPropertyValidator;
|
||||
Assert.IsNotNull(fv);
|
||||
Assert.AreEqual("required", fv.Type);
|
||||
Assert.AreEqual("", fv.Config);
|
||||
|
||||
f = preValueEditor.Fields[1];
|
||||
Assert.AreEqual("key2", f.Key);
|
||||
Assert.AreEqual("Some config 2", f.Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html", f.View);
|
||||
fvalidators = f.Validators;
|
||||
Assert.IsNotNull(fvalidators);
|
||||
Assert.AreEqual(0, fvalidators.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Parameter_Editors()
|
||||
public void CanParseManifest_ParameterEditors()
|
||||
{
|
||||
|
||||
var a = JsonConvert.DeserializeObject<JArray>(@"[
|
||||
const string json = @"{'parameterEditors': [
|
||||
{
|
||||
alias: 'parameter1',
|
||||
name: 'My Parameter',
|
||||
@@ -246,116 +236,47 @@ namespace Umbraco.Tests.Manifest
|
||||
name: 'Another parameter',
|
||||
config: { key1: 'some config val' },
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
|
||||
},
|
||||
{
|
||||
alias: 'parameter3',
|
||||
name: 'Yet another parameter'
|
||||
}
|
||||
]");
|
||||
var parser = _parser.GetParameterEditors(a);
|
||||
]}";
|
||||
|
||||
Assert.AreEqual(2, parser.Count());
|
||||
Assert.AreEqual("parameter1", parser.ElementAt(0).Alias);
|
||||
Assert.AreEqual("My Parameter", parser.ElementAt(0).Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html", parser.ElementAt(0).ValueEditor.View);
|
||||
var manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(3, manifest.ParameterEditors.Length);
|
||||
|
||||
Assert.AreEqual("parameter2", parser.ElementAt(1).Alias);
|
||||
Assert.AreEqual("Another parameter", parser.ElementAt(1).Name);
|
||||
Assert.IsTrue(parser.ElementAt(1).Configuration.ContainsKey("key1"));
|
||||
Assert.AreEqual("some config val", parser.ElementAt(1).Configuration["key1"]);
|
||||
}
|
||||
var editor = manifest.ParameterEditors[1];
|
||||
Assert.AreEqual("parameter2", editor.Alias);
|
||||
Assert.AreEqual("Another parameter", editor.Name);
|
||||
|
||||
[Test]
|
||||
public void Merge_JArrays()
|
||||
{
|
||||
var obj1 = JArray.FromObject(new[] { "test1", "test2", "test3" });
|
||||
var obj2 = JArray.FromObject(new[] { "test1", "test2", "test3", "test4" });
|
||||
var config = editor.Configuration;
|
||||
Assert.AreEqual(1, config.Count);
|
||||
Assert.IsTrue(config.ContainsKey("key1"));
|
||||
Assert.AreEqual("some config val", config["key1"]);
|
||||
|
||||
ManifestParser.MergeJArrays(obj1, obj2);
|
||||
var valueEditor = editor.ValueEditor;
|
||||
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html", valueEditor.View);
|
||||
|
||||
Assert.AreEqual(4, obj1.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Merge_JObjects_Replace_Original()
|
||||
{
|
||||
var obj1 = JObject.FromObject(new
|
||||
{
|
||||
Property1 = "Value1",
|
||||
Property2 = "Value2",
|
||||
Property3 = "Value3"
|
||||
});
|
||||
|
||||
var obj2 = JObject.FromObject(new
|
||||
editor = manifest.ParameterEditors[2];
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
Property3 = "Value3/2",
|
||||
Property4 = "Value4",
|
||||
Property5 = "Value5"
|
||||
var _ = editor.ValueEditor;
|
||||
});
|
||||
|
||||
ManifestParser.MergeJObjects(obj1, obj2);
|
||||
|
||||
Assert.AreEqual(5, obj1.Properties().Count());
|
||||
Assert.AreEqual("Value3/2", obj1.Properties().ElementAt(2).Value.Value<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Merge_JObjects_Keep_Original()
|
||||
public void CanParseManifest_GridEditors()
|
||||
{
|
||||
var obj1 = JObject.FromObject(new
|
||||
{
|
||||
Property1 = "Value1",
|
||||
Property2 = "Value2",
|
||||
Property3 = "Value3"
|
||||
});
|
||||
|
||||
var obj2 = JObject.FromObject(new
|
||||
{
|
||||
Property3 = "Value3/2",
|
||||
Property4 = "Value4",
|
||||
Property5 = "Value5"
|
||||
});
|
||||
|
||||
ManifestParser.MergeJObjects(obj1, obj2, true);
|
||||
|
||||
Assert.AreEqual(5, obj1.Properties().Count());
|
||||
Assert.AreEqual("Value3", obj1.Properties().ElementAt(2).Value.Value<string>());
|
||||
}
|
||||
|
||||
|
||||
[TestCase("C:\\Test", "C:\\Test\\MyFolder\\AnotherFolder", 2)]
|
||||
[TestCase("C:\\Test", "C:\\Test\\MyFolder\\AnotherFolder\\YetAnother", 3)]
|
||||
[TestCase("C:\\Test", "C:\\Test\\", 0)]
|
||||
public void Get_Folder_Depth(string baseFolder, string currFolder, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected,
|
||||
_parser.FolderDepth(
|
||||
new DirectoryInfo(baseFolder),
|
||||
new DirectoryInfo(currFolder)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//[Test]
|
||||
//public void Parse_Property_Editor()
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
[Test]
|
||||
public void Create_Manifests_Editors()
|
||||
{
|
||||
var package1 = @"{
|
||||
propertyEditors: [],
|
||||
javascript: ['~/test.js', '~/test2.js']}";
|
||||
|
||||
var package2 = "{css: ['~/style.css', '~/folder-name/sdsdsd/stylesheet.css']}";
|
||||
|
||||
var package3 = @"{
|
||||
const string json = @"{
|
||||
'javascript': [ ],
|
||||
'css': [ ],
|
||||
'gridEditors': [
|
||||
{
|
||||
'name': 'Small Hero',
|
||||
'alias': 'small-hero',
|
||||
'view': '/App_Plugins/MyPlugin/small-hero/editortemplate.html',
|
||||
'render': '/Views/Partials/Grid/Editors/SmallHero.cshtml',
|
||||
'view': '~/App_Plugins/MyPlugin/small-hero/editortemplate.html',
|
||||
'render': '~/Views/Partials/Grid/Editors/SmallHero.cshtml',
|
||||
'icon': 'icon-presentation',
|
||||
'config': {
|
||||
'image': {
|
||||
@@ -373,144 +294,32 @@ javascript: ['~/test.js', '~/test2.js']}";
|
||||
{
|
||||
'name': 'Document Links By Category',
|
||||
'alias': 'document-links-by-category',
|
||||
'view': '/App_Plugins/MyPlugin/document-links-by-category/editortemplate.html',
|
||||
'render': '/Views/Partials/Grid/Editors/DocumentLinksByCategory.cshtml',
|
||||
'view': '~/App_Plugins/MyPlugin/document-links-by-category/editortemplate.html',
|
||||
'render': '~/Views/Partials/Grid/Editors/DocumentLinksByCategory.cshtml',
|
||||
'icon': 'icon-umb-members'
|
||||
}
|
||||
]
|
||||
}";
|
||||
var package4 = @"{'propertyEditors': [
|
||||
{
|
||||
alias: 'Test.Test1',
|
||||
name: 'Test 1',
|
||||
editor: {
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
|
||||
valueType: 'int',
|
||||
validation: {
|
||||
'required': true,
|
||||
'Regex': '\\d*'
|
||||
}
|
||||
},
|
||||
prevalues: {
|
||||
fields: [
|
||||
{
|
||||
label: 'Some config 1',
|
||||
key: 'key1',
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html',
|
||||
validation: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Some config 2',
|
||||
key: 'key2',
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]}";
|
||||
var manifest = _parser.ParseManifest(json);
|
||||
Assert.AreEqual(2, manifest.GridEditors.Length);
|
||||
|
||||
var package5 = @"{'parameterEditors': [
|
||||
{
|
||||
alias: 'parameter1',
|
||||
name: 'My Parameter',
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html'
|
||||
},
|
||||
{
|
||||
alias: 'parameter2',
|
||||
name: 'Another parameter',
|
||||
config: { key1: 'some config val' },
|
||||
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
|
||||
}
|
||||
]}";
|
||||
var editor = manifest.GridEditors[0];
|
||||
Assert.AreEqual("small-hero", editor.Alias);
|
||||
Assert.AreEqual("Small Hero", editor.Name);
|
||||
Assert.AreEqual("/App_Plugins/MyPlugin/small-hero/editortemplate.html", editor.View);
|
||||
Assert.AreEqual("/Views/Partials/Grid/Editors/SmallHero.cshtml", editor.Render);
|
||||
Assert.AreEqual("icon-presentation", editor.Icon);
|
||||
|
||||
var result = _parser.CreateManifests(package1, package2, package3, package4, package5).ToArray();
|
||||
|
||||
var paramEditors = result.SelectMany(x => _parser.GetParameterEditors(x.ParameterEditors)).ToArray();
|
||||
var propEditors = result.SelectMany(x => _parser.GetPropertyEditors(x.PropertyEditors)).ToArray();
|
||||
var gridEditors = result.SelectMany(x => _parser.GetGridEditors(x.GridEditors)).ToArray();
|
||||
|
||||
Assert.AreEqual(2, gridEditors.Count());
|
||||
Assert.AreEqual(2, paramEditors.Count());
|
||||
Assert.AreEqual(1, propEditors.Count());
|
||||
var config = editor.Config;
|
||||
Assert.AreEqual(2, config.Count);
|
||||
Assert.IsTrue(config.ContainsKey("image"));
|
||||
var c = config["image"];
|
||||
Assert.IsInstanceOf<JObject>(c); // fixme - is this what we want?
|
||||
Assert.IsTrue(config.ContainsKey("link"));
|
||||
c = config["link"];
|
||||
Assert.IsInstanceOf<JObject>(c); // fixme - is this what we want?
|
||||
|
||||
// fixme - should we resolveUrl in configs?
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Manifest_With_Line_Comments()
|
||||
{
|
||||
var content4 = @"{
|
||||
//here's the property editors
|
||||
propertyEditors: [],
|
||||
//and here's the javascript
|
||||
javascript: ['~/test.js', '~/test2.js']}";
|
||||
|
||||
var result = _parser.CreateManifests(null, content4);
|
||||
|
||||
Assert.AreEqual(1, result.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Manifest_With_Surround_Comments()
|
||||
{
|
||||
var content4 = @"{
|
||||
propertyEditors: []/*we have empty property editors**/,
|
||||
javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2.js']}";
|
||||
|
||||
var result = _parser.CreateManifests(null, content4);
|
||||
|
||||
Assert.AreEqual(1, result.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Manifest_With_Error()
|
||||
{
|
||||
//NOTE: This is missing the final closing ]
|
||||
var content4 = @"{
|
||||
propertyEditors: []/*we have empty property editors**/,
|
||||
javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2.js' }";
|
||||
|
||||
var result = _parser.CreateManifests(null, content4);
|
||||
|
||||
//an error has occurred and been logged but processing continues
|
||||
Assert.AreEqual(0, result.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Manifest_From_File_Content()
|
||||
{
|
||||
var content1 = "{}";
|
||||
var content2 = "{javascript: []}";
|
||||
var content3 = "{javascript: ['~/test.js', '~/test2.js']}";
|
||||
var content4 = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
|
||||
var content5 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()) + "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
|
||||
|
||||
var result = _parser.CreateManifests(null, content1, content2, content3, content4, content5);
|
||||
|
||||
Assert.AreEqual(5, result.Count());
|
||||
Assert.AreEqual(0, result.ElementAt(1).JavaScriptInitialize.Count);
|
||||
Assert.AreEqual(2, result.ElementAt(2).JavaScriptInitialize.Count);
|
||||
Assert.AreEqual(2, result.ElementAt(3).JavaScriptInitialize.Count);
|
||||
Assert.AreEqual(2, result.ElementAt(4).JavaScriptInitialize.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Stylesheet_Initialization()
|
||||
{
|
||||
var content1 = "{}";
|
||||
var content2 = "{css: []}";
|
||||
var content3 = "{css: ['~/style.css', '~/folder-name/sdsdsd/stylesheet.css']}";
|
||||
var content4 = "{propertyEditors: [], css: ['~/stylesheet.css', '~/random-long-name.css']}";
|
||||
|
||||
var result = _parser.CreateManifests(null, content1, content2, content3, content4);
|
||||
|
||||
Assert.AreEqual(4, result.Count());
|
||||
Assert.AreEqual(0, result.ElementAt(1).StylesheetInitialize.Count);
|
||||
Assert.AreEqual(2, result.ElementAt(2).StylesheetInitialize.Count);
|
||||
Assert.AreEqual(2, result.ElementAt(3).StylesheetInitialize.Count);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,10 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
{
|
||||
base.Compose();
|
||||
|
||||
var manifestBuilder = new ManifestBuilder(
|
||||
var manifestBuilder = new ManifestParser(
|
||||
CacheHelper.CreateDisabledCacheHelper().RuntimeCache,
|
||||
new ManifestParser(Logger, new DirectoryInfo(TestHelper.CurrentAssemblyDirectory), CacheHelper.CreateDisabledCacheHelper().RuntimeCache));
|
||||
TestHelper.CurrentAssemblyDirectory,
|
||||
Logger);
|
||||
Container.Register(_ => manifestBuilder);
|
||||
|
||||
Func<IEnumerable<Type>> typeListProducerList = Enumerable.Empty<Type>;
|
||||
|
||||
@@ -303,10 +303,7 @@ namespace Umbraco.Tests.Testing
|
||||
Container.RegisterSingleton<ISectionService, SectionService>();
|
||||
|
||||
// somehow property editor ends up wanting this
|
||||
Container.RegisterSingleton(f => new ManifestBuilder(
|
||||
f.GetInstance<IRuntimeCacheProvider>(),
|
||||
new ManifestParser(f.GetInstance<ILogger>(), new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), f.GetInstance<IRuntimeCacheProvider>())
|
||||
));
|
||||
Container.RegisterSingleton<ManifestParser>();
|
||||
|
||||
// note - don't register collections, use builders
|
||||
Container.RegisterCollectionBuilder<PropertyEditorCollectionBuilder>();
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.Web.AngularIntegration
|
||||
[Test]
|
||||
public void Parse_Main()
|
||||
{
|
||||
var result = JsInitialization.ParseMain(new[] {"[World]", "Hello" });
|
||||
var result = JsInitialization.WriteScript(new[] {"[World]", "Hello" });
|
||||
|
||||
Assert.AreEqual(@"LazyLoad.js([World], function () {
|
||||
//we need to set the legacy UmbClientMgr path
|
||||
|
||||
@@ -32,6 +32,7 @@ using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Action = Umbraco.Web._Legacy.Actions.Action;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using JArray = Newtonsoft.Json.Linq.JArray;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -47,7 +48,7 @@ namespace Umbraco.Web.Editors
|
||||
[DisableClientCache]
|
||||
public class BackOfficeController : UmbracoController
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly ManifestParser _manifestParser;
|
||||
private BackOfficeUserManager<BackOfficeIdentityUser> _userManager;
|
||||
private BackOfficeSignInManager _signInManager;
|
||||
|
||||
@@ -55,9 +56,9 @@ namespace Umbraco.Web.Editors
|
||||
private const string TokenPasswordResetCode = "PasswordResetCode";
|
||||
private static readonly string[] TempDataTokenNames = { TokenExternalSignInError, TokenPasswordResetCode };
|
||||
|
||||
public BackOfficeController(IRuntimeState runtime)
|
||||
public BackOfficeController(ManifestParser manifestParser)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_manifestParser = manifestParser;
|
||||
}
|
||||
|
||||
protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager());
|
||||
@@ -184,12 +185,12 @@ namespace Umbraco.Web.Editors
|
||||
[OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)]
|
||||
public JavaScriptResult Application()
|
||||
{
|
||||
var parser = GetManifestParser();
|
||||
var parser = _manifestParser;
|
||||
var initJs = new JsInitialization(parser);
|
||||
var initCss = new CssInitialization(parser);
|
||||
|
||||
//get the legacy ActionJs file references to append as well
|
||||
var legacyActionJsRef = new JArray(GetLegacyActionJs(LegacyJsActionType.JsUrl));
|
||||
var legacyActionJsRef = GetLegacyActionJs(LegacyJsActionType.JsUrl);
|
||||
|
||||
var result = initJs.GetJavascriptInitialization(HttpContext, JsInitialization.GetDefaultInitialization(), legacyActionJsRef);
|
||||
result += initCss.GetStylesheetInitialization(HttpContext);
|
||||
@@ -205,24 +206,24 @@ namespace Umbraco.Web.Editors
|
||||
[HttpGet]
|
||||
public JsonNetResult GetManifestAssetList()
|
||||
{
|
||||
Func<JArray> getResult = () =>
|
||||
JArray GetAssetList()
|
||||
{
|
||||
var parser = GetManifestParser();
|
||||
var parser = _manifestParser;
|
||||
var initJs = new JsInitialization(parser);
|
||||
var initCss = new CssInitialization(parser);
|
||||
var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray());
|
||||
var cssResult = initCss.GetStylesheetInitializationArray(HttpContext);
|
||||
ManifestParser.MergeJArrays(jsResult, cssResult);
|
||||
return jsResult;
|
||||
};
|
||||
var assets = new List<string>();
|
||||
assets.AddRange(initJs.GetScriptFiles(HttpContext, Enumerable.Empty<string>()));
|
||||
assets.AddRange(initCss.GetStylesheetFiles(HttpContext));
|
||||
return new JArray(assets);
|
||||
}
|
||||
|
||||
//cache the result if debugging is disabled
|
||||
var result = HttpContext.IsDebuggingEnabled
|
||||
? getResult()
|
||||
? GetAssetList()
|
||||
: ApplicationCache.RuntimeCache.GetCacheItem<JArray>(
|
||||
typeof(BackOfficeController) + "GetManifestAssetList",
|
||||
() => getResult(),
|
||||
new TimeSpan(0, 10, 0));
|
||||
"Umbraco.Web.Editors.BackOfficeController.GetManifestAssetList",
|
||||
GetAssetList,
|
||||
new TimeSpan(0, 2, 0));
|
||||
|
||||
return new JsonNetResult { Data = result, Formatting = Formatting.Indented };
|
||||
}
|
||||
@@ -333,13 +334,6 @@ namespace Umbraco.Web.Editors
|
||||
return RedirectToLocal(Url.Action("Default", "BackOffice"));
|
||||
}
|
||||
|
||||
private ManifestParser GetManifestParser()
|
||||
{
|
||||
var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins"));
|
||||
var parser = new ManifestParser(Logger, plugins, ApplicationCache.RuntimeCache);
|
||||
return parser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Default and AuthorizeUpgrade to render as per normal if there's no external login info,
|
||||
/// otherwise process the external login info.
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using ClientDependency.Core;
|
||||
using ClientDependency.Core.Config;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
|
||||
@@ -15,88 +12,53 @@ namespace Umbraco.Web.UI.JavaScript
|
||||
{
|
||||
internal abstract class AssetInitialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all dependencies declared on property editors
|
||||
/// </summary>
|
||||
/// <param name="cdfType"></param>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns></returns>
|
||||
protected JArray ScanPropertyEditors(ClientDependencyType cdfType, HttpContextBase httpContext)
|
||||
protected IEnumerable<string> ScanPropertyEditors(ClientDependencyType assetType, HttpContextBase httpContext)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
var cdfAttributes =
|
||||
Current.PropertyEditors
|
||||
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
|
||||
var attributes = Current.PropertyEditors
|
||||
.SelectMany(x => x.GetType().GetCustomAttributes<PropertyEditorAssetAttribute>(false))
|
||||
.Where(x => x.AssetType == cdfType)
|
||||
.Where(x => x.AssetType == assetType)
|
||||
.Select(x => x.DependencyFile)
|
||||
.ToList();
|
||||
|
||||
string jsOut;
|
||||
string cssOut;
|
||||
var renderer = ClientDependencySettings.Instance.MvcRendererCollection["Umbraco.DependencyPathRenderer"];
|
||||
renderer.RegisterDependencies(cdfAttributes, new HashSet<IClientDependencyPath>(), out jsOut, out cssOut, httpContext);
|
||||
renderer.RegisterDependencies(attributes, new HashSet<IClientDependencyPath>(), out var scripts, out var stylesheets, httpContext);
|
||||
|
||||
var toParse = cdfType == ClientDependencyType.Javascript ? jsOut : cssOut;
|
||||
|
||||
var result = new JArray();
|
||||
//split the result by the delimiter and add to the array
|
||||
foreach (var u in toParse.Split(new[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
result.Add(u);
|
||||
}
|
||||
return result;
|
||||
var toParse = assetType == ClientDependencyType.Javascript ? scripts : stylesheets;
|
||||
return toParse.Split(new[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will use CDF to optimize the asset file collection
|
||||
/// </summary>
|
||||
/// <param name="fileRefs"></param>
|
||||
/// <param name="cdfType"></param>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns>
|
||||
/// Return the asset URLs that should be loaded, if the application is in debug mode then the URLs returned will be the same as the ones
|
||||
/// passed in with the CDF version query strings appended so cache busting works correctly.
|
||||
/// </returns>
|
||||
protected JArray OptimizeAssetCollection(JArray fileRefs, ClientDependencyType cdfType, HttpContextBase httpContext)
|
||||
protected IEnumerable<string> OptimizeAssetCollection(IEnumerable<string> assets, ClientDependencyType assetType, HttpContextBase httpContext)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
|
||||
|
||||
var depenencies = fileRefs.Select(x =>
|
||||
var requestUrl = httpContext.Request.Url;
|
||||
if (requestUrl == null) throw new ArgumentException("HttpContext.Request.Url is null.", nameof(httpContext));
|
||||
|
||||
var dependencies = assets.Select(x =>
|
||||
{
|
||||
var asString = x.ToString();
|
||||
if (asString.StartsWith("/") == false)
|
||||
// most declarations with be made relative to the /umbraco folder, so things
|
||||
// ike lib/blah/blah.js so we need to turn them into absolutes here
|
||||
if (x.StartsWith("/") == false && Uri.IsWellFormedUriString(x, UriKind.Relative))
|
||||
{
|
||||
//most declarations with be made relative to the /umbraco folder, so things like lib/blah/blah.js
|
||||
// so we need to turn them into absolutes here
|
||||
if (Uri.IsWellFormedUriString(asString, UriKind.Relative))
|
||||
{
|
||||
var absolute = new Uri(httpContext.Request.Url, asString);
|
||||
return (IClientDependencyFile)new BasicFile(cdfType) { FilePath = absolute.AbsolutePath };
|
||||
}
|
||||
return (IClientDependencyFile) new BasicFile(assetType) { FilePath = new Uri(requestUrl, x).AbsolutePath };
|
||||
}
|
||||
return cdfType == ClientDependencyType.Javascript
|
||||
? (IClientDependencyFile)new JavascriptFile(asString)
|
||||
: (IClientDependencyFile)new CssFile(asString);
|
||||
}).Where(x => x != null).ToList();
|
||||
|
||||
//Get the output string for these registrations which will be processed by CDF correctly to stagger the output based
|
||||
return assetType == ClientDependencyType.Javascript
|
||||
? (IClientDependencyFile) new JavascriptFile(x)
|
||||
: (IClientDependencyFile) new CssFile(x);
|
||||
}).ToList();
|
||||
|
||||
// get the output string for these registrations which will be processed by CDF correctly to stagger the output based
|
||||
// on internal vs external resources. The output will be delimited based on our custom Umbraco.Web.UI.JavaScript.DependencyPathRenderer
|
||||
string jsOut;
|
||||
string cssOut;
|
||||
var renderer = ClientDependencySettings.Instance.MvcRendererCollection["Umbraco.DependencyPathRenderer"];
|
||||
renderer.RegisterDependencies(depenencies, new HashSet<IClientDependencyPath>(), out jsOut, out cssOut, httpContext);
|
||||
renderer.RegisterDependencies(dependencies, new HashSet<IClientDependencyPath>(), out var scripts, out var stylesheets, httpContext);
|
||||
|
||||
var urls = cdfType == ClientDependencyType.Javascript
|
||||
? jsOut.Split(new string[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries)
|
||||
: cssOut.Split(new string[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var urls = assetType == ClientDependencyType.Javascript
|
||||
? scripts.Split(new[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries)
|
||||
: stylesheets.Split(new[] { DependencyPathRenderer.Delimiter }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var result = new JArray();
|
||||
foreach (var u in urls)
|
||||
{
|
||||
result.Add(u);
|
||||
}
|
||||
return result;
|
||||
return urls;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System.Web;
|
||||
using ClientDependency.Core;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Web.UI.JavaScript
|
||||
@@ -14,50 +11,39 @@ namespace Umbraco.Web.UI.JavaScript
|
||||
internal class CssInitialization : AssetInitialization
|
||||
{
|
||||
private readonly ManifestParser _parser;
|
||||
|
||||
public CssInitialization(ManifestParser parser)
|
||||
{
|
||||
_parser = parser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes all found manifest files and outputs yepnope.injectcss calls for all css files found in all manifests
|
||||
/// Processes all found manifest files, and outputs css inject calls for all css files found in all manifests.
|
||||
/// </summary>
|
||||
public string GetStylesheetInitialization(HttpContextBase httpContext)
|
||||
{
|
||||
var result = GetStylesheetInitializationArray(httpContext);
|
||||
|
||||
return ParseMain(result);
|
||||
var files = GetStylesheetFiles(httpContext);
|
||||
return WriteScript(files);
|
||||
}
|
||||
|
||||
public JArray GetStylesheetInitializationArray(HttpContextBase httpContext)
|
||||
public IEnumerable<string> GetStylesheetFiles(HttpContextBase httpContext)
|
||||
{
|
||||
var merged = new JArray();
|
||||
foreach (var m in _parser.GetManifests())
|
||||
{
|
||||
ManifestParser.MergeJArrays(merged, m.StylesheetInitialize);
|
||||
}
|
||||
var stylesheets = new HashSet<string>();
|
||||
var optimizedManifest = OptimizeAssetCollection(_parser.Manifest.Stylesheets, ClientDependencyType.Css, httpContext);
|
||||
foreach (var stylesheet in optimizedManifest)
|
||||
stylesheets.Add(stylesheet);
|
||||
|
||||
//now we can optimize if in release mode
|
||||
merged = OptimizeAssetCollection(merged, ClientDependencyType.Css, httpContext);
|
||||
foreach (var stylesheet in ScanPropertyEditors(ClientDependencyType.Css, httpContext))
|
||||
stylesheets.Add(stylesheet);
|
||||
|
||||
//now we need to merge in any found cdf declarations on property editors
|
||||
ManifestParser.MergeJArrays(merged, ScanPropertyEditors(ClientDependencyType.Css, httpContext));
|
||||
|
||||
return merged;
|
||||
return stylesheets.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parses the CssResources.Main and returns a yepnop.injectCss format
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
internal static string ParseMain(JArray files)
|
||||
internal static string WriteScript(IEnumerable<string> files)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var file in files)
|
||||
sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using ClientDependency.Core;
|
||||
using ClientDependency.Core.Config;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Manifest;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.UI.JavaScript
|
||||
{
|
||||
@@ -28,65 +24,65 @@ namespace Umbraco.Web.UI.JavaScript
|
||||
_parser = parser;
|
||||
}
|
||||
|
||||
//used to strip comments
|
||||
internal static readonly Regex Comments = new Regex("(/\\*.*\\*/)", RegexOptions.Compiled);
|
||||
//used for dealing with js functions inside of json (which is not a supported json syntax)
|
||||
// deal with javascript functions inside of json (not a supported json syntax)
|
||||
private const string PrefixJavaScriptObject = "@@@@";
|
||||
private static readonly Regex JsFunctionParser = new Regex(string.Format("(\"{0}(.*?)\")+", PrefixJavaScriptObject),
|
||||
RegexOptions.Multiline
|
||||
| RegexOptions.CultureInvariant
|
||||
| RegexOptions.Compiled);
|
||||
//used to replace the tokens in the js main
|
||||
private static readonly Regex JsFunctionParser = new Regex($"(\"{PrefixJavaScriptObject}(.*?)\")+",
|
||||
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
|
||||
// replace tokens in the js main
|
||||
private static readonly Regex Token = new Regex("(\"##\\w+?##\")", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Processes all found manifest files and outputs the main.js file containing all plugin manifests
|
||||
/// </summary>
|
||||
public string GetJavascriptInitialization(HttpContextBase httpContext, JArray umbracoInit, JArray additionalJsFiles = null)
|
||||
public string GetJavascriptInitialization(HttpContextBase httpContext, IEnumerable<string> umbracoInit, IEnumerable<string> additionalJsFiles = null)
|
||||
{
|
||||
var result = GetJavascriptInitializationArray(httpContext, umbracoInit, additionalJsFiles);
|
||||
var files = GetScriptFiles(httpContext, umbracoInit, additionalJsFiles);
|
||||
|
||||
return ParseMain(
|
||||
result.ToString(),
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco));
|
||||
var jarray = new StringBuilder();
|
||||
jarray.AppendLine("[");
|
||||
var first = true;
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (first) first = false;
|
||||
else jarray.AppendLine(",");
|
||||
jarray.Append("\"");
|
||||
jarray.Append(file);
|
||||
jarray.Append("\"");
|
||||
|
||||
}
|
||||
jarray.Append("]");
|
||||
|
||||
return WriteScript(jarray.ToString(), IOHelper.ResolveUrl(SystemDirectories.Umbraco));
|
||||
}
|
||||
|
||||
public JArray GetJavascriptInitializationArray(HttpContextBase httpContext, JArray umbracoInit, JArray additionalJsFiles = null)
|
||||
public IEnumerable<string> GetScriptFiles(HttpContextBase httpContext, IEnumerable<string> umbracoInit, IEnumerable<string> additionalJsFiles = null)
|
||||
{
|
||||
foreach (var m in _parser.GetManifests())
|
||||
{
|
||||
ManifestParser.MergeJArrays(umbracoInit, m.JavaScriptInitialize);
|
||||
}
|
||||
|
||||
//merge in the additional ones specified if there are any
|
||||
var scripts = new HashSet<string>();
|
||||
foreach (var script in umbracoInit)
|
||||
scripts.Add(script);
|
||||
foreach (var script in _parser.Manifest.Scripts)
|
||||
scripts.Add(script);
|
||||
if (additionalJsFiles != null)
|
||||
{
|
||||
ManifestParser.MergeJArrays(umbracoInit, additionalJsFiles);
|
||||
}
|
||||
foreach (var script in additionalJsFiles)
|
||||
scripts.Add(script);
|
||||
|
||||
//now we can optimize if in release mode
|
||||
umbracoInit = OptimizeAssetCollection(umbracoInit, ClientDependencyType.Javascript, httpContext);
|
||||
scripts = new HashSet<string>(OptimizeAssetCollection(scripts, ClientDependencyType.Javascript, httpContext));
|
||||
|
||||
//now we need to merge in any found cdf declarations on property editors
|
||||
ManifestParser.MergeJArrays(umbracoInit, ScanPropertyEditors(ClientDependencyType.Javascript, httpContext));
|
||||
foreach (var script in ScanPropertyEditors(ClientDependencyType.Javascript, httpContext))
|
||||
scripts.Add(script);
|
||||
|
||||
return umbracoInit;
|
||||
return scripts.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default config as a JArray
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static JArray GetDefaultInitialization()
|
||||
internal static IEnumerable<string> GetDefaultInitialization()
|
||||
{
|
||||
var init = Resources.JsInitialize;
|
||||
var deserialized = JsonConvert.DeserializeObject<JArray>(init);
|
||||
var result = new JArray();
|
||||
foreach (var j in deserialized.Where(j => j.Type == JTokenType.String))
|
||||
{
|
||||
result.Add(j);
|
||||
}
|
||||
return result;
|
||||
var resources = JsonConvert.DeserializeObject<JArray>(Resources.JsInitialize);
|
||||
return resources.Where(x => x.Type == JTokenType.String).Select(x => x.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,22 +90,18 @@ namespace Umbraco.Web.UI.JavaScript
|
||||
/// </summary>
|
||||
/// <param name="replacements"></param>
|
||||
/// <returns></returns>
|
||||
internal static string ParseMain(params string[] replacements)
|
||||
internal static string WriteScript(params string[] replacements)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
// replace, catering for the special syntax when we have
|
||||
// js function() objects contained in the json
|
||||
|
||||
return Token.Replace(Resources.Main, match =>
|
||||
{
|
||||
var replaced = replacements[count];
|
||||
|
||||
//we need to cater for the special syntax when we have js function() objects contained in the json
|
||||
var jsFunctionParsed = JsFunctionParser.Replace(replaced, "$2");
|
||||
|
||||
count++;
|
||||
|
||||
return jsFunctionParsed;
|
||||
var replacement = replacements[count++];
|
||||
return JsFunctionParser.Replace(replacement, "$2");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user