Merge branch 'temp-U4-11218' into temp-v8-UI-infinite-editing

This commit is contained in:
Mads Rasmussen
2018-04-16 15:27:29 +02:00
committed by GitHub
5169 changed files with 199797 additions and 317466 deletions

View File

@@ -1,2 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp50</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp70</s:String></wpf:ResourceDictionary>

View File

@@ -16,6 +16,7 @@
"tests"
],
"dependencies": {
"signalr": "^2.2.1",
"typeahead.js": "~0.10.5",
"underscore": "~1.7.0",
"rgrove-lazyload": "*",
@@ -42,7 +43,12 @@
"codemirror"
],
"sources": {
"moment": "bower_components/moment/min/moment-with-locales.js",
"moment": [
"bower_components/moment/min/moment.min.js",
"bower_components/moment/min/moment-with-locales.js",
"bower_components/moment/min/moment-with-locales.min.js",
"bower_components/moment/locale/*.js"
],
"underscore": [
"bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map"

View File

@@ -1,605 +0,0 @@
module.exports = function (grunt) {
// Default task.
grunt.registerTask('default', ['jshint:dev', 'build', 'karma:unit']);
grunt.registerTask('dev', ['jshint:dev', 'build-dev', 'webserver', 'open:dev', 'watch']);
grunt.registerTask('docserve', ['docs:api', 'connect:docserver', 'open:docs', 'watch:docs']);
grunt.registerTask('vs', ['jshint:dev', 'build-dev', 'watch']);
//TODO: Too much watching, this brings windows to it's knees when in dev mode
//run by the watch task
grunt.registerTask('watch-js', ['jshint:dev', 'concat', 'copy:app', 'copy:mocks', 'copy:canvasdesigner', 'copy:vs', 'karma:unit']);
grunt.registerTask('watch-less', ['recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'copy:canvasdesigner', 'copy:assets', 'copy:vs']);
grunt.registerTask('watch-html', ['copy:views', 'copy:vs']);
grunt.registerTask('watch-installer', ['concat:install', 'concat:installJs', 'copy:installer', 'copy:vs']);
grunt.registerTask('watch-canvasdesigner', ['copy:canvasdesigner', 'concat:canvasdesignerJs', 'copy:vs']);
grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']);
//triggered from grunt
grunt.registerTask('build', ['concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'bower-install-simple', 'bower', 'copy', 'clean:post']);
//triggered from grunt dev vs or grunt vs
grunt.registerTask('build-dev', ['clean:pre', 'concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'postcss', 'bower-install-simple', 'bower', 'copy']);
//utillity tasks
grunt.registerTask('docs', ['ngdocs']);
grunt.registerTask('webserver', ['connect:devserver']);
// Print a timestamp (useful for when watching)
grunt.registerTask('timestamp', function () {
grunt.log.subhead(Date());
});
// Project configuration.
grunt.initConfig({
buildVersion: grunt.option('buildversion') || '7',
connect: {
devserver: {
options: {
port: 9990,
hostname: '0.0.0.0',
base: './build',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.favicon('images/favicon.ico'),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
testserver: {},
docserver: {
options: {
port: 8880,
hostname: '0.0.0.0',
base: './docs/api',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
},
open: {
dev: {
path: 'http://localhost:9990/belle/'
},
docs: {
path: 'http://localhost:8880/index.html'
}
},
distdir: 'build/belle',
vsdir: '../Umbraco.Web.UI/umbraco',
pkg: grunt.file.readJSON('package.json'),
banner:
'/*! <%= pkg.title || pkg.name %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
src: {
js: ['src/**/*.js', 'src/*.js'],
common: ['src/common/**/*.js'],
controllers: ['src/**/*.controller.js'],
specs: ['test/**/*.spec.js'],
scenarios: ['test/**/*.scenario.js'],
samples: ['sample files/*.js'],
html: ['src/index.html', 'src/install.html'],
everything: ['src/**/*.*', 'test/**/*.*', 'docs/**/*.*'],
tpl: {
app: ['src/views/**/*.html'],
common: ['src/common/**/*.tpl.html']
},
less: ['src/less/belle.less'], // recess:build doesn't accept ** in its file patterns
prod: ['<%= distdir %>/js/*.js']
},
clean: {
pre: ['<%= distdir %>/*'],
post: ['<%= distdir %>/js/*.dev.js']
},
copy: {
assets: {
files: [{ dest: '<%= distdir %>/assets', src: '**', expand: true, cwd: 'src/assets/' }]
},
config: {
files: [{ dest: '<%= distdir %>/../config', src: '**', expand: true, cwd: 'src/config/' }]
},
installer: {
files: [{ dest: '<%= distdir %>/views/install', src: '**/*.html', expand: true, cwd: 'src/installer/steps' }]
},
canvasdesigner: {
files: [
{ dest: '<%= distdir %>/preview', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner' },
{ dest: '<%= distdir %>/preview/editors', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/assets/less', src: '**/*.less', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.config.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.palettes.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.front.js', expand: true, cwd: 'src/canvasdesigner' }
]
},
vendor: {
files: [{ dest: '<%= distdir %>/lib', src: '**', expand: true, cwd: 'lib/' }]
},
views: {
files: [{ dest: '<%= distdir %>/views', src: ['**/*.*', '!**/*.controller.js'], expand: true, cwd: 'src/views' }]
},
app: {
files: [
{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/' }
]
},
mocks: {
files: [{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/common/mocks/' }]
},
vs: {
files: [
//everything except the index.html root file!
//then we need to figure out how to not copy all the test stuff either!?
{ dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' },
{ dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' },
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' },
{ dest: '<%= vsdir %>/preview', src: '**', expand: true, cwd: '<%= distdir %>/preview' },
{ dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' }
]
}
},
karma: {
unit: { configFile: 'test/config/karma.conf.js', keepalive: true },
e2e: { configFile: 'test/config/e2e.js', keepalive: true },
watch: { configFile: 'test/config/unit.js', singleRun: false, autoWatch: true, keepalive: true }
},
concat: {
index: {
src: ['src/index.html'],
dest: '<%= distdir %>/index.html',
options: {
process: true
}
},
install: {
src: ['src/installer/installer.html'],
dest: '<%= distdir %>/installer.html',
options: {
process: true
}
},
installJs: {
src: ['src/installer/**/*.js'],
dest: '<%= distdir %>/js/umbraco.installer.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
canvasdesignerJs: {
src: ['src/canvasdesigner/canvasdesigner.global.js', 'src/canvasdesigner/canvasdesigner.controller.js', 'src/canvasdesigner/editors/*.js', 'src/canvasdesigner/lib/*.js'],
dest: '<%= distdir %>/js/canvasdesigner.panel.js'
},
controllers: {
src: ['src/controllers/**/*.controller.js', 'src/views/**/*.controller.js'],
dest: '<%= distdir %>/js/umbraco.controllers.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
services: {
src: ['src/common/services/*.js'],
dest: '<%= distdir %>/js/umbraco.services.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
security: {
src: ['src/common/security/*.js'],
dest: '<%= distdir %>/js/umbraco.security.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
resources: {
src: ['src/common/resources/*.js'],
dest: '<%= distdir %>/js/umbraco.resources.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
testing: {
src: ['src/common/mocks/*/*.js'],
dest: '<%= distdir %>/js/umbraco.testing.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
directives: {
src: ['src/common/directives/**/*.js'],
dest: '<%= distdir %>/js/umbraco.directives.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
filters: {
src: ['src/common/filters/*.js'],
dest: '<%= distdir %>/js/umbraco.filters.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
}
},
uglify: {
options: {
mangle: true
},
combine: {
files: {
'<%= distdir %>/js/umbraco.min.js': ['<%= distdir %>/js/umbraco.*.js']
}
}
},
recess: {
build: {
files: {
'<%= distdir %>/assets/css/<%= pkg.name %>.css':
['<%= src.less %>']
},
options: {
compile: true,
compress: true
}
},
nonodes: {
files: {
'<%= distdir %>/assets/css/nonodes.style.min.css':
['src/less/pages/nonodes.less']
},
options: {
compile: true,
compress: true
}
},
installer: {
files: {
'<%= distdir %>/assets/css/installer.css':
['src/less/installer.less']
},
options: {
compile: true,
compress: true
}
},
canvasdesigner: {
files: {
'<%= distdir %>/assets/css/canvasdesigner.css':
['src/less/canvas-designer.less', 'src/less/helveticons.less']
},
options: {
compile: true,
compress: true
}
}
},
postcss: {
options: {
processors: [
// add vendor prefixes
require('autoprefixer-core')({
browsers: 'last 2 versions'
})
]
},
dist: {
src: '<%= distdir %>/assets/css/<%= pkg.name %>.css'
}
},
ngTemplateCache: {
views: {
files: {
'<%= distdir %>/js/umbraco.views.js': 'src/views/**/*.html'
},
options: {
trim: 'src/',
module: 'umbraco.views'
}
}
},
watch: {
docs: {
files: ['docs/src/**/*.md'],
tasks: ['watch-docs', 'timestamp']
},
css: {
files: 'src/**/*.less',
tasks: ['watch-less', 'timestamp'],
options: {
livereload: true,
},
},
js: {
files: ['src/**/*.js', 'src/*.js'],
tasks: ['watch-js', 'timestamp'],
},
test: {
files: ['test/**/*.js'],
tasks: ['watch-test', 'timestamp'],
},
installer: {
files: ['src/installer/**/*.*'],
tasks: ['watch-installer', 'timestamp'],
},
canvasdesigner: {
files: ['src/canvasdesigner/**/*.*'],
tasks: ['watch-canvasdesigner', 'timestamp'],
},
html: {
files: ['src/views/**/*.html', 'src/*.html'],
tasks: ['watch-html', 'timestamp']
},
options: {
interval: 500
}
},
ngdocs: {
options: {
dest: 'docs/api',
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
html5Mode: false,
styles: [
'docs/umb-docs.css'
],
image: "https://our.umbraco.org/assets/images/logo.svg"
},
api: {
src: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
title: 'API Documentation'
},
tutorials: {
src: [],
title: ''
}
},
eslint:{
src: ['<%= src.common %>','<%= src.controllers %>'],
options: {quiet: true}
},
jshint: {
dev: {
files: {
src: ['<%= src.common %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globals: {}
}
},
build: {
files: {
src: ['<%= src.prod %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globalstrict: true,
globals: { $: false, jQuery: false, define: false, require: false, window: false }
}
}
},
bower: {
dev: {
dest: '<%= distdir %>/lib',
options: {
expand: true,
ignorePackages: ['bootstrap'],
packageSpecific: {
'moment': {
keepExpandedHierarchy: false,
files: ['min/moment-with-locales.js']
},
'typeahead.js': {
keepExpandedHierarchy: false,
files: ['dist/typeahead.bundle.min.js']
},
'underscore': {
files: ['underscore-min.js', 'underscore-min.map']
},
'rgrove-lazyload': {
files: ['lazyload.js']
},
'bootstrap-social': {
files: ['bootstrap-social.css']
},
'font-awesome': {
files: ['css/font-awesome.min.css', 'fonts/*']
},
"jquery": {
keepExpandedHierarchy: false,
files: ['dist/jquery.min.js', 'dist/jquery.min.map']
},
'jquery-ui': {
keepExpandedHierarchy: false,
files: ['jquery-ui.min.js']
},
'jquery-migrate': {
keepExpandedHierarchy: false,
files: ['jquery-migrate.min.js']
},
'tinymce': {
files: ['plugins/**', 'themes/**', 'tinymce.min.js']
},
'angular-dynamic-locale': {
files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map']
},
'ng-file-upload': {
keepExpandedHierarchy: false,
files: ['ng-file-upload.min.js']
},
'angular-local-storage': {
keepExpandedHierarchy: false,
files: ['dist/angular-local-storage.min.js']
},
'codemirror': {
files: [
'lib/codemirror.js',
'lib/codemirror.css',
'mode/css/*',
'mode/javascript/*',
'mode/xml/*',
'mode/htmlmixed/*',
'addon/search/*',
'addon/edit/*',
'addon/selection/*',
'addon/dialog/*'
]
},
'ace-builds': {
files: [
'src-min-noconflict/ace.js',
'src-min-noconflict/ext-language_tools.js',
'src-min-noconflict/ext-searchbox.js',
'src-min-noconflict/ext-settings_menu.js',
'src-min-noconflict/snippets/text.js',
'src-min-noconflict/snippets/javascript.js',
'src-min-noconflict/theme-chrome.js',
'src-min-noconflict/mode-razor.js',
'src-min-noconflict/mode-javascript.js',
'src-min-noconflict/worker-javascript.js',
]
},
'clipboard': {
keepExpandedHierarchy: false,
files: ['dist/clipboard.min.js']
},
'angular-moment': {
keepExpandedHierarchy: false,
files: ['angular-moment.min.js']
}
}
}
},
options: {
expand: true
}
},
"bower-install-simple": {
options: {
color: true
},
"dev": {}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-open');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks("grunt-bower-install-simple");
grunt.loadNpmTasks('grunt-bower');
grunt.loadNpmTasks('grunt-ngdocs');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-hustler');
};

View File

@@ -73,7 +73,6 @@ var sources = {
js: {
preview: { files: ["src/canvasdesigner/**/*.js"], out: "umbraco.canvasdesigner.js" },
installer: { files: ["src/installer/**/*.js"], out: "umbraco.installer.js" },
controllers: { files: ["src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
directives: { files: ["src/common/directives/**/*.js"], out: "umbraco.directives.js" },
filters: { files: ["src/common/filters/**/*.js"], out: "umbraco.filters.js" },
@@ -85,8 +84,7 @@ var sources = {
//selectors for copying all views into the build
//processed in the views task
views:{
umbraco: {files: ["src/views/**/*html"], folder: ""},
preview: { files: ["src/canvasdesigner/**/*.html"], folder: "../preview"},
umbraco: {files: ["src/views/**/*.html"], folder: ""},
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
},

View File

@@ -330,6 +330,19 @@
return false;
};
}
if (!Object.toBoolean) {
/** Converts a string/integer/bool to true/false */
Object.toBoolean = function (obj) {
if ((typeof obj) === "boolean") {
return obj;
}
if (obj === "1" || obj === 1 || obj === "true") {
return true;
}
return false;
};
}
})();
})();

View File

@@ -106,6 +106,9 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
treeService.clearCache();
});
},
childNodeCreated: function() {
//no-op, just needs to be here for legacy reasons
},
reloadActionNode: function () {
angularHelper.safeApply($rootScope, function() {
var currentMenuNode = appState.getMenuState("currentNode");
@@ -187,20 +190,6 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
var actions = {
openDashboard : function(section){
navService.changeSection(section);
},
actionDisable: function () {
localizationService.localize("defaultdialogs_confirmdisable").then(function (txtConfirmDisable) {
var currentMenuNode = UmbClientMgr.mainTree().getActionNode();
if (currentMenuNode) {
if (confirm(txtConfirmDisable + ' "' + UmbClientMgr.mainTree().getActionNode().nodeName + '"?\n\n')) {
angularHelper.safeApply($rootScope, function () {
usersResource.disableUsers(currentMenuNode.nodeId).then(function () {
UmbClientMgr.mainTree().syncTree("-1," + currentMenuNode.nodeId, true);
});
});
}
}
});
}
};

View File

@@ -0,0 +1,38 @@
.sprTreeDeveloperCacheItem {background-position: -6px -8px ! important;}
.sprTreeDeveloperCacheTypes {background-position: -6px -40px ! important;}
.sprTreeDeveloperMacro {background-position: -6px -72px ! important;}
.sprTreeDeveloperPython {background-position: -6px -104px ! important; width: 15px; height: 15px}
.sprTreeDeveloperRegistry {background-position: -6px -135px ! important;}
.sprTreeDoc {background-position: -6px -199px ! important;}
.sprTreeDoc2 {background-position: -6px -231px ! important;}
.sprTreeDoc3 {background-position: -6px -263px ! important;}
.sprTreeDoc4 {background-position: -6px -295px ! important;}
.sprTreeDoc5 {background-position: -6px -327px ! important;}
.sprTreeDocPic {background-position: -6px -359px ! important;}
.sprTreeFolder {background-position: -6px -391px ! important;}
.sprTreeFolder_o {background-position: -6px -423px ! important;}
.sprTreeMediaFile {background-position: -6px -455px ! important;}
.sprTreeMediaMovie {background-position: -6px -487px ! important;}
.sprTreemediaFile {background-position: -6px -519px ! important;}
.sprTreeMediaPhoto {background-position: -6px -551px ! important;}
.sprTreeMember {background-position: -6px -583px ! important;}
.sprTreeMemberGroup {background-position: -6px -615px ! important;}
.sprTreeMemberType {background-position: -6px -647px ! important;}
.sprTreeNewsletter {background-position: -6px -679px ! important;}
.sprTreePackage {background-position: -6px -711px ! important;}
.sprTreeRepository {background-position: -6px -743px ! important;}
.sprTreeSettingAgent {background-position: -6px -775px ! important;}
.sprTreeSettingCss {background-position: -6px -807px ! important;}
.sprTreeSettingCssItem {background-position: -6px -839px ! important;}
.sprTreeSettingDataType {background-position: -6px -871px ! important;}
.sprTreeSettingDataTypeChild {background-position: -6px -903px ! important;}
.sprTreeSettingDomain {background-position: -6px -935px ! important;}
.sprTreeSettingLanguage {background-position: -6px -967px ! important;}
.sprTreeSettingScript {background-position: -6px -999px ! important;}
.sprTreeSettingTemplate {background-position: -6px -1031px ! important;}
.sprTreeSettingXml {background-position: -6px -1063px ! important;}
.sprTreeStatistik {background-position: -6px -1095px ! important;}
.sprTreeUser {background-position: -6px -1127px ! important;}
.sprTreeUserGroup {background-position: -6px -1159px ! important;}
.sprTreeUserType {background-position: -6px -1191px ! important;}
.sprNew{background-position: -6px -339px;}

9006
src/Umbraco.Web.UI.Client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"author": "Umbraco HQ",
"name": "umbraco",
"homepage": "https://github.com/umbraco/umbraco-cms/",
"version": "7.1.2",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -27,9 +27,9 @@
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-connect": "5.0.0",
"gulp-less": "^3.1.0",
"gulp-less": "^3.5.0",
"gulp-ngdocs": "^0.3.0",
"gulp-open": "^2.0.0",
"gulp-open": "^2.1.0",
"gulp-postcss": "^6.2.0",
"gulp-rename": "^1.2.2",
"gulp-sort": "^2.0.0",
@@ -38,11 +38,11 @@
"gulp-wrap-js": "^0.4.1",
"jasmine-core": "2.5.2",
"karma": "^1.7.0",
"karma-jasmine": "^1.1.0",
"karma-jasmine": "^1.1.1",
"karma-phantomjs-launcher": "^1.0.4",
"less": "^2.6.1",
"lodash": "^4.16.3",
"less": "^2.7.3",
"lodash": "^4.17.5",
"merge-stream": "^1.0.1",
"run-sequence": "^2.1.0"
"run-sequence": "^2.2.1"
}
}

View File

@@ -22,12 +22,21 @@ var packages = angular.module("umbraco.packages", []);
//module is initilized.
angular.module("umbraco.views", ["umbraco.viewcache"]);
angular.module("umbraco.viewcache", [])
.run(function($rootScope, $templateCache) {
.run(function ($rootScope, $templateCache, localStorageService) {
/** For debug mode, always clear template cache to cut down on
dev frustration and chrome cache on templates */
if (Umbraco.Sys.ServerVariables.isDebuggingEnabled) {
$templateCache.removeAll();
}
else {
var storedVersion = localStorageService.get("umbVersion");
if (!storedVersion || storedVersion !== Umbraco.Sys.ServerVariables.application.cacheBuster) {
//if the stored version doesn't match our cache bust version, clear the template cache
$templateCache.removeAll();
//store the current version
localStorageService.set("umbVersion", Umbraco.Sys.ServerVariables.application.cacheBuster);
}
}
})
.config([
//This ensures that all of our angular views are cache busted, if the path starts with views/ and ends with .html, then

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 KiB

After

Width:  |  Height:  |  Size: 184 KiB

View File

@@ -1,20 +1,20 @@
LazyLoad.js([
'../lib/jquery/jquery.min.js',
'../lib/jquery-ui/jquery-ui.min.js',
'../lib/angular/1.1.5/angular.min.js',
'../lib/underscore/underscore-min.js',
'../lib/umbraco/Extensions.js',
'../js/app.js',
'../js/umbraco.resources.js',
'../js/umbraco.services.js',
'../js/umbraco.security.js',
'../ServerVariables',
'../lib/spectrum/spectrum.js',
'../js/umbraco.canvasdesigner.js',
'../js/canvasdesigner.panel.js'
'../lib/jquery/jquery.min.js',
'../lib/angular/1.1.5/angular.min.js',
'../lib/underscore/underscore-min.js',
'../lib/umbraco/Extensions.js',
'../js/app.js',
'../js/umbraco.resources.js',
'../js/umbraco.services.js',
'../js/umbraco.security.js',
'../ServerVariables',
'../lib/spectrum/spectrum.js',
'../lib/signalr/jquery.signalR.js',
'/umbraco/BackOffice/signalr/hubs',
'../js/umbraco.canvasdesigner.js'
], function () {
jQuery(document).ready(function () {
angular.bootstrap(document, ['Umbraco.canvasdesigner']);
});
jQuery(document).ready(function () {
angular.bootstrap(document, ['Umbraco.canvasdesigner']);
});
});

View File

@@ -492,6 +492,26 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
}, 100);
}, true);
// signalr hub
var previewHub = $.connection.previewHub;
previewHub.client.refreshed = function (message, sender) {
console.log("Notified by SignalR preview hub (" + message+ ").");
if ($scope.pageId != message) {
console.log("Not a notification for us (" + $scope.pageId + ").");
return;
}
var resultFrame = document.getElementById("resultFrame");
var iframe = (resultFrame.contentWindow || resultFrame.contentDocument);
//setTimeout(function() { iframe.location.reload(); }, 1000);
iframe.location.reload();
};
$.connection.hub.start()
.done(function () { console.log("Connected to SignalR preview hub (ID=" + $.connection.hub.id + ")"); })
.fail(function () { console.log("Could not connect to SignalR preview hub."); });
})
.directive('onFinishRenderFilters', function ($timeout) {

View File

@@ -1,57 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.background">
<div class="box-slider">
<div colorpicker ng-model="item.values.color"></div>
</div>
<div class="box-slider">
<div class="imagePickerPreview" ng-click="open(item.values)" style="background-image:{{ item.values.imageorpattern }}">
<i ng-if="item.values.imageorpattern == ''" class="icon icon-picture"></i>
<i ng-if="item.values.imageorpattern != ''" class="icon icon-delete" ng-click="item.values.imageorpattern = ''"></i>
</div>
</div>
</div>
<script type="text/ng-template" id="mediaPickerModal.html">
<div ng-controller="canvasdesigner.mediaPickerModal">
<div class="modal-header bodyCanvasdesignerImagePicker ng-scope">
<ul class="breadcrumb">
<li ng-if="startNodeId == -1">
<a href="" ng-click="gotoFolder()">Media</a>
</li>
<!-- ngRepeat: item in path -->
<li ng-repeat="item in currentPath" class="ng-scope">
/ <a ng-if="currentFolder.id == item.id" href="" ng-class="{disabled:currentFolder.id == item.id}">{{ item.name }}</a>
<a ng-if="currentFolder.id != item.id" ng-click="gotoFolder(item)">{{ item.name }}</a>
</li>
</ul>
</div>
<div class="modal-body bodyCanvasdesignerImagePicker">
<ul class="canvasdesignerImagePicker">
<li>
<ul class="media-items">
<li ng-repeat="child in currentFolder.children | orderBy:'isFolder':true">
<div ng-if="!child.isFolder" class="media-preview" ng-class="{selected:selectedMedia.id == child.id}" ng-click="selectMedia(child)" style="background-image: url({{ child.thumbnail }})"></div>
<div ng-if="child.isFolder" class="media-preview" ng-click="selectMedia(child)">
<i class="icon icon-folder folder"><p class="folder-name">{{child.name}}</p></i>
</div>
</li>
</ul>
</li>
</ul>
</div>
<div class="right">
<a class="btn" href="#" ng-click="cancelAndClose()">Cancel</a>
<a class="btn btn-success" href="#" ng-click="submitAndClose()">Done</a>
</div>
</div>
</script>

View File

@@ -1,20 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.border" class="bordereditor">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="border in borderList" class="border-{{border}}" ng-class="{selected: selectedBorder.name == border}" ng-click="setselectedBorder(border)"></li>
</ul>
</div>
<div class="box-slider" ng-repeat="border in borderList" ng-show="selectedBorder.name == border">
<div colorpicker ng-model="item.values[(border !== 'all' ? border : '') + 'bordercolor']"></div>
<select class="borderStyleSelect" ng-model="selectedBorder.type" ng-options="bordertype for bordertype in bordertypes"></select>
<!--<i ng-if="selectedBorder.color != ''" ng-click="selectedBorder.color= ''" class="icon icon-delete colorPickerDelete"></i>-->
</div>
<div class="box-slider">
<div ui-slider min="0" max="40" step="1" ng-model="selectedBorder.size"></div>
</div>
</div>

View File

@@ -1,3 +0,0 @@
<div class="box-slider">
<div colorpicker ng-model="item.values.color"></div>
</div>

View File

@@ -1,34 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.googlefontpicker">
<div class="box-slider">
<div class="fontFamilyPickerPreview" ng-click="open(item.values)" ng-style="setStyleVariant()">
<span>Aa</span>
{{ item.values.fontFamily }}
<i ng-if="item.values.fontFamily != ''" ng-click="item.values.fontFamily = ''" class="icon icon-delete fontPickerDelete"></i>
</div>
</div>
</div>
<script type="text/ng-template" id="googlefontdialog.html">
<div ng-controller="googlefontdialog.controller">
<div class="modal-header canvasdesigner-fontfamilypicker">
<select class="font-list" ng-model="selectedFont" ng-change="showFontPreview(selectedFont)" ng-options="font as font.fontFamily group by font.fontType for font in fonts"></select>
<select class="variant-list" ng-model="selectedFont.variant" ng-change="showFontPreview(selectedFont,selectedFont.variant)" ng-options="variant for variant in selectedFont.variants" />
</div>
<div class="modal-body canvasdesigner-fontfamilypicker">
<span class="show" ng-style="setStyleVariant()">Aa Bb Cc 1 2 3 4&hellip; <br />The quick brown fox jumps over the lazy dog&hellip;</span>
</div>
<div class="right">
<a class="btn" href="#" ng-click="cancelAndClose()">Cancel</a>
<a class="btn btn-success" href="#" ng-click="submitAndClose()">Done</a>
</div>
</div>
</script>

View File

@@ -1,8 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.gridRow">
<div class="box-slider">
<input type="checkbox" ng-model="item.values.fullsize" /><label>Full size</label>
</div>
</div>

View File

@@ -1,10 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.layout">
<div class="box-slider">
<input type="radio" ng-model="item.values.layout" value="box"> Box
<input type="radio" ng-model="item.values.layout" value="wide"> Wide
<input type="radio" ng-model="item.values.layout" value="full"> Full
</div>
</div>

View File

@@ -1,14 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.margin">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="margin in marginList" class="border-{{margin}}" ng-class="{selected: selectedmargin.name == margin}" ng-click="setSelectedmargin(margin)"></li>
</ul>
</div>
<div class="box-slider">
<div ui-slider min="0" max="400" step="1" ng-model="selectedmargin.value"></div>
</div>
</div>

View File

@@ -1,14 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.padding">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="padding in paddingList" class="border-{{padding}}" ng-class="{selected: selectedpadding.name == padding}" ng-click="setSelectedpadding(padding)"></li>
</ul>
</div>
<div class="box-slider">
<div ui-slider min="0" max="400" step="1" ng-model="selectedpadding.value"></div>
</div>
</div>

View File

@@ -1,21 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.radius">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="radius in radiusList" ng-class="{selected: selectedradius.name == radius}" ng-click="setSelectedradius(radius)">
<span ng-show="radius == 'topleft' || radius == 'all'" class="radius-top-left"></span>
<span ng-show="radius == 'topright' || radius == 'all'" class="radius-top-right"></span>
<span ng-show="radius == 'bottomleft' || radius == 'all'" class="radius-bottom-left"></span>
<span ng-show="radius == 'bottomright' || radius == 'all'" class="radius-bottom-right"></span>
</li>
</ul>
</div>
<div class="box-slider">
<div ui-slider min="0" max="40" step="1" ng-model="selectedradius.value"></div>
</div>
</div>

View File

@@ -1,8 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.shadow">
<div class="box-slider">
<div ui-slider min="0" max="100" step="1" ng-model="item.values.shadow"></div>
</div>
</div>

View File

@@ -1,8 +0,0 @@
<div ng-controller="Umbraco.canvasdesigner.slider">
<div class="box-slider">
<div ui-slider min="{{item.min}}" max="{{item.max}}" step="1" ng-model="item.values.slider"></div>
</div>
</div>

View File

@@ -1,165 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Umbraco Canvas Designer</title>
<link href="../assets/css/canvasdesigner.css" type="text/css" rel="stylesheet" />
<link href="../lib/spectrum/spectrum.css" type="text/css" rel="stylesheet" />
<link href="../lib/jquery-ui/jquery-ui-1.10.4.custom.min.css" type="text/css" rel="stylesheet" />
</head>
<body id="canvasdesignerPanel" ng-mouseover="outlinePositionHide()" ng-class="{ leftOpen: (showStyleEditor || showPalettePicker) && !showDevicesPreview }" ng-controller="Umbraco.canvasdesignerController">
<div class="wait" ng-show="!frameLoaded"></div>
<div id="demo-iframe-wrapper" ng-show="frameLoaded" class="{{previewDevice.css}}">
<iframe id="resultFrame" ng-src="{{pageUrl}}" frameborder="0" iframe-is-loaded></iframe>
</div>
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
<div class="fix-left-menu selected">
<div class="avatar">
<img
ng-src="../assets/img/application/logo.png"
ng-srcset="../assets/img/application/logo@2x.png 2x,
../assets/img/application/logo@3x.png 3x" />
</div>
<ul class="sections" ng-class="{selected: showDevicesPreview}">
<li ng-repeat="device in devices" ng-class="{ current:previewDevice==device }" ng-click="updatePreviewDevice(device)">
<a href="#"><i class="icon {{device.icon}}" title="{{device.title}}"></i><span></span></a>
</li>
<li ng-click="closePreviewDevice()" ng-if="enableCanvasdesigner > 0">
<a href="" class="more-options">
<i></i>
<i></i>
<i></i>
</a>
</li>
<li ng-click="exitPreview()">
<a href="#" title="Exit Preview"><i class="icon icon-wrong"></i><span> </span></a>
</li>
</ul>
<ul class="sections" ng-class="{selected: !showDevicesPreview}" ng-if="enableCanvasdesigner > 0">
<li ng-click="openPreviewDevice()">
<a href="#"><i class="icon {{previewDevice.icon}}"></i><span>Preview</span></a>
</li>
<li ng-click="openPalettePicker()" ng-class=" { current:showPalettePicker }">
<a href="#"><i class="icon icon-palette"></i><span>Palette</span></a>
</li>
<li ng-click="openStyleEditor()" ng-class=" { current:showStyleEditor }">
<a href="#"><i class="icon icon-paint-roller"></i><span>UI Designer</span></a>
</li>
</ul>
</div>
<div class="main-panel" ng-class="{selected: !showDevicesPreview && ( showPalettePicker || showStyleEditor )}">
<div class="header">
<h3>Palette Style</h3>
</div>
<div class="content">
<ul class="samples">
<li ng-repeat="palette in canvasdesignerPalette">
<a href="#" ng-click="refreshCanvasdesignerByPalette(palette)">
<h4>{{palette.name}}</h4>
<ul class="samples">
<li style="background-color:{{palette.color1}}"></li>
<li style="background-color:{{palette.color2}}"></li>
<li style="background-color:{{palette.color3}}"></li>
<li style="background-color:{{palette.color4}}"></li>
<li style="background-color:{{palette.color5}}"></li>
</ul>
</a>
</li>
</ul>
</div>
<div class="btn-group">
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu" ng-init="opendropdown = false" ng-show="opendropdown">
<li><a ng-click="createStyle();opendropdown = false">Create Page Style</a></li>
<li><a ng-click="deleteCanvasdesigner();opendropdown = false">Reset page style</a></li>
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
</ul>
</div>
</div>
<div class="main-panel" ng-class="{selected: !showDevicesPreview && showStyleEditor}">
<div ng-show="!currentSelected">
<div class="header">
<h3>Select</h3>
</div>
<div class="content">
<ul class="samples">
<li ng-repeat="configItem in canvasdesignerModel.configs"
ng-mousemove="refreshOutlinePosition(configItem)"
ng-class="{hover: configItem.highlighted == true}"
ng-mouseenter="setCurrentHighlighted(configItem)"
ng-mouseleave="configItem.highlighted = false"
ng-click="setCurrentSelected(configItem)">
{{configItem.name}}
</li>
</ul>
</div>
</div>
<div ng-repeat="configItem in canvasdesignerModel.configs" ng-show="currentSelected && currentSelected.name.toLowerCase() == configItem.name.toLowerCase()
&& currentSelected.schema.toLowerCase() == configItem.schema.toLowerCase()" on-finish-render-filters>
<div class="header">
<h3><i class="icon icon-list" ng-click="outlineSelectedHide()"></i> {{configItem.name}}</h3>
</div>
<div class="content">
<div class="editor-category" ng-repeat="category in getCategories(configItem)" ng-show="hasEditor(configItem.editors, category)">
<h4 class="panel-title" ng-click="setSelectedCategory(category)">
{{category}}
<i class="icon icon-remove small right" ng-show="categoriesVisibility[category] === true"></i>
<i class="icon icon-add small right" ng-hide="categoriesVisibility[category] === true"></i>
</h4>
<div class="canvasdesigner-panel-container" ng-show="categoriesVisibility[category] === true">
<div class="canvasdesigner-panel-property" ng-repeat="item in configItem.editors" ng-show="item.category == category">
<h5>{{item.name}} <i class="icon icon-help-alt"></i></h5>
<div ng-include="'../preview/editors/' + item.type + '.html'"></div>
</div>
</div>
</div>
</div>
</div>
<div class="btn-group">
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu" ng-init="opendropdown = false" ng-show="opendropdown">
<li><a ng-click="createStyle();opendropdown = false">Create Page Style</a></li>
<li><a ng-click="deleteCanvasdesigner();opendropdown = false">Reset page style</a></li>
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
</ul>
</div>
</div>
<div class="float-panel"></div>
</div>
<div id="speechbubble">
<p>Styles saved and published</p>
</div>
<script src="../lib/rgrove-lazyload/lazyload.js"></script>
<script src="../js/canvasdesigner.loader.js"></script>
</body>
</html>

View File

@@ -64,14 +64,19 @@
};
scope.avatarClick = function () {
scope.userDialog = {
view: "user",
show: true,
close: function (oldModel) {
scope.userDialog.show = false;
scope.userDialog = null;
}
};
if(!scope.userDialog) {
scope.userDialog = {
view: "user",
show: true,
close: function (oldModel) {
scope.userDialog.show = false;
scope.userDialog = null;
}
};
} else {
scope.userDialog.show = false;
scope.userDialog = null;
}
};
}

View File

@@ -125,6 +125,12 @@ Use this directive to render an umbraco button. The directive can be used to gen
});
scope.clickButton = function(event) {
if(scope.action) {
scope.action({$event: event});
}
};
scope.$on('$destroy', function() {
unbindStateWatcher();
});

View File

@@ -8,6 +8,8 @@
var evts = [];
var isInfoTab = false;
scope.publishStatus = {};
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
function onInit() {
@@ -50,7 +52,7 @@
scope.openDocumentType = function (documentType) {
var url = "/settings/documenttypes/edit/" + documentType.id;
$location.path(url);
$location.url(url);
};
scope.updateTemplate = function (templateAlias) {
@@ -134,13 +136,13 @@
}
// published node
if(node.hasPublishedVersion === true && node.publishDate && node.published === true) {
if(node.publishDate && node.published === true) {
scope.publishStatus.label = localizationService.localize("content_published");
scope.publishStatus.color = "success";
}
// published node with pending changes
if(node.hasPublishedVersion === true && node.publishDate && node.published === false) {
if (node.edited === true && node.publishDate) {
scope.publishStatus.label = localizationService.localize("content_publishedPendingChanges");
scope.publishStatus.color = "success"
}
@@ -149,8 +151,17 @@
function setPublishDate(date) {
if (!date) {
return;
}
//The date being passed in here is the user's local date/time that they have selected
//we need to convert this date back to the server date on the model.
var serverTime = dateHelper.convertToServerStringTime(moment(date), Umbraco.Sys.ServerVariables.application.serverTimeOffset);
// update publish value
scope.node.releaseDate = date;
scope.node.releaseDate = serverTime;
// make sure dates are formatted to the user's locale
formatDatesToLocal();
@@ -174,8 +185,17 @@
function setUnpublishDate(date) {
if (!date) {
return;
}
//The date being passed in here is the user's local date/time that they have selected
//we need to convert this date back to the server date on the model.
var serverTime = dateHelper.convertToServerStringTime(moment(date), Umbraco.Sys.ServerVariables.application.serverTimeOffset);
// update publish value
scope.node.removeDate = date;
scope.node.removeDate = serverTime;
// make sure dates are formatted to the user's locale
formatDatesToLocal();
@@ -223,7 +243,7 @@
// load audit trail when on the info tab
evts.push(eventsService.on("app.tabChange", function (event, args) {
$timeout(function(){
if (args.id === -1) {
if (args.alias === "info") {
isInfoTab = true;
loadAuditTrail();
} else {

View File

@@ -204,7 +204,7 @@ Use this directive to construct a header inside the main editor window.
(function() {
'use strict';
function EditorHeaderDirective(iconHelper) {
function EditorHeaderDirective(iconHelper, $location) {
function link(scope, el, attr, ctrl) {
@@ -246,12 +246,9 @@ Use this directive to construct a header inside the main editor window.
}
};
scope.selectVariant = function(event, variant) {
console.log("selec variant called");
if(scope.onSelectVariant) {
scope.onSelectVariant({"variant": variant});
scope.vm.dropdownOpen = false;
}
scope.selectVariant = function (event, variant) {
scope.vm.dropdownOpen = false;
$location.search({ languageId: variant.language.id });
};
scope.openIconPicker = function() {
@@ -324,7 +321,6 @@ Use this directive to construct a header inside the main editor window.
hideDescription: "@",
descriptionLocked: "@",
variants: "=",
onSelectVariant: "&",
navigation: "=",
key: "=",
onBack: "&?",

View File

@@ -1,64 +1,64 @@
(function() {
'use strict';
(function () {
'use strict';
function EditorNavigationDirective() {
function EditorNavigationDirective(eventsService) {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
scope.showNavigation = true;
scope.showNavigation = true;
scope.clickNavigationItem = function(selectedItem) {
setItemToActive(selectedItem);
runItemAction(selectedItem);
};
scope.clickNavigationItem = function (selectedItem) {
setItemToActive(selectedItem);
runItemAction(selectedItem);
eventsService.emit("app.tabChange", selectedItem);
};
function runItemAction(selectedItem) {
if (selectedItem.action) {
selectedItem.action(selectedItem);
}
}
function setItemToActive(selectedItem) {
// set all other views to inactive
if (selectedItem.view) {
for (var index = 0; index < scope.navigation.length; index++) {
var item = scope.navigation[index];
item.active = false;
}
// set view to active
selectedItem.active = true;
}
}
function activate() {
// hide navigation if there is only 1 item
if (scope.navigation.length <= 1) {
scope.showNavigation = false;
function runItemAction(selectedItem) {
if (selectedItem.action) {
selectedItem.action(selectedItem);
}
}
}
function setItemToActive(selectedItem) {
// set all other views to inactive
if (selectedItem.view) {
activate();
for (var index = 0; index < scope.navigation.length; index++) {
var item = scope.navigation[index];
item.active = false;
}
}
// set view to active
selectedItem.active = true;
}
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-navigation.html',
scope: {
navigation: "="
},
link: link
};
function activate() {
return directive;
}
// hide navigation if there is only 1 item
if (scope.navigation.length <= 1) {
scope.showNavigation = false;
}
angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective);
}
activate();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-navigation.html',
scope: {
navigation: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective);
})();

View File

@@ -115,7 +115,9 @@ angular.module("umbraco.directives")
toolbar: toolbar,
content_css: stylesheets,
style_formats: styleFormats,
autoresize_bottom_margin: 0
autoresize_bottom_margin: 0,
//see http://archive.tinymce.com/wiki.php/Configuration:cache_suffix
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster
};
@@ -143,6 +145,12 @@ angular.module("umbraco.directives")
}
}
}
if (val === "true") {
tinyMceConfig.customConfig[i] = true;
}
if (val === "false") {
tinyMceConfig.customConfig[i] = false;
}
}
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);

View File

@@ -59,7 +59,6 @@
</pre>
<h1>General Options</h1>
Lorem ipsum dolor sit amet..
<table>
<thead>
<tr>
@@ -74,7 +73,7 @@ Lorem ipsum dolor sit amet..
<td>Set the title of the overlay.</td>
</tr>
<tr>
<td>model.subTitle</td>
<td>model.subtitle</td>
<td>String</td>
<td>Set the subtitle of the overlay.</td>
</tr>
@@ -88,6 +87,11 @@ Lorem ipsum dolor sit amet..
<td>String</td>
<td>Set an alternate submit button label key for localized texts</td>
</tr>
<tr>
<td>model.submitButtonState</td>
<td>String</td>
<td>Set the state for the submit button</td>
</tr>
<tr>
<td>model.hideSubmitButton</td>
<td>Boolean</td>
@@ -408,7 +412,7 @@ Opens an overlay to show a custom YSOD. </br>
(function() {
'use strict';
function OverlayDirective($timeout, formHelper, overlayHelper, localizationService) {
function OverlayDirective($timeout, formHelper, overlayHelper, localizationService, $q) {
function link(scope, el, attr, ctrl) {
@@ -439,7 +443,7 @@ Opens an overlay to show a custom YSOD. </br>
// this has to be done inside a timeout to ensure the destroy
// event on other overlays is run before registering a new one
registerOverlay();
setOverlayIndent();
});
@@ -496,6 +500,7 @@ Opens an overlay to show a custom YSOD. </br>
var activeElementType = document.activeElement.tagName;
var clickableElements = ["A", "BUTTON"];
var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter");
var submitOnEnterValue = submitOnEnter ? document.activeElement.getAttribute("overlay-submit-on-enter") : "";
if(clickableElements.indexOf(activeElementType) === 0) {
document.activeElement.click();
@@ -503,7 +508,9 @@ Opens an overlay to show a custom YSOD. </br>
} else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
} else {
} else if (submitOnEnter && submitOnEnterValue === "false") {
// don't do anything
}else {
scope.$apply(function () {
scope.submitForm(scope.model);
});
@@ -554,8 +561,8 @@ Opens an overlay to show a custom YSOD. </br>
var overlayWidth = el.context.clientWidth;
el.css('width', overlayWidth - indentSize);
if(scope.position === "center" || scope.position === "target") {
if(scope.position === "center" && overlayIndex > 0 || scope.position === "target" && overlayIndex > 0) {
var overlayTopPosition = el.context.offsetTop;
el.css('top', overlayTopPosition + indentSize);
}
@@ -630,15 +637,22 @@ Opens an overlay to show a custom YSOD. </br>
scope.submitForm = function(model) {
if(scope.model.submit) {
if (formHelper.submitForm({scope: scope})) {
formHelper.resetForm({ scope: scope });
if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) {
scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
} else {
unregisterOverlay();
scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
}
if (formHelper.submitForm({ scope: scope, skipValidation: scope.model.skipFormValidation})) {
if (scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) {
//wrap in a when since we don't know if this is a promise or not
$q.when(scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton)).then(
function() {
formHelper.resetForm({ scope: scope });
}, angular.noop);
} else {
unregisterOverlay();
//wrap in a when since we don't know if this is a promise or not
$q.when(scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton)).then(
function() {
formHelper.resetForm({ scope: scope });
}, angular.noop);
}
}
}

View File

@@ -4,29 +4,32 @@
* @restrict E
**/
angular.module("umbraco.directives")
.directive('umbProperty', function (umbPropEditorHelper) {
.directive('umbProperty', function (umbPropEditorHelper, userService) {
return {
scope: {
property: "="
},
transclude: true,
restrict: 'E',
replace: true,
replace: true,
templateUrl: 'views/components/property/umb-property.html',
link: function(scope) {
scope.propertyAlias = Umbraco.Sys.ServerVariables.isDebuggingEnabled === true ? scope.property.alias : null;
link: function (scope) {
userService.getCurrentUser().then(function (u) {
var isAdmin = u.userGroups.indexOf('admin') !== -1;
scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? scope.property.alias : null;
});
},
//Define a controller for this directive to expose APIs to other directives
controller: function ($scope, $timeout) {
var self = this;
//set the API properties/methods
self.property = $scope.property;
self.setPropertyError = function(errorMsg) {
self.setPropertyError = function (errorMsg) {
$scope.property.propertyErrorMessage = errorMsg;
};
}
};
});
});

View File

@@ -420,8 +420,8 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
emitEvent("treeNodeAltSelect", { element: elem, tree: scope.tree, node: n, event: ev });
};
//watch for section changes
scope.$watch("section", function (newVal, oldVal) {
//watch for section changes and customtreeparams changes
scope.$watchCollection("[section, customtreeparams]", function (newVal, oldVal) {
if (!scope.tree) {
loadTree();
@@ -440,7 +440,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
//clear any active trees to reset lookups
lastSection = newVal;
}
});
});
setupExternalEvents();
loadTree();

View File

@@ -125,7 +125,7 @@
function link(scope, el, attr, ngModel) {
// Load in ace library
assetsService.load(['lib/ace-builds/src-min-noconflict/ace.js', 'lib/ace-builds/src-min-noconflict/ext-language_tools.js']).then(function () {
assetsService.load(['lib/ace-builds/src-min-noconflict/ace.js', 'lib/ace-builds/src-min-noconflict/ext-language_tools.js'], scope).then(function () {
if (angular.isUndefined(window.ace)) {
throw new Error('ui-ace need ace to work... (o rly?)');
} else {

View File

@@ -101,7 +101,7 @@
var clipboard;
var target = element[0];
assetsService.loadJs("lib/clipboard/clipboard.min.js")
assetsService.loadJs("lib/clipboard/clipboard.min.js", scope)
.then(function () {
if(scope.umbClipboardTarget) {

View File

@@ -92,10 +92,10 @@ Use this directive to render a date time picker
scope.hasTranscludedContent = element.find('.js-datePicker__transcluded-content')[0].children.length > 0;
// load css file for the date picker
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css');
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', scope);
// load the js file for the date picker
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js').then(function () {
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js', scope).then(function () {
// init date picker
initDatePicker();
});

View File

@@ -489,7 +489,7 @@
scope.editPropertyTypeSettings = function(property, group) {
if (!property.inherited && !property.locked) {
if (!property.inherited) {
scope.propertySettingsDialogModel = {};
scope.propertySettingsDialogModel.title = "Property settings";
@@ -547,6 +547,7 @@
property.validation.pattern = oldModel.property.validation.pattern;
property.showOnMemberProfile = oldModel.property.showOnMemberProfile;
property.memberCanEdit = oldModel.property.memberCanEdit;
property.isSensitiveValue = oldModel.property.isSensitiveValue;
// because we set state to active, to show a preview, we have to check if has been filled out
// label is required so if it is not filled we know it is a placeholder

View File

@@ -10,12 +10,12 @@ function noDirtyCheck() {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
elm.focus(function () {
scope.$watch(function() {
ctrl.$pristine = false;
});
});
var alwaysFalse = {
get: function () { return false; },
set: function () { }
};
Object.defineProperty(ctrl, '$pristine', alwaysFalse);
Object.defineProperty(ctrl, '$dirty', alwaysFalse);
}
};

View File

@@ -255,8 +255,6 @@ angular.module('umbraco.mocks').
"errors_missingTitle": "Please enter a title",
"errors_missingType": "Please choose a type",
"errors_pictureResizeBiggerThanOrg": "You're about to make the picture larger than the original size. Are you sure that you want to proceed?",
"errors_pythonErrorHeader": "Error in python script",
"errors_pythonErrorText": "The python script has not been saved, because it contained error(s)",
"errors_startNodeDoesNotExists": "Startnode deleted, please contact your administrator",
"errors_stylesMustMarkBeforeSelect": "Please mark content before changing style",
"errors_stylesNoStylesOnPage": "No active styles available",
@@ -596,10 +594,6 @@ angular.module('umbraco.mocks').
"speechBubbles_fileSavedHeader": "File saved",
"speechBubbles_fileSavedText": "File saved without any errors",
"speechBubbles_languageSaved": "Language saved",
"speechBubbles_pythonErrorHeader": "Python script not saved",
"speechBubbles_pythonErrorText": "Python script could not be saved due to error",
"speechBubbles_pythonSavedHeader": "Python script saved",
"speechBubbles_pythonSavedText": "No errors in python script",
"speechBubbles_templateErrorHeader": "Template not saved",
"speechBubbles_templateErrorText": "Please make sure that you do not have 2 templates with the same alias",
"speechBubbles_templateSavedHeader": "Template saved",
@@ -702,7 +696,6 @@ angular.module('umbraco.mocks').
"treeHeaders_nodeTypes": "Document Types",
"treeHeaders_packager": "Packages",
"treeHeaders_packages": "Packages",
"treeHeaders_python": "Python Files",
"treeHeaders_repositories": "Install from repository",
"treeHeaders_runway": "Install Runway",
"treeHeaders_runwayModules": "Runway modules",

View File

@@ -71,7 +71,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
performLogin: function (username, password) {
if (!username || !password) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Username or password cannot be empty'
});
}
@@ -125,7 +125,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
performRequestPasswordReset: function (email) {
if (!email) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Email address cannot be empty'
});
}
@@ -135,7 +135,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
// be done properly.
var emailRegex = /\S+@\S+\.\S+/;
if (!emailRegex.test(email)) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Email address is not valid'
});
}
@@ -173,13 +173,13 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
performValidatePasswordResetCode: function (userId, resetCode) {
if (!userId) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'User Id cannot be empty'
});
}
if (!resetCode) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Reset code cannot be empty'
});
}
@@ -238,25 +238,25 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
performSetPassword: function (userId, password, confirmPassword, resetCode) {
if (userId === undefined || userId === null) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'User Id cannot be empty'
});
}
if (!password) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Password cannot be empty'
});
}
if (password !== confirmPassword) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Password and confirmation do not match'
});
}
if (!resetCode) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'Reset code cannot be empty'
});
}
@@ -276,7 +276,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
unlinkLogin: function (loginProvider, providerKey) {
if (!loginProvider || !providerKey) {
return angularHelper.rejectedPromise({
return $q.reject({
errorMsg: 'loginProvider or providerKey cannot be empty'
});
}

View File

@@ -313,17 +313,18 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* });
* </pre>
*
* @param {Int} id id of content item to return
* @param {Int} id id of content item to return
* @param {Int} languageId optional ID of the language to retrieve the item in
* @returns {Promise} resourcePromise object containing the content item.
*
*/
getById: function (id) {
getById: function (id, languageId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetById",
[{ id: id }])),
{ id: id, languageId: languageId })),
'Failed to retrieve data for content id ' + id);
},

View File

@@ -13,7 +13,7 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) {
saveTourStatus: function (tourStatus) {
if (!tourStatus) {
return angularHelper.rejectedPromise({ errorMsg: 'tourStatus cannot be empty' });
return $q.reject({ errorMsg: 'tourStatus cannot be empty' });
}
return umbRequestHelper.resourcePromise(
@@ -37,7 +37,7 @@ function currentUserResource($q, $http, umbRequestHelper, umbDataFormatter) {
performSetInvitedUserPassword: function (newPassword) {
if (!newPassword) {
return angularHelper.rejectedPromise({ errorMsg: 'newPassword cannot be empty' });
return $q.reject({ errorMsg: 'newPassword cannot be empty' });
}
return umbRequestHelper.resourcePromise(

View File

@@ -0,0 +1,64 @@
/**
* @ngdoc service
* @name umbraco.resources.languageResource
* @description Handles retrieving and updating language data
**/
function languageResource($http, umbRequestHelper) {
return {
getCultures: function() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"languageApiBaseUrl",
"GetAllCultures")),
"Failed to get cultures");
},
getAll: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"languageApiBaseUrl",
"GetAllLanguages")),
"Failed to get languages");
},
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"languageApiBaseUrl",
"GetLanguage",
{ id: id })),
"Failed to get language with id " + id);
},
save: function (lang) {
if (!lang)
throw "'lang' parameter cannot be null";
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"languageApiBaseUrl",
"SaveLanguage"), lang),
"Failed to save language " + lang.id);
},
deleteById: function (id) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"languageApiBaseUrl",
"DeleteLanguage",
{ id: id })),
"Failed to delete item " + id);
}
};
}
angular.module('umbraco.resources').factory('languageResource', languageResource);

View File

@@ -493,7 +493,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @methodOf umbraco.resources.mediaResource
*
* @description
* Empties the media recycle bin
* Paginated search for media items starting on the supplied nodeId
*
* ##usage
* <pre>
@@ -506,7 +506,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @param {string} query The search query
* @param {int} pageNumber The page number
* @param {int} pageSize The number of media items on a page
* @param {int} searchFrom Id to search from
* @param {int} searchFrom NodeId to search from (-1 for root)
* @returns {Promise} resourcePromise object.
*
*/

View File

@@ -10,8 +10,8 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
return umbRequestHelper.postSaveContent({
restApiUrl: umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"PostSave"),
"memberApiBaseUrl",
"PostSave"),
content: content,
action: action,
files: files,
@@ -22,8 +22,7 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
}
return {
getPagedResults: function (memberTypeAlias, options) {
getPagedResults: function(memberTypeAlias, options) {
if (memberTypeAlias === 'all-members') {
memberTypeAlias = null;
@@ -67,35 +66,35 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
}
var params = [
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: toBool(options.orderBySystemField) },
{ filter: options.filter }
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: toBool(options.orderBySystemField) },
{ filter: options.filter }
];
if (memberTypeAlias != null) {
params.push({ memberTypeAlias: memberTypeAlias });
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetPagedResults",
params)),
'Failed to retrieve member paged result');
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetPagedResults",
params)),
'Failed to retrieve member paged result');
},
getListNode: function (listName) {
getListNode: function(listName) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetListNodeDisplay",
[{ listName: listName }])),
'Failed to retrieve data for member list ' + listName);
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetListNodeDisplay",
[{ listName: listName }])),
'Failed to retrieve data for member list ' + listName);
},
/**
@@ -119,15 +118,15 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the member item.
*
*/
getByKey: function (key) {
getByKey: function(key) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetByKey",
[{ key: key }])),
'Failed to retrieve data for member id ' + key);
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetByKey",
[{ key: key }])),
'Failed to retrieve data for member id ' + key);
},
/**
@@ -150,14 +149,14 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object.
*
*/
deleteByKey: function (key) {
deleteByKey: function(key) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"DeleteByKey",
[{ key: key }])),
'Failed to delete item ' + key);
$http.post(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"DeleteByKey",
[{ key: key }])),
'Failed to delete item ' + key);
},
/**
@@ -190,24 +189,24 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the member scaffold.
*
*/
getScaffold: function (alias) {
getScaffold: function(alias) {
if (alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }])),
'Failed to retrieve data for empty member item type ' + alias);
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }])),
'Failed to retrieve data for empty member item type ' + alias);
}
else {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty")),
'Failed to retrieve data for empty member item type ' + alias);
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty")),
'Failed to retrieve data for empty member item type ' + alias);
}
},
@@ -240,7 +239,7 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the saved media item.
*
*/
save: function (member, isNew, files) {
save: function(member, isNew, files) {
return saveMember(member, "save" + (isNew ? "New" : ""), files);
}
};

