This commit is contained in:
perploug
2013-08-16 13:30:51 +02:00
35 changed files with 530 additions and 168 deletions

View File

@@ -33,7 +33,10 @@ namespace Umbraco.Core.Manifest
/// <returns></returns>
internal static IEnumerable<PropertyEditor> GetPropertyEditors(JArray jsonEditors)
{
return JsonConvert.DeserializeObject<IEnumerable<PropertyEditor>>(jsonEditors.ToString(), new PropertyEditorConverter());
return JsonConvert.DeserializeObject<IEnumerable<PropertyEditor>>(
jsonEditors.ToString(),
new PropertyEditorConverter(),
new PreValueFieldConverter());
}
/// <summary>

View File

@@ -27,6 +27,28 @@ namespace Umbraco.Core.Manifest
target.StaticallyDefinedValueEditor.Validators = new List<ManifestValidator>();
}
if (jObject["preValueEditor"] != null)
{
target.StaticallyDefinedPreValueEditor.Fields = new List<PreValueField>();
}
base.Deserialize(jObject, target, serializer);
}
}
internal class PreValueFieldConverter : JsonCreationConverter<PreValueField>
{
protected override PreValueField Create(Type objectType, JObject jObject)
{
return new PreValueField()
{
//assign manifest validators so the serialization process can continue
Validators = new List<ManifestValidator>()
};
}
protected override void Deserialize(JObject jObject, PreValueField target, JsonSerializer serializer)
{
base.Deserialize(jObject, target, serializer);
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Umbraco.Core.PropertyEditors
@@ -7,20 +8,26 @@ namespace Umbraco.Core.PropertyEditors
/// Defines a pre-value editor
/// </summary>
/// <remarks>
/// The Json serialization attributes are required for manifest property editors to work
/// A pre-value editor is made up of multiple pre-value fields, each field defines a key that the value is stored against.
/// Each field can have any editor and the value from each field can store any data such as a simple string or a json structure.
///
/// The Json serialization attributes are required for manifest property editors to work.
/// </remarks>
public class PreValueEditor
{
/// <summary>
/// The full virtual path or the relative path to the current Umbraco folder for the angular view
/// </summary>
[JsonProperty("view")]
public string View { get; set; }
public PreValueEditor()
{
Fields = Enumerable.Empty<PreValueField>();
}
/// <summary>
/// A collection of validators for the pre value editor
/// A collection of pre-value fields to be edited
/// </summary>
[JsonProperty("validation")]
public IEnumerable<ValidatorBase> Validators { get; set; }
/// <remarks>
/// If fields are specified then the master View and Validators will be ignored
/// </remarks>
[JsonProperty("fields")]
public IEnumerable<PreValueField> Fields { get; set; }
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Defines a pre value editable field
/// </summary>
public class PreValueField
{
/// <summary>
/// The name to display for this pre-value field
/// </summary>
[JsonProperty("label", Required = Required.Always)]
public string Name { get; set; }
/// <summary>
/// The description to display for this pre-value field
/// </summary>
[JsonProperty("description")]
public string Description { get; set; }
/// <summary>
/// Specifies whether to hide the label for the pre-value
/// </summary>
[JsonProperty("hideLabel")]
public bool HideLabel { get; set; }
/// <summary>
/// The key to store the pre-value against
/// </summary>
[JsonProperty("key", Required = Required.Always)]
public string Key { get; set; }
/// <summary>
/// The view to render for the field
/// </summary>
[JsonProperty("view", Required = Required.Always)]
public string View { get; set; }
/// <summary>
/// A collection of validators for the pre value field
/// </summary>
[JsonProperty("validation")]
public IEnumerable<ValidatorBase> Validators { get; set; }
}
}

View File

@@ -32,7 +32,6 @@ namespace Umbraco.Core.PropertyEditors
StaticallyDefinedValueEditor.ValueType = att.ValueType;
StaticallyDefinedValueEditor.View = att.EditorView;
StaticallyDefinedPreValueEditor.View = att.PreValueEditorView;
}
}
@@ -60,7 +59,7 @@ namespace Umbraco.Core.PropertyEditors
[JsonProperty("name", Required = Required.Always)]
public string Name { get; internal set; }
[JsonProperty("editor")]
[JsonProperty("editor", Required = Required.Always)]
public ValueEditor ValueEditor
{
get { return CreateValueEditor(); }
@@ -105,7 +104,18 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
/// <returns></returns>
protected virtual PreValueEditor CreatePreValueEditor()
{
{
if (StaticallyDefinedPreValueEditor != null)
{
foreach (var f in StaticallyDefinedPreValueEditor.Fields)
{
//detect if the view is a virtual path (in most cases, yes) then convert it
if (f.View.StartsWith("~/"))
{
f.View = IOHelper.ResolveUrl(f.View);
}
}
}
return StaticallyDefinedPreValueEditor;
}

View File

@@ -11,7 +11,11 @@ namespace Umbraco.Core.PropertyEditors
/// <summary>
/// Validates the object with the resolved ValueValidator found for this type
/// </summary>
/// <param name="value"></param>
/// <param name="value">
/// Depending on what is being validated, this value can be a json structure representing an editor's model, it could be a single
/// string representing an editor's model, this class structure is also used to validate pre-values and in that case this value
/// could be a json structure representing the entire pre-value model or it could ba a single value representing a pre-value field.
/// </param>
/// <param name="preValues">The current pre-values stored for the data type</param>
/// <param name="editor">The property editor instance that we are validating for</param>
/// <returns></returns>

View File

@@ -571,6 +571,7 @@
<Compile Include="Profiling\WebProfiler.cs" />
<Compile Include="PropertyEditors\DelimitedValueValidator.cs" />
<Compile Include="PropertyEditors\ManifestValidator.cs" />
<Compile Include="PropertyEditors\PreValueField.cs" />
<Compile Include="PropertyEditors\ValueValidator.cs" />
<Compile Include="PropertyEditors\PreValueEditor.cs" />
<Compile Include="PropertyEditors\PropertyEditor.cs" />

View File

@@ -14,6 +14,63 @@ namespace Umbraco.Belle.Tests
public class ManifestParserTests
{
[Test]
public void Parse_Property_Editors_With_Pre_Vals()
{
var a = JsonConvert.DeserializeObject<JArray>(@"[
{
id: '0EEBB7CE-51BA-4F6B-9D9C-78BB3314366C',
name: 'Test 1',
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
valueType: 'int',
validation: [
{
type: 'Required'
},
{
type: 'Regex',
value: '\\d*'
},
]
},
preValueEditor: {
fields: [
{
label: 'Some config 1',
key: 'key1',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html',
validation: [
{
type: 'Required'
}
]
},
{
label: 'Some config 2',
key: 'key2',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html'
}
]
}
}
]");
var parser = ManifestParser.GetPropertyEditors(a);
Assert.AreEqual(1, parser.Count());
Assert.AreEqual(2, parser.ElementAt(0).PreValueEditor.Fields.Count());
Assert.AreEqual("key1", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Key);
Assert.AreEqual("Some config 1", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Name);
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).View);
Assert.AreEqual(1, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(0).Validators.Count());
Assert.AreEqual("key2", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Key);
Assert.AreEqual("Some config 2", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Name);
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html", parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).View);
Assert.AreEqual(0, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Validators.Count());
}
[Test]
public void Parse_Property_Editors()
{
@@ -39,22 +96,25 @@ namespace Umbraco.Belle.Tests
{
id: '1FCF5C39-5FC7-4BCE-AFBE-6500D9EBA261',
name: 'Test 2',
defaultConfig: { key1: 'some default pre val' },
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
}
},
}
]");
var parser = ManifestParser.GetPropertyEditors(a);
Assert.AreEqual(2, parser.Count());
Assert.AreEqual(new Guid("0EEBB7CE-51BA-4F6B-9D9C-78BB3314366C"), parser.ElementAt(0).Id);
Assert.AreEqual("Test 1", parser.ElementAt(0).Name);
Assert.AreEqual("~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html", parser.ElementAt(0).ValueEditor.View);
Assert.AreEqual("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html", parser.ElementAt(0).ValueEditor.View);
Assert.AreEqual("int", parser.ElementAt(0).ValueEditor.ValueType);
Assert.AreEqual(2, parser.ElementAt(0).ValueEditor.Validators.Count());
Assert.AreEqual(new Guid("1FCF5C39-5FC7-4BCE-AFBE-6500D9EBA261"), parser.ElementAt(1).Id);
Assert.AreEqual("Test 2", parser.ElementAt(1).Name);
Assert.IsTrue(parser.ElementAt(1).DefaultPreValues.ContainsKey("key1"));
Assert.AreEqual("some default pre val", parser.ElementAt(1).DefaultPreValues["key1"]);
}
[Test]

