Compare commits

...

8 Commits

Author SHA1 Message Date
Kevin Giszewski e1deba5b95 Update LICENSE 2017-02-16 12:53:33 -05:00
Kevin Giszewski 5bce4e856a Merge pull request #386 from kgiszewski/fix-385
Fix #385
2017-02-16 12:42:55 -05:00
Kevin Giszewski 6f4cb1e609 Merge pull request #379 from leekelleher/feature/typeconverter
TypeConverter for ArchetypeModel
2017-02-16 12:42:44 -05:00
Kenn Jacobsen 033cbbc8f6 Fix #385 2017-02-16 15:05:21 +01:00
Kenn Jacobsen 7ee3cfca5c Merge pull request #378 from Nicholas-Westby/fix/372-drag-drop-broken
Fix for drag & drop failing when dropping fieldset in the same Archet…
2017-02-16 12:16:44 +01:00
Kenn Jacobsen e9b478c186 Fix #384
For some reason Umbraco has started lowercasing the names of uploaded
files. This fixes the problem by ignoring case when looking for the
uploaded files.
2017-02-15 11:17:01 +01:00
leekelleher 21e784424f TypeConverter for ArchetypeModel
Added a TypeConverter for both `ArchetypeModel` and `ArchetypeFieldsetModel`, so that they can be converted to their `IPublishedContent` equivalent.

Example usage would be, when you would typically do this...

```csharp
var items = Model.Content.GetPropertyValue<ArchetypeModel>myArchetypeAlias");
```

You can now do this...

```csharp
var items = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("myArchetypeAlias");
```

I know it seems more verbose, but the idea is that this could be hot-swappable with other property-editors, such as Nested Content. The frontend/code would deal purely with `IPublishedContent` objects.

---

Also included unit-tests that run via Umbraco Core's `TryConvertTo` extension method, (which is what `Content.GetPropertyValue<T>` uses internally in Umbraco).