View File

@@ -0,0 +1,73 @@
/**
* @ngdoc service
* @name umbraco.resources.memberGroupResource
* @description Loads in data for member groups
**/
function memberGroupResource($q, $http, umbRequestHelper) {
return {
//return all member types
getGroups: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberGroupApiBaseUrl",
"GetAllGroups")),
"Failed to retrieve data for member groups");
},
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberGroupApiBaseUrl",
"GetById",
[{ id: id }])),
"Failed to retrieve member group");
},
deleteById: function (id) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"memberGroupApiBaseUrl",
"DeleteById",
[{ id: id }])),
"Failed to delete member group");
},
getScaffold: function() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberGroupApiBaseUrl",
"GetEmpty")),
"Failed to retrieve data for member group");
},
/**
* @ngdoc method
* @name umbraco.resources.memberGroupResource#save
* @methodOf umbraco.resources.memberGroupResource
*
* @description
* Saves or update a member group
*
* @param {Object} member group object to create/update
* @returns {Promise} resourcePromise object.
*
*/
save: function (memberGroup) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("memberGroupApiBaseUrl", "PostSave"), memberGroup),
"Failed to save data for member group, id: " + memberGroup.id);
}
};
}
angular.module('umbraco.resources').factory('memberGroupResource', memberGroupResource);

