Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c264b0815 | |||
| 12ecc5d650 | |||
| 26dce2c99d | |||
| 4649811abd | |||
| 6af5795a1d | |||
| 68f6e27d20 | |||
| ef035df8b8 | |||
| 35e6a64e73 | |||
| 5bb6ffc654 | |||
| 30905a9442 | |||
| ccfec9e092 | |||
| a2007a96af | |||
| 0879bdb90e | |||
| 1f483bc95c | |||
| e1df9d7a00 | |||
| 41718cb7d5 | |||
| 75d6e02c63 | |||
| c1d09c2516 | |||
| ed429786ca | |||
| 88f108bf2e | |||
| 437bf6bea1 | |||
| 6eb678b821 | |||
| 4d3af9284f | |||
| 38d531894f | |||
| c434558378 | |||
| 777276d2e9 | |||
| b63ff54c19 | |||
| 96f1189fc6 | |||
| 2250389cc2 | |||
| 6a2b6ba75b | |||
| 0a43530852 |
@@ -157,6 +157,7 @@ build.tmp/
|
||||
build/hooks/
|
||||
build/temp/
|
||||
|
||||
/src/Umbraco.Web.UI.Client/src/common/**/build/temp/
|
||||
|
||||
|
||||
# eof
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Mapping
|
||||
{
|
||||
@@ -112,6 +113,18 @@ namespace Umbraco.Core.Mapping
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Maps an enumerable of source objects to a new list of target objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
|
||||
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
|
||||
/// <param name="source">The source objects.</param>
|
||||
/// <returns>A list containing the target objects.</returns>
|
||||
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
|
||||
{
|
||||
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace Umbraco.Core.Mapping
|
||||
if (ctor != null && map != null)
|
||||
{
|
||||
// register (for next time) and do it now (for this time)
|
||||
object NCtor(object s, MapperContext c) => MapEnumerable<TTarget>((IEnumerable) s, targetGenericArg, ctor, map, c);
|
||||
object NCtor(object s, MapperContext c) => MapEnumerableInternal<TTarget>((IEnumerable) s, targetGenericArg, ctor, map, c);
|
||||
DefineCtors(sourceType)[targetType] = NCtor;
|
||||
DefineMaps(sourceType)[targetType] = Identity;
|
||||
return (TTarget) NCtor(source, context);
|
||||
@@ -241,7 +241,7 @@ namespace Umbraco.Core.Mapping
|
||||
throw new InvalidOperationException($"Don't know how to map {sourceType.FullName} to {targetType.FullName}.");
|
||||
}
|
||||
|
||||
private TTarget MapEnumerable<TTarget>(IEnumerable source, Type targetGenericArg, Func<object, MapperContext, object> ctor, Action<object, object, MapperContext> map, MapperContext context)
|
||||
private TTarget MapEnumerableInternal<TTarget>(IEnumerable source, Type targetGenericArg, Func<object, MapperContext, object> ctor, Action<object, object, MapperContext> map, MapperContext context)
|
||||
{
|
||||
var targetList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(targetGenericArg));
|
||||
|
||||
@@ -379,6 +379,46 @@ namespace Umbraco.Core.Mapping
|
||||
throw new Exception("panic");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps an enumerable of source objects to a new list of target objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
|
||||
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
|
||||
/// <param name="source">The source objects.</param>
|
||||
/// <returns>A list containing the target objects.</returns>
|
||||
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
|
||||
{
|
||||
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps an enumerable of source objects to a new list of target objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
|
||||
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
|
||||
/// <param name="source">The source objects.</param>
|
||||
/// <param name="f">A mapper context preparation method.</param>
|
||||
/// <returns>A list containing the target objects.</returns>
|
||||
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f)
|
||||
{
|
||||
var context = new MapperContext(this);
|
||||
f(context);
|
||||
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps an enumerable of source objects to a new list of target objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSourceElement">The type of the source objects.</typeparam>
|
||||
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
|
||||
/// <param name="source">The source objects.</param>
|
||||
/// <param name="context">A mapper context.</param>
|
||||
/// <returns>A list containing the target objects.</returns>
|
||||
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context)
|
||||
{
|
||||
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Tests.Mapping
|
||||
{
|
||||
@@ -11,11 +13,11 @@ namespace Umbraco.Tests.Mapping
|
||||
[Test]
|
||||
public void SimpleMap()
|
||||
{
|
||||
var profiles = new MapDefinitionCollection(new IMapDefinition[]
|
||||
var definitions = new MapDefinitionCollection(new IMapDefinition[]
|
||||
{
|
||||
new Profile1(),
|
||||
new MapperDefinition1(),
|
||||
});
|
||||
var mapper = new UmbracoMapper(profiles);
|
||||
var mapper = new UmbracoMapper(definitions);
|
||||
|
||||
var thing1 = new Thing1 { Value = "value" };
|
||||
var thing2 = mapper.Map<Thing1, Thing2>(thing1);
|
||||
@@ -36,11 +38,11 @@ namespace Umbraco.Tests.Mapping
|
||||
[Test]
|
||||
public void EnumerableMap()
|
||||
{
|
||||
var profiles = new MapDefinitionCollection(new IMapDefinition[]
|
||||
var definitions = new MapDefinitionCollection(new IMapDefinition[]
|
||||
{
|
||||
new Profile1(),
|
||||
new MapperDefinition1(),
|
||||
});
|
||||
var mapper = new UmbracoMapper(profiles);
|
||||
var mapper = new UmbracoMapper(definitions);
|
||||
|
||||
var thing1A = new Thing1 { Value = "valueA" };
|
||||
var thing1B = new Thing1 { Value = "valueB" };
|
||||
@@ -58,16 +60,23 @@ namespace Umbraco.Tests.Mapping
|
||||
Assert.AreEqual(2, thing2.Count);
|
||||
Assert.AreEqual("valueA", thing2[0].Value);
|
||||
Assert.AreEqual("valueB", thing2[1].Value);
|
||||
|
||||
thing2 = mapper.MapEnumerable<Thing1, Thing2>(thing1).ToList();
|
||||
|
||||
Assert.IsNotNull(thing2);
|
||||
Assert.AreEqual(2, thing2.Count);
|
||||
Assert.AreEqual("valueA", thing2[0].Value);
|
||||
Assert.AreEqual("valueB", thing2[1].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InheritedMap()
|
||||
{
|
||||
var profiles = new MapDefinitionCollection(new IMapDefinition[]
|
||||
var definitions = new MapDefinitionCollection(new IMapDefinition[]
|
||||
{
|
||||
new Profile1(),
|
||||
new MapperDefinition1(),
|
||||
});
|
||||
var mapper = new UmbracoMapper(profiles);
|
||||
var mapper = new UmbracoMapper(definitions);
|
||||
|
||||
var thing3 = new Thing3 { Value = "value" };
|
||||
var thing2 = mapper.Map<Thing3, Thing2>(thing3);
|
||||
@@ -85,6 +94,20 @@ namespace Umbraco.Tests.Mapping
|
||||
Assert.AreEqual("value", thing2.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CollectionsMap()
|
||||
{
|
||||
var definitions = new MapDefinitionCollection(new IMapDefinition[]
|
||||
{
|
||||
new MapperDefinition2(),
|
||||
});
|
||||
var mapper = new UmbracoMapper(definitions);
|
||||
|
||||
// can map a PropertyCollection
|
||||
var source = new PropertyCollection();
|
||||
var target = mapper.Map<IEnumerable<ContentPropertyDto>>(source);
|
||||
}
|
||||
|
||||
private class Thing1
|
||||
{
|
||||
public string Value { get; set; }
|
||||
@@ -98,7 +121,7 @@ namespace Umbraco.Tests.Mapping
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
private class Profile1 : IMapDefinition
|
||||
private class MapperDefinition1 : IMapDefinition
|
||||
{
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
@@ -110,5 +133,16 @@ namespace Umbraco.Tests.Mapping
|
||||
target.Value = source.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private class MapperDefinition2 : IMapDefinition
|
||||
{
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<Property, ContentPropertyDto>((source, context) => new ContentPropertyDto(), Map);
|
||||
}
|
||||
|
||||
private static void Map(Property source, ContentPropertyDto target, MapperContext context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ module.exports = {
|
||||
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
|
||||
},
|
||||
|
||||
ts: {
|
||||
services: { files: ["./src/common/services/**/*.ts"], out: "./src/common/services/build/temp/" }
|
||||
},
|
||||
|
||||
//selectors for copying all views into the build
|
||||
//processed in the views task
|
||||
views:{
|
||||
|
||||
@@ -6,5 +6,5 @@ var runSequence = require('run-sequence');
|
||||
|
||||
// Build - build the files ready for production
|
||||
gulp.task('build', function(cb) {
|
||||
runSequence(["js", "dependencies", "less", "views"], "test:unit", cb);
|
||||
runSequence(["typescript", "js", "dependencies", "less", "views"], "test:unit", cb);
|
||||
});
|
||||
|
||||
@@ -6,5 +6,5 @@ var runSequence = require('run-sequence');
|
||||
|
||||
// Dev - build the files ready for development and start watchers
|
||||
gulp.task('dev', function(cb) {
|
||||
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
|
||||
runSequence(["dependencies", "typescript", "js", "less", "views"], "watch", cb);
|
||||
});
|
||||
|
||||
@@ -9,5 +9,5 @@ gulp.task('fastdev', function(cb) {
|
||||
|
||||
global.isProd = false;
|
||||
|
||||
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
|
||||
runSequence(["dependencies", "typescript", "js", "less", "views"], "watch", cb);
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
var config = require('../config');
|
||||
var gulp = require('gulp');
|
||||
|
||||
var _ = require('lodash');
|
||||
var MergeStream = require('merge-stream');
|
||||
|
||||
var processTs = require('../util/processTypescript');
|
||||
|
||||
/**************************
|
||||
* Copies all angular JS files into their seperate umbraco.*.js file
|
||||
**************************/
|
||||
gulp.task('typescript', function () {
|
||||
|
||||
//we run multiple streams, so merge them all together
|
||||
var stream = new MergeStream();
|
||||
|
||||
stream.add(
|
||||
gulp.src(config.sources.globs.js)
|
||||
.pipe(gulp.dest(config.root + config.targets.js))
|
||||
);
|
||||
|
||||
// compile TS
|
||||
_.forEach(config.sources.ts, function (group) {
|
||||
if(group.files.length > 0)
|
||||
stream.add (processTs(group.files, group.out));
|
||||
});
|
||||
|
||||
return stream;
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
var config = require('../config');
|
||||
var gulp = require('gulp');
|
||||
|
||||
var ts = require('gulp-typescript');
|
||||
|
||||
module.exports = function(files, out) {
|
||||
|
||||
var task = gulp.src(files);
|
||||
|
||||
var tsProject = ts.createProject('tsconfig.json');
|
||||
|
||||
|
||||
// sort files in stream by path or any custom sort comparator
|
||||
task = task.pipe(tsProject())
|
||||
.pipe(gulp.dest(out));
|
||||
|
||||
|
||||
return task;
|
||||
|
||||
};
|
||||
+901
-106
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,7 @@
|
||||
"spectrum-colorpicker": "1.8.0",
|
||||
"tinymce": "4.9.2",
|
||||
"typeahead.js": "0.11.1",
|
||||
"typescript": "^3.2.1",
|
||||
"underscore": "1.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -64,6 +65,7 @@
|
||||
"gulp-postcss": "8.0.0",
|
||||
"gulp-rename": "1.4.0",
|
||||
"gulp-sort": "2.0.0",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"gulp-wrap": "0.14.0",
|
||||
"gulp-wrap-js": "0.4.1",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
declare var angular: any;
|
||||
declare var _: any;
|
||||
declare var $q: any;
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
interface StringConstructor {
|
||||
CreateGuid(): string;
|
||||
}
|
||||
+138
-191
@@ -1,191 +1,138 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.angularHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Some angular helper/extension methods
|
||||
*/
|
||||
function angularHelper($q) {
|
||||
return {
|
||||
|
||||
/**
|
||||
* Method used to re-run the $parsers for a given ngModel
|
||||
* @param {} scope
|
||||
* @param {} ngModel
|
||||
* @returns {}
|
||||
*/
|
||||
revalidateNgModel: function (scope, ngModel) {
|
||||
this.safeApply(scope, function() {
|
||||
angular.forEach(ngModel.$parsers, function (parser) {
|
||||
parser(ngModel.$viewValue);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute a list of promises sequentially. Unlike $q.all which executes all promises at once, this will execute them in sequence.
|
||||
* @param {} promises
|
||||
* @returns {}
|
||||
*/
|
||||
executeSequentialPromises: function (promises) {
|
||||
|
||||
//this is sequential promise chaining, it's not pretty but we need to do it this way.
|
||||
//$q.all doesn't execute promises in sequence but that's what we want to do here.
|
||||
|
||||
if (!angular.isArray(promises)) {
|
||||
throw "promises must be an array";
|
||||
}
|
||||
|
||||
//now execute them in sequence... sorry there's no other good way to do it with angular promises
|
||||
var j = 0;
|
||||
function pExec(promise) {
|
||||
j++;
|
||||
return promise.then(function (data) {
|
||||
if (j === promises.length) {
|
||||
return $q.when(data); //exit
|
||||
}
|
||||
else {
|
||||
return pExec(promises[j]); //recurse
|
||||
}
|
||||
});
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return pExec(promises[0]); //start the promise chain
|
||||
}
|
||||
else {
|
||||
return $q.when(true); // just exit, no promises to execute
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @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 && 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", "$submitted", "$pending"];
|
||||
|
||||
// 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.
|
||||
* NOTE: The properties has been added to the null form because we use them to get a form on a scope.
|
||||
*
|
||||
* @param {string} formName The form name to assign
|
||||
*/
|
||||
getNullForm: function (formName) {
|
||||
return {
|
||||
$error: {},
|
||||
$dirty: false,
|
||||
$pristine: true,
|
||||
$valid: true,
|
||||
$submitted: false,
|
||||
$pending: undefined,
|
||||
$addControl: angular.noop,
|
||||
$removeControl: angular.noop,
|
||||
$setValidity: angular.noop,
|
||||
$setDirty: angular.noop,
|
||||
$setPristine: angular.noop,
|
||||
$name: formName
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('angularHelper', angularHelper);
|
||||
/// <reference path="../definitions/global.d.ts" />
|
||||
|
||||
module umbraco.services {
|
||||
export class angularHelper {
|
||||
public revalidateNgModel(scope, ngModel) {
|
||||
this.safeApply(scope, function() {
|
||||
angular.forEach(ngModel.$parsers, function(parser) {
|
||||
parser(ngModel.$viewValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public executeSequentialPromises(promises) {
|
||||
//this is sequential promise chaining, it's not pretty but we need to do it this way.
|
||||
//$q.all doesn't execute promises in sequence but that's what we want to do here.
|
||||
|
||||
if (!angular.isArray(promises)) {
|
||||
throw "promises must be an array";
|
||||
}
|
||||
|
||||
//now execute them in sequence... sorry there's no other good way to do it with angular promises
|
||||
var j = 0;
|
||||
function pExec(promise) {
|
||||
j++;
|
||||
return promise.then(function(data) {
|
||||
if (j === promises.length) {
|
||||
return $q.when(data); //exit
|
||||
} else {
|
||||
return pExec(promises[j]); //recurse
|
||||
}
|
||||
});
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return pExec(promises[0]); //start the promise chain
|
||||
} else {
|
||||
return $q.when(true); // just exit, no promises to execute
|
||||
}
|
||||
}
|
||||
|
||||
public safeApply(scope, fn) {
|
||||
if (scope.$$phase || (scope.$root && scope.$root.$$phase)) {
|
||||
if (angular.isFunction(fn)) {
|
||||
fn();
|
||||
}
|
||||
} else {
|
||||
if (angular.isFunction(fn)) {
|
||||
scope.$apply(fn);
|
||||
} else {
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentForm(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",
|
||||
"$submitted",
|
||||
"$pending"
|
||||
];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
public getRequiredCurrentForm(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;
|
||||
}
|
||||
|
||||
public getNullForm(formName) {
|
||||
return {
|
||||
$error: {},
|
||||
$dirty: false,
|
||||
$pristine: true,
|
||||
$valid: true,
|
||||
$submitted: false,
|
||||
$pending: undefined,
|
||||
$addControl: angular.noop,
|
||||
$removeControl: angular.noop,
|
||||
$setValidity: angular.noop,
|
||||
$setDirty: angular.noop,
|
||||
$setPristine: angular.noop,
|
||||
$name: formName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.service("angularHelper", umbraco.services.angularHelper);
|
||||
@@ -32,6 +32,7 @@
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
namespace
|
||||
function appState(eventsService) {
|
||||
|
||||
//Define all variables here - we are never returning this objects so they cannot be publicly mutable
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
namespace umbraco.services.AppState {
|
||||
export interface GlobalState {
|
||||
showNavigation: boolean;
|
||||
touchDevice: boolean;
|
||||
showTray: boolean;
|
||||
stickyNavigation: boolean;
|
||||
navMode: boolean;
|
||||
isReady: boolean;
|
||||
isTablet: boolean;
|
||||
}
|
||||
|
||||
export interface SectionState {
|
||||
//The currently active section
|
||||
currentSection: string;
|
||||
showSearchResults: boolean;
|
||||
}
|
||||
|
||||
export interface TreeState {
|
||||
//The currently selected node
|
||||
selectedNode: any;
|
||||
//The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root.
|
||||
//We keep this reference so we can lookup nodes to interact with in the UI via the tree service
|
||||
currentRootNode: any;
|
||||
}
|
||||
|
||||
export interface MenuState {
|
||||
// The list of menu items to display
|
||||
menuActions: any;
|
||||
// The title to display in the context menu dialog
|
||||
dialogTitle: string;
|
||||
// The tree node that the ctx menu is launched for
|
||||
currentNode: any;
|
||||
// Whether the menu's dialog is being shown or not
|
||||
showMenuDialog: boolean;
|
||||
// Whether the menu's dialog can be hidden or not
|
||||
allowHideMenuDialog: boolean;
|
||||
// The dialogs template
|
||||
dialogTemplateUrl: string;
|
||||
//Whether the context menu is being shown or not
|
||||
showMenu: boolean;
|
||||
}
|
||||
|
||||
export interface SearchState {
|
||||
//Whether the search is being shown or not
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export interface DrawerState {
|
||||
//this view to show
|
||||
view: string;
|
||||
// bind custom values to the drawer
|
||||
model: any;
|
||||
//Whether the drawer is being shown or not
|
||||
showDrawer: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Tracks the various application state variables when working in the back office, raises events when state changes.
|
||||
*
|
||||
* ##Samples
|
||||
*
|
||||
* ####Subscribe to global state changes:
|
||||
*
|
||||
* <pre>
|
||||
* scope.showTree = appState.getGlobalState("showNavigation");
|
||||
*
|
||||
* eventsService.on("appState.globalState.changed", function (e, args) {
|
||||
* if (args.key === "showNavigation") {
|
||||
* scope.showTree = args.value;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* ####Subscribe to section-state changes
|
||||
*
|
||||
* <pre>
|
||||
* scope.currentSection = appState.getSectionState("currentSection");
|
||||
*
|
||||
* eventsService.on("appState.sectionState.changed", function (e, args) {
|
||||
* if (args.key === "currentSection") {
|
||||
* scope.currentSection = args.value;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
export class AppState {
|
||||
private eventsService: any;
|
||||
|
||||
constructor(eventsService) {
|
||||
this.eventsService = eventsService;
|
||||
}
|
||||
//Define all variables here - we are never returning this objects so they cannot be publicly mutable
|
||||
// changed, we only expose methods to interact with the values.
|
||||
|
||||
private globalState: GlobalState = {
|
||||
showNavigation: null,
|
||||
touchDevice: null,
|
||||
showTray: null,
|
||||
stickyNavigation: null,
|
||||
navMode: null,
|
||||
isReady: null,
|
||||
isTablet: null
|
||||
};
|
||||
|
||||
private sectionState: SectionState = {
|
||||
//The currently active section
|
||||
currentSection: null,
|
||||
showSearchResults: null
|
||||
};
|
||||
|
||||
private treeState: TreeState = {
|
||||
//The currently selected node
|
||||
selectedNode: null,
|
||||
//The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root.
|
||||
//We keep this reference so we can lookup nodes to interact with in the UI via the tree service
|
||||
currentRootNode: null
|
||||
};
|
||||
|
||||
private menuState: MenuState = {
|
||||
// The list of menu items to display
|
||||
menuActions: null,
|
||||
// The title to display in the context menu dialog
|
||||
dialogTitle: null,
|
||||
// The tree node that the ctx menu is launched for
|
||||
currentNode: null,
|
||||
// Whether the menu's dialog is being shown or not
|
||||
showMenuDialog: null,
|
||||
// Whether the menu's dialog can be hidden or not
|
||||
allowHideMenuDialog: true,
|
||||
// The dialogs template
|
||||
dialogTemplateUrl: null,
|
||||
//Whether the context menu is being shown or not
|
||||
showMenu: null
|
||||
};
|
||||
|
||||
private searchState: SearchState = {
|
||||
//Whether the search is being shown or not
|
||||
show: null
|
||||
};
|
||||
|
||||
private drawerState: DrawerState = {
|
||||
//this view to show
|
||||
view: null,
|
||||
// bind custom values to the drawer
|
||||
model: null,
|
||||
//Whether the drawer is being shown or not
|
||||
showDrawer: null
|
||||
};
|
||||
|
||||
/** function to validate and set the state on a state object */
|
||||
// Using scary and rare index types there (see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types)
|
||||
private setState<T, K extends keyof T>(
|
||||
stateObj: T,
|
||||
key: string,
|
||||
value: any,
|
||||
stateObjName: string
|
||||
) {
|
||||
if (!_.has(stateObj, key)) {
|
||||
throw "The variable " +
|
||||
key +
|
||||
" does not exist in " +
|
||||
stateObjName;
|
||||
}
|
||||
var changed = stateObj[key] !== value;
|
||||
stateObj[key] = value;
|
||||
if (changed) {
|
||||
this.eventsService.emit(
|
||||
"appState." + stateObjName + ".changed",
|
||||
{
|
||||
key: key,
|
||||
value: value
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** function to validate and set the state on a state object */
|
||||
// Using scary and rare index types there (see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types)
|
||||
private getState<T, K extends keyof T>(
|
||||
stateObj: T,
|
||||
key: K,
|
||||
stateObjName: string
|
||||
): T[K] {
|
||||
if (!_.has(stateObj, key)) {
|
||||
throw "The variable " +
|
||||
key +
|
||||
" does not exist in " +
|
||||
stateObjName;
|
||||
}
|
||||
return stateObj[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getGlobalState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current global state value by key - we do not return an object reference here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getGlobalState<K extends keyof GlobalState>(
|
||||
key: K
|
||||
): GlobalState[K] {
|
||||
return this.getState(this.globalState, key, "globalState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setGlobalState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a global state value by key
|
||||
*
|
||||
*/
|
||||
public setGlobalState<K extends keyof GlobalState>(
|
||||
key: K,
|
||||
value: GlobalState[K]
|
||||
) {
|
||||
this.setState(this.globalState, key, value, "globalState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getSectionState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current section state value by key - we do not return an object here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getSectionState<K extends keyof SectionState>(
|
||||
key: K
|
||||
): SectionState[K] {
|
||||
return this.getState(this.sectionState, key, "sectionState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setSectionState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a section state value by key
|
||||
*
|
||||
*/
|
||||
public setSectionState<K extends keyof SectionState>(
|
||||
key: K,
|
||||
value: SectionState[K]
|
||||
) {
|
||||
this.setState(this.sectionState, key, value, "sectionState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getTreeState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current tree state value by key - we do not return an object here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getTreeState<K extends keyof TreeState>(key: K): TreeState[K] {
|
||||
return this.getState(this.treeState, key, "treeState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setTreeState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a section state value by key
|
||||
*
|
||||
*/
|
||||
public setTreeState<K extends keyof TreeState>(
|
||||
key: K,
|
||||
value: TreeState[K]
|
||||
) {
|
||||
this.setState(this.treeState, key, value, "treeState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getMenuState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current menu state value by key - we do not return an object here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getMenuState<K extends keyof MenuState>(key: K): MenuState[K] {
|
||||
return this.getState(this.menuState, key, "menuState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setMenuState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a section state value by key
|
||||
*
|
||||
*/
|
||||
public setMenuState<K extends keyof MenuState>(
|
||||
key: K,
|
||||
value: MenuState[K]
|
||||
) {
|
||||
this.setState(this.menuState, key, value, "menuState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getSearchState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current search state value by key - we do not return an object here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getSearchState<K extends keyof SearchState>(
|
||||
key: K
|
||||
): SearchState[K] {
|
||||
return this.getState(this.searchState, key, "searchState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setSearchState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a section state value by key
|
||||
*
|
||||
*/
|
||||
public setSearchState<K extends keyof SearchState>(
|
||||
key: K,
|
||||
value: SearchState[K]
|
||||
) {
|
||||
this.setState(this.searchState, key, value, "searchState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#getDrawerState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the current drawer state value by key - we do not return an object here - we do NOT want this
|
||||
* to be publicly mutable and allow setting arbitrary values
|
||||
*
|
||||
*/
|
||||
public getDrawerState<K extends keyof DrawerState>(
|
||||
key: K
|
||||
): DrawerState[K] {
|
||||
return this.getState(this.drawerState, key, "drawerState");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.angularHelper#setDrawerState
|
||||
* @methodOf umbraco.services.appState
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Sets a drawer state value by key
|
||||
*
|
||||
*/
|
||||
public setDrawerState<K extends keyof DrawerState>(
|
||||
key: K,
|
||||
value: DrawerState[K]
|
||||
) {
|
||||
this.setState(this.drawerState, key, value, "drawerState");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.factory("appState", umbraco.services.AppState);
|
||||
+113
-60
@@ -1,3 +1,5 @@
|
||||
/// <reference path="notifications.service.ts" />
|
||||
/// <reference path="angularhelper.service.ts" />
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.formHelper
|
||||
@@ -7,8 +9,33 @@
|
||||
* A utility class used to streamline how forms are developed, to ensure that validation is check and displayed consistently and to ensure that the correct events
|
||||
* fire when they need to.
|
||||
*/
|
||||
function formHelper(angularHelper, serverValidationManager, notificationsService, overlayService) {
|
||||
return {
|
||||
namespace umbraco.services {
|
||||
export enum FormHelperEvents {
|
||||
FormSubmitting = "formSubmitting",
|
||||
FormSubmitted = "formSubmitted"
|
||||
}
|
||||
|
||||
export interface FormSubmissionOptions {
|
||||
scope: any;
|
||||
}
|
||||
|
||||
export class FormHelper {
|
||||
private angularHelper: umbraco.services.angularHelper;
|
||||
private serverValidationManager: any; // TODO: add the type to this once we've actually converted it
|
||||
private notificationsService: umbraco.services.Notifications.NotificationsService;
|
||||
private overlayService: any; // TODO: add the type to this once we've actually converted it
|
||||
|
||||
public constructor(
|
||||
angularHelper: umbraco.services.angularHelper,
|
||||
serverValidationManager: any,
|
||||
notificationsService: umbraco.services.Notifications.NotificationsService,
|
||||
overlayService: any
|
||||
) {
|
||||
this.angularHelper = angularHelper;
|
||||
this.serverValidationManager = serverValidationManager;
|
||||
this.notificationsService = notificationsService;
|
||||
this.overlayService = overlayService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -17,14 +44,13 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Called by controllers when submitting a form - this ensures that all client validation is checked,
|
||||
* Called by controllers when submitting a form - this ensures that all client validation is checked,
|
||||
* server validation is cleared, that the correct events execute and status messages are displayed.
|
||||
* This returns true if the form is valid, otherwise false if form submission cannot continue.
|
||||
*
|
||||
*
|
||||
* @param {object} args An object containing arguments for form submission
|
||||
*/
|
||||
submitForm: function (args) {
|
||||
|
||||
public submitForm(args: FormSubmissionData) {
|
||||
var currentForm;
|
||||
|
||||
if (!args) {
|
||||
@@ -35,14 +61,18 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
}
|
||||
if (!args.formCtrl) {
|
||||
//try to get the closest form controller
|
||||
currentForm = angularHelper.getRequiredCurrentForm(args.scope);
|
||||
}
|
||||
else {
|
||||
currentForm = this.angularHelper.getRequiredCurrentForm(
|
||||
args.scope
|
||||
);
|
||||
} else {
|
||||
currentForm = args.formCtrl;
|
||||
}
|
||||
|
||||
|
||||
//the first thing any form must do is broadcast the formSubmitting event
|
||||
args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action });
|
||||
args.scope.$broadcast(FormHelperEvents.FormSubmitting, {
|
||||
scope: args.scope,
|
||||
action: args.action
|
||||
});
|
||||
|
||||
//then check if the form is valid
|
||||
if (!args.skipValidation) {
|
||||
@@ -52,11 +82,11 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
}
|
||||
|
||||
//reset the server validations
|
||||
serverValidationManager.reset();
|
||||
|
||||
this.serverValidationManager.reset();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.formHelper#submitForm
|
||||
@@ -65,32 +95,36 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
*
|
||||
* @description
|
||||
* Called by controllers when a form has been successfully submitted, this ensures the correct events are raised.
|
||||
*
|
||||
*
|
||||
* @param {object} args An object containing arguments for form submission
|
||||
*/
|
||||
resetForm: function (args) {
|
||||
public resetForm(args: FormSubmissionOptions) {
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (!args.scope) {
|
||||
throw "args.scope cannot be null";
|
||||
}
|
||||
|
||||
args.scope.$broadcast("formSubmitted", { scope: args.scope });
|
||||
},
|
||||
|
||||
showNotifications: function (args) {
|
||||
if (!args || !args.notifications) {
|
||||
return false;
|
||||
}
|
||||
if (angular.isArray(args.notifications)) {
|
||||
for (var i = 0; i < args.notifications.length; i++) {
|
||||
notificationsService.showNotification(args.notifications[i]);
|
||||
args.scope.$broadcast(FormHelperEvents.FormSubmitted, {
|
||||
scope: args.scope
|
||||
});
|
||||
}
|
||||
|
||||
public showNotifications(args: ShowNotificationsArgs) {
|
||||
if (!args || !args.notifications) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
if (angular.isArray(args.notifications)) {
|
||||
for (var i = 0; i < args.notifications.length; i++) {
|
||||
this.notificationsService.showNotification(
|
||||
args.notifications[i]
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -101,31 +135,28 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
* @description
|
||||
* Needs to be called when a form submission fails, this will wire up all server validation errors in ModelState and
|
||||
* add the correct messages to the notifications. If a server error has occurred this will show a ysod.
|
||||
*
|
||||
*
|
||||
* @param {object} err The error object returned from the http promise
|
||||
*/
|
||||
handleError: function (err) {
|
||||
public handleError(err: any) {
|
||||
// TODO: Do we bother mapping out this error type into a TypeScript type?
|
||||
//When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors.
|
||||
//Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something).
|
||||
//Or, some strange server error
|
||||
if (err.status === 400) {
|
||||
//now we need to look through all the validation errors
|
||||
if (err.data && err.data.ModelState) {
|
||||
|
||||
//wire up the server validation errs
|
||||
this.handleServerValidation(err.data.ModelState);
|
||||
|
||||
//execute all server validation events and subscribers
|
||||
serverValidationManager.notifyAndClearAllSubscriptions();
|
||||
this.serverValidationManager.notifyAndClearAllSubscriptions();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
// TODO: All YSOD handling should be done with an interceptor
|
||||
overlayService.ysod(err);
|
||||
this.overlayService.ysod(err);
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -135,18 +166,17 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
*
|
||||
* @description
|
||||
* This wires up all of the server validation model state so that valServer and valServerField directives work
|
||||
*
|
||||
*
|
||||
* @param {object} err The error object returned from the http promise
|
||||
*/
|
||||
handleServerValidation: function (modelState) {
|
||||
public handleServerValidation(modelState: any) {
|
||||
for (var e in modelState) {
|
||||
|
||||
//This is where things get interesting....
|
||||
// We need to support validation for all editor types such as both the content and content type editors.
|
||||
// The Content editor ModelState is quite specific with the way that Properties are validated especially considering
|
||||
// that each property is a User Developer property editor.
|
||||
// The way that Content Type Editor ModelState is created is simply based on the ASP.Net validation data-annotations
|
||||
// system.
|
||||
// The way that Content Type Editor ModelState is created is simply based on the ASP.Net validation data-annotations
|
||||
// system.
|
||||
// So, to do this there's some special ModelState syntax we need to know about.
|
||||
// For Content Properties, which are user defined, we know that they will exist with a prefixed
|
||||
// ModelState of "_Properties.", so if we detect this, then we know it's for a content Property.
|
||||
@@ -159,10 +189,9 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
|
||||
var parts = e.split(".");
|
||||
|
||||
//Check if this is for content properties - specific to content/media/member editors because those are special
|
||||
//Check if this is for content properties - specific to content/media/member editors because those are special
|
||||
// user defined properties with custom controls.
|
||||
if (parts.length > 1 && parts[0] === "_Properties") {
|
||||
|
||||
var propertyAlias = parts[1];
|
||||
|
||||
var culture = null;
|
||||
@@ -177,23 +206,47 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
//if it contains 3 '.' then we will wire it up to a property's html field
|
||||
if (parts.length > 3) {
|
||||
//add an error with a reference to the field for which the validation belongs too
|
||||
serverValidationManager.addPropertyError(propertyAlias, culture, parts[3], modelState[e][0]);
|
||||
}
|
||||
else {
|
||||
this.serverValidationManager.addPropertyError(
|
||||
propertyAlias,
|
||||
culture,
|
||||
parts[3],
|
||||
modelState[e][0]
|
||||
);
|
||||
} else {
|
||||
//add a generic error for the property, no reference to a specific html field
|
||||
serverValidationManager.addPropertyError(propertyAlias, culture, "", modelState[e][0]);
|
||||
this.serverValidationManager.addPropertyError(
|
||||
propertyAlias,
|
||||
culture,
|
||||
"",
|
||||
modelState[e][0]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
//Everthing else is just a 'Field'... the field name could contain any level of 'parts' though, for example:
|
||||
// Groups[0].Properties[2].Alias
|
||||
serverValidationManager.addFieldError(e, modelState[e][0]);
|
||||
this.serverValidationManager.addFieldError(
|
||||
e,
|
||||
modelState[e][0]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface FormSubmissionData {
|
||||
scope: any;
|
||||
formCtrl?: any;
|
||||
skipValidation?: boolean;
|
||||
action: any;
|
||||
}
|
||||
|
||||
export interface ShowNotificationsArgs {
|
||||
notifications: Array<
|
||||
umbraco.services.Notifications.GenericNotification
|
||||
>;
|
||||
}
|
||||
}
|
||||
angular.module('umbraco.services').factory('formHelper', formHelper);
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.factory("formHelper", umbraco.services.FormHelper);
|
||||
@@ -1,300 +0,0 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.notificationsService
|
||||
*
|
||||
* @requires $rootScope
|
||||
* @requires $timeout
|
||||
* @requires angularHelper
|
||||
*
|
||||
* @description
|
||||
* Application-wide service for handling notifications, the umbraco application
|
||||
* maintains a single collection of notications, which the UI watches for changes.
|
||||
* By default when a notication is added, it is automaticly removed 7 seconds after
|
||||
* This can be changed on add()
|
||||
*
|
||||
* ##usage
|
||||
* To use, simply inject the notificationsService into any controller that needs it, and make
|
||||
* sure the umbraco.services module is accesible - which it should be by default.
|
||||
*
|
||||
* <pre>
|
||||
* notificationsService.success("Document Published", "hooraaaay for you!");
|
||||
* notificationsService.error("Document Failed", "booooh");
|
||||
* </pre>
|
||||
*/
|
||||
angular.module('umbraco.services')
|
||||
.factory('notificationsService', function ($rootScope, $timeout, angularHelper) {
|
||||
|
||||
var nArray = [];
|
||||
function setViewPath(view){
|
||||
if(view.indexOf('/') < 0)
|
||||
{
|
||||
view = "views/common/notifications/" + view;
|
||||
}
|
||||
|
||||
if(view.indexOf('.html') < 0)
|
||||
{
|
||||
view = view + ".html";
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
var service = {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#add
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Lower level api for adding notifcations, support more advanced options
|
||||
* @param {Object} item The notification item
|
||||
* @param {String} item.headline Short headline
|
||||
* @param {String} item.message longer text for the notication, trimmed after 200 characters, which can then be exanded
|
||||
* @param {String} item.type Notification type, can be: "success","warning","error" or "info"
|
||||
* @param {String} item.url url to open when notification is clicked
|
||||
* @param {String} item.view path to custom view to load into the notification box
|
||||
* @param {Array} item.actions Collection of button actions to append (label, func, cssClass)
|
||||
* @param {Boolean} item.sticky if set to true, the notification will not auto-close
|
||||
* @returns {Object} args notification object
|
||||
*/
|
||||
|
||||
add: function(item) {
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
|
||||
if(item.view){
|
||||
item.view = setViewPath(item.view);
|
||||
item.sticky = true;
|
||||
item.type = "form";
|
||||
item.headline = null;
|
||||
}
|
||||
|
||||
|
||||
//add a colon after the headline if there is a message as well
|
||||
if (item.message) {
|
||||
item.headline += ": ";
|
||||
if(item.message.length > 200) {
|
||||
item.sticky = true;
|
||||
}
|
||||
}
|
||||
|
||||
//we need to ID the item, going by index isn't good enough because people can remove at different indexes
|
||||
// whenever they want. Plus once we remove one, then the next index will be different. The only way to
|
||||
// effectively remove an item is by an Id.
|
||||
item.id = String.CreateGuid();
|
||||
|
||||
nArray.push(item);
|
||||
|
||||
if(!item.sticky) {
|
||||
$timeout(
|
||||
function() {
|
||||
var found = _.find(nArray, function(i) {
|
||||
return i.id === item.id;
|
||||
});
|
||||
if (found) {
|
||||
var index = nArray.indexOf(found);
|
||||
nArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
, 10000);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
hasView : function(view){
|
||||
if(!view){
|
||||
return _.find(nArray, function(notification){ return notification.view;});
|
||||
}else{
|
||||
view = setViewPath(view).toLowerCase();
|
||||
return _.find(nArray, function(notification){ return notification.view.toLowerCase() === view;});
|
||||
}
|
||||
},
|
||||
addView: function(view, args){
|
||||
var item = {
|
||||
args: args,
|
||||
view: view
|
||||
};
|
||||
|
||||
service.add(item);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#showNotification
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Shows a notification based on the object passed in, normally used to render notifications sent back from the server
|
||||
*
|
||||
* @returns {Object} args notification object
|
||||
*/
|
||||
showNotification: function(args) {
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (args.type === undefined || args.type === null) {
|
||||
throw "args.type cannot be null";
|
||||
}
|
||||
if (!args.header) {
|
||||
throw "args.header cannot be null";
|
||||
}
|
||||
|
||||
switch(args.type) {
|
||||
case 0:
|
||||
//save
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 1:
|
||||
//info
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 2:
|
||||
//error
|
||||
this.error(args.header, args.message);
|
||||
break;
|
||||
case 3:
|
||||
//success
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 4:
|
||||
//warning
|
||||
this.warning(args.header, args.message);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#success
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Adds a green success notication to the notications collection
|
||||
* This should be used when an operations *completes* without errors
|
||||
*
|
||||
* @param {String} headline Headline of the notification
|
||||
* @param {String} message longer text for the notication, trimmed after 200 characters, which can then be exanded
|
||||
* @returns {Object} notification object
|
||||
*/
|
||||
success: function (headline, message) {
|
||||
return service.add({ headline: headline, message: message, type: 'success', time: new Date() });
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#error
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Adds a red error notication to the notications collection
|
||||
* This should be used when an operations *fails* and could not complete
|
||||
*
|
||||
* @param {String} headline Headline of the notification
|
||||
* @param {String} message longer text for the notication, trimmed after 200 characters, which can then be exanded
|
||||
* @returns {Object} notification object
|
||||
*/
|
||||
error: function (headline, message) {
|
||||
return service.add({ headline: headline, message: message, type: 'error', time: new Date() });
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#warning
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Adds a yellow warning notication to the notications collection
|
||||
* This should be used when an operations *completes* but something was not as expected
|
||||
*
|
||||
*
|
||||
* @param {String} headline Headline of the notification
|
||||
* @param {String} message longer text for the notication, trimmed after 200 characters, which can then be exanded
|
||||
* @returns {Object} notification object
|
||||
*/
|
||||
warning: function (headline, message) {
|
||||
return service.add({ headline: headline, message: message, type: 'warning', time: new Date() });
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#warning
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Adds a yellow warning notication to the notications collection
|
||||
* This should be used when an operations *completes* but something was not as expected
|
||||
*
|
||||
*
|
||||
* @param {String} headline Headline of the notification
|
||||
* @param {String} message longer text for the notication, trimmed after 200 characters, which can then be exanded
|
||||
* @returns {Object} notification object
|
||||
*/
|
||||
info: function (headline, message) {
|
||||
return service.add({ headline: headline, message: message, type: 'info', time: new Date() });
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#remove
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Removes a notification from the notifcations collection at a given index
|
||||
*
|
||||
* @param {Int} index index where the notication should be removed from
|
||||
*/
|
||||
remove: function (index) {
|
||||
if(angular.isObject(index)){
|
||||
var i = nArray.indexOf(index);
|
||||
angularHelper.safeApply($rootScope, function() {
|
||||
nArray.splice(i, 1);
|
||||
});
|
||||
}else{
|
||||
angularHelper.safeApply($rootScope, function() {
|
||||
nArray.splice(index, 1);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#removeAll
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Removes all notifications from the notifcations collection
|
||||
*/
|
||||
removeAll: function () {
|
||||
angularHelper.safeApply($rootScope, function() {
|
||||
nArray = [];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name umbraco.services.notificationsService#current
|
||||
* @propertyOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Returns an array of current notifications to display
|
||||
*
|
||||
* @returns {string} returns an array
|
||||
*/
|
||||
current: nArray,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.notificationsService#getCurrent
|
||||
* @methodOf umbraco.services.notificationsService
|
||||
*
|
||||
* @description
|
||||
* Method to return all notifications from the notifcations collection
|
||||
*/
|
||||
getCurrent: function(){
|
||||
return nArray;
|
||||
}
|
||||
};
|
||||
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,262 @@
|
||||
/// <reference path="../definitions/global.d.ts" />
|
||||
/// <reference path="angularhelper.service.ts" />
|
||||
/// <reference path="../definitions/string.ts" />
|
||||
|
||||
namespace umbraco.services.Notifications {
|
||||
// TODO: These are the same type really and should be merged
|
||||
export enum NotificationType {
|
||||
Info = "info",
|
||||
Error = "error",
|
||||
Success = "success",
|
||||
Warning = "warning",
|
||||
Form = "form"
|
||||
}
|
||||
|
||||
export enum GenericNotificationType {
|
||||
Save = 0,
|
||||
Info = 1,
|
||||
Error = 2,
|
||||
Success = 3,
|
||||
Warning = 4
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
id?: string;
|
||||
headline?: string;
|
||||
message?: string;
|
||||
type?: NotificationType;
|
||||
url?: string;
|
||||
view?: string;
|
||||
actions?: Array<any>;
|
||||
sticky?: boolean;
|
||||
time?: Date;
|
||||
args?: Array<any>;
|
||||
}
|
||||
|
||||
// TODO: These are only mildly differnt from the above, ideally they could be merged?
|
||||
export interface GenericNotification {
|
||||
header?: string;
|
||||
message?: string;
|
||||
type?: GenericNotificationType;
|
||||
url?: string;
|
||||
view?: string;
|
||||
actions?: Array<any>;
|
||||
sticky?: boolean;
|
||||
time?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.notificationsService
|
||||
*
|
||||
* @requires $rootScope
|
||||
* @requires $timeout
|
||||
* @requires angularHelper
|
||||
*
|
||||
* @description
|
||||
* Application-wide service for handling notifications, the umbraco application
|
||||
* maintains a single collection of notications, which the UI watches for changes.
|
||||
* By default when a notication is added, it is automaticly removed 7 seconds after
|
||||
* This can be changed on add()
|
||||
*
|
||||
* ##usage
|
||||
* To use, simply inject the notificationsService into any controller that needs it, and make
|
||||
* sure the umbraco.services module is accesible - which it should be by default.
|
||||
*
|
||||
* <pre>
|
||||
* notificationsService.success("Document Published", "hooraaaay for you!");
|
||||
* notificationsService.error("Document Failed", "booooh");
|
||||
* </pre>
|
||||
*/
|
||||
export class NotificationsService {
|
||||
private nArray: Array<any> = new Array<any>();
|
||||
|
||||
public current: Array<any> = this.nArray;
|
||||
|
||||
private $rootScope: any;
|
||||
private $timeout: any;
|
||||
private angularHelper: umbraco.services.angularHelper;
|
||||
|
||||
public constructor(
|
||||
$rootScope: any,
|
||||
$timeout: any,
|
||||
angularHelper: umbraco.services.angularHelper
|
||||
) {
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.angularHelper = angularHelper;
|
||||
}
|
||||
|
||||
public add(item: Notification) {
|
||||
var pete = item.message;
|
||||
this.angularHelper.safeApply(this.$rootScope, function() {
|
||||
if (item.view) {
|
||||
item.view = this.setViewPath(item.view);
|
||||
item.sticky = true;
|
||||
item.type = NotificationType.Form;
|
||||
item.headline = null;
|
||||
}
|
||||
|
||||
//add a colon after the headline if there is a message as well
|
||||
if (item.message) {
|
||||
item.headline += ": ";
|
||||
if (item.message.length > 200) {
|
||||
item.sticky = true;
|
||||
}
|
||||
}
|
||||
|
||||
//we need to ID the item, going by index isn't good enough because people can remove at different indexes
|
||||
// whenever they want. Plus once we remove one, then the next index will be different. The only way to
|
||||
// effectively remove an item is by an Id.
|
||||
item.id = String.CreateGuid();
|
||||
|
||||
this.nArray.push(item);
|
||||
|
||||
if (!item.sticky) {
|
||||
this.$timeout(function() {
|
||||
var found = _.find(this.nArray, function(i) {
|
||||
return i.id === item.id;
|
||||
});
|
||||
if (found) {
|
||||
var index = this.nArray.indexOf(found);
|
||||
this.nArray.splice(index, 1);
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
public hasView(view) {
|
||||
if (!view) {
|
||||
return _.find(this.nArray, function(notification) {
|
||||
return notification.view;
|
||||
});
|
||||
} else {
|
||||
view = this.setViewPath(view).toLowerCase();
|
||||
return _.find(this.nArray, function(notification) {
|
||||
return notification.view.toLowerCase() === view;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public addView(view: string, args: Array<any>) {
|
||||
var item = {
|
||||
args: args,
|
||||
view: view
|
||||
};
|
||||
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
public showNotification(notification: GenericNotification) {
|
||||
if (!notification) {
|
||||
throw "notification cannot be null";
|
||||
}
|
||||
if (notification.type === undefined || notification.type === null) {
|
||||
throw "notification.type cannot be null";
|
||||
}
|
||||
if (!notification.header) {
|
||||
throw "notification.header cannot be null";
|
||||
}
|
||||
|
||||
switch (notification.type) {
|
||||
case GenericNotificationType.Save:
|
||||
this.success(notification.header, notification.message);
|
||||
break;
|
||||
case GenericNotificationType.Info:
|
||||
this.success(notification.header, notification.message);
|
||||
break;
|
||||
case GenericNotificationType.Error:
|
||||
this.error(notification.header, notification.message);
|
||||
break;
|
||||
case GenericNotificationType.Success:
|
||||
this.success(notification.header, notification.message);
|
||||
break;
|
||||
case GenericNotificationType.Warning:
|
||||
this.warning(notification.header, notification.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public success(headline: string, message: string) {
|
||||
return this.add({
|
||||
headline: headline,
|
||||
message: message,
|
||||
type: NotificationType.Success,
|
||||
time: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
public error(headline: string, message: string) {
|
||||
return this.add({
|
||||
headline: headline,
|
||||
message: message,
|
||||
type: NotificationType.Error,
|
||||
time: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
public warning(headline: string, message: string) {
|
||||
return this.add({
|
||||
headline: headline,
|
||||
message: message,
|
||||
type: NotificationType.Warning,
|
||||
time: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
public info(headline: string, message: string) {
|
||||
return this.add({
|
||||
headline: headline,
|
||||
message: message,
|
||||
type: NotificationType.Info,
|
||||
time: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
public remove(index) {
|
||||
if (angular.isObject(index)) {
|
||||
var i = this.nArray.indexOf(index);
|
||||
this.angularHelper.safeApply(this.$rootScope, function() {
|
||||
this.nArray.splice(i, 1);
|
||||
});
|
||||
} else {
|
||||
this.angularHelper.safeApply(this.$rootScope, function() {
|
||||
this.nArray.splice(index, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removeAll() {
|
||||
this.angularHelper.safeApply(this.$rootScope, function() {
|
||||
this.nArray = [];
|
||||
});
|
||||
}
|
||||
|
||||
public getCurrent() {
|
||||
return this.nArray;
|
||||
}
|
||||
|
||||
private setViewPath(view: string): string {
|
||||
if (view.indexOf("/") < 0) {
|
||||
view = "views/common/notifications/" + view;
|
||||
}
|
||||
|
||||
if (view.indexOf(".html") < 0) {
|
||||
view = view + ".html";
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.service("notificationsService", [
|
||||
"$rootScope",
|
||||
"$timeout",
|
||||
"angularHelper",
|
||||
umbraco.services.Notifications.NotificationsService
|
||||
]);
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
@ngdoc service
|
||||
* @name umbraco.services.overlayService
|
||||
*
|
||||
* @description
|
||||
* <b>Added in Umbraco 8.0</b>. Application-wide service for handling overlays.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function overlayService(eventsService, backdropService) {
|
||||
|
||||
var currentOverlay = null;
|
||||
|
||||
function open(newOverlay) {
|
||||
|
||||
// prevent two open overlays at the same time
|
||||
if(currentOverlay) {
|
||||
close();
|
||||
}
|
||||
|
||||
var backdropOptions = {};
|
||||
var overlay = newOverlay;
|
||||
|
||||
// set the default overlay position to center
|
||||
if(!overlay.position) {
|
||||
overlay.position = "center";
|
||||
}
|
||||
|
||||
// use a default empty view if nothing is set
|
||||
if(!overlay.view) {
|
||||
overlay.view = "views/common/overlays/default/default.html";
|
||||
}
|
||||
|
||||
// option to disable backdrop clicks
|
||||
if(overlay.disableBackdropClick) {
|
||||
backdropOptions.disableEventsOnClick = true;
|
||||
}
|
||||
|
||||
overlay.show = true;
|
||||
backdropService.open(backdropOptions);
|
||||
currentOverlay = overlay;
|
||||
eventsService.emit("appState.overlay", overlay);
|
||||
}
|
||||
|
||||
function close() {
|
||||
backdropService.close();
|
||||
currentOverlay = null;
|
||||
eventsService.emit("appState.overlay", null);
|
||||
}
|
||||
|
||||
function ysod(error) {
|
||||
const overlay = {
|
||||
view: "views/common/overlays/ysod/ysod.html",
|
||||
error: error,
|
||||
close: function() {
|
||||
close();
|
||||
}
|
||||
};
|
||||
open(overlay);
|
||||
}
|
||||
|
||||
var service = {
|
||||
open: open,
|
||||
close: close,
|
||||
ysod: ysod
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco.services").factory("overlayService", overlayService);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
@ngdoc service
|
||||
* @name umbraco.services.overlayService
|
||||
*
|
||||
* @description
|
||||
* <b>Added in Umbraco 8.0</b>. Application-wide service for handling overlays.
|
||||
*/
|
||||
namespace umbraco.services.Overlays {
|
||||
// TODO: Move this to the appstate.service.js
|
||||
export enum AppState {
|
||||
Overlay = "appState.overlay"
|
||||
}
|
||||
|
||||
export interface Overlay {
|
||||
// TODO: These optional fields are a bit dirty, for instance for booleans I suspect its using the lack of them being on an object as them being false (more like falsey), ideally should have to set them so we can make them no optional and TypeScript will let us know about it
|
||||
position?: string;
|
||||
view: string;
|
||||
disableBackdropClick?: boolean;
|
||||
show?: boolean;
|
||||
error?: any;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface BackDropOptions {
|
||||
disableEventsOnClick: boolean;
|
||||
}
|
||||
|
||||
export class OverlayService {
|
||||
private backdropService: any;
|
||||
private eventsService: any;
|
||||
private currentOverlay: Overlay;
|
||||
|
||||
public constructor(eventsService: any, backdropService: any) {
|
||||
this.eventsService = eventsService;
|
||||
this.backdropService = backdropService;
|
||||
this.currentOverlay = null;
|
||||
}
|
||||
|
||||
public open(newOverlay: Overlay) {
|
||||
// prevent two open overlays at the same time
|
||||
if (this.currentOverlay) {
|
||||
close();
|
||||
}
|
||||
|
||||
var backdropOptions: BackDropOptions = {
|
||||
disableEventsOnClick: false
|
||||
};
|
||||
|
||||
var overlay: Overlay = newOverlay;
|
||||
|
||||
// set the default overlay position to center
|
||||
if (!overlay.position) {
|
||||
overlay.position = "center";
|
||||
}
|
||||
|
||||
// use a default empty view if nothing is set
|
||||
if (!overlay.view) {
|
||||
overlay.view = "views/common/overlays/default/default.html";
|
||||
}
|
||||
|
||||
// option to disable backdrop clicks
|
||||
if (overlay.disableBackdropClick) {
|
||||
backdropOptions.disableEventsOnClick = true;
|
||||
}
|
||||
|
||||
overlay.show = true;
|
||||
this.backdropService.open(backdropOptions);
|
||||
this.currentOverlay = overlay;
|
||||
this.eventsService.emit(AppState.Overlay, overlay);
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.backdropService.close();
|
||||
this.currentOverlay = null;
|
||||
this.eventsService.emit(AppState.Overlay, null);
|
||||
}
|
||||
|
||||
public ysod(error: any) {
|
||||
var overlay: Overlay = {
|
||||
view: "views/common/overlays/ysod/ysod.html",
|
||||
error: error,
|
||||
close: function() {
|
||||
close();
|
||||
}
|
||||
};
|
||||
this.open(overlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.factory("overlayService", umbraco.services.Overlays.OverlayService);
|
||||
@@ -1,37 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function overlayHelper() {
|
||||
|
||||
var numberOfOverlays = 0;
|
||||
|
||||
function registerOverlay() {
|
||||
numberOfOverlays++;
|
||||
return numberOfOverlays;
|
||||
}
|
||||
|
||||
function unregisterOverlay() {
|
||||
numberOfOverlays--;
|
||||
return numberOfOverlays;
|
||||
}
|
||||
|
||||
function getNumberOfOverlays() {
|
||||
return numberOfOverlays;
|
||||
}
|
||||
|
||||
var service = {
|
||||
numberOfOverlays: numberOfOverlays,
|
||||
registerOverlay: registerOverlay,
|
||||
unregisterOverlay: unregisterOverlay,
|
||||
getNumberOfOverlays: getNumberOfOverlays
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
|
||||
angular.module('umbraco.services').factory('overlayHelper', overlayHelper);
|
||||
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace umbraco.services {
|
||||
export class OverlayHelper {
|
||||
private numberOfOverlays: number = 0;
|
||||
|
||||
public registerOverlay() {
|
||||
this.numberOfOverlays++;
|
||||
return this.numberOfOverlays;
|
||||
}
|
||||
|
||||
public unregisterOverlay() {
|
||||
this.numberOfOverlays--;
|
||||
return this.numberOfOverlays;
|
||||
}
|
||||
|
||||
public getNumberOfOverlays() {
|
||||
return this.numberOfOverlays;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.factory("overlayHelper", umbraco.services.OverlayHelper);
|
||||
@@ -1,160 +0,0 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.searchService
|
||||
*
|
||||
*
|
||||
* @description
|
||||
* Service for handling the main application search, can currently search content, media and members
|
||||
*
|
||||
* ##usage
|
||||
* To use, simply inject the searchService into any controller that needs it, and make
|
||||
* sure the umbraco.services module is accesible - which it should be by default.
|
||||
*
|
||||
* <pre>
|
||||
* searchService.searchMembers({term: 'bob'}).then(function(results){
|
||||
* angular.forEach(results, function(result){
|
||||
* //returns:
|
||||
* {name: "name", id: 1234, menuUrl: "url", editorPath: "url", metaData: {}, subtitle: "/path/etc" }
|
||||
* })
|
||||
* var result =
|
||||
* })
|
||||
* </pre>
|
||||
*/
|
||||
angular.module('umbraco.services')
|
||||
.factory('searchService', function ($q, $log, entityResource, contentResource, umbRequestHelper, $injector, searchResultFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchMembers
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default member search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching members
|
||||
*/
|
||||
searchMembers: function (args) {
|
||||
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) {
|
||||
_.each(data, function (item) {
|
||||
searchResultFormatter.configureMemberResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchContent
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default internal content search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching content items
|
||||
*/
|
||||
searchContent: function (args) {
|
||||
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) {
|
||||
_.each(data, function (item) {
|
||||
searchResultFormatter.configureContentResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchMedia
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default media search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching media items
|
||||
*/
|
||||
searchMedia: function (args) {
|
||||
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) {
|
||||
_.each(data, function (item) {
|
||||
searchResultFormatter.configureMediaResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchAll
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches all available indexes and returns all results in one collection
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching items
|
||||
*/
|
||||
searchAll: function (args) {
|
||||
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return entityResource.searchAll(args.term, args.canceler).then(function (data) {
|
||||
|
||||
_.each(data, function (resultByType) {
|
||||
|
||||
//we need to format the search result data to include things like the subtitle, urls, etc...
|
||||
// this is done with registered angular services as part of the SearchableTreeAttribute, if that
|
||||
// is not found, than we format with the default formatter
|
||||
var formatterMethod = searchResultFormatter.configureDefaultResult;
|
||||
//check if a custom formatter is specified...
|
||||
if (resultByType.jsSvc) {
|
||||
var searchFormatterService = $injector.get(resultByType.jsSvc);
|
||||
if (searchFormatterService) {
|
||||
if (!resultByType.jsMethod) {
|
||||
resultByType.jsMethod = "format";
|
||||
}
|
||||
formatterMethod = searchFormatterService[resultByType.jsMethod];
|
||||
|
||||
if (!formatterMethod) {
|
||||
throw "The method " + resultByType.jsMethod + " on the angular service " + resultByType.jsSvc + " could not be found";
|
||||
}
|
||||
}
|
||||
}
|
||||
//now apply the formatter for each result
|
||||
_.each(resultByType.results, function (item) {
|
||||
formatterMethod.apply(this, [item, resultByType.treeAlias, resultByType.appAlias]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
// TODO: This doesn't do anything!
|
||||
setCurrent: function (sectionAlias) {
|
||||
|
||||
var currentSection = sectionAlias;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.searchService
|
||||
*
|
||||
*
|
||||
* @description
|
||||
* Service for handling the main application search, can currently search content, media and members
|
||||
*
|
||||
* ##usage
|
||||
* To use, simply inject the searchService into any controller that needs it, and make
|
||||
* sure the umbraco.services module is accesible - which it should be by default.
|
||||
*
|
||||
* <pre>
|
||||
* searchService.searchMembers({term: 'bob'}).then(function(results){
|
||||
* angular.forEach(results, function(result){
|
||||
* //returns:
|
||||
* {name: "name", id: 1234, menuUrl: "url", editorPath: "url", metaData: {}, subtitle: "/path/etc" }
|
||||
* })
|
||||
* var result =
|
||||
* })
|
||||
* </pre>
|
||||
*/
|
||||
namespace umbraco.services.Search {
|
||||
export interface SearchArgs {
|
||||
term: string;
|
||||
searchFrom?: any; // is this a typo? Search form?
|
||||
canceler?: any; // Shouldn't this be canceller?
|
||||
}
|
||||
|
||||
export enum SearchType {
|
||||
Members = "Member",
|
||||
Content = "Document",
|
||||
Media = "Media"
|
||||
}
|
||||
|
||||
export class SearchService {
|
||||
private $q: any;
|
||||
private $log: any;
|
||||
private entityResource: any;
|
||||
private contentResource: any;
|
||||
private umbRequestHelper: any;
|
||||
private $injector: any;
|
||||
private searchResultFormatter: any;
|
||||
|
||||
constructor(
|
||||
$q,
|
||||
$log,
|
||||
entityResource,
|
||||
contentResource,
|
||||
umbRequestHelper,
|
||||
$injector,
|
||||
searchResultFormatter
|
||||
) {
|
||||
this.$q = $q;
|
||||
this.entityResource = entityResource;
|
||||
this.contentResource = contentResource;
|
||||
this.umbRequestHelper = umbRequestHelper;
|
||||
this.$injector = $injector;
|
||||
this.searchResultFormatter = searchResultFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchMembers
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default member search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching members
|
||||
*/
|
||||
public searchMembers(args: SearchArgs) {
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return this.entityResource
|
||||
.search(args.term, SearchType.Members, args.searchFrom)
|
||||
.then(function(data) {
|
||||
_.each(data, function(item) {
|
||||
this.searchResultFormatter.configureMemberResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchContent
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default internal content search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching content items
|
||||
*/
|
||||
public searchContent(args: SearchArgs) {
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return this.entityResource
|
||||
.search(
|
||||
args.term,
|
||||
SearchType.Content,
|
||||
args.searchFrom,
|
||||
args.canceler
|
||||
)
|
||||
.then(function(data) {
|
||||
_.each(data, function(item) {
|
||||
this.searchResultFormatter.configureContentResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchMedia
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches the default media search index
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching media items
|
||||
*/
|
||||
public searchMedia(args: SearchArgs) {
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return this.entityResource
|
||||
.search(args.term, SearchType.Media, args.searchFrom)
|
||||
.then(function(data) {
|
||||
_.each(data, function(item) {
|
||||
this.searchResultFormatter.configureMediaResult(item);
|
||||
});
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.searchService#searchAll
|
||||
* @methodOf umbraco.services.searchService
|
||||
*
|
||||
* @description
|
||||
* Searches all available indexes and returns all results in one collection
|
||||
* @param {Object} args argument object
|
||||
* @param {String} args.term seach term
|
||||
* @returns {Promise} returns promise containing all matching items
|
||||
*/
|
||||
public searchAll(args: SearchArgs) {
|
||||
if (!args.term) {
|
||||
throw "args.term is required";
|
||||
}
|
||||
|
||||
return this.entityResource
|
||||
.searchAll(args.term, args.canceler)
|
||||
.then(function(data) {
|
||||
_.each(data, function(resultByType) {
|
||||
//we need to format the search result data to include things like the subtitle, urls, etc...
|
||||
// this is done with registered angular services as part of the SearchableTreeAttribute, if that
|
||||
// is not found, than we format with the default formatter
|
||||
var formatterMethod = this.searchResultFormatter
|
||||
.configureDefaultResult;
|
||||
//check if a custom formatter is specified...
|
||||
if (resultByType.jsSvc) {
|
||||
var searchFormatterService = this.$injector.get(
|
||||
resultByType.jsSvc
|
||||
);
|
||||
if (searchFormatterService) {
|
||||
if (!resultByType.jsMethod) {
|
||||
resultByType.jsMethod = "format";
|
||||
}
|
||||
formatterMethod =
|
||||
searchFormatterService[
|
||||
resultByType.jsMethod
|
||||
];
|
||||
|
||||
if (!formatterMethod) {
|
||||
throw "The method " +
|
||||
resultByType.jsMethod +
|
||||
" on the angular service " +
|
||||
resultByType.jsSvc +
|
||||
" could not be found";
|
||||
}
|
||||
}
|
||||
}
|
||||
//now apply the formatter for each result
|
||||
_.each(resultByType.results, function(item) {
|
||||
formatterMethod.apply(this, [
|
||||
item,
|
||||
resultByType.treeAlias,
|
||||
resultByType.appAlias
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: This doesn't do anything!
|
||||
public setCurrent(sectionAlias: string) {
|
||||
var currentSection = sectionAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module("umbraco.services")
|
||||
.factory("searchService", umbraco.services.Search.SearchService);
|
||||
+50
-43
@@ -1,75 +1,84 @@
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListController",
|
||||
function ($scope) {
|
||||
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.configItems = [];
|
||||
vm.viewItems = [];
|
||||
vm.changed = changed;
|
||||
|
||||
function init() {
|
||||
|
||||
//we can't really do anything if the config isn't an object
|
||||
|
||||
// currently the property editor will onyl work if our input is an object.
|
||||
if (angular.isObject($scope.model.config.items)) {
|
||||
|
||||
//now we need to format the items in the dictionary because we always want to have an array
|
||||
var configItems = [];
|
||||
// formatting the items in the dictionary into an array
|
||||
var sortedItems = [];
|
||||
var vals = _.values($scope.model.config.items);
|
||||
var keys = _.keys($scope.model.config.items);
|
||||
for (var i = 0; i < vals.length; i++) {
|
||||
configItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value });
|
||||
sortedItems.push({ key: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value});
|
||||
}
|
||||
|
||||
//ensure the items are sorted by the provided sort order
|
||||
configItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
|
||||
|
||||
sortedItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
|
||||
|
||||
vm.configItems = sortedItems;
|
||||
|
||||
if ($scope.model.value === null || $scope.model.value === undefined) {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
|
||||
updateViewModel(configItems);
|
||||
|
||||
|
||||
// update view model.
|
||||
generateViewModel($scope.model.value);
|
||||
|
||||
//watch the model.value in case it changes so that we can keep our view model in sync
|
||||
$scope.$watchCollection("model.value",
|
||||
function (newVal) {
|
||||
updateViewModel(configItems);
|
||||
});
|
||||
$scope.$watchCollection("model.value", updateViewModel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateViewModel(configItems) {
|
||||
|
||||
function updateViewModel(newVal) {
|
||||
|
||||
//get the checked vals from the view model
|
||||
var selectedVals = _.map(
|
||||
_.filter($scope.selectedItems,
|
||||
function(f) {
|
||||
return f.checked;
|
||||
}
|
||||
),
|
||||
function(m) {
|
||||
return m.value;
|
||||
var i = vm.configItems.length;
|
||||
while(i--) {
|
||||
|
||||
var item = vm.configItems[i];
|
||||
|
||||
// are this item the same in the model
|
||||
if (item.checked !== (newVal.indexOf(item.value) !== -1)) {
|
||||
|
||||
// if not lets update the full model.
|
||||
generateViewModel(newVal);
|
||||
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
//if the length is zero, then we are in sync, just exit.
|
||||
if (_.difference($scope.model.value, selectedVals).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.selectedItems = [];
|
||||
|
||||
}
|
||||
|
||||
function generateViewModel(newVal) {
|
||||
|
||||
vm.viewItems = [];
|
||||
|
||||
var iConfigItem;
|
||||
for (var i = 0; i < configItems.length; i++) {
|
||||
iConfigItem = configItems[i];
|
||||
var isChecked = _.contains($scope.model.value, iConfigItem.value);
|
||||
$scope.selectedItems.push({
|
||||
for (var i = 0; i < vm.configItems.length; i++) {
|
||||
iConfigItem = vm.configItems[i];
|
||||
var isChecked = _.contains(newVal, iConfigItem.value);
|
||||
vm.viewItems.push({
|
||||
checked: isChecked,
|
||||
key: iConfigItem.id,
|
||||
val: iConfigItem.value
|
||||
key: iConfigItem.key,
|
||||
value: iConfigItem.value
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function changed(model, value) {
|
||||
|
||||
var index = $scope.model.value.indexOf(value);
|
||||
|
||||
if (model) {
|
||||
if (model === true) {
|
||||
//if it doesn't exist in the model, then add it
|
||||
if (index < 0) {
|
||||
$scope.model.value.push(value);
|
||||
@@ -80,11 +89,9 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListContro
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$scope.selectedItems = [];
|
||||
$scope.changed = changed;
|
||||
|
||||
init();
|
||||
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="umb-property-editor umb-checkboxlist" ng-controller="Umbraco.PropertyEditors.CheckboxListController">
|
||||
<div class="umb-property-editor umb-checkboxlist" ng-controller="Umbraco.PropertyEditors.CheckboxListController as vm">
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="item in selectedItems track by item.key">
|
||||
<umb-checkbox name="{{model.alias}}" value="{{item.val}}" model="item.checked" text="{{item.val}}" on-change="changed(model, value)" required="model.validation.mandatory && !model.value.length"></umb-checkbox>
|
||||
<li ng-repeat="item in vm.viewItems track by item.key">
|
||||
<umb-checkbox name="{{::model.alias}}" value="{{::item.value}}" model="item.checked" text="{{::item.value}}" on-change="vm.changed(model, value)" required="vm.model.validation.mandatory && !vm.model.value.length"></umb-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"rootDir": "src"
|
||||
},
|
||||
//"include": [
|
||||
// "src/common/**/*.ts"
|
||||
//],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"compileOnSave": true,
|
||||
"allowJS": true
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/// <reference path="../definitions/global.d.ts" />
|
||||
/// <reference path="angularhelper.service.ts" />
|
||||
/// <reference path="../definitions/string.ts" />
|
||||
var umbraco;
|
||||
(function (umbraco) {
|
||||
var services;
|
||||
(function (services) {
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.notificationsService
|
||||
*
|
||||
* @requires $rootScope
|
||||
* @requires $timeout
|
||||
* @requires angularHelper
|
||||
*
|
||||
* @description
|
||||
* Application-wide service for handling notifications, the umbraco application
|
||||
* maintains a single collection of notications, which the UI watches for changes.
|
||||
* By default when a notication is added, it is automaticly removed 7 seconds after
|
||||
* This can be changed on add()
|
||||
*
|
||||
* ##usage
|
||||
* To use, simply inject the notificationsService into any controller that needs it, and make
|
||||
* sure the umbraco.services module is accesible - which it should be by default.
|
||||
*
|
||||
* <pre>
|
||||
* notificationsService.success("Document Published", "hooraaaay for you!");
|
||||
* notificationsService.error("Document Failed", "booooh");
|
||||
* </pre>
|
||||
*/
|
||||
class notificationsService {
|
||||
constructor($rootScope, $timeout, angularHelper) {
|
||||
this.nArray = new Array();
|
||||
this.current = this.nArray;
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.angularHelper = angularHelper;
|
||||
}
|
||||
add(item) {
|
||||
this.angularHelper.safeApply(this.$rootScope, function () {
|
||||
if (item.view) {
|
||||
item.view = this.setViewPath(item.view);
|
||||
item.sticky = true;
|
||||
item.type = "form";
|
||||
item.headline = null;
|
||||
}
|
||||
//add a colon after the headline if there is a message as well
|
||||
if (item.message) {
|
||||
item.headline += ": ";
|
||||
if (item.message.length > 200) {
|
||||
item.sticky = true;
|
||||
}
|
||||
}
|
||||
//we need to ID the item, going by index isn't good enough because people can remove at different indexes
|
||||
// whenever they want. Plus once we remove one, then the next index will be different. The only way to
|
||||
// effectively remove an item is by an Id.
|
||||
item.id = String.CreateGuid();
|
||||
this.nArray.push(item);
|
||||
if (!item.sticky) {
|
||||
this.$timeout(function () {
|
||||
var found = _.find(this.nArray, function (i) {
|
||||
return i.id === item.id;
|
||||
});
|
||||
if (found) {
|
||||
var index = this.nArray.indexOf(found);
|
||||
this.nArray.splice(index, 1);
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
hasView(view) {
|
||||
if (!view) {
|
||||
return _.find(this.nArray, function (notification) { return notification.view; });
|
||||
}
|
||||
else {
|
||||
view = this.setViewPath(view).toLowerCase();
|
||||
return _.find(this.nArray, function (notification) { return notification.view.toLowerCase() === view; });
|
||||
}
|
||||
}
|
||||
addView(view, args) {
|
||||
var item = {
|
||||
args: args,
|
||||
view: view
|
||||
};
|
||||
this.add(item);
|
||||
}
|
||||
showNotification(args) {
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (args.type === undefined || args.type === null) {
|
||||
throw "args.type cannot be null";
|
||||
}
|
||||
if (!args.header) {
|
||||
throw "args.header cannot be null";
|
||||
}
|
||||
switch (args.type) {
|
||||
case 0:
|
||||
//save
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 1:
|
||||
//info
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 2:
|
||||
//error
|
||||
this.error(args.header, args.message);
|
||||
break;
|
||||
case 3:
|
||||
//success
|
||||
this.success(args.header, args.message);
|
||||
break;
|
||||
case 4:
|
||||
//warning
|
||||
this.warning(args.header, args.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
success(headline, message) {
|
||||
return this.add({ headline: headline, message: message, type: 'success', time: new Date() });
|
||||
}
|
||||
error(headline, message) {
|
||||
return this.add({ headline: headline, message: message, type: 'error', time: new Date() });
|
||||
}
|
||||
warning(headline, message) {
|
||||
return this.add({ headline: headline, message: message, type: 'warning', time: new Date() });
|
||||
}
|
||||
info(headline, message) {
|
||||
return this.add({ headline: headline, message: message, type: 'info', time: new Date() });
|
||||
}
|
||||
remove(index) {
|
||||
if (angular.isObject(index)) {
|
||||
var i = this.nArray.indexOf(index);
|
||||
this.angularHelper.safeApply(this.$rootScope, function () {
|
||||
this.nArray.splice(i, 1);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.angularHelper.safeApply(this.$rootScope, function () {
|
||||
this.nArray.splice(index, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
removeAll() {
|
||||
this.angularHelper.safeApply(this.$rootScope, function () {
|
||||
this.nArray = [];
|
||||
});
|
||||
}
|
||||
getCurrent() {
|
||||
return this.nArray;
|
||||
}
|
||||
setViewPath(view) {
|
||||
if (view.indexOf('/') < 0) {
|
||||
view = "views/common/notifications/" + view;
|
||||
}
|
||||
if (view.indexOf('.html') < 0) {
|
||||
view = view + ".html";
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
services.notificationsService = notificationsService;
|
||||
})(services = umbraco.services || (umbraco.services = {}));
|
||||
})(umbraco || (umbraco = {}));
|
||||
angular.module('umbraco.services').service('notificationsService', ['$rootScope', '$timeout', 'angularHelper', umbraco.services.notificationsService]);
|
||||
@@ -190,7 +190,7 @@ namespace Umbraco.Web.Editors
|
||||
//get all user groups and map their default permissions to the AssignedUserGroupPermissions model.
|
||||
//we do this because not all groups will have true assigned permissions for this node so if they don't have assigned permissions, we need to show the defaults.
|
||||
|
||||
var defaultPermissionsByGroup = Mapper.Map<IEnumerable<AssignedUserGroupPermissions>>(allUserGroups).ToArray();
|
||||
var defaultPermissionsByGroup = Mapper.MapEnumerable<IUserGroup, AssignedUserGroupPermissions>(allUserGroups);
|
||||
|
||||
var defaultPermissionsAsDictionary = defaultPermissionsByGroup
|
||||
.ToDictionary(x => Convert.ToInt32(x.Id), x => x);
|
||||
|
||||
@@ -917,7 +917,7 @@ namespace Umbraco.Web.Editors
|
||||
.SelectMany(x => x.PropertyTypes)
|
||||
.DistinctBy(composition => composition.Alias);
|
||||
var filteredPropertyTypes = ExecutePostFilter(propertyTypes, postFilter);
|
||||
return Mapper.Map<IEnumerable<PropertyType>, IEnumerable<EntityBasic>>(filteredPropertyTypes);
|
||||
return Mapper.MapEnumerable<PropertyType, EntityBasic>(filteredPropertyTypes);
|
||||
|
||||
case UmbracoEntityTypes.PropertyGroup:
|
||||
|
||||
@@ -928,13 +928,13 @@ namespace Umbraco.Web.Editors
|
||||
.SelectMany(x => x.PropertyGroups)
|
||||
.DistinctBy(composition => composition.Name);
|
||||
var filteredpropertyGroups = ExecutePostFilter(propertyGroups, postFilter);
|
||||
return Mapper.Map<IEnumerable<PropertyGroup>, IEnumerable<EntityBasic>>(filteredpropertyGroups);
|
||||
return Mapper.MapEnumerable<PropertyGroup, EntityBasic>(filteredpropertyGroups);
|
||||
|
||||
case UmbracoEntityTypes.User:
|
||||
|
||||
var users = Services.UserService.GetAll(0, int.MaxValue, out _);
|
||||
var filteredUsers = ExecutePostFilter(users, postFilter);
|
||||
return Mapper.Map<IEnumerable<IUser>, IEnumerable<EntityBasic>>(filteredUsers);
|
||||
return Mapper.MapEnumerable<IUser, EntityBasic>(filteredUsers);
|
||||
|
||||
case UmbracoEntityTypes.Stylesheet:
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
var allLanguages = Services.LocalizationService.GetAllLanguages();
|
||||
|
||||
return Mapper.Map<IEnumerable<ILanguage>, IEnumerable<Language>>(allLanguages);
|
||||
return Mapper.MapEnumerable<ILanguage, Language>(allLanguages);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Web.Editors
|
||||
var dateQuery = sinceDate.HasValue ? SqlContext.Query<IAuditItem>().Where(x => x.CreateDate >= sinceDate) : null;
|
||||
var userId = Security.GetUserId().ResultOr(0);
|
||||
var result = Services.AuditService.GetPagedItemsByUser(userId, pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter:dateQuery);
|
||||
var mapped = Mapper.Map<IEnumerable<AuditLog>>(result);
|
||||
var mapped = Mapper.MapEnumerable<IAuditItem, AuditLog>(result);
|
||||
return new PagedResult<AuditLog>(totalRecords, pageNumber, pageSize)
|
||||
{
|
||||
Items = MapAvatarsAndNames(mapped)
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Umbraco.Web.Editors
|
||||
? redirectUrlService.GetAllRedirectUrls(page, pageSize, out resultCount)
|
||||
: redirectUrlService.SearchRedirectUrls(searchTerm, page, pageSize, out resultCount);
|
||||
|
||||
searchResult.SearchResults = Mapper.Map<IEnumerable<ContentRedirectUrl>>(redirects).ToArray();
|
||||
searchResult.SearchResults = Mapper.MapEnumerable<IRedirectUrl, ContentRedirectUrl>(redirects);
|
||||
searchResult.TotalCount = resultCount;
|
||||
searchResult.CurrentPage = page;
|
||||
searchResult.PageCount = ((int)resultCount + pageSize - 1) / pageSize;
|
||||
@@ -71,9 +71,10 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
var redirectUrlService = Services.RedirectUrlService;
|
||||
var redirects = redirectUrlService.GetContentRedirectUrls(guidIdi.Guid);
|
||||
redirectsResult.SearchResults = Mapper.Map<IEnumerable<ContentRedirectUrl>>(redirects).ToArray();
|
||||
var mapped = Mapper.MapEnumerable<IRedirectUrl, ContentRedirectUrl>(redirects);
|
||||
redirectsResult.SearchResults = mapped;
|
||||
//not doing paging 'yet'
|
||||
redirectsResult.TotalCount = redirects.Count();
|
||||
redirectsResult.TotalCount = mapped.Count();
|
||||
redirectsResult.CurrentPage = 1;
|
||||
redirectsResult.PageCount = 1;
|
||||
}
|
||||
|
||||
@@ -34,11 +34,11 @@ namespace Umbraco.Web.Editors
|
||||
if (string.IsNullOrWhiteSpace(relationTypeAlias) == false)
|
||||
{
|
||||
return
|
||||
Mapper.Map<IEnumerable<IRelation>, IEnumerable<RelationDisplay>>(
|
||||
Mapper.MapEnumerable<IRelation, RelationDisplay>(
|
||||
relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)));
|
||||
}
|
||||
|
||||
return Mapper.Map<IEnumerable<IRelation>, IEnumerable<RelationDisplay>>(relations);
|
||||
return Mapper.MapEnumerable<IRelation, RelationDisplay>(relations);
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Web.Editors
|
||||
var relations = Services.RelationService.GetByRelationTypeId(relationType.Id);
|
||||
|
||||
var display = Mapper.Map<IRelationType, RelationTypeDisplay>(relationType);
|
||||
display.Relations = Mapper.Map<IEnumerable<IRelation>, IEnumerable<RelationDisplay>>(relations);
|
||||
display.Relations = Mapper.MapEnumerable<IRelation, RelationDisplay>(relations);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
public IEnumerable<UserGroupBasic> GetUserGroups(bool onlyCurrentUserGroups = true)
|
||||
{
|
||||
var allGroups = Mapper.Map<IEnumerable<IUserGroup>, IEnumerable<UserGroupBasic>>(Services.UserService.GetAllUserGroups())
|
||||
var allGroups = Mapper.MapEnumerable<IUserGroup, UserGroupBasic>(Services.UserService.GetAllUserGroups())
|
||||
.ToList();
|
||||
|
||||
var isAdmin = Security.CurrentUser.IsAdmin();
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var paged = new PagedUserResult(total, pageNumber, pageSize)
|
||||
{
|
||||
Items = Mapper.Map<IEnumerable<UserBasic>>(result),
|
||||
Items = Mapper.MapEnumerable<IUser, UserBasic>(result),
|
||||
UserStates = Services.UserService.GetUserStates()
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(IContent source, ContentPropertyCollectionDto target, MapperContext context)
|
||||
{
|
||||
target.Properties = source.Properties.Select(context.Map<ContentPropertyDto>);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyDto>(source.Properties);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -AllowPreview -Errors -PersistedContent
|
||||
@@ -98,7 +98,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Variants = _contentVariantMapper.Map(source, context);
|
||||
|
||||
target.ContentDto = new ContentPropertyCollectionDto();
|
||||
target.ContentDto.Properties = source.Properties.Select(context.Map<ContentPropertyDto>);
|
||||
target.ContentDto.Properties = context.MapEnumerable<Property, ContentPropertyDto>(source.Properties);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Segment -Language
|
||||
@@ -127,7 +127,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Owner = _commonMapper.GetOwner(source, context);
|
||||
target.ParentId = source.ParentId;
|
||||
target.Path = source.Path;
|
||||
target.Properties = source.Properties.Select(context.Map<ContentPropertyBasic>);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyBasic>(source.Properties);
|
||||
target.SortOrder = source.SortOrder;
|
||||
target.State = _basicStateMapper.Map(source, context);
|
||||
target.Trashed = source.Trashed;
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.AllowCultureVariant = source.VariesByCulture();
|
||||
|
||||
//sync templates
|
||||
target.AllowedTemplates = source.AllowedTemplates.Select(context.Map<EntityBasic>).ToArray();
|
||||
target.AllowedTemplates = context.MapEnumerable<ITemplate, EntityBasic>(source.AllowedTemplates);
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
target.DefaultTemplate = context.Map<EntityBasic>(source.DefaultTemplate);
|
||||
@@ -312,7 +312,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Name = source.Name;
|
||||
target.SortOrder = source.SortOrder;
|
||||
|
||||
target.Properties = source.Properties.Select(context.Map<PropertyTypeDisplay>);
|
||||
target.Properties = context.MapEnumerable<PropertyTypeBasic, PropertyTypeDisplay>(source.Properties);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -ContentTypeId -ParentTabContentTypes -ParentTabContentTypeNames
|
||||
@@ -325,7 +325,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Name = source.Name;
|
||||
target.SortOrder = source.SortOrder;
|
||||
|
||||
target.Properties = source.Properties.Select(context.Map<MemberPropertyTypeDisplay>);
|
||||
target.Properties = context.MapEnumerable<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(source.Properties);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Editor -View -Config -ContentTypeId -ContentTypeName -Locked
|
||||
@@ -531,7 +531,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
MapTypeToDisplayBase(source, target);
|
||||
|
||||
target.Groups = source.Groups.Select(context.Map<PropertyGroupDisplay<TTargetPropertyType>>);
|
||||
target.Groups = context.MapEnumerable<PropertyGroupBasic<TSourcePropertyType>, PropertyGroupDisplay<TTargetPropertyType>>(source.Groups);
|
||||
}
|
||||
|
||||
private IEnumerable<string> MapLockedCompositions(IContentTypeComposition source)
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariantDisplay>(); //this should never happen
|
||||
|
||||
var langs = context.Map<IEnumerable<Language>>(allLanguages).ToList();
|
||||
var langs = context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
|
||||
|
||||
//create a variant for each language, then we'll populate the values
|
||||
var variants = langs.Select(x =>
|
||||
|
||||
@@ -127,10 +127,10 @@ namespace Umbraco.Web.Models.Mapping
|
||||
private IEnumerable<PropertyEditorBasic> MapAvailableEditors(IDataType source, MapperContext context)
|
||||
{
|
||||
var contentSection = Current.Configs.Settings().Content;
|
||||
return _propertyEditors
|
||||
var properties = _propertyEditors
|
||||
.Where(x => !x.IsDeprecated || contentSection.ShowDeprecatedPropertyEditors || source.EditorAlias == x.Alias)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(context.Map<PropertyEditorBasic>);
|
||||
.OrderBy(x => x.Name);
|
||||
return context.MapEnumerable<IDataEditor, PropertyEditorBasic>(properties);
|
||||
}
|
||||
|
||||
private IEnumerable<DataTypeConfigurationFieldDisplay> MapPreValues(IDataType dataType, MapperContext context)
|
||||
@@ -143,7 +143,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
throw new InvalidOperationException($"Could not find a property editor with alias \"{dataType.EditorAlias}\".");
|
||||
|
||||
var configurationEditor = editor.GetConfigurationEditor();
|
||||
var fields = configurationEditor.Fields.Select(context.Map<DataTypeConfigurationFieldDisplay>).ToArray();
|
||||
var fields = context.MapEnumerable<ConfigurationField,DataTypeConfigurationFieldDisplay>(configurationEditor.Fields);
|
||||
var configurationDictionary = configurationEditor.ToConfigurationEditor(dataType.Configuration);
|
||||
|
||||
MapConfigurationFields(fields, configurationDictionary);
|
||||
@@ -151,7 +151,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return fields;
|
||||
}
|
||||
|
||||
private void MapConfigurationFields(DataTypeConfigurationFieldDisplay[] fields, IDictionary<string, object> configuration)
|
||||
private void MapConfigurationFields(List<DataTypeConfigurationFieldDisplay> fields, IDictionary<string, object> configuration)
|
||||
{
|
||||
if (fields == null) throw new ArgumentNullException(nameof(fields));
|
||||
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
|
||||
@@ -190,7 +190,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
var configurationEditor = source.GetConfigurationEditor();
|
||||
|
||||
var fields = configurationEditor.Fields.Select(context.Map<DataTypeConfigurationFieldDisplay>).ToArray();
|
||||
var fields = context.MapEnumerable<ConfigurationField, DataTypeConfigurationFieldDisplay>(configurationEditor.Fields);
|
||||
|
||||
var defaultConfiguration = configurationEditor.DefaultConfiguration;
|
||||
if (defaultConfiguration != null)
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace Umbraco.Web.Models.Mapping
|
||||
mapper.Define<IContentTypeComposition, EntityBasic>((source, context) => new EntityBasic(), Map);
|
||||
mapper.Define<EntitySlim, SearchResultEntity>((source, context) => new SearchResultEntity(), Map);
|
||||
mapper.Define<ISearchResult, SearchResultEntity>((source, context) => new SearchResultEntity(), Map);
|
||||
mapper.Define<ISearchResults, IEnumerable<SearchResultEntity>>((source, context) => source.Select(context.Map<SearchResultEntity>).ToList());
|
||||
mapper.Define<IEnumerable<ISearchResult>, IEnumerable<SearchResultEntity>>((source, context) => source.Select(context.Map<SearchResultEntity>).ToList());
|
||||
mapper.Define<ISearchResults, IEnumerable<SearchResultEntity>>((source, context) => context.MapEnumerable<ISearchResult, SearchResultEntity>(source));
|
||||
mapper.Define<IEnumerable<ISearchResult>, IEnumerable<SearchResultEntity>>((source, context) => context.MapEnumerable<ISearchResult, SearchResultEntity>(source));
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Alias
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
if (!(target is List<Language> list))
|
||||
throw new NotSupportedException($"{nameof(target)} must be a List<Language>.");
|
||||
|
||||
var temp = source.Select(context.Map<ILanguage, Language>).ToList();
|
||||
var temp = context.MapEnumerable<ILanguage, Language>(source);
|
||||
|
||||
//Put the default language first in the list & then sort rest by a-z
|
||||
var defaultLang = temp.SingleOrDefault(x => x.IsDefault);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<IMacro, EntityBasic>((source, context) => new EntityBasic(), Map);
|
||||
mapper.Define<IMacro, IEnumerable<MacroParameter>>((source, context) => source.Properties.Values.Select(context.Map<MacroParameter>).ToList());
|
||||
mapper.Define<IMacro, IEnumerable<MacroParameter>>((source, context) => context.MapEnumerable<IMacroProperty, MacroParameter>(source.Properties.Values));
|
||||
mapper.Define<IMacroProperty, MacroParameter>((source, context) => new MacroParameter(), Map);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(IMedia source, ContentPropertyCollectionDto target, MapperContext context)
|
||||
{
|
||||
target.Properties = source.Properties.Select(context.Map<ContentPropertyDto>);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyDto>(source.Properties);
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Properties -Errors -Edited -Updater -Alias -IsContainer
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Owner = _commonMapper.GetOwner(source, context);
|
||||
target.ParentId = source.ParentId;
|
||||
target.Path = source.Path;
|
||||
target.Properties = source.Properties.Select(context.Map<ContentPropertyBasic>);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyBasic>(source.Properties);
|
||||
target.SortOrder = source.SortOrder;
|
||||
target.State = null;
|
||||
target.Trashed = source.Trashed;
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Owner = _commonMapper.GetOwner(source, context);
|
||||
target.ParentId = source.ParentId;
|
||||
target.Path = source.Path;
|
||||
target.Properties = context.Map<IEnumerable<ContentPropertyBasic>>(source.Properties);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyBasic>(source.Properties);
|
||||
target.SortOrder = source.SortOrder;
|
||||
target.State = null;
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.Member, source.Key);
|
||||
@@ -149,7 +149,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
// Umbraco.Code.MapAll
|
||||
private static void Map(IMember source, ContentPropertyCollectionDto target, MapperContext context)
|
||||
{
|
||||
target.Properties = source.Properties.Select(context.Map<ContentPropertyDto>);
|
||||
target.Properties = context.MapEnumerable<Property, ContentPropertyDto>(source.Properties);
|
||||
}
|
||||
|
||||
private MembershipScenario GetMembershipScenario()
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <returns></returns>
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(IContentBase content, List<Property> properties, MapperContext context)
|
||||
{
|
||||
return properties.OrderBy(x => x.PropertyType.SortOrder).Select(context.Map<ContentPropertyDisplay>).ToList();
|
||||
return context.MapEnumerable<Property, ContentPropertyDisplay>(properties.OrderBy(x => x.PropertyType.SortOrder));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Sections;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Actions;
|
||||
using Umbraco.Web.Services;
|
||||
@@ -215,7 +216,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
//Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that
|
||||
// this will cause an N+1 and we'll need to change how this works.
|
||||
var users = _userService.GetAllInGroup(source.Id);
|
||||
target.Users = context.Map<IEnumerable<UserBasic>>(users);
|
||||
target.Users = context.MapEnumerable<IUser, UserBasic>(users);
|
||||
|
||||
//Deal with assigned permissions:
|
||||
|
||||
@@ -286,7 +287,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.StartContentIds = GetStartNodes(source.StartContentIds.ToArray(), UmbracoObjectTypes.Document, "content/contentRoot", context);
|
||||
target.StartMediaIds = GetStartNodes(source.StartMediaIds.ToArray(), UmbracoObjectTypes.Media, "media/mediaRoot", context);
|
||||
target.UpdateDate = source.UpdateDate;
|
||||
target.UserGroups = source.Groups.Select(context.Map<UserGroupBasic>);
|
||||
target.UserGroups = context.MapEnumerable<IReadOnlyUserGroup, UserGroupBasic>(source.Groups);
|
||||
target.Username = source.Username;
|
||||
target.UserState = source.UserState;
|
||||
}
|
||||
@@ -307,7 +308,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Name = source.Name;
|
||||
target.ParentId = -1;
|
||||
target.Path = "-1," + source.Id;
|
||||
target.UserGroups = source.Groups.Select(context.Map<UserGroupBasic>);
|
||||
target.UserGroups = context.MapEnumerable<IReadOnlyUserGroup, UserGroupBasic>(source.Groups);
|
||||
target.Username = source.Username;
|
||||
target.UserState = source.UserState;
|
||||
}
|
||||
@@ -336,7 +337,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
private void MapUserGroupBasic(UserGroupBasic target, IEnumerable<string> sourceAllowedSections, int? sourceStartContentId, int? sourceStartMediaId, MapperContext context)
|
||||
{
|
||||
var allSections = _sectionService.GetSections();
|
||||
target.Sections = allSections.Where(x => sourceAllowedSections.Contains(x.Alias)).Select(context.Map<Section>);
|
||||
target.Sections = context.MapEnumerable<ISection, Section>(allSections.Where(x => sourceAllowedSections.Contains(x.Alias)));
|
||||
|
||||
if (sourceStartMediaId > 0)
|
||||
target.MediaStartNode = context.Map<EntityBasic>(_entityService.Get(sourceStartMediaId.Value, UmbracoObjectTypes.Media));
|
||||
@@ -387,7 +388,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
startNodes.Add(CreateRootNode(_textService.Localize(localizedKey)));
|
||||
|
||||
var mediaItems = _entityService.GetAll(objectType, startNodeIds);
|
||||
startNodes.AddRange(context.Map<IEnumerable<EntityBasic>>(mediaItems));
|
||||
startNodes.AddRange(context.MapEnumerable<IEntitySlim, EntityBasic>(mediaItems));
|
||||
return startNodes;
|
||||
}
|
||||
|
||||
|
||||
@@ -298,23 +298,6 @@ namespace Umbraco.Web
|
||||
|
||||
#region IsSomething: misc.
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is visible.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>A value indicating whether the content is visible.</returns>
|
||||
/// <remarks>A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise,
|
||||
/// the content is visible.</remarks>
|
||||
public static bool IsVisible(this IPublishedContent content)
|
||||
{
|
||||
// note: would be better to ensure we have an IPropertyEditorValueConverter for booleans
|
||||
// and then treat the umbracoNaviHide property as a boolean - vs. the hard-coded "1".
|
||||
|
||||
// rely on the property converter - will return default bool value, ie false, if property
|
||||
// is not defined, or has no value, else will return its value.
|
||||
return content.Value<bool>(Constants.Conventions.Content.NaviHide) == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified content is a specified content type.
|
||||
/// </summary>
|
||||
|
||||
@@ -170,5 +170,23 @@ namespace Umbraco.Web
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsSomething
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is visible.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>A value indicating whether the content is visible.</returns>
|
||||
/// <remarks>A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise,
|
||||
/// the content is visible.</remarks>
|
||||
public static bool IsVisible(this IPublishedElement content)
|
||||
{
|
||||
// rely on the property converter - will return default bool value, ie false, if property
|
||||
// is not defined, or has no value, else will return its value.
|
||||
return content.Value<bool>(Constants.Conventions.Content.NaviHide) == false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models;
|
||||
@@ -66,37 +67,37 @@ namespace Umbraco.Web
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetAllTags(string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetAllTags(group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetAllTags(group, culture));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetAllContentTags(string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetAllContentTags(group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetAllContentTags(group, culture));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetAllMediaTags(string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetAllMediaTags(group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetAllMediaTags(group, culture));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetAllMemberTags(string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetAllMemberTags(group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetAllMemberTags(group, culture));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetTagsForProperty(contentId, propertyTypeAlias, group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetTagsForProperty(contentId, propertyTypeAlias, group, culture));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TagModel> GetTagsForEntity(int contentId, string group = null, string culture = null)
|
||||
{
|
||||
return _mapper.Map<IEnumerable<TagModel>>(_tagService.GetTagsForEntity(contentId, group, culture));
|
||||
return _mapper.MapEnumerable<ITag, TagModel>(_tagService.GetTagsForEntity(contentId, group, culture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.DocumentType, pageIndex, pageSize, out totalFound,
|
||||
filter: SqlContext.Query<IUmbracoEntity>().Where(x => x.Name.Contains(query)));
|
||||
return Mapper.Map<IEnumerable<SearchResultEntity>>(results);
|
||||
return Mapper.MapEnumerable<IEntitySlim, SearchResultEntity>(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.DataType, pageIndex, pageSize, out totalFound,
|
||||
filter: SqlContext.Query<IUmbracoEntity>().Where(x => x.Name.Contains(query)));
|
||||
return Mapper.Map<IEnumerable<SearchResultEntity>>(results);
|
||||
return Mapper.MapEnumerable<IEntitySlim, SearchResultEntity>(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.MediaType, pageIndex, pageSize, out totalFound,
|
||||
filter: SqlContext.Query<IUmbracoEntity>().Where(x => x.Name.Contains(query)));
|
||||
return Mapper.Map<IEnumerable<SearchResultEntity>>(results);
|
||||
return Mapper.MapEnumerable<IEntitySlim, SearchResultEntity>(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.Template, pageIndex, pageSize, out totalFound,
|
||||
filter: SqlContext.Query<IUmbracoEntity>().Where(x => x.Name.Contains(query)));
|
||||
return Mapper.Map<IEnumerable<SearchResultEntity>>(results);
|
||||
return Mapper.MapEnumerable<IEntitySlim, SearchResultEntity>(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user