View File

@@ -9,7 +9,8 @@ angular.module("umbraco.directives")
.directive('umbEditor', function (umbPropEditorHelper) {
return {
scope: {
model: "="
model: "=",
isPreValue: "@"
},
require: "^form",
restrict: 'E',
@@ -26,7 +27,7 @@ angular.module("umbraco.directives")
scope.model.alias = Math.random().toString(36).slice(2);
}
scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view);
scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue);
}
};
});

View File

@@ -36,11 +36,7 @@ function valPropertyMsg(serverValidationManager) {
//listen for error changes
scope.$watch("formCtrl.$error", function () {
if (formCtrl.$valid === undefined) {
return;
}
if (!formCtrl.$valid) {
if (formCtrl.$invalid) {
//first we need to check if the valPropertyMsg validity is invalid
if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) {
@@ -53,7 +49,11 @@ function valPropertyMsg(serverValidationManager) {
hasError = true;
//update the validation message if we don't already have one assigned.
if (showValidation && scope.errorMsg === "") {
var err = serverValidationManager.getPropertyError(scope.property.alias, "");
var err;
//this can be null if no property was assigned
if (scope.property) {
err = serverValidationManager.getPropertyError(scope.property.alias, "");
}
scope.errorMsg = err ? err.errorMsg : "Property has errors";
}
}
@@ -72,7 +72,11 @@ function valPropertyMsg(serverValidationManager) {
scope.$on("saving", function (ev, args) {
showValidation = true;
if (hasError && scope.errorMsg === "") {
var err = serverValidationManager.getPropertyError(scope.property.alias, "");
var err;
//this can be null if no property was assigned
if (scope.property) {
err = serverValidationManager.getPropertyError(scope.property.alias, "");
}
scope.errorMsg = err ? err.errorMsg : "Property has errors";
}
else if (!hasError) {
@@ -109,27 +113,30 @@ function valPropertyMsg(serverValidationManager) {
// It's important to note that we need to subscribe to server validation changes here because we always must
// indicate that a content property is invalid at the property level since developers may not actually implement
// the correct field validation in their property editors.
serverValidationManager.subscribe(scope.property.alias, "", function (isValid, propertyErrors, allErrors) {
hasError = !isValid;
if (hasError) {
//set the error message to the server message
scope.errorMsg = propertyErrors[0].errorMsg;
//flag that the current validator is invalid
formCtrl.$setValidity('valPropertyMsg', false);
}
else {
scope.errorMsg = "";
//flag that the current validator is valid
formCtrl.$setValidity('valPropertyMsg', true);
}
});
//when the element is disposed we need to unsubscribe!
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
// but they are a different callback instance than the above.
element.bind('$destroy', function () {
serverValidationManager.unsubscribe(scope.property.alias, "");
});
if (scope.property) { //this can be null if no property was assigned
serverValidationManager.subscribe(scope.property.alias, "", function(isValid, propertyErrors, allErrors) {
hasError = !isValid;
if (hasError) {
//set the error message to the server message
scope.errorMsg = propertyErrors[0].errorMsg;
//flag that the current validator is invalid
formCtrl.$setValidity('valPropertyMsg', false);
}
else {
scope.errorMsg = "";
//flag that the current validator is valid
formCtrl.$setValidity('valPropertyMsg', true);
}
});
//when the element is disposed we need to unsubscribe!
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
// but they are a different callback instance than the above.
element.bind('$destroy', function() {
serverValidationManager.unsubscribe(scope.property.alias, "");
});
}
}
};
}

View File

@@ -12,13 +12,12 @@ function valRegex() {
link: function (scope, elm, attrs, ctrl) {
var regex;
if (scope[attrs.valRegex]) {
try {
regex = new RegExp(scope.$eval(attrs.valRegex));
}
else {
catch(e) {
regex = new RegExp(attrs.valRegex);
}
var patternValidator = function (viewValue) {
//NOTE: we don't validate on empty values, use required validator for that

View File

@@ -18,14 +18,10 @@ function valTab() {
scope.tabHasError = false;
//watch the current form's validation for the current field name
scope.$watch("formCtrl.$valid", function (isValid, lastValue) {
if (isValid === undefined) {
return;
}
scope.$watch("formCtrl.$valid", function () {
var tabContent = element.closest(".umb-panel").find("#" + tabId);
if (!isValid) {
if (formCtrl.$invalid) {
//check if the validation messages are contained inside of this tabs
if (tabContent.find(".ng-invalid").length > 0) {
scope.tabHasError = true;

View File

@@ -14,18 +14,19 @@ function valToggleMsg(serverValidationManager) {
if (!attr.valMsgFor){
throw "valToggleMsg requires that the attribute valMsgFor exists on the element";
}
if (!formCtrl[attr.valMsgFor]) {
throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name;
}
//assign the form control to our isolated scope so we can watch it's values
scope.formCtrl = formCtrl;
//if there's any remaining errors in the server validation service then we should show them.
var showValidation = serverValidationManager.items.length > 0;
var hasError = false;
//add a watch to the validator for the value (i.e. myForm.value.$error.required )
scope.$watch(formCtrl.$name + "." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function (isInvalid, oldValue) {
hasError = isInvalid;
if (hasError && showValidation) {
scope.$watch("formCtrl." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function () {
if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) {
element.show();
}
else {
@@ -35,7 +36,7 @@ function valToggleMsg(serverValidationManager) {
scope.$on("saving", function(ev, args) {
showValidation = true;
if (hasError) {
if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) {
element.show();
}
else {

View File

@@ -2,30 +2,84 @@ angular.module('umbraco.mocks').
factory('dataTypeMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) {
'use strict';
function returnNodebyId(status, data, headers) {
function returnById(status, data, headers) {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var id = mocksUtils.getParameterByName(data, "id") || 1234;
var selectedId = String.CreateGuid();
var dataType = {
id: id,
name: "Data type " + id,
selectedEditor: String.CreateGuid()
name: "Simple editor " + id,
selectedEditor: selectedId,
availableEditors: [
{ name: "Simple editor 1", editorId: String.CreateGuid() },
{ name: "Simple editor 2", editorId: String.CreateGuid() },
{ name: "Simple editor 3", editorId: selectedId },
{ name: "Simple editor 4", editorId: String.CreateGuid() },
{ name: "Simple editor 5", editorId: String.CreateGuid() },
{ name: "Simple editor 6", editorId: String.CreateGuid() }
],
preValues: [
{
label: "Custom pre value 1",
description: "Enter a value for this pre-value",
key: "myPreVal",
view: "requiredfield",
validation: [
{
type: "Required"
}
]
},
{
label: "Custom pre value 2",
description: "Enter a value for this pre-value",
key: "myPreVal",
view: "requiredfield",
validation: [
{
type: "Required"
}
]
}
]
};
return [200, dataType, null];
}
function returnEmpty(status, data, headers) {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var response = returnById(200, "", null);
var node = response[1];
node.name = "";
node.selectedEditor = "";
node.id = 0;
node.preValues = [];
return response;
}
return {
register: function() {
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/DataType/GetById'))
.respond(returnNodebyId);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/DataType/GetById'))
.respond(returnById);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/DataType/GetEmpty'))
.respond(returnEmpty);
},
expectGetById: function() {
$httpBackend

View File

@@ -63,6 +63,38 @@ angular.module('umbraco.mocks').
return [200, children, null];
}
function returnDataTypes(status, data, headers) {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var children = [
{ name: "Textstring", childNodesUrl: null, id: 10, icon: "icon-file-alt", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
{ name: "Multiple textstring", childNodesUrl: null, id: 11, icon: "icon-file-alt", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
{ name: "Yes/No", childNodesUrl: null, id: 12, icon: "icon-file-alt", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
{ name: "Rich Text Editor", childNodesUrl: null, id: 13, icon: "icon-file-alt", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null }
];
return [200, children, null];
}
function returnDataTypeMenu(status, data, headers) {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var menu = [
{
name: "Create", cssclass: "plus", alias: "create", metaData: {
jsAction: "umbracoMenuActions.CreateChildEntity"
}
},
{ seperator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} }
];
return [200, menu, null];
}
function returnApplicationTrees(status, data, headers) {
if (!mocksUtils.checkAuth()) {
@@ -111,12 +143,16 @@ angular.module('umbraco.mocks').
};
break;
case "developer":
case "developer":
var dataTypeChildrenUrl = "/umbraco/UmbracoTrees/DataTypeTree/GetNodes?id=-1&application=developer";
var dataTypeMenuUrl = "/umbraco/UmbracoTrees/DataTypeTree/GetMenu?id=-1&application=developer";
t = {
name: "developer",
id: -1,
children: [
{ name: "Data types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "datatype" } },
{ name: "Data types", childNodesUrl: dataTypeChildrenUrl, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: dataTypeMenuUrl, metaData: { treeAlias: "datatype" } },
{ name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "macros" } },
{ name: "Packages", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "packager" } },
{ name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "xslt" } },
@@ -184,6 +220,15 @@ angular.module('umbraco.mocks').
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren'))
.respond(returnChildren);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoTrees/DataTypeTree/GetNodes'))
.respond(returnDataTypes);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoTrees/DataTypeTree/GetMenu'))
.respond(returnDataTypeMenu);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetMenu'))
.respond(getMenuItems);

View File

@@ -34,12 +34,12 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
/** saves or updates a data type object */
save: function (dataType, isNew) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"PostSave",
[{ id: id }])),
'Failed to save data for data type id ' + id);
$http.post(umbRequestHelper.getApiUrl("dataTypeApiBaseUrl", "PostSave"),
{
//TODO: SD: I need to finish this on Monday!
action: "save" + (isNew ? "New" : "")
}),
'Failed to save data for data type id ' + id);
}
};
}

View File

@@ -8,7 +8,7 @@
* @description
* Defines the methods that are called when menu items declare only an action to execute
*/
function umbracoMenuActions($q, treeService) {
function umbracoMenuActions($q, treeService, $location) {
return {
@@ -26,6 +26,24 @@ function umbracoMenuActions($q, treeService) {
*/
"RefreshNodeMenuItem": function (args) {
treeService.loadNodeChildren({ node: args.treeNode, section: args.section });
},
/**
* @ngdoc method
* @name umbraco.services.umbracoMenuActions#CreateChildEntity
* @methodOf umbraco.services.umbracoMenuActions
* @function
*
* @description
* This will re-route to a route for creating a new entity as a child of the current node
* @param {object} args An arguments object
* @param {object} args.treeNode The tree node
* @param {object} args.section The current section
*/
"CreateChildEntity": function (args) {
var route = "/" + args.section + "/" + treeService.getTreeAlias(args.treeNode) + "/edit/" + args.treeNode.id;
//change to new path
$location.path(route).search({ create: true });
}
};
}

View File

@@ -196,15 +196,21 @@ function umbPropEditorHelper() {
*
* @param {string} input the view path currently stored for the property editor
*/
getViewPath: function (input) {
getViewPath: function (input, isPreValue) {
var path = String(input);
if (path.startsWith('/')) {
return path;
}
else {
var pathName = path.replace('.', '/');
//i.e. views/propertyeditors/fileupload/fileupload.html
return "views/propertyeditors/" + pathName + "/" + pathName + ".html";
if (!isPreValue) {
//i.e. views/propertyeditors/fileupload/fileupload.html
return "views/propertyeditors/" + pathName + "/" + pathName + ".html";
}
else {
//i.e. views/prevalueeditors/requiredfield.html
return "views/prevalueeditors/" + pathName + ".html";
}
}
}
};

View File

@@ -1,50 +1,51 @@
<div ng-form name="contentForm" ng-show="loaded">
<umb-panel ng-controller="Umbraco.Editors.Content.EditController" val-show-validation>
<umb-header tabs="content.tabs">
<umb-content-name
ng-model="content.name"
placeholder="Enter a page title">
</umb-content-name>
<div class="span8">
<div class="btn-toolbar pull-right umb-btn-toolbar">
<div class="btn-group">
<a class="btn" ng-click="preview(content)">Preview page</a>
</div>
<div class="btn-group">
<a class="btn btn-success" href="#" ng-click="saveAndPublish(content)"
prevent-default data-hotkey="ctrl+p">Publish</a>
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li><a href="#" ng-click="save(content)"
prevent-default data-hotkey="ctrl+s">Save draft</a></li>
</ul>
</div>
</div>
</div>
</umb-header>
<umb-tab-view>
<umb-tab id="tab{{tab.id}}" rel="{{tab.id}}" ng-repeat="tab in content.tabs">
<div class="umb-pane">
<umb-property
property="property"
ng-repeat="property in tab.properties">
<umb-editor model="property"></umb-editor>
</umb-property>
</div>
</umb-tab>
</umb-tab-view>
</umb-panel>
</div>
<form novalidate name="contentForm" ng-show="loaded" ng-submit="save(content)">
<umb-panel ng-controller="Umbraco.Editors.Content.EditController" val-show-validation>
<umb-header tabs="content.tabs">
<umb-content-name
ng-model="content.name"
placeholder="Enter a page title">
</umb-content-name>
<div class="span8">
<div class="btn-toolbar pull-right umb-btn-toolbar">
<div class="btn-group">
<a class="btn" ng-click="preview(content)">Preview page</a>
data-hotkey="ctrl+s">Preview page</a>
</div>
<div class="btn-group">
<a class="btn btn-success" href="#" ng-click="saveAndPublish(content)"
prevent-default data-hotkey="ctrl+p">Publish</a>
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li><a href="#" ng-click="save(content)"
prevent-default data-hotkey="ctrl+s">Save draft</a></li>
</ul>
</div>
</div>
</div>
</umb-header>
<umb-tab-view>
<umb-tab id="tab{{tab.id}}" rel="{{tab.id}}" ng-repeat="tab in content.tabs">
<div class="umb-pane">
<umb-property
property="property"
ng-repeat="property in tab.properties">
<umb-editor model="property"></umb-editor>
</umb-property>
</div>
</umb-tab>
</umb-tab-view>
</umb-panel>
</form>

View File

@@ -7,13 +7,43 @@
* The controller for the content editor
*/
function DataTypeEditController($scope, $routeParams, $location, dataTypeResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper) {
//set up the standard data type props
function createDisplayProps() {
$scope.properties = {
selectedEditor: {
alias: "selectedEditor",
description: "Select a property editor",
label: "Property editor"
},
selectedEditorId: {
alias: "selectedEditorId",
label: "Property editor GUID"
}
};
}
//setup the pre-values as props
function createPreValueProps(preVals) {
$scope.preValues = [];
for (var i = 0; i < preVals.length; i++) {
$scope.preValues.push({
hideLabel: preVals[i].hideLabel,
alias: preVals[i].key,
description: preVals[i].description,
label: preVals[i].label,
view: preVals[i].view,
});
}
}
if ($routeParams.create) {
//we are creating so get an empty content item
dataTypeResource.getScaffold($routeParams.id, $routeParams.doctype)
.then(function(data) {
$scope.loaded = true;
$scope.content = data;
createDisplayProps();
});
}
else {
@@ -22,6 +52,8 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc
.then(function(data) {
$scope.loaded = true;
$scope.content = data;
createDisplayProps();
createPreValueProps($scope.content.preValues);
//in one particular special case, after we've created a new item we redirect back to the edit
// route but there might be server validation errors in the collection which we need to display
@@ -34,6 +66,8 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc
//ensure there is a form object assigned.
var currentForm = angularHelper.getRequiredCurrentForm($scope);
//TODO: We need to handle the dynamic loading of the pre-value editor view whenever the drop down changes!
$scope.save = function (cnt) {
$scope.$broadcast("saving", { scope: $scope });
@@ -42,12 +76,12 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc
serverValidationManager.reset();
dataTypeResource.save(cnt, $routeParams.create, $scope.files)
dataTypeResource.save(cnt, $routeParams.create)
.then(function (data) {
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
newContent: data
});
//TODO: SD: I need to finish this on monday!
alert("Woot!");
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
});
@@ -55,4 +89,4 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc
}
angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);
angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);

View File

@@ -1,4 +1,4 @@
<div ng-form name="contentForm" ng-show="loaded">
<form novalidate name="contentForm" ng-show="loaded" ng-submit="save(content)">
<umb-panel ng-controller="Umbraco.Editors.DataType.EditController" val-show-validation>
<umb-header>
@@ -13,28 +13,44 @@
<div class="btn-group">
<a class="btn btn-success" href="#" ng-click="save(content)" data-shortcut="ctrl+s"
prevent-default>Save</a>
<button type="submit" class="btn btn-success" href="#">Save</button>
</div>
</div>
</div>
</umb-header>
<umb-tab-view>
<umb-tab id="tab0" rel="0">
<div class="umb-panel-body umb-scrollable row-fluid" auto-scale="1">
<div class="tab-content form-horizontal">
<div class="umb-pane">
<umb-property
property="property"
ng-repeat="property in properties">
<umb-editor model="property"></umb-editor>
</umb-property>
</div>
<umb-property property="properties.selectedEditor">
<div>
<select name="selectedEditor"
ng-model="content.selectedEditor"
required
ng-options="e.editorId as e.name for e in content.availableEditors"></select>
<span class="help-inline" val-msg-for="selectedEditor" val-toggle-msg="required">Required</span>
</div>
</umb-tab>
</umb-tab-view>
</umb-property>
<umb-property property="properties.selectedEditorId">
<div>{{content.selectedEditor}}</div>
</umb-property>
<hr />
<umb-property
property="preValue"
ng-repeat="preValue in preValues">
<umb-editor model="preValue" is-pre-value="true"></umb-editor>
</umb-property>
</div>
</div>
</div>
</umb-panel>
</div>
</form>

View File

@@ -1,4 +1,4 @@
<ng-form name="contentForm" ng-show="loaded">
<form novalidate name="contentForm" ng-show="loaded" ng-submit="save(content)">
<umb-panel ng-controller="Umbraco.Editors.Media.EditController" val-show-validation>
<umb-header tabs="content.tabs">
@@ -9,8 +9,7 @@
<div class="btn-group">
<a class="btn btn-success" href="#" ng-click="save(content)" data-shortcut="ctrl+s"
prevent-default>Save</a>
<button type="submit" class="btn btn-success" href="#">Save</button>
</div>
</div>
@@ -32,4 +31,4 @@
</umb-tab>
</umb-tab-view>
</umb-panel>
</ng-form>
</form>

View File

@@ -0,0 +1,7 @@
<div>
<input name="requiredField" type="text" id="{{model.alias}}" class="umb-textstring span7 textstring"
ng-model="model.value"
required />
<span class="help-inline" val-msg-for="requiredField" val-toggle-msg="required">Required</span>
</div>

View File

@@ -1,12 +1,12 @@
<div>
<p>Enter a numeric value</p>
<input type="text" ng-model="model.value" id="{{model.alias}}" class="umb-textstring span7 textstring"
<input name="myInput" type="text" ng-model="model.value" class="umb-textstring span7 textstring"
required
val-regex="^\d*$"
val-server="value" />
<span class="help-inline" val-msg-for="regex" val-toggle-msg="required">Required!</span>
<span class="help-inline" val-msg-for="regex" val-toggle-msg="valRegex">The value entered is not a number</span>
<span class="help-inline" val-msg-for="regex" val-toggle-msg="valServer">A server error occurred</span>
<span class="help-inline" val-msg-for="myInput" val-toggle-msg="required">Required!</span>
<span class="help-inline" val-msg-for="myInput" val-toggle-msg="valRegex">The value entered is not a number</span>
<span class="help-inline" val-msg-for="myInput" val-toggle-msg="valServer">A server error occurred</span>
</div>

View File

@@ -3,6 +3,9 @@
{
id: "0BA0F832-D759-4526-9B3E-94BBFC98F92E",
name: "Regex",
defaultConfig: {
regexStatement: "^\\d*$"
},
editor: {
view: "~/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html",
validation: [
@@ -15,8 +18,20 @@
}
]
},
preValues: {
view: "myPreValues1"
preValueEditor: {
fields: [
{
label: "Regular expression",
description: "Enter a regular expression to use to validate this editor",
key: "regex",
view: "~/App_Plugins/MyPackage/PropertyEditors/Views/regexStatement.html",
validation: [
{
type: "Required"
}
]
}
]
}
},
{

View File

@@ -4,7 +4,7 @@
</p>
<input name="myPackage_postcode" type="text" ng-model="model.value"
val-server="value"
val-postcode="config.country"/>
val-postcode="model.config.country"/>
<span class="help-inline" val-msg-for="myPackage_postcode" val-toggle-msg="valServer">{{propertyForm.myPackage_postcode.errorMsg}}</span>
<span class="help-inline" val-msg-for="myPackage_postcode" val-toggle-msg="valPostcode">{{propertyForm.myPackage_postcode.errorMsg}}</span>

View File

@@ -8,7 +8,7 @@
<input name="regex" id="regex"
type="text" ng-model="model.value"
required
val-regex="config"
val-regex="model.config[0]"
val-server="value" />
<span class="help-inline" val-msg-for="regex" val-toggle-msg="required">Required!</span>

View File

@@ -96,11 +96,11 @@ namespace Umbraco.Web
routeName = string.Format("umbraco-{0}-{1}-{2}", "api", area, controllerName);
if (id == null)
{
return url.Link(routeName, new {controller = controllerName, action = actionName, area = area});
return url.Link(routeName, new {controller = controllerName, action = actionName});
}
else
{
return url.Link(routeName, new { controller = controllerName, action = actionName, area = area, id = id });
return url.Link(routeName, new { controller = controllerName, action = actionName, id = id });
}
}
}

View File

@@ -9,15 +9,18 @@ using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Services;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees.Menu;
using umbraco;
using umbraco.BusinessLogic.Actions;
using umbraco.businesslogic;
using umbraco.interfaces;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Content, Constants.Trees.Content, "Content")]
[PluginController("UmbracoTrees")]
public class ContentTreeController : ContentTreeControllerBase
{
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)

View File

@@ -5,12 +5,15 @@ using System.Net;
using System.Net.Http.Formatting;
using System.Web.Http;
using Umbraco.Core;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees.Menu;
using umbraco.BusinessLogic.Actions;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, "Data Types")]
[PluginController("UmbracoTrees")]
public class DataTypeTreeController : TreeApiController
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)

View File

@@ -3,12 +3,15 @@ using System.Net.Http.Formatting;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees.Menu;
using umbraco.BusinessLogic.Actions;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Media, Constants.Trees.Media, "Media")]
[PluginController("UmbracoTrees")]
public class MediaTreeController : ContentTreeControllerBase
{
protected override int RecycleBinId