View File

@@ -8,26 +8,7 @@
*/
function angularHelper($log, $q) {
return {
/**
* @ngdoc function
* @name umbraco.services.angularHelper#rejectedPromise
* @methodOf umbraco.services.angularHelper
* @function
*
* @description
* In some situations we need to return a promise as a rejection, normally based on invalid data. This
* is a wrapper to do that so we can save on writing a bit of code.
*
* @param {object} objReject The object to send back with the promise rejection
*/
rejectedPromise: function (objReject) {
var deferred = $q.defer();
//return an error object including the error message for UI
deferred.reject(objReject);
return deferred.promise;
},
/**
* @ngdoc function
* @name safeApply
@@ -153,4 +134,4 @@ function angularHelper($log, $q) {
}
};
}
angular.module('umbraco.services').factory('angularHelper', angularHelper);
angular.module('umbraco.services').factory('angularHelper', angularHelper);

View File

@@ -129,14 +129,12 @@ angular.module('umbraco.services')
asset.state = "loading";
LazyLoad.css(appendRnd(path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
scope = $rootScope;
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
@@ -171,14 +169,12 @@ angular.module('umbraco.services')
LazyLoad.js(appendRnd(path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
scope = $rootScope;
}
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
});
} else if (asset.state === "loaded") {
@@ -234,8 +230,7 @@ angular.module('umbraco.services')
assets.push(asset);
}
//we need to always push to the promises collection to monitor correct
//execution
//we need to always push to the promises collection to monitor correct execution
promises.push(asset.deferred.promise);
}
});
@@ -256,8 +251,7 @@ angular.module('umbraco.services')
function assetLoaded(asset) {
asset.state = "loaded";
if (!scope) {
asset.deferred.resolve(true);
return;
scope = $rootScope;
}
angularHelper.safeApply(scope,
function () {
@@ -280,4 +274,4 @@ angular.module('umbraco.services')
};
return service;
});
});

View File

@@ -40,9 +40,6 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
if (!args.content) {
throw "args.content is not defined";
}
if (!args.statusMessage) {
throw "args.statusMessage is not defined";
}
if (!args.saveMethod) {
throw "args.saveMethod is not defined";
}
@@ -54,13 +51,11 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
//we will use the default one for content if not specified
var rebindCallback = args.rebindCallback === undefined ? self.reBindChangedProperties : args.rebindCallback;
var deferred = $q.defer();
if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage, action: args.action })) {
if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, action: args.action })) {
args.scope.busy = true;
args.saveMethod(args.content, $routeParams.create, fileManager.getFiles())
return args.saveMethod(args.content, $routeParams.create, fileManager.getFiles())
.then(function (data) {
formHelper.resetForm({ scope: args.scope, notifications: data.notifications });
@@ -74,7 +69,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
});
args.scope.busy = false;
deferred.resolve(data);
return $q.when(data);
}, function (err) {
self.handleSaveError({
@@ -91,14 +86,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
}
}
args.scope.busy = false;
deferred.reject(err);
return $q.reject(err);
});
}
else {
deferred.reject();
return $q.reject();
}
return deferred.promise;
},
/** Used by the content editor and media editor to add an info tab to the tabs array (normally known as the properties tab) */
@@ -159,7 +153,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
//publish action
return {
letter: ch,
labelKey: "buttons_saveAndPublish",
labelKey: args.content.variants && args.content.variants.length > 1 ? "buttons_saveAndPublishMany" : "buttons_saveAndPublish",
handler: args.methods.saveAndPublish,
hotKey: "ctrl+p",
hotKeyWhenHidden: true,
@@ -440,7 +434,25 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
//a method to ignore built-in prop changes
var shouldIgnore = function(propName) {
return _.some(["tabs", "notifications", "ModelState", "tabs", "properties"], function(i) {
return _.some([
"tabs",
"notifications",
"ModelState",
"tabs",
"properties",
"apps",
"createDateFormatted",
"releaseDateYear",
"releaseDateMonth",
"releaseDateDayNumber",
"releaseDateDay",
"releaseDateTime",
"removeDateYear",
"removeDateMonth",
"removeDateDayNumber",
"removeDateDay",
"removeDateTime",
], function (i) {
return i === propName;
});
};
@@ -489,6 +501,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
*
* @description
* A function to handle what happens when we have validation issues from the server side
*
*/
handleSaveError: function (args) {

View File

@@ -40,20 +40,12 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
else {
currentForm = args.formCtrl;
}
//if no statusPropertyName is set we'll default to formStatus.
if (!args.statusPropertyName) {
args.statusPropertyName = "formStatus";
}
//if no statusTimeout is set, we'll default to 2500 ms
if (!args.statusTimeout) {
args.statusTimeout = 2500;
}
//the first thing any form must do is broadcast the formSubmitting event
args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action });
//then check if the form is valid
if (!args.skipValidation) {
if (!args.skipValidation) {
if (currentForm.$invalid) {
return false;
}
@@ -62,16 +54,6 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
//reset the server validations
serverValidationManager.reset();
//check if a form status should be set on the scope
if (args.statusMessage) {
args.scope[args.statusPropertyName] = args.statusMessage;
//clear the message after the timeout
$timeout(function () {
args.scope[args.statusPropertyName] = undefined;
}, args.statusTimeout);
}
return true;
},
@@ -95,14 +77,7 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
throw "args.scope cannot be null";
}
//if no statusPropertyName is set we'll default to formStatus.
if (!args.statusPropertyName) {
args.statusPropertyName = "formStatus";
}
//clear the status
args.scope[args.statusPropertyName] = null;
this.showNotifications(args);
this.showNotifications(args);
args.scope.$broadcast("formSubmitted", { scope: args.scope });
},
@@ -216,4 +191,4 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
}
};
}
angular.module('umbraco.services').factory('formHelper', formHelper);
angular.module('umbraco.services').factory('formHelper', formHelper);