There's a small caveat with the TypeConverter for `ArchetypeFieldsetModel`, I had to explicitly reject `typeof(string)`, otherwise the JSON deserializer fails (in `ArchetypeHelper.DeserializeJsonToArchetype`).
All other unit-tests still pass.
2016-10-19 20:26:46 +01:00
Nicholas-Westby 155355aa00 Fix for drag & drop failing when dropping fieldset in the same Archetype and the max fieldsets is reached: https://github.com/kgiszewski/Archetype/issues/372 2016-09-14 22:08:04 -07:00
12 changed files with 198 additions and 34 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Imulus
Copyright (c) 2016 Kevin Giszewski LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -263,6 +263,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PropertyValueConverter\PropertyValueConverterTests.cs" />
<Compile Include="PublishedContent\ArchetypePublishedContentTests.cs" />
<Compile Include="PublishedContent\TypeConverterTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Archetype.Models;
using Archetype.PropertyConverters;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace Archetype.Tests.PublishedContent
{
[TestFixture]
public class TypeConverterTests
{
private ArchetypeModel _archetype;
[TestFixtureSetUp]
public void Init()
{
var archetypeJson = System.IO.File.ReadAllText("..\\..\\Data\\sample-1.json");
var converter = new ArchetypeValueConverter();
_archetype = (ArchetypeModel)converter.ConvertDataToSource(null, archetypeJson, false);
}
[TestCase(typeof(ArchetypeModel), true)]
[TestCase(typeof(ArchetypePublishedContentSet), true)]
[TestCase(typeof(IEnumerable<IPublishedContent>), true)]
[TestCase(typeof(string), true)]
[TestCase(typeof(int), false)]
[TestCase(typeof(ArchetypeFieldsetModel), false)]
public void ArchetypeModel_ConvertsTo_EnumerablePublishedContent(Type destinationType, bool expected)
{
Assert.IsNotNull(_archetype);
var attempt = _archetype.TryConvertTo(destinationType);
Assert.That(attempt.Success, Is.EqualTo(expected));
if (attempt.Success)
{
Assert.IsNotNull(attempt.Result);
Assert.That(attempt.Result, Is.InstanceOf(destinationType));
}
}
[TestCase(typeof(ArchetypeFieldsetModel), true)]
[TestCase(typeof(ArchetypePublishedContent), true)]
[TestCase(typeof(IPublishedContent), true)]
[TestCase(typeof(string), true)]
[TestCase(typeof(int), false)]
[TestCase(typeof(ArchetypeModel), false)]
public void ArchetypeFieldsetModel_ConvertsTo_PublishedContent(Type destinationType, bool expected)
{
CollectionAssert.IsNotEmpty(_archetype.Fieldsets);
var attempt = _archetype.First().TryConvertTo(destinationType);
Assert.That(attempt.Success, Is.EqualTo(expected));
if (attempt.Success)
{
Assert.IsNotNull(attempt.Result);
Assert.That(attempt.Result, Is.InstanceOf(destinationType));
}
}
}
}
@@ -252,6 +252,8 @@
<Compile Include="Properties\VersionInfo.cs" />
<Compile Include="PropertyConverters\ArchetypeValueConverter.cs" />
<Compile Include="PropertyEditors\ArchetypePropertyEditor.cs" />
<Compile Include="TypeConverters\ArchetypeFieldsetModelConverter.cs" />
<Compile Include="TypeConverters\ArchetypeModelConverter.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
@@ -98,7 +98,7 @@ namespace Archetype.Extensions
return archetype;
}
catch
catch (Exception ex)
{
return new ArchetypeModel();
}
@@ -1,15 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Security;
using Archetype.TypeConverters;
using Newtonsoft.Json;
using Umbraco.Core;
using System;
namespace Archetype.Models
{
/// <summary>
/// Model that represents a fieldset stored as content JSON.
/// </summary>
[TypeConverter(typeof(ArchetypeFieldsetModelConverter))]
public class ArchetypeFieldsetModel
{
[JsonProperty("alias")]
@@ -63,7 +66,7 @@ namespace Archetype.Models
{
var property = GetProperty(propertyAlias);
if (IsEmptyProperty(property))
if (IsEmptyProperty(property))
{
return default(T);
}
@@ -87,7 +90,7 @@ namespace Archetype.Models
{
var property = GetProperty(propertyAlias);
if (IsEmptyProperty(property))
if (IsEmptyProperty(property))
{
return defaultValue;
}
@@ -100,7 +103,7 @@ namespace Archetype.Models
/// </summary>
/// <param name="property">The property.</param>
/// <returns></returns>
private bool IsEmptyProperty(ArchetypePropertyModel property)
private bool IsEmptyProperty(ArchetypePropertyModel property)
{
return (property == null || property.Value == null || string.IsNullOrEmpty(property.Value.ToString()));
}
@@ -145,24 +148,24 @@ namespace Archetype.Models
/// <returns>true if this fieldset is disabled, false otherwise</returns>
internal bool IsAvailable()
{
// explicitly disabled or implicitly disabled through publishing?
var disabled = Disabled
// explicitly disabled or implicitly disabled through publishing?
var disabled = Disabled
|| (ReleaseDate.HasValue && ReleaseDate > DateTime.UtcNow)
|| (ExpireDate.HasValue && DateTime.UtcNow > ExpireDate);
if(disabled)
{
// yes - the fieldset is not available
return false;
}
// limitation on member group access?
if(string.IsNullOrEmpty(AllowedMemberGroups))
{
// no - the fieldset is available
return true;
}
// maybe - the fieldset is available if the current member is a member of the configured member groups
var currentUserGroups = Roles.GetRolesForUser() ?? new string[0];
return currentUserGroups.ContainsAny(AllowedMemberGroups.Split(','));
if (disabled)
{
// yes - the fieldset is not available
return false;
}
// limitation on member group access?
if (string.IsNullOrEmpty(AllowedMemberGroups))
{
// no - the fieldset is available
return true;
}
// maybe - the fieldset is available if the current member is a member of the configured member groups
var currentUserGroups = Roles.GetRolesForUser() ?? new string[0];
return currentUserGroups.ContainsAny(AllowedMemberGroups.Split(','));
}
#endregion
@@ -1,7 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Archetype.TypeConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -11,6 +13,7 @@ namespace Archetype.Models
/// Model that represents an entire Archetype.
/// </summary>
[JsonObject]
[TypeConverter(typeof(ArchetypeModelConverter))]
public class ArchetypeModel : IEnumerable<ArchetypeFieldsetModel>
{
[JsonProperty("fieldsets")]
@@ -206,7 +206,7 @@ namespace Archetype.PropertyEditors
else if (propDef.EditorState != null && propDef.EditorState.FileNames != null && propDef.EditorState.FileNames.Any())
{
// pass the uploaded files that belongs to this property (if any) to the value editor
var propertyFiles = propDef.EditorState.FileNames.Select(f => uploadedFiles.FirstOrDefault(u => u.FileName == f)).Where(f => f != null).ToList();
var propertyFiles = propDef.EditorState.FileNames.Select(f => uploadedFiles.FirstOrDefault(u => u.FileName != null && u.FileName.Equals(f, StringComparison.OrdinalIgnoreCase))).Where(f => f != null).ToList();
if(propertyFiles.Any())
{
additionalData["files"] = propertyFiles;
@@ -0,0 +1,39 @@
using System;
using System.ComponentModel;
using System.Globalization;
using Archetype.Extensions;
using Archetype.Models;
using Umbraco.Core.Models;
namespace Archetype.TypeConverters
{
public class ArchetypeFieldsetModelConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(IPublishedContent) ||
destinationType == typeof(ArchetypePublishedContent))
{
return true;
}
if (destinationType == typeof(string))
{
// NOTE: The converter needs to return false here, otherwise `ArchetypeHelper.DeserializeJsonToArchetype` fails.
return false;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is ArchetypeFieldsetModel && (destinationType == typeof(IPublishedContent) || destinationType == typeof(ArchetypePublishedContent)))
{
return ((ArchetypeFieldsetModel)value).ToPublishedContent();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using Archetype.Extensions;
using Archetype.Models;
using Umbraco.Core.Models;
namespace Archetype.TypeConverters
{
public class ArchetypeModelConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(ArchetypePublishedContentSet) ||
destinationType == typeof(IEnumerable<IPublishedContent>))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is ArchetypeModel && (destinationType == typeof(ArchetypePublishedContentSet) || destinationType == typeof(IEnumerable<IPublishedContent>)))
{
return ((ArchetypeModel)value).ToPublishedContentSet();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
+16 -3
View File
@@ -211,6 +211,16 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
}
// Checks if the specified array of fieldsets contains the specified fieldset.
function arrayContainsFieldset(fieldset, fieldsets) {
for (var i = 0; i < fieldsets.length; i++) {
if (fieldsets[i] === fieldset) {
return true;
}
}
return false;
}
// Confirms that an array of properties is a subset of another array of properties.
function arePropertiesSubset(subset, superset) {
@@ -857,12 +867,15 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
// The source scope is the scope of the fieldset being dragged.
// The target scope is the scope of the Archetype to drop into.
function canMove(sourceScope, targetScope) {
var targetFieldsets = targetScope.model.config.fieldsets;
var targetFieldsetConfigs = targetScope.model.config.fieldsets;
var targetFieldsets = targetScope.model.value.fieldsets;
var sourceFieldset = sourceScope.fieldset;
var sameArchetype = arrayContainsFieldset(sourceFieldset, targetFieldsets);
var model = sourceScope.fieldsetConfigModel;
var canRemove = sourceScope.canRemove();
var canAdd = targetScope.canAdd();
var valid = canRemove && canAdd;
valid = valid && modelMatchesAnyFieldset(model, targetFieldsets);
var valid = sameArchetype || (canRemove && canAdd);
valid = valid && modelMatchesAnyFieldset(model, targetFieldsetConfigs);
return valid;
}
+9 -8
View File
@@ -123,16 +123,17 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
});
scope.$on('archetypeFormSubmitting', function (ev, args) {
if(args.action !== 'save') {
// validate all fieldset properties
_.each(scope.fieldset.properties, function (property) {
archetypeService.validateProperty(scope.fieldset, property, configFieldsetModel);
});
// #385 - revert the changes made in #311 to avoid publishing invalid fieldsets (leaving the code from #311 here, in case we figure out how to re-introduce "save as draft")
// if(args.action !== 'save') {
// validate all fieldset properties
_.each(scope.fieldset.properties, function (property) {
archetypeService.validateProperty(scope.fieldset, property, configFieldsetModel);
});
var validationKey = "validation-f" + scope.fieldsetIndex;
var validationKey = "validation-f" + scope.fieldsetIndex;
ngModelCtrl.$setValidity(validationKey, scope.fieldset.isValid);
}
ngModelCtrl.$setValidity(validationKey, scope.fieldset.isValid);
// }
// did the value change (if it did, it most likely did so during the "formSubmitting" event)
var property = archetypeService.getFieldsetProperty(scope);