Compare commits

...

31 Commits

Author SHA1 Message Date
Niels Lyngsø 8c264b0815 correcting k to K 2019-05-03 09:54:09 +02:00
Peter Duncanson 12ecc5d650 Index types added in this commit (see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types) 2019-04-29 00:40:28 +01:00
Peter Duncanson 26dce2c99d Strengthened the types up in the appstate.service with some interfaces and index types as we use a funky accessor method in this one
See: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types for details on index types.
2019-04-29 00:39:41 +01:00
Peter Duncanson 4649811abd Initial conversion of appstate.service to typescript 2019-04-28 23:38:21 +01:00
Peter Duncanson 6af5795a1d Renamed appstate.service to .ts extension 2019-04-28 22:31:17 +01:00
Peter Duncanson 68f6e27d20 Refactored the namespaces for Overlay.service 2019-04-26 18:28:10 +01:00
Peter Duncanson ef035df8b8 Updated formHelper to use the new Notification namespaces, also binned off iHasScope 2019-04-26 18:27:38 +01:00
Peter Duncanson 35e6a64e73 Changed namespace to be umbrac.services.Notifications and removed the old alias too 2019-04-26 18:22:27 +01:00
Peter Duncanson 5bb6ffc654 Added some missed types to Overlay and changed to umbraco.services.Overlays namespace 2019-04-26 18:19:30 +01:00
Peter Duncanson 30905a9442 Converted search.service to TypeScript (after changing extension in the last commit) 2019-04-26 18:17:44 +01:00
Peter Duncanson ccfec9e092 Converted search.service to TypeScript 2019-04-26 18:17:13 +01:00
Peter Duncanson a2007a96af Converted overlayhelper.,service to TypeScript 2019-04-26 02:23:52 +01:00
Peter Duncanson 0879bdb90e Renamed overlayhelper.service.js to .ts 2019-04-26 02:19:25 +01:00
Peter Duncanson 1f483bc95c Converted overlay.service to TypeScript 2019-04-26 02:18:54 +01:00
Peter Duncanson e1df9d7a00 Renamed overlay.service.js to .ts 2019-04-26 01:50:30 +01:00
Peter Duncanson 41718cb7d5 package-lock.json had changed 2019-04-26 01:27:07 +01:00
Peter Duncanson 75d6e02c63 Changed iHasScope to "any" type instead of "object" as we don't know what shape it might be 2019-04-26 01:25:59 +01:00
Peter Duncanson c1d09c2516 Move the enum's for notification types out of the models namespace on notification service 2019-04-26 01:25:19 +01:00
Peter Duncanson ed429786ca Switched from using object to any for a few types I didn't know, lesson learnt 2019-04-26 01:24:18 +01:00
Peter Duncanson 88f108bf2e Finished converting over formHelper.service.ts 2019-04-26 00:50:14 +01:00
Peter Duncanson 437bf6bea1 Added new iHasScope interface to angularHelper
Used when anyone is passing around an object with a scope field on it
2019-04-26 00:49:52 +01:00
Peter Duncanson 6eb678b821 Converted all the classes to begin with Uppercase as per JS common practise, added a lowercase alias though so we don't break anything 2019-04-25 23:56:16 +01:00
Peter Duncanson 4d3af9284f "Prettified" notifications.service.ts no logic changes just formatting 2019-04-25 23:48:58 +01:00
Peter Duncanson 38d531894f Renamed formhelper to .ts and started converting to TypeScript 2019-04-25 23:42:34 +01:00
Stephan c434558378 IsVisible moves from publishde content to element 2019-04-10 12:44:21 +01:00
Stephan 777276d2e9 Better mapping of enumerable 2019-04-10 12:44:20 +01:00
Stephan b63ff54c19 Fix mapping of enumerable 2019-04-10 12:44:20 +01:00
Niels Lyngsø 96f1189fc6 Fixing model sync, much weird code cleaned up 2019-04-10 12:44:20 +01:00
Niels Lyngsø 2250389cc2 Fixing checkbox list to work in v8 2019-04-10 12:44:20 +01:00
Kenn Jacobsen 6a2b6ba75b Add IsVisible extension on IPublishedElement 2019-04-10 12:44:20 +01:00
Craig Noble 0a43530852 #5215 - Set up the initial build for typescript
I have rewritten the notificationsService and the AngularHelper (used by the NotificationsService) in Typescript. This gives a very good example of how the notificationsService can reference a concrete type even though the angularHelper is being injected in the constructor.

Open the Umbraco.Web.UI.Client in an editor that supports Typescript, such as VS Code or Atom.

In Umbraco.Web.UI.Client, run npm install.  Then run "npm run dev" and it will do the following:
1. Compile the typescript into Umbraco.Web.UI.Client/src/common/services/build/temp/, into their respective files such as notifications.service.js
2. The existing JS build picks up the compiled JS files and merges them into umbraco.services.js, then is saved in Umbraco.Web.UI/Umbraco/js (all stuff it currently does)

I converted the controller into a class, within the umbraco.services namespace.  The benefit of this is having the ability to generate a definitions file that will retain all namespaces and therefore make it extremely easy to find what you are looking for when creating Umbraco extensions such as custom property types, dashboards etc.

Note: I have set up a definitions folder for global variables that are declared outside of Typescript.  We can add typings in for Angular etc at a later date if needed.
2019-04-10 11:58:19 +01:00
57 changed files with 2627 additions and 1059 deletions
+1
View File
@@ -157,6 +157,7 @@ build.tmp/
build/hooks/
build/temp/
/src/Umbraco.Web.UI.Client/src/common/**/build/temp/
# eof
+13
View File
@@ -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
}
}
+42 -2
View File
@@ -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
}
}
+44 -10
View File
@@ -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)
{ }
}
}
}
+4
View File
@@ -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);
});
+1 -1
View File
@@ -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;
};
File diff suppressed because it is too large Load Diff
+2
View File
@@ -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;
}
@@ -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);
@@ -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);
@@ -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>
+16
View File
@@ -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]);
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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]
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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
}
}
+7 -6
View File
@@ -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);
}
}
}