View File

@@ -0,0 +1,37 @@
(function () {
"use strict";
function javascriptLibraryService($q, $http, umbRequestHelper) {
var existingLocales = [];
function getSupportedLocalesForMoment() {
var deferred = $q.defer();
if (existingLocales.length === 0) {
umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"backOfficeAssetsApiBaseUrl",
"GetSupportedMomentLocales")),
"Failed to get cultures").then(function(locales) {
existingLocales = locales;
deferred.resolve(existingLocales);
});
} else {
deferred.resolve(existingLocales);
}
return deferred.promise;
}
var service = {
getSupportedLocalesForMoment: getSupportedLocalesForMoment
};
return service;
}
angular.module("umbraco.services").factory("javascriptLibraryService", javascriptLibraryService);
})();

View File

@@ -181,11 +181,13 @@
var firstAllowedLayout = {};
for (var i = 0; layouts.length > i; i++) {
var layout = layouts[i];
if (layout.selected === true) {
firstAllowedLayout = layout;
break;
if (layouts != null) {
for (var i = 0; layouts.length > i; i++) {
var layout = layouts[i];
if (layout.selected === true) {
firstAllowedLayout = layout;
break;
}
}
}

View File

@@ -8,10 +8,39 @@
* @description
* Defines the methods that are called when menu items declare only an action to execute
*/
function umbracoMenuActions($q, treeService, $location, navigationService, appState) {
function umbracoMenuActions($q, treeService, $location, navigationService, appState, localizationService, userResource, umbRequestHelper, notificationsService) {
return {
"ExportMember": function(args) {
var url = umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"ExportMemberData",
[{ key: args.entity.id }]);
umbRequestHelper.downloadFile(url).then(function() {
localizationService.localize("speechBubbles_memberExportedSuccess").then(function (value) {
notificationsService.success(value);
})
}, function(data) {
localizationService.localize("speechBubbles_memberExportedError").then(function (value) {
notificationsService.error(value);
})
});
},
"DisableUser": function(args) {
localizationService.localize("defaultdialogs_confirmdisable").then(function (txtConfirmDisable) {
var currentMenuNode = UmbClientMgr.mainTree().getActionNode();
if (confirm(txtConfirmDisable + ' "' + args.entity.name + '"?\n\n')) {
userResource.disableUser(args.entity.id).then(function () {
navigationService.syncTree({ tree: args.treeAlias, path: [args.entity.parentId, args.entity.id], forceReload: true });
});
}
});
},
/**
* @ngdoc method
* @name umbraco.services.umbracoMenuActions#RefreshNode

View File

@@ -288,13 +288,16 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
throw "args.tree cannot be null";
}
if (mainTreeEventHandler) {
//returns a promise
return mainTreeEventHandler.syncTree(args);
if (mainTreeEventHandler) {
if (mainTreeEventHandler.syncTree) {
//returns a promise,
return mainTreeEventHandler.syncTree(args);
}
}
//couldn't sync
return angularHelper.rejectedPromise();
return $q.reject();
},
/**

View File

@@ -0,0 +1,38 @@
/**
@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) {
function open(overlay) {
if(!overlay.position) {
overlay.position = "center";
}
overlay.show = true;
backdropService.open();
eventsService.emit("appState.overlay", overlay);
}
function close() {
backdropService.close();
eventsService.emit("appState.overlay", null);
}
var service = {
open: open,
close: close
};
return service;
}
angular.module("umbraco.services").factory("overlayService", overlayService);
})();

View File

@@ -56,7 +56,7 @@
});
var saveProperties = _.map(realProperties, function (p) {
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile');
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData');
return saveProperty;
});
@@ -267,10 +267,10 @@
// by looking at the key
switch (foundAlias[0]) {
case "umbracoMemberLockedOut":
saveModel.isLockedOut = prop.value.toString() === "1" ? true : false;
saveModel.isLockedOut = prop.value ? (prop.value.toString() === "1" ? true : false) : false;
break;
case "umbracoMemberApproved":
saveModel.isApproved = prop.value.toString() === "1" ? true : false;
saveModel.isApproved = prop.value ? (prop.value.toString() === "1" ? true : false) : true;
break;
case "umbracoMemberComments":
saveModel.comments = prop.value;
@@ -304,14 +304,14 @@
_.each(tab.properties, function (prop) {
//don't include the custom generic tab properties
if (!prop.alias.startsWith("_umb_")) {
//don't include a property that is marked readonly
if (!prop.alias.startsWith("_umb_") && !prop.readonly) {
saveModel.properties.push({
id: prop.id,
alias: prop.alias,
value: prop.value
});
}
});
});
@@ -324,6 +324,22 @@
//this is basically the same as for media but we need to explicitly add some extra properties
var saveModel = this.formatMediaPostData(displayModel, action);
//get the selected variant and build the additional published variants
saveModel.publishVariations = [];
_.each(displayModel.variants,
function (d) {
//set the selected variant if this is current
if (d.current === true) {
saveModel.languageId = d.language.id;
}
if (d.publish === true) {
saveModel.publishVariations.push({
languageId: d.language.id,
segment: d.segment
});
}
});
var propExpireDate = displayModel.removeDate;
var propReleaseDate = displayModel.releaseDate;
var propTemplate = displayModel.template;
@@ -338,4 +354,4 @@
}
angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter);
})();
})();

View File

@@ -333,6 +333,122 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
failureCallback.apply(this, [data, status, headers, config]);
}
});
},
/**
* Downloads a file to the client using AJAX/XHR
* Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html
* See https://stackoverflow.com/a/24129082/694494
*/
downloadFile : function (httpPath) {
var deferred = $q.defer();
// Use an arraybuffer
$http.get(httpPath, { responseType: 'arraybuffer' })
.success(function (data, status, headers) {
var octetStreamMime = 'application/octet-stream';
var success = false;
// Get the headers
headers = headers();
// Get the filename from the x-filename header or default to "download.bin"
var filename = headers['x-filename'] || 'download.bin';
// Determine the content type from the header or default to "application/octet-stream"
var contentType = headers['content-type'] || octetStreamMime;
try {
// Try using msSaveBlob if supported
console.log("Trying saveBlob method ...");
var blob = new Blob([data], { type: contentType });
if (navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
else {
// Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if (saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename);
}
console.log("saveBlob succeeded");
success = true;
} catch (ex) {
console.log("saveBlob method failed with the following exception:");
console.log(ex);
}
if (!success) {
// Get the blob url creator
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator) {
// Try to use a download link
var link = document.createElement('a');
if ('download' in link) {
// Try to simulate a click
try {
// Prepare a blob URL
console.log("Trying download link method with simulated click ...");
var blob = new Blob([data], { type: contentType });
var url = urlCreator.createObjectURL(blob);
link.setAttribute('href', url);
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
console.log("Download link method with simulated click succeeded");
success = true;
} catch (ex) {
console.log("Download link method with simulated click failed with the following exception:");
console.log(ex);
}
}
if (!success) {
// Fallback to window.location method
try {
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
console.log("Trying download link method with window.location ...");
var blob = new Blob([data], { type: octetStreamMime });
var url = urlCreator.createObjectURL(blob);
window.location = url;
console.log("Download link method with window.location succeeded");
success = true;
} catch (ex) {
console.log("Download link method with window.location failed with the following exception:");
console.log(ex);
}
}
}
}
if (!success) {
// Fallback to window.open method
console.log("No methods worked for saving the arraybuffer, using last resort window.open");
window.open(httpPath, '_blank', '');
}
deferred.resolve();
})
.error(function (data, status) {
console.log("Request failed with status: " + status);
deferred.reject({
errorMsg: "An error occurred downloading the file",
data: data,
status: status
});
});
return deferred.promise;
}
};
}

View File

@@ -1,285 +1,325 @@
angular.module('umbraco.services')
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryService) {
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
//very special flag which means that global events cannot close this dialog
manualClose: true,
//very special flag which means that global events cannot close this dialog
manualClose: true,
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
function onLoginDialogClose(success) {
loginDialog = null;
if (success) {
securityRetryQueue.retryAll(currentUser.name);
}
else {
securityRetryQueue.cancelAll();
$location.path('/');
}
}
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
}
//recurse the countdown!
countdownUserTimeout();
}
else {
function onLoginDialogClose(success) {
loginDialog = null;
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
if (success) {
securityRetryQueue.retryAll(currentUser.name);
}
else {
//we've got less than 30 seconds remaining so let's check the server
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
//recurse the countdown!
countdownUserTimeout();
securityRetryQueue.cancelAll();
$location.path('/');
}
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
$timeout(function () {
//when it's successful, return the user data
setCurrentUser(data);
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
},
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
/** Logs the user out
*/
logout: function () {
if (seconds > 30) {
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function() {
var deferred = $q.defer();
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
}
authResource.getCurrentUser()
.then(function (data) {
//recurse the countdown!
countdownUserTimeout();
}
else {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
setCurrentUser(data);
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
}
else {
//we've got less than 30 seconds remaining so let's check the server
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
return deferred.promise;
},
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
//recurse the countdown!
countdownUserTimeout();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
}
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
}
return result;
},
setCurrentUser(data);
/** Logs the user out
*/
logout: function () {
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
}
else {
deferred.resolve(currentUser);
}
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function () {
var deferred = $q.defer();
return deferred.promise;
},
authResource.getCurrentUser()
.then(function (data) {
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
});
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return deferred.promise;
},
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Loads the Moment.js Locale for the current user. */
loadMomentLocaleForCurrentUser: function () {
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
if (locale.indexOf('-') > -1) {
var majorLocale = locale.split('-')[0] + '.js';
if (supportedLocales.indexOf(majorLocale) > -1) {
localeUrls.push('lib/moment/' + majorLocale);
}
}
return assetsService.load(localeUrls, $rootScope);
}
else {
//return a noop promise
var deferred = $q.defer();
var promise = deferred.promise;
deferred.resolve(true);
return promise;
}
}
var promises = {
currentUser: this.getCurrentUser(),
supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment()
}
return $q.all(promises).then(function (values) {
return loadLocales(values.currentUser, values.supportedLocales);
});
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
});

