diff --git a/Gruntfile.js b/Gruntfile.js index a26446f..6bacbeb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -29,11 +29,6 @@ module.exports = function(grunt) { tasks: ['copy:html'] }, - config: { - files: ['config/package.manifest'], - tasks: ['copy:config'] - }, - dll: { files: ['app/Umbraco/**/*.dll'], tasks: ['copy:dll'] @@ -76,25 +71,13 @@ module.exports = function(grunt) { expand: true }, dll: { - cwd: 'app/Umbraco/Umbraco.Archetype/bin/Debug/', + cwd: 'app/Umbraco/Umbraco.Archetype/bin/Release/', src: '*.*', dest: '<%= dest %>/bin/', expand: true }, config: { files: [ - { - cwd: 'config/', - src: ['package.manifest'], - dest: '<%= basePath %>', - expand: true - }, - { - cwd: 'app/config/', - src: ['propertyEditors.views.js'], - dest: '<%= basePath %>/js', - expand: true - }, { cwd: 'app/langs/', src: ['**'], @@ -173,7 +156,7 @@ module.exports = function(grunt) { clean: { build: '<%= grunt.config("basePath").substring(0, 4) == "dist" ? "dist/**/*" : "null" %>', - tmp: ['pkg/tmp'] + tmp: ['tmp'] }, assemblyinfo: { @@ -225,6 +208,7 @@ module.exports = function(grunt) { grunt.registerTask('default', ['clean', 'less', 'concat', 'assemblyinfo', 'msbuild:dist', 'copy:dll', 'copy:config', 'copy:html']); - grunt.registerTask('nuget', ['clean:tmp', 'default', 'copy:nuget', 'template:nuspec', 'nugetpack', 'clean:tmp']); + grunt.registerTask('nuget', ['clean:tmp', 'default', 'copy:nuget', 'template:nuspec', 'nugetpack', 'clean:tmp']); grunt.registerTask('umbraco', ['clean:tmp', 'default', 'copy:umbraco', 'umbracoPackage', 'clean:tmp']); + grunt.registerTask('package', ['clean:tmp', 'default', 'copy:nuget', 'template:nuspec', 'nugetpack', 'copy:umbraco', 'umbracoPackage', 'clean:tmp']); }; \ No newline at end of file diff --git a/README.md b/README.md index 5565d10..e15a4e1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Install the selected rele ## Official Docs ## http://imulus.github.io/Archetype +Follow us on Twitter https://twitter.com/ArchetypeKit + ## Contribute ## Want to contribute to Archetype? You'll want to use Grunt (our task runner) to help you integrate with a local copy of Umbraco. @@ -32,4 +34,4 @@ You can also watch for changes using: grunt watch --target="D:\inetpub\mysite" -Add `--touch` to either command to automatically touch the web.config on a deploy \ No newline at end of file +Add `--touch` to either command to automatically touch the web.config on a deploy diff --git a/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj b/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj index df29425..f3082fc 100644 --- a/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj +++ b/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj @@ -31,21 +31,189 @@ 4 + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\businesslogic.dll + + + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll + + + ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\cms.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\controls.dll + + + ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\Examine.dll + + + ..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll + + + ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + + + ..\packages\ImageProcessor.1.8.7.0\lib\ImageProcessor.dll + + + ..\packages\ImageProcessor.Web.3.2.1.0\lib\net45\ImageProcessor.Web.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\interfaces.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\log4net.dll + + + ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\Microsoft.ApplicationBlocks.Data.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\Microsoft.Web.Helpers.dll + + + True + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + ..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.1\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + + + ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll + ..\packages\Moq.4.2.1312.1622\lib\net40\Moq.dll + + ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll + + + False + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\SQLCE4Umbraco.dll + + + True + ..\packages\UmbracoCms.Core.7.1.1\lib\System.Data.SqlServerCe.dll + + + True + ..\packages\UmbracoCms.Core.7.1.1\lib\System.Data.SqlServerCe.Entity.dll + + + + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll + + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll + + + True + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll + + + False + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll + True + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll + - - ..\packages\UmbracoCms.Core.7.0.1\lib\Umbraco.Core.dll + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\TidyNet.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\Umbraco.Core.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.DataLayer.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.editorControls.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.MacroEngines.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.providers.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\Umbraco.Web.UI.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.XmlSerializers.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\UmbracoExamine.dll + + + False + ..\packages\UmbracoCms.Core.7.1.1\lib\UrlRewritingNet.UrlRewriter.dll @@ -54,6 +222,7 @@ + diff --git a/app/Umbraco/Archetype.Tests/app.config b/app/Umbraco/Archetype.Tests/app.config new file mode 100644 index 0000000..72cac16 --- /dev/null +++ b/app/Umbraco/Archetype.Tests/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/Umbraco/Archetype.Tests/packages.config b/app/Umbraco/Archetype.Tests/packages.config index c90677c..493b067 100644 --- a/app/Umbraco/Archetype.Tests/packages.config +++ b/app/Umbraco/Archetype.Tests/packages.config @@ -1,5 +1,28 @@  + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Api/ArchetypeDataTypeController.cs b/app/Umbraco/Umbraco.Archetype/Api/ArchetypeDataTypeController.cs index 6bdb7ce..3a03b2f 100644 --- a/app/Umbraco/Umbraco.Archetype/Api/ArchetypeDataTypeController.cs +++ b/app/Umbraco/Umbraco.Archetype/Api/ArchetypeDataTypeController.cs @@ -1,4 +1,7 @@ -using System.Net; +using System.Collections.Generic; +using System; +using System.Linq; +using System.Net; using System.Web.Http; using AutoMapper; using Umbraco.Core.Models; @@ -11,9 +14,23 @@ namespace Archetype.Umbraco.Api [PluginController("ArchetypeApi")] public class ArchetypeDataTypeController : UmbracoAuthorizedJsonController { - public object GetById(int id) + + public IEnumerable GetAllPropertyEditors() { - var dataType = Services.DataTypeService.GetDataTypeDefinitionById(id); + return + global::Umbraco.Core.PropertyEditors.PropertyEditorResolver.Current.PropertyEditors + .Select(x => new {defaultPreValues = x.DefaultPreValues, alias = x.Alias, view = x.ValueEditor.View}); + } + + public object GetAll() + { + var dataTypes = Services.DataTypeService.GetAllDataTypeDefinitions(); + return dataTypes.Select(t => new { guid = t.Key, name = t.Name }); + } + + public object GetByGuid(Guid guid) + { + var dataType = Services.DataTypeService.GetDataTypeDefinitionById(guid); if (dataType == null) { throw new HttpResponseException(HttpStatusCode.NotFound); diff --git a/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj b/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj index 05ed8ed..53469ca 100644 --- a/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj +++ b/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj @@ -41,11 +41,10 @@ False - ..\packages\UmbracoCms.Core.7.0.1\lib\businesslogic.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\businesslogic.dll - ..\packages\ClientDependency.1.7.0.4\lib\ClientDependency.Core.dll + ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll False @@ -53,20 +52,17 @@ False - ..\packages\UmbracoCms.Core.7.0.1\lib\cms.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\cms.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\controls.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\controls.dll ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll False - ..\packages\UmbracoCms.Core.7.0.1\lib\Examine.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\Examine.dll ..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll @@ -76,25 +72,29 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll False - - ..\packages\UmbracoCms.Core.7.0.1\lib\interfaces.dll + + ..\packages\ImageProcessor.1.8.7.0\lib\ImageProcessor.dll False - - ..\packages\UmbracoCms.Core.7.0.1\lib\log4net.dll + + ..\packages\ImageProcessor.Web.3.2.1.0\lib\net45\ImageProcessor.Web.dll False + + ..\packages\UmbracoCms.Core.7.1.1\lib\interfaces.dll + + + ..\packages\UmbracoCms.Core.7.1.1\lib\log4net.dll + ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll False - ..\packages\UmbracoCms.Core.7.0.1\lib\Microsoft.ApplicationBlocks.Data.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\Microsoft.ApplicationBlocks.Data.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\Microsoft.Web.Helpers.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\Microsoft.Web.Helpers.dll False @@ -113,22 +113,20 @@ False - ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll - False + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\SQLCE4Umbraco.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\SQLCE4Umbraco.dll - False - ..\packages\UmbracoCms.Core.7.0.1\lib\System.Data.SqlServerCe.dll + True + ..\packages\UmbracoCms.Core.7.1.1\lib\System.Data.SqlServerCe.dll - False - ..\packages\UmbracoCms.Core.7.0.1\lib\System.Data.SqlServerCe.Entity.dll + True + ..\packages\UmbracoCms.Core.7.1.1\lib\System.Data.SqlServerCe.Entity.dll @@ -136,6 +134,7 @@ False + False ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll @@ -174,53 +173,44 @@ - ..\packages\UmbracoCms.Core.7.0.1\lib\TidyNet.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\TidyNet.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\Umbraco.Core.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\Umbraco.Core.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.DataLayer.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.DataLayer.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.editorControls.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.editorControls.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.MacroEngines.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.MacroEngines.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.providers.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.providers.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\Umbraco.Web.UI.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\Umbraco.Web.UI.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.XmlSerializers.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\umbraco.XmlSerializers.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\UmbracoExamine.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\UmbracoExamine.dll - ..\packages\UmbracoCms.Core.7.0.1\lib\UrlRewritingNet.UrlRewriter.dll - False + ..\packages\UmbracoCms.Core.7.1.1\lib\UrlRewritingNet.UrlRewriter.dll + + @@ -231,8 +221,10 @@ + + diff --git a/app/Umbraco/Umbraco.Archetype/Constants.cs b/app/Umbraco/Umbraco.Archetype/Constants.cs index 9bc7f0d..83d036b 100644 --- a/app/Umbraco/Umbraco.Archetype/Constants.cs +++ b/app/Umbraco/Umbraco.Archetype/Constants.cs @@ -5,5 +5,9 @@ public const string PropertyEditorAlias = "Imulus.Archetype"; public const string PreValueAlias = "archetypeConfig"; + + public const string CacheKey_DataTypeByGuid = ""; + + public const string CacheKey_PreValueFromDataTypeId = "Archetype_GetArchetypePreValueFromDataTypeId_"; } } diff --git a/app/Umbraco/Umbraco.Archetype/Events/ExpireCache.cs b/app/Umbraco/Umbraco.Archetype/Events/ExpireCache.cs new file mode 100644 index 0000000..3db68df --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Events/ExpireCache.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web; + +namespace Archetype.Umbraco.Events +{ + public class ExpireCache : ApplicationEventHandler + { + + protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + base.ApplicationStarting(umbracoApplication, applicationContext); + + DataTypeService.Saved += ExpirePreValueCache; + } + + void ExpirePreValueCache(IDataTypeService sender, global::Umbraco.Core.Events.SaveEventArgs e) + { + foreach (var dataType in e.SavedEntities) + { + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(Constants.CacheKey_PreValueFromDataTypeId + dataType.Id); + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(Constants.CacheKey_DataTypeByGuid + dataType.Key); + } + } + } +} diff --git a/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeHelper.cs b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeHelper.cs new file mode 100644 index 0000000..6517af7 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeHelper.cs @@ -0,0 +1,151 @@ +using System; +using System.Linq; +using Archetype.Umbraco.Models; +using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.Models; + +namespace Archetype.Umbraco.Extensions +{ + public class ArchetypeHelper + { + + protected JsonSerializerSettings _jsonSettings; + protected ApplicationContext _app; + + internal ArchetypeHelper() + { + var dcr = new Newtonsoft.Json.Serialization.DefaultContractResolver(); + dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; + + _jsonSettings = new JsonSerializerSettings { ContractResolver = dcr }; + _app = ApplicationContext.Current; + } + + internal Models.Archetype DeserializeJsonToArchetype(string sourceJson, PreValueCollection dataTypePreValues) + { + try + { + var archetype = JsonConvert.DeserializeObject(sourceJson, _jsonSettings); + + try + { + // Get list of configured properties and their types and map them to the deserialized archetype model + var preValue = GetArchetypePreValueFromPreValuesCollection(dataTypePreValues); + RetrieveAdditionalProperties(ref archetype, preValue); + } + catch (Exception ex) + { + } + + return archetype; + } + catch + { + return new Models.Archetype(); + } + } + + internal Models.Archetype DeserializeJsonToArchetype(string sourceJson, int dataTypeId) + { + try + { + var archetype = JsonConvert.DeserializeObject(sourceJson, _jsonSettings); + + try + { + // Get list of configured properties and their types and map them to the deserialized archetype model + var preValue = GetArchetypePreValueFromDataTypeId(dataTypeId); + RetrieveAdditionalProperties(ref archetype, preValue); + } + catch (Exception ex) + { + } + + return archetype; + } + catch + { + return new Models.Archetype(); + } + } + + private ArchetypePreValue GetArchetypePreValueFromDataTypeId(int dataTypeId) + { + return _app.ApplicationCache.RuntimeCache.GetCacheItem( + Constants.CacheKey_PreValueFromDataTypeId + dataTypeId, + () => + { + var preValues = _app.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId); + + var configJson = preValues.IsDictionaryBased + ? preValues.PreValuesAsDictionary[Constants.PreValueAlias].Value + : preValues.PreValuesAsArray.First().Value; + + var config = JsonConvert.DeserializeObject(configJson, _jsonSettings); + RetrieveAdditionalProperties(ref config); + + return config; + + }) as ArchetypePreValue; + } + + private ArchetypePreValue GetArchetypePreValueFromPreValuesCollection(PreValueCollection dataTypePreValues) + { + var preValueAsString = dataTypePreValues.PreValuesAsDictionary.First().Value.Value; + var preValue = JsonConvert.DeserializeObject(preValueAsString, _jsonSettings); + return preValue; + } + + + private IDataTypeDefinition GetDataTypeByGuid(Guid guid) + { + return (IDataTypeDefinition) ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( + Constants.CacheKey_DataTypeByGuid + guid, + () => _app.Services.DataTypeService.GetDataTypeDefinitionById(guid)); + } + + /// + /// Retrieves additional metadata that isn't available on the stored model of an Archetype + /// + /// The Archetype to add the additional metadata to + /// The configuration of the Archetype + private void RetrieveAdditionalProperties(ref Models.Archetype archetype, ArchetypePreValue preValue) + { + foreach (var fieldset in preValue.Fieldsets) + { + var fieldsetAlias = fieldset.Alias; + foreach (var fieldsetInst in archetype.Fieldsets.Where(x => x.Alias == fieldsetAlias)) + { + foreach (var property in fieldset.Properties) + { + var propertyAlias = property.Alias; + foreach ( var propertyInst in fieldsetInst.Properties.Where(x => x.Alias == propertyAlias)) + { + propertyInst.DataTypeGuid = property.DataTypeGuid.ToString(); + propertyInst.DataTypeId = GetDataTypeByGuid(property.DataTypeGuid).Id; + propertyInst.PropertyEditorAlias = property.PropertyEditorAlias; + } + } + } + } + } + + /// + /// Retrieves additional metadata that isn't available on the stored model of an ArchetypePreValue + /// + /// The Archetype to add the additional metadata to + /// The configuration of the Archetype + private void RetrieveAdditionalProperties(ref Models.ArchetypePreValue preValue) + { + foreach (var fieldset in preValue.Fieldsets) + { + foreach (var property in fieldset.Properties) + { + property.PropertyEditorAlias = GetDataTypeByGuid(property.DataTypeGuid).PropertyEditorAlias; + } + } + } + + } +} diff --git a/app/Umbraco/Umbraco.Archetype/Extensions/Extensions.cs b/app/Umbraco/Umbraco.Archetype/Extensions/Extensions.cs index 2232aa5..c9f3953 100644 --- a/app/Umbraco/Umbraco.Archetype/Extensions/Extensions.cs +++ b/app/Umbraco/Umbraco.Archetype/Extensions/Extensions.cs @@ -1,4 +1,10 @@ -using Umbraco.Core; +using System; +using System.Web; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Web; using Archetype.Umbraco.Models; namespace Archetype.Umbraco.Extensions diff --git a/app/Umbraco/Umbraco.Archetype/Models/Archetype.cs b/app/Umbraco/Umbraco.Archetype/Models/Archetype.cs index 207e9f6..91763ff 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/Archetype.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/Archetype.cs @@ -1,6 +1,9 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Archetype.Umbraco.Models { @@ -24,5 +27,18 @@ namespace Archetype.Umbraco.Models { return this.GetEnumerator(); } + + internal string SerializeForPersistence() + { + var json = JObject.Parse(JsonConvert.SerializeObject(this)); + var propertiesToRemove = new String[] { "propertyEditorAlias", "dataTypeId", "dataTypeGuid" }; + + json.Descendants().OfType() + .Where(p => propertiesToRemove.Contains(p.Name)) + .ToList() + .ForEach(x => x.Remove()); + + return json.ToString(Formatting.None); + } } } diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValue.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValue.cs index 1a7e4a2..653af0e 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValue.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValue.cs @@ -6,7 +6,10 @@ namespace Archetype.Umbraco.Models public class ArchetypePreValue { [JsonProperty("showAdvancedOptions")] - public bool ShowAdvancedOptions { get; set; } + public bool ShowAdvancedOptions { get; set; } + + [JsonProperty("startWithAddButton")] + public bool StartWithAddButton { get; set; } [JsonProperty("hideFieldsetToolbar")] public bool HideFieldsetToolbar { get; set; } diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValueProperty.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValueProperty.cs index c8c13e4..e2cc12b 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValueProperty.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValueProperty.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; namespace Archetype.Umbraco.Models { @@ -18,12 +19,9 @@ namespace Archetype.Umbraco.Models [JsonProperty("helpText")] public string HelpText { get; set; } - - [JsonProperty("dataTypeId")] - public int DataTypeId { get; set; } - + [JsonProperty("dataTypeGuid")] - public string DataTypeGuid { get; set; } + public Guid DataTypeGuid { get; set; } [JsonProperty("propertyEditorAlias")] public string PropertyEditorAlias { get; set; } diff --git a/app/Umbraco/Umbraco.Archetype/Models/Property.cs b/app/Umbraco/Umbraco.Archetype/Models/Property.cs index 09605b7..902e434 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/Property.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/Property.cs @@ -1,9 +1,11 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Web; namespace Archetype.Umbraco.Models { @@ -26,18 +28,31 @@ namespace Archetype.Umbraco.Models public T GetValue() { + + // Try Umbraco's PropertyValueConverters + var converters = UmbracoContext.Current != null ? PropertyValueConvertersResolver.Current.Converters : Enumerable.Empty(); + if (converters.Any()) + { + var convertedAttempt = TryConvertWithPropertyValueConverters(Value, converters); + if (convertedAttempt.Success) + return convertedAttempt.Result; + } + // If the value is of type T, just return it if (Value is T) return (T)Value; - // Umbraco has the concept of a IPropertyEditorValueConverter which it - // also queries for property resolvers. However I'm not sure what these - // are for, nor can I find any implementations in core, so am currently - // just ignoring these when looking up converters. - // NB: IPropertyEditorValueConverter not to be confused with - // IPropertyValueConverter which are the ones most people are creating + // No PropertyValueConverters matched, so try a regular type conversion + var convertAttempt2 = Value.TryConvertTo(); + if (convertAttempt2.Success) + return convertAttempt2.Result; + + return default(T); + } + + private Attempt TryConvertWithPropertyValueConverters(object value, IEnumerable converters) + { var properyType = CreateDummyPropertyType(DataTypeId, PropertyEditorAlias); - var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); // In umbraco, there are default value converters that try to convert the // value if all else fails. The problem is, they are also in the list of @@ -49,24 +64,19 @@ namespace Archetype.Umbraco.Models foreach (var converter in converters.Where(x => x.IsConverter(properyType))) { // Convert the type using a found value converter - var value2 = converter.ConvertDataToSource(properyType, Value, false); + var value2 = converter.ConvertDataToSource(properyType, value, false); // If the value is of type T, just return it if (value2 is T) - return (T)value2; + return Attempt.Succeed((T)value2); // Value is not final value type, so try a regular type conversion aswell var convertAttempt = value2.TryConvertTo(); if (convertAttempt.Success) - return convertAttempt.Result; + return Attempt.Succeed(convertAttempt.Result); } - // Value is not final value type, so try a regular type conversion - var convertAttempt2 = Value.TryConvertTo(); - if (convertAttempt2.Success) - return convertAttempt2.Result; - - return default(T); + return Attempt.Fail(); } private PublishedPropertyType CreateDummyPropertyType(int dataTypeId, string propertyEditorAlias) diff --git a/app/Umbraco/Umbraco.Archetype/Properties/VersionInfo.cs b/app/Umbraco/Umbraco.Archetype/Properties/VersionInfo.cs index 7a33150..799f844 100644 --- a/app/Umbraco/Umbraco.Archetype/Properties/VersionInfo.cs +++ b/app/Umbraco/Umbraco.Archetype/Properties/VersionInfo.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("0.0.0.0")] -[assembly: AssemblyFileVersion("0.0.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.0")] +[assembly: AssemblyFileVersion("1.0-beta")] \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/PropertyConverters/ArchetypeValueConverter.cs b/app/Umbraco/Umbraco.Archetype/PropertyConverters/ArchetypeValueConverter.cs index a8bfc8b..2f096d3 100644 --- a/app/Umbraco/Umbraco.Archetype/PropertyConverters/ArchetypeValueConverter.cs +++ b/app/Umbraco/Umbraco.Archetype/PropertyConverters/ArchetypeValueConverter.cs @@ -1,29 +1,21 @@ -using System; -using System.Linq; -using Archetype.Umbraco.Extensions; -using Archetype.Umbraco.Models; -using Newtonsoft.Json; -using Umbraco.Core; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; - +using System; +using System.Collections.Generic; +using System.Linq; +using Archetype.Umbraco.Extensions; +using Archetype.Umbraco.Models; +using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + namespace Archetype.Umbraco.PropertyConverters { - /* based on the Tim Geyssens sample at: https://github.com/TimGeyssens/MatrixPropEditor/blob/master/SamplePropertyValueConverter/SamplePropertyValueConverter/MatrixValueConverter.cs */ - [PropertyValueType(typeof(Archetype.Umbraco.Models.Archetype))] + [PropertyValueType(typeof(Models.Archetype))] [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] public class ArchetypeValueConverter : PropertyValueConverterBase { - protected JsonSerializerSettings _jsonSettings; - - public ArchetypeValueConverter() - { - var dcr = new Newtonsoft.Json.Serialization.DefaultContractResolver(); - dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; - - _jsonSettings = new JsonSerializerSettings { ContractResolver = dcr }; - } public ServiceContext Services { @@ -35,85 +27,22 @@ namespace Archetype.Umbraco.PropertyConverters return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditorAlias); } - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { var defaultValue = new Models.Archetype(); if (source == null) return defaultValue; - var sourceString = source.ToString(); - - if (sourceString.DetectIsJson()) - { - try - { - // Deserialize value to archetype model - var archetype = JsonConvert.DeserializeObject(sourceString, _jsonSettings); - - try - { - // Get list of configured properties and their types - // and map them to the deserialized archetype model - var preValue = GetArchetypePreValueFromDataTypeId(propertyType.DataTypeId); - foreach (var fieldset in preValue.Fieldsets) - { - var fieldsetAlias = fieldset.Alias; - foreach (var fieldsetInst in archetype.Fieldsets.Where(x => x.Alias == fieldsetAlias)) - { - foreach (var property in fieldset.Properties) - { - var propertyAlias = property.Alias; - foreach (var propertyInst in fieldsetInst.Properties.Where(x => x.Alias == propertyAlias)) - { - propertyInst.DataTypeId = property.DataTypeId; - propertyInst.PropertyEditorAlias = property.PropertyEditorAlias; - } - } - } - } - } - catch (Exception ex) - { - } - - return archetype; - } - catch (Exception ex) - { - return defaultValue; - } - } - - return defaultValue; - } - - internal ArchetypePreValue GetArchetypePreValueFromDataTypeId(int dataTypeId) - { - var preValues = Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId); - - var configJson = preValues.IsDictionaryBased - ? preValues.PreValuesAsDictionary[Constants.PreValueAlias].Value - : preValues.PreValuesAsArray.First().Value; - - var config = JsonConvert.DeserializeObject(configJson, _jsonSettings); - - foreach (var fieldset in config.Fieldsets) - { - foreach (var property in fieldset.Properties) - { - // Lookup the properties property editor alias - // (See if we've already looked it up first though to save a database hit) - var propertyWithSameDataType = config.Fieldsets.SelectMany(x => x.Properties) - .FirstOrDefault(x => x.DataTypeId == property.DataTypeId && !string.IsNullOrWhiteSpace(x.PropertyEditorAlias)); - - property.PropertyEditorAlias = propertyWithSameDataType != null - ? propertyWithSameDataType.PropertyEditorAlias - : Services.DataTypeService.GetDataTypeDefinitionById(property.DataTypeId).PropertyEditorAlias; - } - } - - return config; - } - } -} + var sourceString = source.ToString(); + + if (!sourceString.DetectIsJson()) + return defaultValue; + + var archetype = new ArchetypeHelper().DeserializeJsonToArchetype(source.ToString(), + (propertyType != null ? propertyType.DataTypeId : -1)); + + return archetype; + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/PropertyEditors/ArcheTypePropertyEditor.cs b/app/Umbraco/Umbraco.Archetype/PropertyEditors/ArcheTypePropertyEditor.cs new file mode 100644 index 0000000..3192130 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/PropertyEditors/ArcheTypePropertyEditor.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Archetype.Umbraco.Extensions; +using ClientDependency.Core; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Web.PropertyEditors; +using Umbraco.Core.Logging; + +namespace Archetype.Umbraco.PropertyEditors +{ + [PropertyEditorAsset(ClientDependencyType.Javascript, "/App_Plugins/Archetype/js/archetype.js")] + [PropertyEditor("Imulus.Archetype", "Archetype", "/App_Plugins/Archetype/views/archetype.html", + ValueType = "JSON")] + public class ArchetypePropertyEditor : PropertyEditor + { + #region Pre Value Editor + + protected override PreValueEditor CreatePreValueEditor() + { + return new ArchetypePreValueEditor(); + } + + internal class ArchetypePreValueEditor : PreValueEditor + { + [PreValueField("archetypeConfig", "Config", "/App_Plugins/Archetype/views/archetype.config.html", + Description = "(Required) Describe your Archetype.")] + public string Config { get; set; } + + [PreValueField("hideLabel", "Hide Label", "boolean", + Description = "Hide the Umbraco property title and description, making the Archetype span the entire page width")] + public bool HideLabel { get; set; } + } + + #endregion + + #region Value Editor + + protected override PropertyValueEditor CreateValueEditor() + { + return new ArchetypePropertyValueEditor(base.CreateValueEditor()); + } + + internal class ArchetypePropertyValueEditor : PropertyValueEditorWrapper + { + protected JsonSerializerSettings _jsonSettings; + + public ArchetypePropertyValueEditor(PropertyValueEditor wrapped) + : base(wrapped) { } + + public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService) + { + if (property.Value == null || property.Value.ToString() == "") + return string.Empty; + + var archetype = new ArchetypeHelper().DeserializeJsonToArchetype(property.Value.ToString(), propertyType.DataTypeDefinitionId); + + foreach (var fieldset in archetype.Fieldsets) + { + foreach (var propDef in fieldset.Properties) + { + try + { + var dtd = dataTypeService.GetDataTypeDefinitionById(Guid.Parse(propDef.DataTypeGuid)); + var propType = new PropertyType(dtd) { Alias = propDef.Alias }; + var prop = new Property(propType, propDef.Value); + var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias); + propDef.Value = propEditor.ValueEditor.ConvertDbToString(prop, propType, dataTypeService); + } + catch (Exception ex) + { + LogHelper.Error(ex.Message, ex); + } + } + } + + return archetype.SerializeForPersistence(); + } + + public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService) + { + if (property.Value == null || property.Value.ToString() == "") + return string.Empty; + + var archetype = new ArchetypeHelper().DeserializeJsonToArchetype(property.Value.ToString(), propertyType.DataTypeDefinitionId); + + foreach (var fieldset in archetype.Fieldsets) + { + foreach (var propDef in fieldset.Properties) + { + try + { + var dtd = dataTypeService.GetDataTypeDefinitionById(Guid.Parse(propDef.DataTypeGuid)); + var propType = new PropertyType(dtd) { Alias = propDef.Alias }; + var prop = new Property(propType, propDef.Value); + var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias); + propDef.Value = propEditor.ValueEditor.ConvertDbToEditor(prop, propType, dataTypeService); + } + catch (Exception ex) + { + LogHelper.Error(ex.Message, ex); + } + } + } + + return archetype; + } + public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) + { + if (editorValue.Value == null || editorValue.Value.ToString() == "") + return string.Empty; + + var archetype = new ArchetypeHelper().DeserializeJsonToArchetype(editorValue.Value.ToString(), editorValue.PreValues); + + foreach (var fieldset in archetype.Fieldsets) + { + foreach (var propDef in fieldset.Properties) + { + try + { + var dtd = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(Guid.Parse(propDef.DataTypeGuid)); + var preValues = ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtd.Id); + var propData = new ContentPropertyData(propDef.Value, preValues, new Dictionary()); + var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias); + propDef.Value = propEditor.ValueEditor.ConvertEditorToDb(propData, propDef.Value); + } + catch (Exception ex) + { + LogHelper.Error(ex.Message, ex); + } + } + } + + return archetype.SerializeForPersistence(); + } + } + + #endregion + } +} diff --git a/app/Umbraco/Umbraco.Archetype/app.config b/app/Umbraco/Umbraco.Archetype/app.config new file mode 100644 index 0000000..4429813 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/packages.config b/app/Umbraco/Umbraco.Archetype/packages.config index 7918cdf..d006ee4 100644 --- a/app/Umbraco/Umbraco.Archetype/packages.config +++ b/app/Umbraco/Umbraco.Archetype/packages.config @@ -1,9 +1,11 @@  - + + + @@ -17,8 +19,8 @@ - + - + \ No newline at end of file diff --git a/app/config/propertyEditors.views.js b/app/config/propertyEditors.views.js deleted file mode 100644 index ba99e59..0000000 --- a/app/config/propertyEditors.views.js +++ /dev/null @@ -1,148 +0,0 @@ -[ - { - "propertyEditorAlias": "Umbraco.CheckBoxList", - "view": "checkboxlist" - }, - { - "propertyEditorAlias": "Umbraco.ColorPickerAlias", - "view": "colorpicker" - }, - { - "propertyEditorAlias": "Umbraco.ContentPickerAlias", - "view": "contentpicker" - }, - { - "propertyEditorAlias": "Umbraco.Date", - "view": "datepicker", - "defaultPreValues": [{ "format": "yyyy-MM-dd", "pickTime": false }] - }, - { - "propertyEditorAlias": "Umbraco.DateTime", - "view": "datepicker", - "format": "yyyy-MM-dd hh:mm:ss" - }, - { - "propertyEditorAlias": "Umbraco.DropDown", - "view": "dropdown" - }, - { - "propertyEditorAlias": "Umbraco.DropDownMultiple", - "view": "dropdown" - }, - { - "propertyEditorAlias": "Umbraco.DropDownMultiplePublishKeys", - "view": "dropdown" - }, - { - "propertyEditorAlias": "Umbraco.DropdownlistPublishingKeys", - "view": "dropdown" - }, - { - "propertyEditorAlias": "Umbraco.EmailAddress", - "view": "email" - }, - { - "propertyEditorAlias": "Umbraco.UploadField", - "view": "fileupload" - }, - { - "propertyEditorAlias": "Umbraco.FolderBrowser", - "view": "folderbrowser" - }, - { - "propertyEditorAlias": "Umbraco.Integer", - "view": "integer" - }, - { - "propertyEditorAlias": "Umbraco.NoEdit", - "view": "readonlyvalue" - }, - { - "propertyEditorAlias": "Umbraco.ListView", - "view": "listview" - }, - { - "propertyEditorAlias": "Umbraco.MacroContainer", - "view": "macrocontainer" - }, - { - "propertyEditorAlias": "Umbraco.MarkdownEditor", - "view": "markdowneditor" - }, - { - "propertyEditorAlias": "Umbraco.MediaPicker", - "view": "mediapicker", - "defaultPreValues": [{ "multiPicker": "0" }] - }, - { - "propertyEditorAlias": "Umbraco.MemberGroupPicker", - "view": "membergrouppicker" - }, - { - "propertyEditorAlias": "Umbraco.MemberPicker", - "view": "memberpicker" - }, - { - "propertyEditorAlias": "Umbraco.MultiNodeTreePicker", - "view": "contentpicker", - "defaultPreValues": [{ "multiPicker": "1" }] - }, - { - "propertyEditorAlias": "Umbraco.MultipleMediaPicker", - "view": "mediapicker" - }, - { - "propertyEditorAlias": "Umbraco.MultipleTextstring", - "view": "multipletextbox" - }, - { - "propertyEditorAlias": "Umbraco.RadioButtonList", - "view": "radiobuttons" - }, - { - "propertyEditorAlias": "Umbraco.RelatedLinks", - "view": "relatedlinks" - }, - { - "propertyEditorAlias": "Umbraco.TinyMCEv3", - "view": "rte" - }, - { - "propertyEditorAlias": "Umbraco.Slider", - "view": "slider" - }, - { - "propertyEditorAlias": "Umbraco.Tags", - "view": "tags", - "defaultPreValues": [{ "group": "default" }] - }, - { - "propertyEditorAlias": "Umbraco.TextboxMultiple", - "view": "textarea" - }, - { - "propertyEditorAlias": "Umbraco.Textbox", - "view": "textbox" - }, - { - "propertyEditorAlias": "Umbraco.TrueFalse", - "view": "boolean" - }, - { - "propertyEditorAlias": "Umbraco.UserPicker", - "view": "entitypicker", - "defaultPreValues": [{ "entityType": "User" }] - }, - { - "propertyEditorAlias": "Umbraco.NoEdit", - "view": "readonlyvalue" - }, - { - "propertyEditorAlias": "Umbraco.Textbox", - "view": "textbox" - }, - { - "propertyEditorAlias": "Imulus.Archetype", - "view": "/App_Plugins/Archetype/views/archetype.html" - } -] \ No newline at end of file diff --git a/app/controllers/config.controller.js b/app/controllers/config.controller.js index 0a3828f..6dd8ef5 100644 --- a/app/controllers/config.controller.js +++ b/app/controllers/config.controller.js @@ -1,19 +1,19 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", function ($scope, $http, assetsService, dialogService, archetypePropertyEditorResource) { - - //$scope.model.value = ""; - //console.log($scope.model.value); + + //$scope.model.value = ""; + //console.log($scope.model.value); //define empty items - var newPropertyModel = '{"alias": "", "remove": false, "collapse": false, "label": "", "helpText": "", "dataTypeId": "-88", "value": ""}'; + var newPropertyModel = '{"alias": "", "remove": false, "collapse": false, "label": "", "helpText": "", "dataTypeGuid": "0cc0eba1-9960-42c9-bf9b-60e150b429ae", "value": ""}'; var newFieldsetModel = '{"alias": "", "remove": false, "collapse": false, "labelTemplate": "", "icon": "", "label": "", "properties": [' + newPropertyModel + ']}'; - var defaultFieldsetConfigModel = JSON.parse('{"showAdvancedOptions": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "maxFieldsets": null, "enableCollapsing": true, "fieldsets": [' + newFieldsetModel + ']}'); + var defaultFieldsetConfigModel = JSON.parse('{"showAdvancedOptions": false, "startWithAddButton": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "maxFieldsets": null, "enableCollapsing": true, "fieldsets": [' + newFieldsetModel + ']}'); //ini the model $scope.model.value = $scope.model.value || defaultFieldsetConfigModel; - + //ini the render model initConfigRenderModel(); - + //get the available datatypes archetypePropertyEditorResource.getAllDataTypes().then(function(data) { $scope.availableDataTypes = data; @@ -40,16 +40,16 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio } }; - + //function that determines how to manage expanding/collapsing fieldsets $scope.focusFieldset = function(fieldset){ var iniState; - + if(fieldset) { iniState = fieldset.collapse; } - + _.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){ if($scope.archetypeConfigRenderModel.fieldsets.length == 1 && fieldset.remove == false) { @@ -66,7 +66,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio fieldset.collapse = false; } }); - + if(iniState) { fieldset.collapse = !iniState; @@ -79,7 +79,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio //function that determines how to manage expanding/collapsing properties $scope.focusProperty = function(properties, property){ var iniState; - + if(property) { iniState = property.collapse; @@ -95,7 +95,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio property.collapse = false; } }); - + if(iniState) { property.collapse = !iniState; @@ -106,24 +106,24 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio _.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){ $scope.focusProperty(fieldset.properties); }); - + //setup JSON.stringify helpers $scope.archetypeConfigRenderModel.toString = stringify; - + //encapsulate stringify (should be built into browsers, not sure of IE support) function stringify() { return JSON.stringify(this); } - + //watch for changes $scope.$watch('archetypeConfigRenderModel', function (v) { //console.log(v); - if (typeof v === 'string') { + if (typeof v === 'string') { $scope.archetypeConfigRenderModel = JSON.parse(v); $scope.archetypeConfigRenderModel.toString = stringify; } }); - + $scope.autoPopulateAlias = function(s) { var modelType = s.hasOwnProperty('fieldset') ? 'fieldset' : 'property'; var modelProperty = s[modelType]; @@ -144,7 +144,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio //helper that returns if an item can be removed $scope.canRemoveFieldset = function () - { + { return countVisibleFieldset() > 1; } @@ -153,10 +153,10 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio { return countVisibleFieldset() > 1; } - + //helper that returns if an item can be removed $scope.canRemoveProperty = function (fieldset) - { + { return countVisibleProperty(fieldset) > 1; } @@ -166,17 +166,17 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio return countVisibleProperty(fieldset) > 1; } - $scope.getDataTypeNameById = function (id) { + $scope.getDataTypeNameByGuid = function (guid) { if ($scope.availableDataTypes == null) // Might not be initialized yet? return ""; - + var dataType = _.find($scope.availableDataTypes, function(d) { - return d.id == id; + return d.guid == guid; }); return dataType == null ? "" : dataType.name; } - + //helper to count what is visible function countVisibleFieldset() { @@ -190,7 +190,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio return count; } - + //determines how many properties are visible function countVisibleProperty(fieldset) { @@ -204,13 +204,13 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio return count; } - + //handles a fieldset add $scope.addFieldsetRow = function ($index, $event) { $scope.archetypeConfigRenderModel.fieldsets.splice($index + 1, 0, JSON.parse(newFieldsetModel)); $scope.focusFieldset(); } - + //rather than splice the archetypeConfigRenderModel, we're hiding this and cleaning onFormSubmitting $scope.removeFieldsetRow = function ($index) { if ($scope.canRemoveFieldset()) { @@ -219,12 +219,12 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio } } } - + //handles a property add $scope.addPropertyRow = function (fieldset, $index) { fieldset.properties.splice($index + 1, 0, JSON.parse(newPropertyModel)); } - + //rather than splice the archetypeConfigRenderModel, we're hiding this and cleaning onFormSubmitting $scope.removePropertyRow = function (fieldset, $index) { if ($scope.canRemoveProperty(fieldset)) { @@ -233,7 +233,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio } } } - + //helper to ini the render model function initConfigRenderModel() { @@ -257,38 +257,38 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio }); }); } - + //sync things up on save $scope.$on("formSubmitting", function (ev, args) { syncModelToRenderModel(); }); - + //helper to sync the model to the renderModel function syncModelToRenderModel() { $scope.model.value = $scope.archetypeConfigRenderModel; var fieldsets = []; - + _.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){ //check fieldsets if (!fieldset.remove) { fieldsets.push(fieldset); - + var properties = []; _.each(fieldset.properties, function(property){ if (!property.remove) { properties.push(property); - } + } }); fieldset.properties = properties; } }); - + $scope.model.value.fieldsets = fieldsets; } - + //archetype css assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css"); }); diff --git a/app/controllers/controller.js b/app/controllers/controller.js index f89e0a9..3f31ac3 100644 --- a/app/controllers/controller.js +++ b/app/controllers/controller.js @@ -1,5 +1,5 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, assetsService, angularHelper, notificationsService, $timeout) { - + //$scope.model.value = ""; $scope.model.hideLabel = $scope.model.config.hideLabel == 1; @@ -8,17 +8,14 @@ //set the config equal to our prevalue config $scope.model.config = $scope.model.config.archetypeConfig; - - //ini the model - $scope.model.value = $scope.model.value || { fieldsets: [getEmptyRenderFieldset($scope.model.config.fieldsets[0])] }; - //ini the render model - $scope.archetypeRenderModel = {}; - initArchetypeRenderModel(); - + //ini the model + $scope.model.value = $scope.model.value || getDefaultModel($scope.model.config); + init(); + //helper to get $eval the labelTemplate $scope.getFieldsetTitle = function(fieldsetConfigModel, fieldsetIndex) { - var fieldset = $scope.archetypeRenderModel.fieldsets[fieldsetIndex]; + var fieldset = $scope.model.value.fieldsets[fieldsetIndex]; var fieldsetConfig = $scope.getConfigFieldsetByAlias(fieldset.alias); var template = fieldsetConfigModel.labelTemplate; @@ -61,11 +58,11 @@ if (typeof $index != 'undefined') { - $scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, newFieldset); + $scope.model.value.fieldsets.splice($index + 1, 0, newFieldset); } else { - $scope.archetypeRenderModel.fieldsets.push(newFieldset); + $scope.model.value.fieldsets.push(newFieldset); } } @@ -74,15 +71,10 @@ } } - //rather than splice the archetypeRenderModel, we're hiding this and cleaning onFormSubmitting $scope.removeRow = function ($index) { if ($scope.canRemove()) { if (confirm('Are you sure you want to remove this?')) { - $scope.archetypeRenderModel.fieldsets[$index].remove = true; - /* - Touches the model. Not sure why this is needed but without it the Digest doesn't run on a remove row. - */ - $scope.archetypeRenderModel = JSON.parse(JSON.stringify($scope.archetypeRenderModel)); + $scope.model.value.fieldsets.splice($index, 1); } } } @@ -100,8 +92,10 @@ //helper that returns if an item can be removed $scope.canRemove = function () - { - return countVisible() > 1; + { + return countVisible() > 1 + || ($scope.model.config.maxFieldsets == 1 && $scope.model.config.fieldsets.length > 1) + || $scope.model.config.startWithAddButton; } //helper that returns if an item can be sorted @@ -110,16 +104,26 @@ return countVisible() > 1; } - //helper, ini the render model from the server (model.value) - function initArchetypeRenderModel() { - $scope.archetypeRenderModel = removeNulls($scope.model.value); + //helpers for determining if the add button should be shown + $scope.showAddButton = function () { + return $scope.model.config.startWithAddButton + && countVisible() === 0 + && $scope.model.config.fieldsets.length == 1; + } - _.each($scope.archetypeRenderModel.fieldsets, function (fieldset) + //helper, ini the render model from the server (model.value) + function init() { + $scope.model.value = removeNulls($scope.model.value); + addDefaultProperties($scope.model.value.fieldsets); + } + + function addDefaultProperties(fieldsets) + { + _.each(fieldsets, function (fieldset) { - fieldset.remove = false; fieldset.collapse = false; fieldset.isValid = true; - }); + }); } //helper to get the correct fieldset from config @@ -136,7 +140,16 @@ }); return (typeof property !== 'undefined') ? property.value : ''; }; - + + $scope.isCollapsed = function(fieldset) + { + if(typeof fieldset.collapse === "undefined") + { + fieldset.collapse = true; + } + return fieldset.collapse; + } + //helper for expanding/collapsing fieldsets $scope.focusFieldset = function(fieldset){ fixDisableSelection(); @@ -144,35 +157,35 @@ if (!$scope.model.config.enableCollapsing) { return; } - + var iniState; - + if(fieldset) { iniState = fieldset.collapse; } - - _.each($scope.archetypeRenderModel.fieldsets, function(fieldset){ + + _.each($scope.model.value.fieldsets, function(fieldset){ fieldset.collapse = true; }); - - if(!fieldset && $scope.archetypeRenderModel.fieldsets.length == 1 && $scope.archetypeRenderModel.fieldsets[0].remove == false) + + if(!fieldset && $scope.model.value.fieldsets.length == 1) { - $scope.archetypeRenderModel.fieldsets[0].collapse = false; + $scope.model.value.fieldsets[0].collapse = false; return; } - + if(iniState && fieldset) { fieldset.collapse = !iniState; } } - + //ini the fieldset expand/collapse $scope.focusFieldset(); //developerMode helpers - $scope.archetypeRenderModel.toString = stringify; + $scope.model.value.toString = stringify; //encapsulate stringify (should be built into browsers, not sure of IE support) function stringify() { @@ -180,12 +193,12 @@ } //watch for changes - $scope.$watch('archetypeRenderModel', function (v) { + $scope.$watch('model.value', function (v) { if ($scope.model.config.developerMode) { console.log(v); if (typeof v === 'string') { - $scope.archetypeRenderModel = JSON.parse(v); - $scope.archetypeRenderModel.toString = stringify; + $scope.model.value = JSON.parse(v); + $scope.model.value.toString = stringify; } } }); @@ -193,70 +206,20 @@ //helper to count what is visible function countVisible() { - var count = 0; - - _.each($scope.archetypeRenderModel.fieldsets, function(fieldset){ - if (fieldset.remove == false) { - count++; - } - }); - - return count; + return $scope.model.value.fieldsets.length; } - //helper to sync the model to the renderModel - function syncModelToRenderModel() - { - $scope.model.value = { fieldsets: [] }; + // helper to get initial model if none was provided + function getDefaultModel(config) { + if (config.startWithAddButton) + return { fieldsets: [] }; - _.each($scope.archetypeRenderModel.fieldsets, function(fieldset){ - var cleanedFieldset = cleanFieldset(fieldset); - - if(cleanedFieldset){ - $scope.model.value.fieldsets.push(cleanedFieldset); - } - }); - } - - //helper to remove properties only used during editing that we don't want in the saved data - //also removes properties that are no longer in the config - function cleanFieldset(fieldset) - { - if (typeof fieldset != 'function' && !fieldset.remove){ - - var fieldsetConfig = $scope.getConfigFieldsetByAlias(fieldset.alias); - - //clone and clean - var tempFieldset = JSON.parse(JSON.stringify(fieldset)); - delete tempFieldset.remove; - delete tempFieldset.isValid; - delete tempFieldset.collapse; - - _.each(tempFieldset.properties, function(property, index){ - var propertyConfig = _.find(fieldsetConfig.properties, function(p){ - return property.alias == p.alias; - }); - - //just prune the property - if(propertyConfig){ - delete property.isValid; - } - else - { - //need to remove the whole property - tempFieldset.properties.splice(index, 1); - } - - }); - - return tempFieldset; - } + return { fieldsets: [getEmptyRenderFieldset(config.fieldsets[0])] }; } //helper to add an empty fieldset to the render model - function getEmptyRenderFieldset (fieldsetModel) - { - return JSON.parse('{"alias": "' + fieldsetModel.alias + '", "remove": false, "isValid": true, "properties": []}'); + function getEmptyRenderFieldset (fieldsetModel) { + return {alias: fieldsetModel.alias, collapse: false, isValid: true, properties: []}; } //helper to ensure no nulls make it into the model @@ -268,7 +231,7 @@ removeNulls(model); } }); - + return model; } } @@ -277,18 +240,18 @@ function fixDisableSelection() { $timeout(function() { $('.archetypeEditor .controls') - .bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) { + .bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) { e.stopImmediatePropagation(); - }); + }); }, 1000); } //helper to lookup validity when given a fieldsetIndex and property alias $scope.getPropertyValidity = function(fieldsetIndex, alias) { - if($scope.archetypeRenderModel.fieldsets[fieldsetIndex]) + if($scope.model.value.fieldsets[fieldsetIndex]) { - var property = _.find($scope.archetypeRenderModel.fieldsets[fieldsetIndex].properties, function(property){ + var property = _.find($scope.model.value.fieldsets[fieldsetIndex].properties, function(property){ return property.alias == alias; }); } @@ -296,27 +259,10 @@ return (typeof property == 'undefined') ? true : property.isValid; } - //sync things up on save - $scope.$on("formSubmitting", function (ev, args) { - - //test for form; may have to do this differently for nested archetypes - if(!form) - return; - - if(form.$invalid) - { - notificationsService.warning("Cannot Save Document", "The document could not be saved because of missing required fields.") - } - else - { - syncModelToRenderModel(); - } - }); - //custom js if ($scope.model.config.customJsPath) { assetsService.loadJs($scope.model.config.customJsPath); - } + } //archetype css assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css"); diff --git a/app/directives/archetypeproperty.js b/app/directives/archetypeproperty.js index 5286fca..7358cbd 100644 --- a/app/directives/archetypeproperty.js +++ b/app/directives/archetypeproperty.js @@ -51,7 +51,7 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c var configFieldsetModel = getFieldsetByAlias(scope.archetypeConfig.fieldsets, scope.fieldset.alias); var view = ""; var label = configFieldsetModel.properties[scope.propertyConfigIndex].label; - var dataTypeId = configFieldsetModel.properties[scope.propertyConfigIndex].dataTypeId; + var dataTypeGuid = configFieldsetModel.properties[scope.propertyConfigIndex].dataTypeGuid; var config = null; var alias = configFieldsetModel.properties[scope.propertyConfigIndex].alias; var defaultValue = configFieldsetModel.properties[scope.propertyConfigIndex].value; @@ -61,7 +61,7 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c defaultValue = jsonOrString(defaultValue, scope.archetypeConfig.developerMode, "defaultValue"); //grab info for the selected datatype, prepare for view - archetypePropertyEditorResource.getDataType(dataTypeId).then(function (data) { + archetypePropertyEditorResource.getDataType(dataTypeGuid).then(function (data) { //transform preValues array into object expected by propertyeditor views var configObj = {}; _.each(data.preValues, function(p) { @@ -75,11 +75,10 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c //load in the DefaultPreValues for the PropertyEditor, if any var defaultConfigObj = {}; - if (propertyEditor.hasOwnProperty('defaultPreValues')) { - _.each(propertyEditor.defaultPreValues, function(p) { - _.extend(defaultConfigObj, p) - }); + if (propertyEditor.hasOwnProperty('defaultPreValues') && propertyEditor.defaultPreValues != null) { + _.extend(defaultConfigObj, propertyEditor.defaultPreValues); } + var mergedConfig = _.extend(defaultConfigObj, config); loadView(pathToView, mergedConfig, defaultValue, alias, umbracoPropertyAlias, scope, element, ngModelCtrl); diff --git a/app/langs/da-dk.js b/app/langs/da-dk.js index 7446d25..923133f 100644 --- a/app/langs/da-dk.js +++ b/app/langs/da-dk.js @@ -1,34 +1,34 @@ { "label": "Label", "alias": "Alias", - "hideFieldsetControls": "Hide Fieldset Controls?", - "hideFieldsetControlsDescription": "Hides the fieldset add/remove/sort controls.", - "toggleAdvanced": "Toggle Advanced Options", - "toggleAdvancedDescription": "Show advanced options.", - "hidePropertyLabels": "Hide Property Labels?", - "hidePropertyLabelsDescription": "Hides the property labels.", - "maxFieldsets": "Max Fieldsets", - "maxFieldsetsDescription": "How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.", - "enableMultipleFieldsets": "Enable Multiple Fieldsets?", - "enableMultipleFieldsetsDescription": "Allows multiple types of fieldsets within this archetype.", - "hideFieldsetToolbar": "Hide Fieldset Toolbar?", - "hideFieldsetToolbarDescription": "Hides the fieldset toolbar that appears when more than one fieldset model appears.", - "customWrapperClass": "Custom Wrapper Class", - "customWrapperClassDescription": "(Optional) Enter a custom CSS class that will be applied to the wrapper div.", - "cssFile": "CSS File", - "cssFileDescription": "(Optional) Enter a path for a custom CSS file to be included on the page.", - "jsFile": "JS File", - "jsFileDescription": "(Optional) Enter a path for a custom JS file to be included on the page.", - "toggleDeveloperMode": "Toggle Developer Mode", - "toggleDeveloperModeDescription": "Turn on for developer options.", - "configModel": "Config Model", - "configModelDescription": "Be careful editing the text below, it controls the schema for this archetype.", - "helpText": "Help Text", - "defaultValue": "Default Value", - "dataType": "DataType", - "required": "Required", - "properties": "Properties", - "labelTemplate": "Label Template", - "select": "Select", - "icon": "Icon" + "hideFieldsetControls": "Skjul fieldsets?", + "hideFieldsetControlsDescription": "Skjuler kontrollerne tilføj/fjern/sortér.", + "toggleAdvanced": "Slå avancerede muligheder til/fra", + "toggleAdvancedDescription": "Vis avancerede muligheder.", + "hidePropertyLabels": "Skjul labels på egenskaber?", + "hidePropertyLabelsDescription": "Skjuler labels på egenskaber.", + "maxFieldsets": "Maks antal fieldsets", + "maxFieldsetsDescription": "Hvor mange fieldsets er tilladt? Hvis der skrives '1' vil deaktivere kontrollerne. Som standard er det ubegrænset.", + "enableMultipleFieldsets": "Aktivér flere Fieldsets?", + "enableMultipleFieldsetsDescription": "Tillader flere typer af fieldsets i denne archetype.", + "hideFieldsetToolbar": "Skjul Fieldset værktøjslinie?", + "hideFieldsetToolbarDescription": "Skjuler den fieldset værktøjslinie der fremkommer når der findes mere end ét fieldset.", + "customWrapperClass": "Egen Wrapper Class", + "customWrapperClassDescription": "(Mulighed) Angiv din egen CSS class som tilføjes på wrapper div'en.", + "cssFile": "CSS fil", + "cssFileDescription": "(Mulighed) Tilføj en sti til din egen CSS fil der skal inkluderes på siden.", + "jsFile": "JavaScript fil", + "jsFileDescription": "(Mulighed) Tilføj en sti til din egen JavaScript fil der skal inkluderes på siden.", + "toggleDeveloperMode": "Slå udvikler muligheder til/fra.", + "toggleDeveloperModeDescription": "Aktivér på udvikler muligheder.", + "configModel": "Konfigurationsmodel", + "configModelDescription": "Vær forsigt med at redigere teksten for neden, den kontrollerer skemaet for denne archetype.", + "helpText": "Hjælpe tekst", + "defaultValue": "Standard værdi", + "dataType": "Datatype", + "required": "Påkrævet", + "properties": "Egenskaber", + "labelTemplate": "Label skabelon", + "select": "Vælg", + "icon": "Ikon" } diff --git a/app/less/archetype.less b/app/less/archetype.less index eb7370f..215aac8 100644 --- a/app/less/archetype.less +++ b/app/less/archetype.less @@ -36,7 +36,7 @@ cursor: pointer; label { - span { + span { text-decoration: underline; } &:hover { @@ -297,6 +297,14 @@ } } +.archetypeEditor .archetypeAddButton:hover { + text-decoration: none; + + .archetypeAddButtonText { + text-decoration: underline; + } +} + .ui-sortable-placeholder { visibility: visible !important; background-color: #d0e7f1 !important; diff --git a/app/resources/propertyeditor.js b/app/resources/propertyeditor.js index 0a1cc9b..13b6afb 100644 --- a/app/resources/propertyeditor.js +++ b/app/resources/propertyeditor.js @@ -3,24 +3,20 @@ angular.module('umbraco.resources').factory('archetypePropertyEditorResource', f getAllDataTypes: function() { // Hack - grab DataTypes from Tree API, as `dataTypeService.getAll()` isn't implemented yet return umbRequestHelper.resourcePromise( - $http.get("/umbraco/backoffice/UmbracoTrees/DataTypeTree/GetNodes?id=-1&application=developer&tree=&isDialog=false"), 'Failed to retrieve datatypes from tree service' - ).then(function (data) { - return data.map(function(d) { - return { "id": d.id, "name": d.name } - }); - }); + $http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetAll"), 'Failed to retrieve datatypes from tree service' + ); }, - getDataType: function(id) { + getDataType: function(guid) { return umbRequestHelper.resourcePromise( - $http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetById?id=" + id), 'Failed to retrieve datatype' + $http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetByGuid?guid=" + guid), 'Failed to retrieve datatype' ); }, getPropertyEditorMapping: function(alias) { return umbRequestHelper.resourcePromise( - $http.get("/App_plugins/Archetype/js/propertyEditors.views.js"), 'Failed to retrieve datatype mappings' + $http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetAllPropertyEditors"), 'Failed to retrieve datatype mappings' ).then(function (data) { var result = _.find(data, function(d) { - return d.propertyEditorAlias === alias; + return d.alias === alias; }); if (result != null) diff --git a/app/views/archetype.config.html b/app/views/archetype.config.html index c80bf10..5f769b1 100644 --- a/app/views/archetype.config.html +++ b/app/views/archetype.config.html @@ -27,7 +27,7 @@
-
+
  • @@ -37,7 +37,7 @@
- {{property.label}} ({{property.alias}}) - {{getDataTypeNameById(property.dataTypeId)}} + {{property.label}} ({{property.alias}}) - {{getDataTypeNameByGuid(property.dataTypeGuid)}}
@@ -54,7 +54,7 @@
- +
@@ -81,6 +81,10 @@
+
+ + +
@@ -96,11 +100,11 @@
-
+
-
+
diff --git a/app/views/archetype.html b/app/views/archetype.html index 344d37f..5e1134a 100644 --- a/app/views/archetype.html +++ b/app/views/archetype.html @@ -1,5 +1,5 @@
- +
  • @@ -8,39 +8,33 @@
- -
diff --git a/config/meta.json b/config/meta.json index df9f7e5..e963137 100644 --- a/config/meta.json +++ b/config/meta.json @@ -1,6 +1,6 @@ { "name": "Archetype", - "version": "0.5.1-alpha", + "version": "1.0-beta", "url": "http://github.com/imulus/archetype/", "author": "Imulus", "authorUrl": "http://imulus.com/", diff --git a/config/package.manifest b/config/package.manifest deleted file mode 100644 index 3632228..0000000 --- a/config/package.manifest +++ /dev/null @@ -1,36 +0,0 @@ -{ - propertyEditors: [ - { - alias: "Imulus.Archetype", - name: "Archetype", - editor: { - view: "~/App_Plugins/Archetype/views/archetype.html", - valueType: "JSON" - }, - prevalues: { - fields: [ - { - label: "Config", - description: "(Required) Describe your Archetype.", - key: "archetypeConfig", - view: "/App_Plugins/Archetype/views/archetype.config.html", - validation: [ - { - type: "Required" - } - ] - }, - { - label: "Hide Label", - description: "Hide the Umbraco property title and description, making the Archetype span the entire page width", - key: "hideLabel", - view: "boolean" - } - ] - } - } - ], - javascript: [ - '~/App_Plugins/Archetype/js/archetype.js' - ] -} \ No newline at end of file