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()