View File

@@ -1,79 +1,79 @@
(function () {
'use strict';
function usersHelperService(localizationService) {
var userStates = [
{ "name": "All", "key": "All"} ,
{ "value": 0, "name": "Active", "key": "Active", "color": "success" },
{ "value": 1, "name": "Disabled", "key": "Disabled", "color": "danger" },
{ "value": 2, "name": "Locked out", "key": "LockedOut", "color": "danger" },
{ "value": 3, "name": "Invited", "key": "Invited", "color": "warning" }
];
angular.forEach(userStates, function (userState) {
var key = "user_state" + userState.key;
localizationService.localize(key).then(function (value) {
var reg = /^\[[\S\s]*]$/g;
var result = reg.test(value);
if (result === false) {
// Only translate if key exists
userState.name = value;
}
});
});
function getUserStateFromValue(value) {
var foundUserState;
angular.forEach(userStates, function (userState) {
if(userState.value === value) {
foundUserState = userState;
}
});
return foundUserState;
}
function getUserStateByKey(key) {
var foundUserState;
angular.forEach(userStates, function (userState) {
if(userState.key === key) {
foundUserState = userState;
}
});
return foundUserState;
}
function getUserStatesFilter(userStatesObject) {
var userStatesFilter = [];
for (var key in userStatesObject) {
if (userStatesObject.hasOwnProperty(key)) {
var userState = getUserStateByKey(key);
if(userState) {
userState.count = userStatesObject[key];
userStatesFilter.push(userState);
}
}
}
return userStatesFilter;
}
////////////
var service = {
getUserStateFromValue: getUserStateFromValue,
getUserStateByKey: getUserStateByKey,
getUserStatesFilter: getUserStatesFilter
};
return service;
}
angular.module('umbraco.services').factory('usersHelper', usersHelperService);
})();
(function () {
'use strict';
function usersHelperService(localizationService) {
var userStates = [
{ "name": "All", "key": "All"} ,
{ "value": 0, "name": "Active", "key": "Active", "color": "success" },
{ "value": 1, "name": "Disabled", "key": "Disabled", "color": "danger" },
{ "value": 2, "name": "Locked out", "key": "LockedOut", "color": "danger" },
{ "value": 3, "name": "Invited", "key": "Invited", "color": "warning" }
];
angular.forEach(userStates, function (userState) {
var key = "user_state" + userState.key;
localizationService.localize(key).then(function (value) {
var reg = /^\[[\S\s]*]$/g;
var result = reg.test(value);
if (result === false) {
// Only translate if key exists
userState.name = value;
}
});
});
function getUserStateFromValue(value) {
var foundUserState;
angular.forEach(userStates, function (userState) {
if(userState.value === value) {
foundUserState = userState;
}
});
return foundUserState;
}
function getUserStateByKey(key) {
var foundUserState;
angular.forEach(userStates, function (userState) {
if(userState.key === key) {
foundUserState = userState;
}
});
return foundUserState;
}
function getUserStatesFilter(userStatesObject) {
var userStatesFilter = [];
for (var key in userStatesObject) {
if (userStatesObject.hasOwnProperty(key)) {
var userState = getUserStateByKey(key);
if(userState) {
userState.count = userStatesObject[key];
userStatesFilter.push(userState);
}
}
}
return userStatesFilter;
}
////////////
var service = {
getUserStateFromValue: getUserStateFromValue,
getUserStateByKey: getUserStateByKey,
getUserStatesFilter: getUserStatesFilter
};
return service;
}
angular.module('umbraco.services').factory('usersHelper', usersHelperService);
})();

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$scope.authenticated = null;
$scope.touchDevice = appState.getGlobalState("touchDevice");
$scope.editors = [];
$scope.overlay = {};
$scope.removeNotification = function (index) {
notificationsService.remove(index);
@@ -112,6 +112,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
};
}));
// events for drawer
// manage the help dialog by subscribing to the showHelp appState
$scope.drawer = {};
evts.push(eventsService.on("appState.drawerState.changed", function (e, args) {
@@ -129,6 +130,12 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
}
}));
// events for overlays
evts.push(eventsService.on("appState.overlay", function (name, args) {
$scope.overlay = args;
}));
// events for tours
evts.push(eventsService.on("appState.tour.start", function (name, args) {
$scope.tour = args;
$scope.tour.show = true;
@@ -142,6 +149,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$scope.tour = null;
}));
// events for backdrop
evts.push(eventsService.on("appState.backdrop", function (name, args) {
$scope.backdrop = args;
}));

