Compare commits

...

8 Commits

Author SHA1 Message Date
kgiszewski e9da0aa351 What a mess this is turing out to be 👎 2015-08-18 12:09:16 -04:00
kgiszewski b42e411e49 Add MinFieldsets to the prevalue model 2015-08-17 16:09:53 -04:00
kgiszewski 4c4b09b187 Init 2015-08-17 16:04:41 -04:00
kgiszewski 43a72ed5e5 Use relative paths instead of tilde due to core compatibility issues 2015-08-17 12:16:38 -04:00
kgiszewski 709198f943 Merge branch 'master' of github.com:imulus/Archetype 2015-08-17 11:53:44 -04:00
Kevin Giszewski 77f72ed72e Merge pull request #283 from kipusoep/master
Fixes for Virtual Directories #156
2015-08-17 11:47:52 -04:00
kgiszewski a03fe3af32 Remove some cruft 2015-08-17 11:47:08 -04:00
Stefan Kip 697a797c7e Fixes for Virtual Directories #156 2015-08-14 14:49:41 +02:00
15 changed files with 188 additions and 78 deletions
@@ -32,6 +32,9 @@ namespace Archetype.Models
[JsonProperty("hidePropertyLabel")]
public bool HidePropertyLabel { get; set; }
[JsonProperty("minFieldsets", NullValueHandling = NullValueHandling.Ignore)]
public int MinFieldsets { get; set; }
[JsonProperty("maxFieldsets", NullValueHandling = NullValueHandling.Ignore)]
public int MaxFieldsets { get; set; }
@@ -35,7 +35,6 @@ namespace Archetype.Models
public T GetValue<T>()
{
// Try Umbraco's PropertyValueConverters
var converters = UmbracoContext.Current != null ? PropertyValueConvertersResolver.Current.Converters : Enumerable.Empty<IPropertyValueConverter>();
if (!string.IsNullOrWhiteSpace(this.PropertyEditorAlias) && converters.Any())
@@ -4,7 +4,6 @@ using System.Linq;
using Archetype.Extensions;
using ClientDependency.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -16,8 +15,8 @@ using Umbraco.Web.Models.ContentEditing;
namespace Archetype.PropertyEditors
{
[PropertyEditorAsset(ClientDependencyType.Javascript, "/App_Plugins/Archetype/js/archetype.js")]
[PropertyEditor(Constants.PropertyEditorAlias, "Archetype", "/App_Plugins/Archetype/views/archetype.html", ValueType = "JSON")]
[PropertyEditorAsset(ClientDependencyType.Javascript, "../App_Plugins/Archetype/js/archetype.js")]
[PropertyEditor(Constants.PropertyEditorAlias, "Archetype", "../App_Plugins/Archetype/views/archetype.html", ValueType = "JSON")]
public class ArchetypePropertyEditor : PropertyEditor
{
#region Pre Value Editor
@@ -29,7 +28,7 @@ namespace Archetype.PropertyEditors
internal class ArchetypePreValueEditor : PreValueEditor
{
[PreValueField("archetypeConfig", "Config", "/App_Plugins/Archetype/views/archetype.config.html",
[PreValueField("archetypeConfig", "Config", "../App_Plugins/Archetype/views/archetype.config.html",
Description = "(Required) Describe your Archetype.")]
public string Config { get; set; }
@@ -187,6 +186,6 @@ namespace Archetype.PropertyEditors
}
}
#endregion
#endregion
}
}
+3 -3
View File
@@ -6,7 +6,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio
//define empty items
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, "startWithAddButton": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "maxFieldsets": null, "enableCollapsing": true, "enableCloning": false, "enableDisabling": true, "enableDeepDatatypeRequests": false, "fieldsets": [' + newFieldsetModel + ']}');
var defaultFieldsetConfigModel = JSON.parse('{"showAdvancedOptions": false, "startWithAddButton": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "minFieldsets": null, "maxFieldsets": null, "enableCollapsing": true, "enableCloning": false, "enableDisabling": true, "enableDeepDatatypeRequests": false, "fieldsets": [' + newFieldsetModel + ']}');
//ini the model
$scope.model.value = $scope.model.value || defaultFieldsetConfigModel;
@@ -104,7 +104,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio
//ini the properties
_.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){
$scope.focusProperty(fieldset.properties);
$scope.focusProperty(fieldset.properties);
});
//setup JSON.stringify helpers
@@ -290,5 +290,5 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio
}
//archetype css
assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css");
assetsService.loadCss("../App_Plugins/Archetype/css/archetype.css");
});
+8 -2
View File
@@ -88,8 +88,11 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
$scope.model.value.fieldsets.push(newFieldset);
}
}
$scope.setDirty();
$scope.$broadcast("archetypeAddFieldset", {index: $index, visible: countVisible()});
newFieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
$scope.focusFieldset(newFieldset);
}
@@ -100,7 +103,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
if (confirm('Are you sure you want to remove this?')) {
$scope.setDirty();
$scope.model.value.fieldsets.splice($index, 1);
$scope.$broadcast("archetypeRemoveFieldset", {index: $index});
$scope.$broadcast("archetypeRemoveFieldset", {index: $index, visible: countVisible()});
}
}
}
@@ -364,6 +367,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
// recursive validation of nested fieldsets
var nestedFieldsetsValid = true;
_.each(fieldset.properties, function (property) {
if (property != null && property.value != null && property.propertyEditorAlias == "Imulus.Archetype") {
_.each(property.value.fieldsets, function (inner) {
@@ -390,7 +394,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
}
//archetype css
assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css");
assetsService.loadCss("../App_Plugins/Archetype/css/archetype.css");
//custom css
if($scope.model.config.customCssPath)
@@ -403,10 +407,12 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
// we need to monitor the "formSubmitting" event from a custom property and broadcast our own event
// to forcefully update the appropriate model.value's
$scope.activeSubmitWatcher = 0;
$scope.submitWatcherOnLoad = function () {
$scope.activeSubmitWatcher++;
return $scope.activeSubmitWatcher;
}
$scope.submitWatcherOnSubmit = function () {
$scope.$broadcast("archetypeFormSubmitting");
}
+40 -57
View File
@@ -1,4 +1,4 @@
angular.module("umbraco.directives").directive('archetypeProperty', function ($compile, $http, archetypePropertyEditorResource, umbPropEditorHelper, $timeout, $rootScope, $q, fileManager, editorState, archetypeService, archetypeCacheService) {
angular.module("umbraco.directives").directive('archetypeProperty', function ($compile, $http, archetypePropertyEditorResource, umbPropEditorHelper, $timeout, $rootScope, $q, fileManager, editorState, archetypeService, archetypeCacheService, notificationsService) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var configFieldsetModel = archetypeService.getFieldsetByAlias(scope.archetypeConfig.fieldsets, scope.fieldset.alias);
@@ -40,64 +40,12 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
var mergedConfig = _.extend(defaultConfigObj, config);
loadView(pathToView, mergedConfig, defaultValue, alias, propertyAlias, scope, element, ngModelCtrl, propertyValueChanged);
loadView(pathToView, mergedConfig, defaultValue, alias, propertyAlias, scope, element, ngModelCtrl, configFieldsetModel);
});
});
scope.$on("archetypeFormSubmitting", function (ev, args) {
// validate all fieldset properties
_.each(scope.fieldset.properties, function (property) {
validateProperty(scope.fieldset, property);
});
var validationKey = "validation-f" + scope.fieldsetIndex;
ngModelCtrl.$setValidity(validationKey, scope.fieldset.isValid);
});
scope.$on("archetypeRemoveFieldset", function (ev, args) {
var validationKey = "validation-f" + args.index;
ngModelCtrl.$setValidity(validationKey, true);
});
// called when the value of any property in a fieldset changes
function propertyValueChanged(fieldset, property) {
// it's the Umbraco way to hide the invalid state when altering an invalid property, even if the new value isn't valid either
property.isValid = true;
setFieldsetValidity(fieldset);
}
// validate a property in a fieldset
function validateProperty(fieldset, property) {
var propertyConfig = archetypeService.getPropertyByAlias(configFieldsetModel, property.alias);
if (propertyConfig) {
// use property.value !== property.value to check for NaN values on numeric inputs
if (propertyConfig.required && (property.value == null || property.value === "" || property.value !== property.value)) {
property.isValid = false;
}
// issue 116: RegEx validate property value
// Only validate the property value if anything has been entered - RegEx is considered a supplement to "required".
if (property.isValid == true && propertyConfig.regEx && property.value) {
var regEx = new RegExp(propertyConfig.regEx);
if (regEx.test(property.value) == false) {
property.isValid = false;
}
}
}
setFieldsetValidity(fieldset);
}
function setFieldsetValidity(fieldset) {
// mark the entire fieldset as invalid if there are any invalid properties in the fieldset, otherwise mark it as valid
fieldset.isValid =
_.find(fieldset.properties, function (property) {
return property.isValid == false
}) == null;
}
}
function loadView(view, config, defaultValue, alias, propertyAlias, scope, element, ngModelCtrl, propertyValueChanged) {
function loadView(view, config, defaultValue, alias, propertyAlias, scope, element, ngModelCtrl, configFieldsetModel) {
if (view)
{
$http.get(view, { cache: true }).success(function (data) {
@@ -120,6 +68,7 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
archetypeService.getFieldset(scope).properties.push(JSON.parse('{"alias": "' + alias + '", "value": "' + defaultValue + '"}'));
scope.renderModelPropertyIndex = archetypeService.getPropertyIndexByAlias(archetypeService.getFieldset(scope).properties, alias);
}
scope.renderModel = {};
scope.model.value = archetypeService.getFieldsetProperty(scope).value;
@@ -158,13 +107,28 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
//watch for changes since there is no two-way binding with the local model.value
scope.$watch('model.value', function (newValue, oldValue) {
archetypeService.getFieldsetProperty(scope).value = newValue;
// notify the linker that the property value changed
propertyValueChanged(archetypeService.getFieldset(scope), archetypeService.getFieldsetProperty(scope));
archetypeService.propertyValueChanged(archetypeService.getFieldset(scope), archetypeService.getFieldsetProperty(scope));
});
scope.$on('formSubmitting', function(ev, args){
archetypeCacheService.clearInvalidations();
archetypeCacheService.clearNotifications();
});
scope.$on('archetypeFormSubmitting', function (ev, args) {
// validate all fieldset properties
_.each(scope.fieldset.properties, function (property) {
archetypeService.validateProperty(scope.fieldset, property, configFieldsetModel);
});
var validationKey = "validation-f" + scope.fieldsetIndex;
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);
@@ -174,15 +138,21 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
archetypeService.getFieldsetProperty(scope).value = scope.model.value;
// notify the linker that the property value changed
propertyValueChanged(archetypeService.getFieldset(scope), archetypeService.getFieldsetProperty(scope));
archetypeService.propertyValueChanged(archetypeService.getFieldset(scope), archetypeService.getFieldsetProperty(scope));
}
archetypeService.validateMinFieldsets(scope);
archetypeCacheService.notifyEditor();
});
// issue 114: handle file selection on property editors
scope.$on("filesSelected", function (event, args) {
// populate the fileNames collection on the property editor state
var property = archetypeService.getFieldsetProperty(scope);
property.editorState.fileNames = [];
_.each(args.files, function (item) {
property.editorState.fileNames.push(item.name);
});
@@ -195,6 +165,19 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
scope.archetypeRenderModel.setFiles(args.files);
});
scope.$on("archetypeRemoveFieldset", function (ev, args) {
var validationKey = "validation-f" + args.index;
ngModelCtrl.$setValidity(validationKey, true);
scope.archetypeRenderModel.fieldsets.length = args.visible;
archetypeService.validateMinFieldsets(scope);
});
scope.$on("archetypeAddFieldset", function (ev, args) {
archetypeService.validateMinFieldsets(scope);
});
element.html(data).show();
$compile(element.contents())(scope);
+1
View File
@@ -7,6 +7,7 @@
"toggleAdvancedDescription": "Show advanced options.",
"hidePropertyLabels": "Hide Property Labels?",
"hidePropertyLabelsDescription": "Hides the property labels.",
"minFieldsets": "Min Fieldsets",
"maxFieldsets": "Max Fieldsets",
"maxFieldsetsDescription": "How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.",
"enableMultipleFieldsets": "Enable Multiple Fieldsets?",
+1
View File
@@ -7,6 +7,7 @@
"toggleAdvancedDescription": "Show advanced options.",
"hidePropertyLabels": "Hide Property Labels?",
"hidePropertyLabelsDescription": "Hides the property labels.",
"minFieldsets": "Min Fieldsets",
"maxFieldsets": "Max Fieldsets",
"maxFieldsetsDescription": "How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.",
"enableMultipleFieldsets": "Enable Multiple Fieldsets?",
+1
View File
@@ -7,6 +7,7 @@
"toggleAdvancedDescription": "Show advanced options.",
"hidePropertyLabels": "Hide Property Labels?",
"hidePropertyLabelsDescription": "Hides the property labels.",
"minFieldsets": "Min Fieldsets",
"maxFieldsets": "Max Fieldsets",
"maxFieldsetsDescription": "How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.",
"enableMultipleFieldsets": "Enable Multiple Fieldsets?",
+1 -1
View File
@@ -325,7 +325,7 @@
}
}
.archetypeMaxFieldsets{
.archetypeMaxFieldsets, .archetypeMinFieldsets {
border: 1px solid #ddd;
width: 40px;
text-align: right;
@@ -3,24 +3,24 @@ 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/ArchetypeApi/ArchetypeDataType/GetAll"), 'Failed to retrieve datatypes from tree service'
$http.get("backoffice/ArchetypeApi/ArchetypeDataType/GetAll"), 'Failed to retrieve datatypes from tree service'
);
},
getDataType: function (guid, useDeepDatatypeLookup, contentTypeAlias, propertyTypeAlias, archetypeAlias, nodeId) {
if(useDeepDatatypeLookup) {
return umbRequestHelper.resourcePromise(
$http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetByGuid?guid=" + guid + "&contentTypeAlias=" + contentTypeAlias + "&propertyTypeAlias=" + propertyTypeAlias + "&archetypeAlias=" + archetypeAlias + "&nodeId=" + nodeId), 'Failed to retrieve datatype'
$http.get("backoffice/ArchetypeApi/ArchetypeDataType/GetByGuid?guid=" + guid + "&contentTypeAlias=" + contentTypeAlias + "&propertyTypeAlias=" + propertyTypeAlias + "&archetypeAlias=" + archetypeAlias + "&nodeId=" + nodeId), 'Failed to retrieve datatype'
);
}
else {
return umbRequestHelper.resourcePromise(
$http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetByGuid?guid=" + guid , { cache: true }), 'Failed to retrieve datatype'
$http.get("backoffice/ArchetypeApi/ArchetypeDataType/GetByGuid?guid=" + guid , { cache: true }), 'Failed to retrieve datatype'
);
}
},
getPropertyEditorMapping: function(alias) {
return umbRequestHelper.resourcePromise(
$http.get("/umbraco/backoffice/ArchetypeApi/ArchetypeDataType/GetAllPropertyEditors", { cache: true }), 'Failed to retrieve datatype mappings'
$http.get("backoffice/ArchetypeApi/ArchetypeDataType/GetAllPropertyEditors", { cache: true }), 'Failed to retrieve datatype mappings'
).then(function (data) {
var result = _.find(data, function(d) {
return d.alias === alias;
+72 -1
View File
@@ -1,4 +1,4 @@
angular.module('umbraco.services').factory('archetypeCacheService', function (archetypePropertyEditorResource) {
angular.module('umbraco.services').factory('archetypeCacheService', function (archetypePropertyEditorResource, notificationsService) {
//private
var isEntityLookupLoading = false;
@@ -7,7 +7,78 @@ angular.module('umbraco.services').factory('archetypeCacheService', function (ar
var isDatatypeLookupLoading = false;
var datatypeCache = [];
var notificationQueue = [];
var notificationCache = [];
var invalidationCache = [];
function findItem(array, item) {
return _.find(array, function(value){
return value == item;
});
}
return {
notifyEditor: function() {
console.log("queue-v");
console.log(notificationQueue);
console.log("sent-v");
console.log(notificationCache);
if(this.shouldBeNotified("minFieldsets") && !this.hasBeenNotified("minFieldsets")) {
notificationsService.error("Error", "Some of your properties do not contain enough fieldsets.");
this.removeNotification("minFieldsets");
notificationCache.push("minFieldsets");
}
},
clearInvalidations: function() {
invalidationCache = [];
},
addInvalidation: function(key) {
if(!this.hasBeenInvalidated(invalidationCache, key)) {
invalidationCache.push(key);
}
},
removeInvalidation: function(key) {
invalidationCache = _.reject(invalidationCache, function(value){
return value == key;
});
},
hasBeenInvalidated: function(key) {
return (typeof findItem(invalidationCache, key) != 'undefined');
},
clearNotifications: function() {
notificationCache = [];
},
addNotification: function(key) {
if(!this.shouldBeNotified(key) && !this.hasBeenNotified(notificationCache, key)) {
notificationQueue.push(key);
}
},
removeNotification: function(key) {
notificationQueue = _.reject(notificationQueue, function(value){
return value == key;
});
},
shouldBeNotified: function(key) {
return (typeof findItem(notificationQueue, key) != 'undefined');
},
hasBeenNotified: function(key) {
return (typeof findItem(notificationCache, key) != 'undefined');
},
getDataTypeFromCache: function(guid) {
return _.find(datatypeCache, function (dt){
return dt.dataTypeGuid == guid;
+1 -1
View File
@@ -24,7 +24,7 @@ angular.module('umbraco.services').factory('archetypeLocalizationService', funct
initLocalizedResources:function () {
var deferred = $q.defer();
userService.getCurrentUser().then(function(user){
$http.get("/App_plugins/Archetype/langs/" + user.locale + ".js", { cache: true })
$http.get("../App_plugins/Archetype/langs/" + user.locale + ".js", { cache: true })
.then(function(response){
service.resourceFileLoaded = true;
service.dictionary = response.data;
+45 -3
View File
@@ -1,5 +1,4 @@
angular.module('umbraco.services').factory('archetypeService', function () {
angular.module('umbraco.services').factory('archetypeService', function (archetypeCacheService) {
//public
return {
//helper that returns a JS ojbect from 'value' string or the original string
@@ -67,6 +66,49 @@ angular.module('umbraco.services').factory('archetypeService', function () {
},
getFieldsetProperty: function (scope) {
return this.getFieldset(scope).properties[scope.renderModelPropertyIndex];
},
setFieldsetValidity: function (fieldset) {
// mark the entire fieldset as invalid if there are any invalid properties in the fieldset, otherwise mark it as valid
fieldset.isValid =
_.find(fieldset.properties, function (property) {
return property.isValid == false
}) == null;
},
validateProperty: function (fieldset, property, configFieldsetModel) {
var propertyConfig = this.getPropertyByAlias(configFieldsetModel, property.alias);
if (propertyConfig) {
// use property.value !== property.value to check for NaN values on numeric inputs
if (propertyConfig.required && (property.value == null || property.value === "" || property.value !== property.value)) {
property.isValid = false;
}
// issue 116: RegEx validate property value
// Only validate the property value if anything has been entered - RegEx is considered a supplement to "required".
if (property.isValid == true && propertyConfig.regEx && property.value) {
var regEx = new RegExp(propertyConfig.regEx);
if (regEx.test(property.value) == false) {
property.isValid = false;
}
}
}
this.setFieldsetValidity(fieldset);
},
// called when the value of any property in a fieldset changes
propertyValueChanged: function (fieldset, property) {
// it's the Umbraco way to hide the invalid state when altering an invalid property, even if the new value isn't valid either
property.isValid = true;
this.setFieldsetValidity(fieldset);
},
validateMinFieldsets: function(scope) {
//ngModelCtrl.$setValidity('propertyForm', true);
archetypeCacheService.removeInvalidation("minFieldsets");
if(scope.archetypeConfig.minFieldsets && scope.archetypeRenderModel.fieldsets.length < scope.archetypeConfig.minFieldsets) {
//ngModelCtrl.$setValidity('propertyForm', false);
archetypeCacheService.addInvalidation("minFieldsets");
archetypeCacheService.addNotification("minFieldsets");
}
}
}
});
});
+4
View File
@@ -97,6 +97,10 @@
<label for="archetypeAdvancedOptionsHideLabels"><archetype-localize key="hidePropertyLabels">Hide Property Labels?</archetype-localize><small><archetype-localize key="hidePropertyLabelsDescription">Hides the property labels.</archetype-localize></small></label>
<input type="checkbox" id="archetypeAdvancedOptionsHideLabels" ng-model="archetypeConfigRenderModel.hidePropertyLabels"/>
</div>
<div>
<label for="archetypeAdvancedOptionsMinFieldsets"><archetype-localize key="minFieldsets">Min Fieldsets</archetype-localize><small><archetype-localize key="minFieldsetsDescription">How many Fieldsets are required? Leaving blank will disable the control. Default is unlimited.</archetype-localize></small></label>
<input type="number" id="archetypeAdvancedOptionsMinFieldsets" class="archetypeMinFieldsets" ng-model="archetypeConfigRenderModel.minFieldsets"/>
</div>
<div>
<label for="archetypeAdvancedOptionsMaxFieldsets"><archetype-localize key="maxFieldsets">Max Fieldsets</archetype-localize><small><archetype-localize key="maxFieldsetsDescription">How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.</archetype-localize></small></label>
<input type="number" id="archetypeAdvancedOptionsMaxFieldsets" class="archetypeMaxFieldsets" ng-model="archetypeConfigRenderModel.maxFieldsets"/>