From 6127cfccbdff6e8f5f98bde6b32bc6e757b39d6b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Aug 2013 16:37:21 +1000 Subject: [PATCH] Fixed up the umbPropEditorHelper to ensure that the correct path is returned for views (because we can have relative views specified too). Moved angularhelper to it's own file, started on the DropDown property editor in c#. --- .../PropertyEditors/PreValueField.cs | 5 +- .../PropertyEditorAttribute.cs | 5 +- .../PropertyEditors/ValueEditor.cs | 5 +- .../common/services/angularhelper.service.js | 156 +++++++++++++++ .../src/common/services/util.service.js | 180 ++---------------- .../datatype/datatype.edit.controller.js | 2 +- .../dropdown/dropdown.prevalue.html | 4 + .../PropertyEditors/DatePropertyEditor.cs | 29 ++- 8 files changed, 214 insertions(+), 172 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.html diff --git a/src/Umbraco.Core/PropertyEditors/PreValueField.cs b/src/Umbraco.Core/PropertyEditors/PreValueField.cs index ec8283c1b9..94309e2ec7 100644 --- a/src/Umbraco.Core/PropertyEditors/PreValueField.cs +++ b/src/Umbraco.Core/PropertyEditors/PreValueField.cs @@ -33,7 +33,10 @@ namespace Umbraco.Core.PropertyEditors public string Key { get; set; } /// - /// The view to render for the field + /// Defines the view to use for the editor, this can be one of 3 things: + /// * the full virtual path or + /// * the relative path to the current Umbraco folder + /// * a simple view name which will map to the views/prevalueeditors/{view}.html /// [JsonProperty("view", Required = Required.Always)] public string View { get; set; } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs index f9c189e0ee..6f48ede182 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs @@ -34,25 +34,22 @@ namespace Umbraco.Core.PropertyEditors ValueType = "string"; } - public PropertyEditorAttribute(string id, string name, string valueType, string editorView, string preValueEditorView) + public PropertyEditorAttribute(string id, string name, string valueType, string editorView) { Mandate.ParameterNotNullOrEmpty(id, "id"); Mandate.ParameterNotNullOrEmpty(name, "name"); Mandate.ParameterNotNullOrEmpty(valueType, "valueType"); Mandate.ParameterNotNullOrEmpty(editorView, "editorView"); - Mandate.ParameterNotNullOrEmpty(preValueEditorView, "preValueEditorView"); Id = id; Name = name; ValueType = valueType; EditorView = editorView; - PreValueEditorView = preValueEditorView; } public string Id { get; private set; } public string Name { get; private set; } public string EditorView { get; private set; } public string ValueType { get; set; } - public string PreValueEditorView { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index 5929b96512..1b5b0e7118 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -36,7 +36,10 @@ namespace Umbraco.Core.PropertyEditors } /// - /// The full virtual path or the relative path to the current Umbraco folder for the angular view + /// Defines the view to use for the editor, this can be one of 3 things: + /// * the full virtual path or + /// * the relative path to the current Umbraco folder + /// * a simple view name which will map to the views/propertyeditors/{view}/{view}.html /// [JsonProperty("view", Required = Required.Always)] public string View { get; set; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js new file mode 100644 index 0000000000..dd439e14b7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js @@ -0,0 +1,156 @@ +/** + * @ngdoc service + * @name umbraco.services.angularHelper + * @function + * + * @description + * Some angular helper/extension methods + */ +function angularHelper($log, $q) { + return { + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#rejectedPromise + * @methodOf umbraco.services.angularHelper + * @function + * + * @description + * In some situations we need to return a promise as a rejection, normally based on invalid data. This + * is a wrapper to do that so we can save one writing a bit of code. + * + * @param {object} objReject The object to send back with the promise rejection + */ + rejectedPromise: function (objReject) { + var deferred = $q.defer(); + //return an error object including the error message for UI + deferred.reject(objReject); + return deferred.promise; + }, + + /** + * @ngdoc function + * @name safeApply + * @methodOf umbraco.services.angularHelper + * @function + * + * @description + * This checks if a digest/apply is already occuring, if not it will force an apply call + */ + safeApply: function (scope, fn) { + if (scope.$$phase || scope.$root.$$phase) { + if (angular.isFunction(fn)) { + fn(); + } + } + else { + if (angular.isFunction(fn)) { + scope.$apply(fn); + } + else { + scope.$apply(); + } + } + }, + + /** + * @ngdoc function + * @name getCurrentForm + * @methodOf umbraco.services.angularHelper + * @function + * + * @description + * Returns the current form object applied to the scope or null if one is not found + */ + getCurrentForm: function (scope) { + + //NOTE: There isn't a way in angular to get a reference to the current form object since the form object + // is just defined as a property of the scope when it is named but you'll always need to know the name which + // isn't very convenient. If we want to watch for validation changes we need to get a form reference. + // The way that we detect the form object is a bit hackerific in that we detect all of the required properties + // that exist on a form object. + // + //The other way to do it in a directive is to require "^form", but in a controller the only other way to do it + // is to inject the $element object and use: $element.inheritedData('$formController'); + + var form = null; + //var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$invalid", "$addControl", "$removeControl", "$setValidity", "$setDirty"]; + var requiredFormProps = ["$addControl", "$removeControl", "$setValidity", "$setDirty", "$setPristine"]; + + // a method to check that the collection of object prop names contains the property name expected + function propertyExists(objectPropNames) { + //ensure that every required property name exists on the current scope property + return _.every(requiredFormProps, function (item) { + + return _.contains(objectPropNames, item); + }); + } + + for (var p in scope) { + + if (_.isObject(scope[p]) && p !== "this" && p.substr(0, 1) !== "$") { + //get the keys of the property names for the current property + var props = _.keys(scope[p]); + //if the length isn't correct, try the next prop + if (props.length < requiredFormProps.length) { + continue; + } + + //ensure that every required property name exists on the current scope property + var containProperty = propertyExists(props); + + if (containProperty) { + form = scope[p]; + break; + } + } + } + + return form; + }, + + /** + * @ngdoc function + * @name validateHasForm + * @methodOf umbraco.services.angularHelper + * @function + * + * @description + * This will validate that the current scope has an assigned form object, if it doesn't an exception is thrown, if + * it does we return the form object. + */ + getRequiredCurrentForm: function (scope) { + var currentForm = this.getCurrentForm(scope); + if (!currentForm || !currentForm.$name) { + throw "The current scope requires a current form object (or ng-form) with a name assigned to it"; + } + return currentForm; + }, + + /** + * @ngdoc function + * @name getNullForm + * @methodOf umbraco.services.angularHelper + * @function + * + * @description + * Returns a null angular FormController, mostly for use in unit tests + * NOTE: This is actually the same construct as angular uses internally for creating a null form but they don't expose + * any of this publicly to us, so we need to create our own. + * + * @param {string} formName The form name to assign + */ + getNullForm: function (formName) { + return { + $addControl: angular.noop, + $removeControl: angular.noop, + $setValidity: angular.noop, + $setDirty: angular.noop, + $setPristine: angular.noop, + $name: formName + //NOTE: we don't include the 'properties', just the methods. + }; + } + }; +} +angular.module('umbraco.services').factory('angularHelper', angularHelper); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 5556e3403f..421cba5fe4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -20,164 +20,6 @@ function legacyJsLoader(assetsService, umbRequestHelper) { } angular.module('umbraco.services').factory('legacyJsLoader', legacyJsLoader); - -/** - * @ngdoc service - * @name umbraco.services.angularHelper - * @function - * - * @description - * Some angular helper/extension methods - */ -function angularHelper($log, $q) { - return { - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#rejectedPromise - * @methodOf umbraco.services.angularHelper - * @function - * - * @description - * In some situations we need to return a promise as a rejection, normally based on invalid data. This - * is a wrapper to do that so we can save one writing a bit of code. - * - * @param {object} objReject The object to send back with the promise rejection - */ - rejectedPromise: function (objReject) { - var deferred = $q.defer(); - //return an error object including the error message for UI - deferred.reject(objReject); - return deferred.promise; - }, - - /** - * @ngdoc function - * @name safeApply - * @methodOf umbraco.services.angularHelper - * @function - * - * @description - * This checks if a digest/apply is already occuring, if not it will force an apply call - */ - safeApply: function (scope, fn) { - if (scope.$$phase || scope.$root.$$phase) { - if (angular.isFunction(fn)) { - fn(); - } - } - else { - if (angular.isFunction(fn)) { - scope.$apply(fn); - } - else { - scope.$apply(); - } - } - }, - - /** - * @ngdoc function - * @name getCurrentForm - * @methodOf umbraco.services.angularHelper - * @function - * - * @description - * Returns the current form object applied to the scope or null if one is not found - */ - getCurrentForm: function (scope) { - - //NOTE: There isn't a way in angular to get a reference to the current form object since the form object - // is just defined as a property of the scope when it is named but you'll always need to know the name which - // isn't very convenient. If we want to watch for validation changes we need to get a form reference. - // The way that we detect the form object is a bit hackerific in that we detect all of the required properties - // that exist on a form object. - // - //The other way to do it in a directive is to require "^form", but in a controller the only other way to do it - // is to inject the $element object and use: $element.inheritedData('$formController'); - - var form = null; - //var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$invalid", "$addControl", "$removeControl", "$setValidity", "$setDirty"]; - var requiredFormProps = ["$addControl", "$removeControl", "$setValidity", "$setDirty", "$setPristine"]; - - // a method to check that the collection of object prop names contains the property name expected - function propertyExists(objectPropNames) { - //ensure that every required property name exists on the current scope property - return _.every(requiredFormProps, function (item) { - - return _.contains(objectPropNames, item); - }); - } - - for (var p in scope) { - - if (_.isObject(scope[p]) && p !== "this" && p.substr(0, 1) !== "$") { - //get the keys of the property names for the current property - var props = _.keys(scope[p]); - //if the length isn't correct, try the next prop - if (props.length < requiredFormProps.length) { - continue; - } - - //ensure that every required property name exists on the current scope property - var containProperty = propertyExists(props); - - if (containProperty) { - form = scope[p]; - break; - } - } - } - - return form; - }, - - /** - * @ngdoc function - * @name validateHasForm - * @methodOf umbraco.services.angularHelper - * @function - * - * @description - * This will validate that the current scope has an assigned form object, if it doesn't an exception is thrown, if - * it does we return the form object. - */ - getRequiredCurrentForm: function(scope) { - var currentForm = this.getCurrentForm(scope); - if (!currentForm || !currentForm.$name) { - throw "The current scope requires a current form object (or ng-form) with a name assigned to it"; - } - return currentForm; - }, - - /** - * @ngdoc function - * @name getNullForm - * @methodOf umbraco.services.angularHelper - * @function - * - * @description - * Returns a null angular FormController, mostly for use in unit tests - * NOTE: This is actually the same construct as angular uses internally for creating a null form but they don't expose - * any of this publicly to us, so we need to create our own. - * - * @param {string} formName The form name to assign - */ - getNullForm: function(formName) { - return { - $addControl: angular.noop, - $removeControl: angular.noop, - $setValidity: angular.noop, - $setDirty: angular.noop, - $setPristine: angular.noop, - $name: formName - //NOTE: we don't include the 'properties', just the methods. - }; - } - }; -} -angular.module('umbraco.services').factory('angularHelper', angularHelper); - /** * @ngdoc service * @name umbraco.services.umbPropertyEditorHelper @@ -198,19 +40,29 @@ function umbPropEditorHelper() { */ getViewPath: function (input, isPreValue) { var path = String(input); + if (path.startsWith('/')) { + + //This is an absolute path, so just leave it return path; } else { - var pathName = path.replace('.', '/'); - if (!isPreValue) { - //i.e. views/propertyeditors/fileupload/fileupload.html - return "views/propertyeditors/" + pathName + "/" + pathName + ".html"; + + if (path.indexOf("/") >= 0) { + //This is a relative path, so just leave it + return path; } else { - //i.e. views/prevalueeditors/requiredfield.html - return "views/prevalueeditors/" + pathName + ".html"; + if (!isPreValue) { + //i.e. views/propertyeditors/fileupload/fileupload.html + return "views/propertyeditors/" + path + "/" + path + ".html"; + } + else { + //i.e. views/prevalueeditors/requiredfield.html + return "views/prevalueeditors/" + path + ".html"; + } } + } } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js index 206f4c4a50..40f91b7b03 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js @@ -67,7 +67,7 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc $scope.$watch("content.selectedEditor", function (newVal, oldVal) { //when the value changes, we need to dynamically load in the new editor - if (newVal && oldVal && newVal != oldVal) { + if (newVal !== null && newVal !== undefined && newVal != oldVal) { //we are editing so get the content item from the server dataTypeResource.getPreValues(newVal) .then(function (data) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.html new file mode 100644 index 0000000000..e67d96f0a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.html @@ -0,0 +1,4 @@ +
+ Hello world +
+ \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index 135a76ee3a..20831fadfc 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -5,10 +5,37 @@ using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; +using umbraco; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.Date, "Date", "datepicker", ValueType = "DATE")] + [PropertyEditor(Constants.PropertyEditors.DropDownList, "Dropdown list", "dropdown")] + public class DropDownPropertyEditor : PropertyEditor + { + protected override PreValueEditor CreatePreValueEditor() + { + var editor = base.CreatePreValueEditor(); + + editor.Fields = new List + { + new PreValueField + { + Description = "Add and remove values for the drop down list", + //we're going to call this 'temp' because we are going to override the + //serialization of the pre-values to ensure that each one gets saved with it's own key + //(new db row per pre-value, thus to maintain backwards compatibility) + Key = "temp", + Name = ui.Text("editdatatype", "addPrevalue"), + View = "Views/PropertyEditors/dropdown/dropdown.prevalue.html" + } + }; + + return editor; + } + } + + + [PropertyEditor(Constants.PropertyEditors.Date, "Date", "DATE", "datepicker")] public class DatePropertyEditor : PropertyEditor { public DatePropertyEditor()