View File

@@ -9,7 +9,7 @@
*
* @param {navigationService} navigationService A reference to the navigationService
*/
function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) {
function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper, languageResource) {
//TODO: Need to think about this and an nicer way to acheive what this is doing.
//the tree event handler i used to subscribe to the main tree click events
@@ -31,6 +31,10 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
$scope.menuDialogTitle = null;
$scope.menuActions = [];
$scope.menuNode = null;
$scope.languages = [];
$scope.selectedLanguage = {};
$scope.page = {};
$scope.page.languageSelectorIsOpen = false;
$scope.currentSection = appState.getSectionState("currentSection");
$scope.showNavigation = appState.getGlobalState("showNavigation");
@@ -98,6 +102,19 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
}
}));
// Listen for language updates
evts.push(eventsService.on("editors.languages.languageDeleted", function(e, args) {
languageResource.getAll().then(function(languages) {
$scope.languages = languages;
});
}));
evts.push(eventsService.on("editors.languages.languageCreated", function(e, args) {
languageResource.getAll().then(function(languages) {
$scope.languages = languages;
});
}));
//This reacts to clicks passed to the body element which emits a global call to close all dialogs
evts.push(eventsService.on("app.closeDialogs", function(event) {
if (appState.getGlobalState("stickyNavigation")) {
@@ -115,8 +132,28 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
//when the application is ready and the user is authorized setup the data
evts.push(eventsService.on("app.ready", function(evt, data) {
$scope.authenticated = true;
// load languages
languageResource.getAll().then(function(languages) {
$scope.languages = languages;
// select the default language
$scope.languages.forEach(function(language) {
if(language.isDefault) {
$scope.selectLanguage(language);
}
});
});
}));
$scope.selectLanguage = function(language, languages) {
$scope.selectedLanguage = language;
// close the language selector
$scope.page.languageSelectorIsOpen = false;
};
//this reacts to the options item in the tree
//todo, migrate to nav service
$scope.searchShowMenu = function (ev, args) {
@@ -155,8 +192,8 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
}
};
$scope.test = function() {
$scope.open = !$scope.open;
$scope.toggleLanguageSelector = function() {
$scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen;
};
//ensure to unregister from all events!

View File

@@ -18,14 +18,26 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
eventsService.on("app.authenticated", function(evt, data) {
assetsService._loadInitAssets().then(function() {
//Register all of the tours on the server
tourService.registerAllTours().then(function (dashboards) {
appReady(data);
}, function(){
appReady(data);
});
// Loads the user's locale settings for Moment.
userService.loadMomentLocaleForCurrentUser().then(function() {
//Register all of the tours on the server
tourService.registerAllTours().then(function () {
appReady(data);
// Auto start intro tour
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
// start intro tour if it hasn't been completed or disabled
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
tourService.startTour(introTour);
}
});
}, function(){
appReady(data);
});
});
});
});

View File

@@ -1,18 +1,19 @@
<div>
<h1>Upgrading Umbraco</h1>
<p>
Welcome to the Umbraco installer. You see this screen because your Umbraco installation needs a quick upgrade of its database and files, which will ensure your website is kept as fast, secure and up to date as possible.
Welcome to the Umbraco installer. You see this screen because your Umbraco installation needs a quick upgrade
of its database and files, which will ensure your website is kept as fast, secure and up to date as possible.
</p>
<p>
To read a report of changes between your current version <strong>{{installer.current.model.currentVersion}}</strong> and this version you're upgrading to <strong>{{installer.current.model.newVersion}}</strong>
Detected current version <strong>{{installer.current.model.currentVersion}}</strong> ({{installer.current.model.currentState}}),
which needs to be upgraded to <strong>{{installer.current.model.newVersion}}</strong> ({{installer.current.model.newState}}).
To compare versions and read a report of changes between versions, use the <em>View Report</em> button below.
</p>
<p>
<a ng-href="{{installer.current.model.reportUrl}}" target="_blank" class="btn btn-info">View Report</a>
</p>
<p>
Simply click <strong>continue</strong> below to be guided through the rest of the upgrade
Simply click <strong>continue</strong> below to be guided through the rest of the upgrade.
</p>
<p>
<button class="btn btn-success" ng-click="install()">Continue</button>

View File

@@ -1,7 +1,7 @@
angular.module("umbraco.install").controller("Umbraco.Install.UserController", function($scope, installerService) {
$scope.passwordPattern = /.*/;
$scope.installer.current.model.subscribeToNewsLetter = true;
$scope.installer.current.model.subscribeToNewsLetter = false;
if ($scope.installer.current.model.minNonAlphaNumericLength > 0) {
var exp = "";

View File

@@ -1,7 +1,7 @@
<div ng-controller="Umbraco.Install.UserController">
<h1>Install Umbraco 7</h1>
<h1>Install Umbraco 8 (alpha)</h1>
<p>Enter your name, email and password to install Umbraco 7 with its default settings, alternatively you can customize your installation</p>
<p>Enter your name, email and password to install Umbraco 8 with its default settings, alternatively you can customize your installation</p>
<form name="myForm" class="form-horizontal" novalidate ng-submit="validateAndInstall();">

View File

@@ -67,15 +67,14 @@
}
.alert-form {
background-color: @gray-10;
background-color: @white;
border: 1px solid @gray-3 !important;
color: @gray-3;
box-shadow: 0 -1px 6px 0 rgba(0,0,0,0.16);
}
.alert-form.-no-border {
border: none !important;
margin-left: 1px;
margin-bottom: 1px;
}
.alert-form h4 {

View File

@@ -62,10 +62,6 @@ body.umb-drawer-is-visible #mainwrapper{
margin: 0
}
#contentwrapper {
top: @appHeaderHeight;
}
#umb-notifications-wrapper {
left: 80px;
}
@@ -87,7 +83,7 @@ body.umb-drawer-is-visible #mainwrapper{
z-index: 1100;
float: left;
position: absolute;
top: @appHeaderHeight;
top: 0;
}
#navigation {
@@ -98,17 +94,18 @@ body.umb-drawer-is-visible #mainwrapper{
z-index: 100;
background: @white;
height: 100%;
}
.navigation-inner-container{
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
border-right: 1px solid @gray-8;
z-index: 100;
.navigation-inner-container {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
border-right: 1px solid @gray-9;
z-index: 100;
display: flex;
flex-direction: column;
}
#dialog {
@@ -124,6 +121,7 @@ body.umb-drawer-is-visible #mainwrapper{
padding: 0px;
z-index: 100 !important;
overflow: auto;
height: 100%;
}
#tree .umb-tree {

View File

@@ -82,12 +82,15 @@
// Umbraco Components
@import "components/application/umb-app-header.less";
@import "components/application/umb-app-content.less";
@import "components/application/umb-tour.less";
@import "components/application/umb-backdrop.less";
@import "components/application/umb-drawer.less";
@import "components/application/umb-language-picker.less";
@import "components/application/umb-dashboard.less";
@import "components/html/umb-expansion-panel.less";
@import "components/html/umb-alert.less";
@import "components/editor.less";
@import "components/overlays.less";
@@ -173,6 +176,9 @@
//used for property editors
@import "property-editors.less";
//used for prevalue editors
@import "components/prevalues/multivalues.less";
@import "typeahead.less";
@import "hacks.less";
@@ -180,4 +186,4 @@
@import "healthcheck.less";
// cleanup properties.less when it is done
@import "properties.less";
@import "properties.less";

View File

@@ -0,0 +1,7 @@
.umb-app-content {
position: absolute;
top: @appHeaderHeight;
right: 0;
bottom: 0;
left: 0;
}

View File

@@ -2,7 +2,7 @@
height: 100%;
width: 100%;
position: fixed;
z-index: 9999;
z-index: @zindexOverlayBackdrop;
top: 0;
left: 0;
pointer-events: none;

View File

@@ -0,0 +1,19 @@
.umb-dashboard {
position: absolute;
height: 100%;
width: 100%;
top: 0;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.umb-dashboard__header {
padding: 20px 20px 0 20px;
border-bottom: 1px solid @gray-9;
}
.umb-dashboard__content {
padding: 20px;
overflow: auto;
}

View File

@@ -1,15 +1,17 @@
.umb-language-picker {
position: relative;
z-index: 1;
z-index: @zindexDropdown;
}
.umb-language-picker__toggle {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 20px;
padding: 0 20px;
cursor: pointer;
border-bottom: 1px solid @gray-9;
height: 50px;
box-sizing: border-box;
}
.umb-language-picker__toggle:hover {
@@ -24,9 +26,12 @@
.umb-language-picker__dropdown {
width: 100%;
background: @white;
box-shadow: 0 5px 5px rgba(0,0,0,.2);
box-shadow: 0 3px 6px rgba(0,0,0,.16);
box-sizing: border-box;
position: absolute;
border-radius: 0 0 3px 3px;
max-height: 200px;
overflow: scroll;
}
.umb-language-picker__dropdown a {

View File

@@ -185,3 +185,30 @@
border: none !important;
background: none !important;
}
.umb-training-videos {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.umb-training-video {
background: @white;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
border-radius: @baseBorderRadius;
transition: box-shadow 0.1s ease-in-out, border 0.1s ease-in-out;
box-sizing: border-box;
padding: 20px;
}
.umb-training-video:hover {
box-shadow: 0 3px 6px 0 rgba(0,0,0,0.16);
text-decoration: none;
}
.umb-training-video__label {
font-weight: bold;
margin-top: 20px;
display: block;
text-align: center;
}

View File

@@ -100,9 +100,9 @@ input.umb-editor-header__name-input {
height: 32px;
line-height: 32px;
width: 100%;
padding: 0 15px;
background: @gray-10;
border-radius: 3px;
padding: 0 10px;
background: @white;
border: 1px solid @gray-8;
&:hover {
background-color: @gray-10;
border: 1px solid @gray-8;
@@ -127,15 +127,17 @@ a.umb-editor-header__close-split-view:hover {
.umb-variant-switcher__toggle {
display: flex;
align-items: center;
padding: 0 15px;
padding: 0 10px;
text-decoration: none !important;
font-size: 13px;
border-left: none;
color: @gray-4;
background-color: @gray-10;
background-color: @white;
border: 1px solid @gray-8;
border-left: none;
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
margin-left: -1px;
&:hover {
background-color: @gray-9;
background-color: @gray-10;
}
}
@@ -157,6 +159,11 @@ a.umb-editor-header__close-split-view:hover {
border-bottom: none;
}
.umb-variant-switcher_item--current .umb-variant-switcher__name-wrapper {
background-color: @gray-10;
border-left: 2px solid @turquoise;
}
.umb-variant-switcher__item:hover,
.umb-variant-switcher__item:focus,
.umb-variant-switcher__name-wrapper:hover,
@@ -172,11 +179,11 @@ a.umb-editor-header__close-split-view:hover {
.umb-variant-switcher__name-wrapper {
font-size: 14px;
padding: 5px 20px;
flex: 1;
cursor: pointer;
padding-top: 6px;
padding-bottom: 6px;
padding-top: 6px !important;
padding-bottom: 6px !important;
border-left: 2px solid transparent;
}
.umb-variant-switcher__name {

View File

@@ -0,0 +1,11 @@
.umb-alert {
padding: 15px;
box-sizing: border-box;
background-color: @turquoise-washed;
border: 1px solid @turquoise;
}
.umb-alert--info {
background-color: @turquoise-washed;
border: 1px solid @turquoise;
}

View File

@@ -2,7 +2,7 @@
//border: 1px solid @gray-8;
background: @white;
border-radius: 3px;
margin-bottom: 8px;
margin-bottom: 16px;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.16);
}

View File

@@ -1,7 +1,7 @@
.umb-notifications {
z-index: 1000;
position: absolute;
bottom: 52px;
bottom: @editorFooterHeight;
left: 0;
right: 0;
border-bottom: none;

View File

@@ -75,21 +75,35 @@
/* ---------- OVERLAY CENTER ---------- */
.umb-overlay.umb-overlay-center {
position: absolute;
width: 600px;
height: 500px;
top: 10px;
width: 500px;
height: auto;
top: 50%;
left: 50%;
transform: translate(-50%, 0);
transform: translate(-50%, -50%);
border-radius: @baseBorderRadius;
}
.umb-overlay.umb-overlay-center .umb-overlay-header {
text-align: center;
border: none;
background: transparent;
padding: 20px 20px 0 20px;
}
.umb-overlay.umb-overlay-center .umb-overlay__form {
max-height: 80vh;
}
.umb-overlay.umb-overlay-center .umb-overlay-container {
padding: 20px;
}
.umb-overlay.umb-overlay-center .umb-overlay-drawer {
border: none;
background: transparent;
padding: 0 20px 20px 20px;
}
/* ---------- OVERLAY TARGET ---------- */
.umb-overlay.umb-overlay-target {
width: 400px;

View File

@@ -4,7 +4,7 @@
height: 100%;
background-color: rgba(255, 255, 255, 0.50);
z-index: @zindexOverlayBackdrop;
top: 0;
top: @appHeaderHeight;
left: 0;
animation: fadeIn 0.2s;
}

View File

@@ -0,0 +1,53 @@
.umb-prevalues-multivalues {
width: 400px;
}
.umb-prevalues-multivalues__left {
display: flex;
flex: 1 1 auto;
}
.umb-prevalues-multivalues__right {
display: flex;
flex: 0 0 auto;
align-items: center;
}
.umb-prevalues-multivalues__add {
display: flex;
}
.umb-prevalues-multivalues__add input {
width: 320px;
}
.umb-prevalues-multivalues__add input {
display: flex;
}
.umb-prevalues-multivalues__add button {
margin: 0 6px 0 0;
float: right
}
.umb-prevalues-multivalues__listitem {
display: flex;
padding: 6px;
margin: 10px 0px !important;
background: #F3F3F5;
cursor: move;
}
.umb-prevalues-multivalues__listitem i {
display: flex;
align-items: center;
margin-right: 5px
}
.umb-prevalues-multivalues__listitem a {
cursor: pointer;
}
.umb-prevalues-multivalues__listitem input {
width: 295px;
}

View File

@@ -7,6 +7,7 @@
.umb-box-header {
padding: 10px 20px;
border-bottom: 1px solid @gray-9;
}
.umb-box-header-title {
@@ -24,5 +25,4 @@
.umb-box-content {
padding: 20px;
border-top: 1px solid @gray-9;
}

View File

@@ -280,7 +280,6 @@
.umb-grid .umb-control {
position: relative;
display: block;
overflow: hidden;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;

View File

@@ -426,103 +426,114 @@ input.umb-group-builder__group-title-input {
.content-type-editor-dialog.edit-property-settings {
.validation-wrapper {
position: relative;
}
.validation-label {
position: absolute;
top: 50%;
right: 0;
font-size: 12px;
color: @red;
transform: translate(0, -50%);
}
textarea.editor-label {
border-color:transparent;
box-shadow: none;
width: 100%;
box-sizing: border-box;
margin-bottom: 0;
font-size: 16px;
font-weight: bold;
resize: none;
line-height: 1.5em;
padding-left: 0;
border: none;
&:focus {
outline: none;
box-shadow: none !important;
.validation-wrapper {
position: relative;
}
}
.editor-placeholder {
border: 1px dashed @gray-8;
width: 100%;
height: 80px;
line-height: 80px;
text-align: center;
display: block;
border-radius: 5px;
color: @gray-3;
font-weight: bold;
font-size: 14px;
color: @turquoise-d1;
&:hover {
text-decoration: none;
}
}
.editor {
margin-bottom: 10px;
.editor-icon-wrapper {
border: 1px solid @gray-8;
width: 60px;
height: 60px;
text-align: center;
line-height: 60px;
border-radius: 5px;
float: left;
margin-right: 20px;
.icon {
font-size: 26px;
}
}
.editor-details {
float: left;
margin-top: 10px;
.editor-name {
display: block;
font-weight: bold;
}
.editor-editor {
display: block;
.validation-label {
position: absolute;
top: 50%;
right: 0;
font-size: 12px;
}
color: @red;
transform: translate(0, -50%);
}
.editor-settings-icon {
font-size: 18px;
margin-top: 8px;
}
}
.checkbox {
margin-bottom: 20px;
}
textarea.editor-label {
border-color: transparent;
box-shadow: none;
width: 100%;
box-sizing: border-box;
margin-bottom: 0;
font-size: 16px;
font-weight: bold;
resize: none;
line-height: 1.5em;
padding-left: 0;
border: none;
.editor-description,
.editor-validation-pattern {
min-width: 100%;
min-height: 25px;
resize: none;
box-sizing: border-box;
border: none;
overflow: hidden;
}
&:focus {
outline: none;
box-shadow: none !important;
}
}
.umb-dropdown {
width: 100%;
}
.editor-placeholder {
border: 1px dashed @gray-8;
width: 100%;
height: 80px;
line-height: 80px;
text-align: center;
display: block;
border-radius: 5px;
color: @gray-3;
font-weight: bold;
font-size: 14px;
color: @turquoise-d1;
&:hover {
text-decoration: none;
}
}
.editor {
margin-bottom: 10px;
.editor-icon-wrapper {
border: 1px solid @gray-8;
width: 60px;
height: 60px;
text-align: center;
line-height: 60px;
border-radius: 5px;
float: left;
margin-right: 20px;
.icon {
font-size: 26px;
}
}
.editor-details {
float: left;
margin-top: 10px;
.editor-name {
display: block;
font-weight: bold;
}
.editor-editor {
display: block;
font-size: 12px;
}
}
.editor-settings-icon {
font-size: 18px;
margin-top: 8px;
}
}
.checkbox {
margin-bottom: 20px;
}
.editor-description,
.editor-validation-pattern {
min-width: 100%;
min-height: 25px;
resize: none;
box-sizing: border-box;
border: none;
overflow: hidden;
}
.umb-dropdown {
width: 100%;
}
label.checkbox.no-indent {
width: 100%;
}
}

View File

@@ -1,3 +1,10 @@
.umb-list--condensed {
.umb-list-item {
padding-top: 5px;
padding-bottom: 5px;
}
}
.umb-list-item {
border-bottom: 1px solid @gray-9;
padding-top: 15px;

View File

@@ -48,7 +48,7 @@
}
50% {
transform: scale(1);
background: white;
background: transparent;
}
100% {
transform: scale(0.5);

View File

@@ -1,15 +1,3 @@
.umb-nav-tabs {
position: absolute;
z-index: 10;
}
.umb-nav-tabs.-padding-left {
padding-left: 20px;
}
.umb-tab-content {
padding-top: 20px;
position: relative;
top: 22px;
border-top: 1px solid @purple-l3;
}

View File

@@ -4,6 +4,10 @@
padding: 7px 0;
}
.umb-permission--disabled {
opacity: 0.8;
}
.umb-permission:last-of-type {
border-bottom: none;
}

View File

@@ -24,17 +24,18 @@
display: flex;
flex-wrap: wrap;
flex-direction: column;
background: @gray-10;
background: @white;
border-radius: 3px;
padding: 15px 10px;
padding: 20px;
box-sizing: border-box;
text-align: center;
border: 1px solid @gray-8;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
height: 100%;
box-sizing: border-box;
}
.umb-healthcheck-group:hover {
border: 1px solid @turquoise;
box-shadow: 0 3px 6px 0 rgba(0,0,0,0.16);
cursor: pointer;
}
@@ -60,7 +61,7 @@
.umb-healthcheck-message {
position: relative;
background: @white;
background: @gray-10;
border-radius: 50px;
display: inline-flex;
align-items: center;
@@ -125,6 +126,12 @@
margin-bottom: 40px;
}
.umb-healthcheck-group__details-group {
background: @white;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
border-radius: @baseBorderRadius;
}
.umb-healthcheck-group__details-group-title {
background-color: @purple-l1;
padding: 10px 20px;
@@ -141,9 +148,9 @@
}
.umb-healthcheck-group__details-checks {
border: 1px solid @gray-8;
border-top: none;
border-radius: 0 0 3px 3px;
background: @white;
}
.umb-healthcheck-group__details-check {
@@ -152,14 +159,13 @@
.umb-healthcheck-group__details-check-title {
padding: 15px 20px;
background-color: @gray-10;
}
.umb-healthcheck-group__details-check-name {
font-size: 15px;
color: @black;
font-weight: bold;
margin-bottom: 5px;
margin-bottom: 3px;
}
.umb-healthcheck-group__details-check-description {
@@ -171,7 +177,7 @@
.umb-healthcheck-group__details-status {
padding: 15px 0;
display: flex;
border-bottom: 2px solid @gray-10;
border-top: 2px solid @gray-10;
}
.umb-healthcheck-group__details-status-overlay {

View File

@@ -11,7 +11,7 @@
min-height: 100%;
}
.shadow {
box-shadow: 3px 0px 7px @gray-8;
box-shadow: 3px 0px 7px rgba(0,0,0,0.16);
}
.umb-scrollable, .umb-auto-overflow {

View File

@@ -7,10 +7,12 @@
}
.umb-modalcolumn-header {
background: @gray-10;
border-bottom: 1px solid @purple-l3;
height: 94px;
padding: 5px 20px 0px 20px;
border-bottom: 1px solid @gray-9;
height: @editorHeaderHeight;
box-sizing: border-box;
padding: 0 20px;
display: flex;
align-items: center;
white-space: nowrap
}
@@ -19,13 +21,12 @@
white-space: nowrap;
font-size: @fontSizeLarge;
font-weight: 400;
padding-top: 10px !important;
}
.umb-modalcolumn-body {
padding: 0px;
background: @white;
top: 100px;
top: @editorHeaderHeight;
position: absolute;
left: 0px;
right: 0px;
@@ -181,7 +182,7 @@
width: 640px !important;
}
.umb-modal i {
font-size: 14px;
font-size: 20px;
}
.umb-modal .breadcrumb {
background: none;

View File

@@ -109,11 +109,7 @@
}
.nav-tabs > li > a,
.nav-pills > li > a {
padding-right: 8px;
padding-left: 8px;
margin-right: 2px;
line-height: 14px; // keeps the overall height an even number
border-radius: 3px 3px 0 0;
margin-right: 15px;
}
// TABS
@@ -121,26 +117,24 @@
// Give the tabs something to sit on
.nav-tabs {
border-bottom: 1px solid @purple-l3;
// border-bottom: 1px solid @gray-9;
}
// Make the list-items overlay the bottom border
.nav-tabs > li {
margin-bottom: -1px;
// margin-bottom: -1px;
}
// Actual tabs (as links)
.nav-tabs > li > a {
color: @gray-3;
padding-top: 5px;
padding-bottom: 4px;
line-height: @baseLineHeight;
border: 1px solid transparent;
border-bottom: 2px solid transparent;
padding-bottom: 15px;
&:hover {
color: @black;
}
&:hover,
&:focus {
border-color: transparent transparent @purple-l3;
// border-color: transparent transparent @purple-l3;
}
}
// Active state, and it's :hover/:focus to override normal :hover/:focus
@@ -148,9 +142,7 @@
.nav-tabs > .active > a:hover,
.nav-tabs > .active > a:focus {
color: @black;
background-color: @bodyBackground;
border: 1px solid @purple-l3;
border-bottom-color: transparent;
border-bottom-color: @turquoise;
cursor: default;
}
@@ -175,10 +167,6 @@
.show-validation .nav-tabs > li.active.error > a:focus {
}
.umb-nav-tabs {
margin: -8px 0 0 0;
}
// PILLS
// -----
@@ -247,6 +235,8 @@
.dropdown-menu {
border-radius: @dropdownBorderRadius;
box-shadow: 0 5px 20px rgba(0,0,0,.3);
padding-top: 0;
padding-bottom: 0;
}
// fix dropdown with checkbox + long text in label

View File

@@ -74,10 +74,6 @@
padding-right: 10px;
}
.login-overlay .btn-success {
padding: 12px 24px;
}
.login-overlay .form label {
font-weight: bold;
}

View File

@@ -428,10 +428,6 @@
font-size: 10px;
}
.umb-panel-header .umb-nav-tabs {
bottom: -1px;
}
input.umb-panel-header-name-input.name-is-empty {
border: 1px dashed @gray-8;
background: @white;

View File

@@ -40,6 +40,16 @@
padding: 10px;
}
.umb-contentpicker__min-max-help {
font-size: 13px;
margin-top: 5px;
color: @gray-4;
}
.show-validation .umb-contentpicker__min-max-help {
display: none;
}
.umb-contentpicker small {
&:not(:last-child) {
@@ -203,10 +213,22 @@ ul.color-picker li a {
.umb-thumbnails{
position: relative;
.umb-thumbnails {
position: relative;
display: flex;
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
justify-content: flex-start;
}
.umb-thumbnails > li.icon {
width: 14%;
text-align: center;
}
.umb-thumbnails i{margin: auto;}
.umb-thumbnails a{
@@ -243,7 +265,7 @@ ul.color-picker li a {
.umb-mediapicker .umb-sortable-thumbnails li {
flex-direction: column;
margin: 0 5px 0 0;
margin: 0 5px 5px 0;
padding: 5px;
}
@@ -282,8 +304,8 @@ ul.color-picker li a {
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__wrapper {
width: 120px;
height: 114px;
width: 124px;
height: 124px;
overflow: hidden;
}
@@ -298,6 +320,12 @@ ul.color-picker li a {
visibility: hidden;
}
.umb-sortable-thumbnails.ui-sortable:not(.ui-sortable-disabled) {
> li:not(.unsortable) {
cursor: move;
}
}
.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions {
opacity: 1;
visibility: visible;

View File

@@ -13,22 +13,20 @@ table {
border-spacing: 0;
}
table thead {
background-color: @gray-10;
}
// BASELINE STYLES
// ---------------
.table {
width: 100%;
margin-bottom: @baseLineHeight;
border: 1px solid @gray-8;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
background: @tableBackground;
border-radius: @baseBorderRadius;
font-size: 14px;
// Cells
th,
td {
padding: 10px 8px;
padding: 10px 20px;
line-height: @baseLineHeight;
text-align: left;
border-top: 1px solid @tableBorder;
@@ -38,6 +36,9 @@ table thead {
}
// Bottom align for column headings
thead th {
padding-top: 15px;
padding-bottom: 15px;
color: @gray-3;
vertical-align: bottom;
}
// Remove top border from thead by default
@@ -81,6 +82,7 @@ table thead {
border-collapse: separate; // Done so we can round those corners!
*border-collapse: collapse; // IE7 can't round corners anyway
border-left: 0;
box-shadow: none;
.border-radius(@baseBorderRadius);
th,
td {

View File

@@ -100,6 +100,27 @@
.color-green, .color-green i{color: @green-d1 !important;}
.color-yellow, .color-yellow i{color: @yellow-d1 !important;}
/* Colors based on http://zavoloklom.github.io/material-design-color-palette/colors.html */
.color-black, .color-black i { color: #000 !important; }
.color-blue-grey, .color-blue-grey i { color: #607d8b !important; }
.color-grey, .color-grey i { color: #9e9e9e !important; }
.color-brown, .color-brown i { color: #795548 !important; }
.color-blue, .color-blue i { color: #2196f3 !important; }
.color-light-blue, .color-light-blue i {color: #03a9f4 !important; }
.color-cyan, .color-cyan i { color: #00bcd4 !important; }
.color-green, .color-green i { color: #4caf50 !important; }
.color-light-green, .color-light-green i {color: #8bc34a !important; }
.color-lime, .color-lime i { color: #cddc39 !important; }
.color-yellow, .color-yellow i { color: #ffeb3b !important; }
.color-amber, .color-amber i { color: #ffc107 !important; }
.color-orange, .color-orange i { color: #ff9800 !important; }
.color-deep-orange, .color-deep-orange i { color: #ff5722 !important; }
.color-red, .color-red i { color: #f44336 !important; }
.color-pink, .color-pink i { color: #e91e63 !important; }
.color-purple,.color-purple i { color: #9c27b0 !important; }
.color-deep-purple, .color-deep-purple i { color: #673ab7 !important; }
.color-indigo, .color-indigo i { color: #3f51b5 !important; }
// Scaffolding
// -------------------------
@@ -154,10 +175,10 @@
// Tables
// -------------------------
@tableBackground: transparent; // overall background-color
@tableBackground: @white; // overall background-color
@tableBackgroundAccent: @gray-10; // for striping
@tableBackgroundHover: @gray-10; // for hover
@tableBorder: @gray-8; // table and cell border
@tableBorder: @gray-9; // table and cell border
// Buttons
// -------------------------
@@ -231,6 +252,7 @@
// -------------------------
// Used for a bird's eye view of components dependent on the z-axis
// Try to avoid customizing these :)
@zIndexTree: 100;
@zindexDropdown: 1000;
@zindexPopover: 1010;
@zindexTooltip: 1030;

View File

@@ -1,51 +1,41 @@
<div ng-controller="Umbraco.DashboardController">
<umb-load-indicator ng-if="page.loading"></umb-load-indicator>
<form name="dashboardForm" val-form-manager>
<form
ng-show="!page.loading"
class="umb-dashboard"
val-form-manager>
<umb-load-indicator ng-show="page.loading"></umb-load-indicator>
<umb-editor-view
footer="false"
umb-tabs>
<div class="umb-dashboard" ng-if="!page.loading">
<umb-editor-header
name="dashboard.name"
name-locked="page.nameLocked"
tabs="dashboard.tabs"
hide-icon="true"
hide-description="true"
hide-alias="true">
</umb-editor-header>
<div class="umb-dashboard__header" ng-show="dashboard.tabs.length > 1">
<umb-tabs-nav ng-if="dashboard.tabs" model="dashboard.tabs" style="margin-bottom: 0;"></umb-tabs-nav>
</div>
<umb-editor-container>
<div class="umb-dashboard__content">
<umb-tabs-content view="true">
<umb-tab id="tab{{tab.id}}" ng-repeat="tab in dashboard.tabs" rel="{{tab.id}}">
<umb-tabs-content view="true">
<umb-tab id="tab{{tab.id}}" ng-repeat="tab in dashboard.tabs" rel="{{tab.id}}" class="row-fluid">
<div ng-repeat="property in tab.properties" ng-switch on="property.serverSide">
<div class="clearfix" ng-switch-when="false">
<h3 ng-show="property.caption">{{property.caption}}</h3>
<div ng-include="property.path"></div>
</div>
<div class="umb-dashboard-control clearfix" ng-switch-when="true">
<h3 ng-show="property.caption">{{property.caption}}</h3>
<iframe ng-src="dashboard/usercontrolproxy.aspx?ctrl={{ property.path}}"></iframe>
</div>
</div>
</umb-tab>
</umb-tabs-content>
<div ng-repeat="property in tab.properties" ng-switch on="property.serverSide">
</div>
<div class="span12 clearfix" ng-switch-when="false">
<h3 ng-show="property.caption">{{property.caption}}</h3>
<div ng-include="property.path"></div>
</div>
</div>
<div class="span12 umb-dashboard-control clearfix" ng-switch-when="true">
<h3 ng-show="property.caption">{{property.caption}}</h3>
<iframe ng-src="dashboard/usercontrolproxy.aspx?ctrl={{ property.path}}"></iframe>
</div>
</div>
</umb-tab>
</umb-tabs-content>
</umb-editor-container>
</umb-editor-view>
</form>
</div>
</form>
</div>

Some files were not shown because too many files have changed in this diff Show More