Merge remote-tracking branch 'origin/temp8' into temp8-4037

# Conflicts:
#	src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs
This commit is contained in:
Bjarke Berg
2019-01-18 13:28:57 +01:00
256 changed files with 2755 additions and 5441 deletions

View File

@@ -0,0 +1,52 @@
'use strict';
module.exports = {
sources: {
//less files used by backoffice and preview
//processed in the less task
less: {
installer: { files: ["./src/less/installer.less"], out: "installer.css" },
nonodes: { files: ["./src/less/pages/nonodes.less"], out: "nonodes.style.min.css"},
preview: { files: ["./src/less/canvas-designer.less"], out: "canvasdesigner.css" },
umbraco: { files: ["./src/less/belle.less"], out: "umbraco.css" }
},
//js files for backoffie
//processed in the js task
js: {
preview: { files: ["./src/preview/**/*.js"], out: "umbraco.preview.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" },
resources: { files: ["./src/common/resources/**/*.js"], out: "umbraco.resources.js" },
services: { files: ["./src/common/services/**/*.js"], out: "umbraco.services.js" },
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
},
//selectors for copying all views into the build
//processed in the views task
views:{
umbraco: {files: ["./src/views/**/*.html"], folder: ""},
installer: {files: ["./src/installer/steps/*.html"], folder: "install/"}
},
//globs for file-watching
globs:{
views: "./src/views/**/*.html",
less: "./src/less/**/*.less",
js: "./src/*.js",
lib: "./lib/**/*",
assets: "./src/assets/**"
}
},
root: "../Umbraco.Web.UI/Umbraco/",
targets: {
js: "js/",
lib: "lib/",
views: "views/",
css: "assets/css/",
assets: "assets/"
}
};

View File

@@ -0,0 +1,10 @@
'use strict';
var fs = require('fs');
var onlyScripts = require('./util/scriptFilter');
var tasks = fs.readdirSync('./gulp/tasks/').filter(onlyScripts);
tasks.forEach(function(task) {
require('./tasks/' + task);
});

View File

@@ -0,0 +1,10 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var runSequence = require('run-sequence');
// Build - build the files ready for production
gulp.task('build', function(cb) {
runSequence(["dependencies", "js", "less", "views"], cb);
});

View File

@@ -0,0 +1,288 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var MergeStream = require('merge-stream');
var imagemin = require('gulp-imagemin');
/**************************
* Task processes and copies all dependencies, either installed by npm or stored locally in the project
**************************/
gulp.task('dependencies', function () {
//as we do multiple things in this task, we merge the multiple streams
var stream = new MergeStream();
// Pick the dependencies we need from each package
// so we don't just ship with a lot of files that aren't needed
const nodeModules = [
{
"name": "ace-builds",
"src": [
"./node_modules/ace-builds/src-min-noconflict/ace.js",
"./node_modules/ace-builds/src-min-noconflict/ext-language_tools.js",
"./node_modules/ace-builds/src-min-noconflict/ext-searchbox.js",
"./node_modules/ace-builds/src-min-noconflict/ext-settings_menu.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/text.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/javascript.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/css.js",
"./node_modules/ace-builds/src-min-noconflict/theme-chrome.js",
"./node_modules/ace-builds/src-min-noconflict/mode-razor.js",
"./node_modules/ace-builds/src-min-noconflict/mode-javascript.js",
"./node_modules/ace-builds/src-min-noconflict/mode-css.js",
"./node_modules/ace-builds/src-min-noconflict/worker-javascript.js",
"./node_modules/ace-builds/src-min-noconflict/worker-css.js"
],
"base": "./node_modules/ace-builds"
},
{
"name": "angular",
"src": ["./node_modules/angular/angular.js"],
"base": "./node_modules/angular"
},
{
"name": "angular-cookies",
"src": ["./node_modules/angular-cookies/angular-cookies.js"],
"base": "./node_modules/angular-cookies"
},
{
"name": "angular-dynamic-locale",
"src": [
"./node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js",
"./node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js.map"
],
"base": "./node_modules/angular-dynamic-locale/dist"
},
{
"name": "angular-sanitize",
"src": ["./node_modules/angular-sanitize/angular-sanitize.js"],
"base": "./node_modules/angular-sanitize"
},
{
"name": "angular-touch",
"src": ["./node_modules/angular-touch/angular-touch.js"],
"base": "./node_modules/angular-touch"
},
{
"name": "angular-ui-sortable",
"src": ["./node_modules/angular-ui-sortable/dist/sortable.js"],
"base": "./node_modules/angular-ui-sortable/dist"
},
{
"name": "angular-route",
"src": ["./node_modules/angular-route/angular-route.js"],
"base": "./node_modules/angular-route"
},
{
"name": "angular-animate",
"src": ["./node_modules/angular-animate/angular-animate.js"],
"base": "./node_modules/angular-animate"
},
{
"name": "angular-i18n",
"src": [
"./node_modules/angular-i18n/angular-i18n.js",
"./node_modules/angular-i18n/angular-locale_*.js"
],
"base": "./node_modules/angular-i18n"
},
{
"name": "angular-local-storage",
"src": [
"./node_modules/angular-local-storage/dist/angular-local-storage.min.js",
"./node_modules/angular-local-storage/dist/angular-local-storage.min.js.map"
],
"base": "./node_modules/angular-local-storage/dist"
},
{
"name": "angular-messages",
"src": ["./node_modules/angular-messages/angular-messages.js"],
"base": "./node_modules/angular-messages"
},
{
"name": "angular-mocks",
"src": ["./node_modules/angular-mocks/angular-mocks.js"],
"base": "./node_modules/angular-mocks"
},
{
"name": "animejs",
"src": ["./node_modules/animejs/anime.min.js"],
"base": "./node_modules/animejs"
},
{
"name": "bootstrap-social",
"src": ["./node_modules/bootstrap-social/bootstrap-social.css"],
"base": "./node_modules/bootstrap-social"
},
{
"name": "angular-chart.js",
"src": ["./node_modules/angular-chart.js/dist/angular-chart.min.js"],
"base": "./node_modules/angular-chart.js/dist"
},
{
"name": "chart.js",
"src": ["./node_modules/chart.js/dist/chart.min.js"],
"base": "./node_modules/chart.js/dist"
},
{
"name": "clipboard",
"src": ["./node_modules/clipboard/dist/clipboard.min.js"],
"base": "./node_modules/clipboard/dist"
},
{
"name": "jsdiff",
"src": ["./node_modules/diff/dist/diff.min.js"],
"base": "./node_modules/diff/dist"
},
{
"name": "flatpickr",
"src": [
"./node_modules/flatpickr/dist/flatpickr.js",
"./node_modules/flatpickr/dist/flatpickr.css"
],
"base": "./node_modules/flatpickr/dist"
},
{
"name": "font-awesome",
"src": [
"./node_modules/font-awesome/fonts/*",
"./node_modules/font-awesome/css/font-awesome.min.css"
],
"base": "./node_modules/font-awesome"
},
{
"name": "jquery",
"src": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/jquery/dist/jquery.min.map"
],
"base": "./node_modules/jquery/dist"
},
{
"name": "jquery-ui",
"src": ["./node_modules/jquery-ui-dist/jquery-ui.min.js"],
"base": "./node_modules/jquery-ui-dist"
},
{
"name": "jquery-ui-touch-punch",
"src": ["./node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js"],
"base": "./node_modules/jquery-ui-touch-punch"
},
{
"name": "lazyload-js",
"src": ["./node_modules/lazyload-js/lazyload.min.js"],
"base": "./node_modules/lazyload-js"
},
{
"name": "moment",
"src": ["./node_modules/moment/min/moment.min.js"],
"base": "./node_modules/moment/min"
},
{
"name": "moment",
"src": ["./node_modules/moment/locale/*.js"],
"base": "./node_modules/moment/locale"
},
{
"name": "ng-file-upload",
"src": ["./node_modules/ng-file-upload/dist/ng-file-upload.min.js"],
"base": "./node_modules/ng-file-upload/dist"
},
{
"name": "nouislider",
"src": [
"./node_modules/nouislider/distribute/nouislider.min.js",
"./node_modules/nouislider/distribute/nouislider.min.css"
],
"base": "./node_modules/nouislider/distribute"
},
{
"name": "signalr",
"src": ["./node_modules/signalr/jquery.signalR.js"],
"base": "./node_modules/signalr"
},
{
"name": "spectrum",
"src": [
"./node_modules/spectrum-colorpicker/spectrum.js",
"./node_modules/spectrum-colorpicker/spectrum.css"
],
"base": "./node_modules/spectrum-colorpicker"
},
{
"name": "tinymce",
"src": [
"./node_modules/tinymce/tinymce.min.js",
"./node_modules/tinymce/plugins/**",
"./node_modules/tinymce/skins/**",
"./node_modules/tinymce/themes/**"
],
"base": "./node_modules/tinymce"
},
{
"name": "typeahead.js",
"src": ["./node_modules/typeahead.js/dist/typeahead.bundle.min.js"],
"base": "./node_modules/typeahead.js/dist"
},
{
"name": "underscore",
"src": ["node_modules/underscore/underscore-min.js"],
"base": "./node_modules/underscore"
}
];
// add streams for node modules
nodeModules.forEach(module => {
stream.add(
gulp.src(module.src,
{ base: module.base })
.pipe(gulp.dest(config.root + config.targets.lib + "/" + module.name))
);
});
//copy over libs which are not on npm (/lib)
stream.add(
gulp.src(config.sources.globs.lib)
.pipe(gulp.dest(config.root + config.targets.lib))
);
//Copies all static assets into /root / assets folder
//css, fonts and image files
stream.add(
gulp.src(config.sources.globs.assets)
.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
{removeViewBox: true},
{cleanupIDs: false}
]
})
]))
.pipe(gulp.dest(config.root + config.targets.assets))
);
// Copies all the less files related to the preview into their folder
//these are not pre-processed as preview has its own less combiler client side
stream.add(
gulp.src("src/canvasdesigner/editors/*.less")
.pipe(gulp.dest(config.root + config.targets.assets + "/less"))
);
// Todo: check if we need these fileSize
stream.add(
gulp.src("src/views/propertyeditors/grid/config/*.*")
.pipe(gulp.dest(config.root + config.targets.views + "/propertyeditors/grid/config"))
);
stream.add(
gulp.src("src/views/dashboard/default/*.jpg")
.pipe(gulp.dest(config.root + config.targets.views + "/dashboard/default"))
);
return stream;
});

View File

@@ -0,0 +1,10 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var runSequence = require('run-sequence');
// Dev - build the files ready for development and start watchers
gulp.task('dev', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});

View File

@@ -0,0 +1,55 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var connect = require('gulp-connect');
var open = require('gulp-open');
var gulpDocs = require('gulp-ngdocs');
/**************************
* Build Backoffice UI API documentation
**************************/
gulp.task('docs', [], function (cb) {
var options = {
html5Mode: false,
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
dest: 'docs/api',
styles: ['docs/umb-docs.css'],
image: "https://our.umbraco.com/assets/images/logo.svg"
}
return gulpDocs.sections({
api: {
glob: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
api: true,
title: 'API Documentation'
}
})
.pipe(gulpDocs.process(options))
.pipe(gulp.dest('docs/api'));
cb();
});
gulp.task('connect:docs', function (cb) {
connect.server({
root: 'docs/api',
livereload: true,
fallback: 'docs/api/index.html',
port: 8880
});
cb();
});
gulp.task('open:docs', function (cb) {
var options = {
uri: 'http://localhost:8880/index.html'
};
gulp.src(__filename)
.pipe(open(options));
cb();
});

View File

@@ -0,0 +1,10 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var runSequence = require('run-sequence');
// Docserve - build and open the back office documentation
gulp.task('docserve', function(cb) {
runSequence('docs', 'connect:docs', 'open:docs', cb);
});

View File

@@ -0,0 +1,13 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var runSequence = require('run-sequence');
// Dev - build the files ready for development and start watchers
gulp.task('fastdev', function(cb) {
global.isProd = false;
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});

View File

@@ -0,0 +1,29 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
var processJs = require('../util/processJs');
/**************************
* Copies all angular JS files into their seperate umbraco.*.js file
**************************/
gulp.task('js', function () {
//we run multiple streams, so merge them all together
var stream = new MergeStream();
stream.add(
gulp.src(config.sources.globs.js)
.pipe(gulp.dest(config.root + config.targets.js))
);
_.forEach(config.sources.js, function (group) {
stream.add (processJs(group.files, group.out) );
});
return stream;
});

View File

@@ -0,0 +1,20 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
var processLess = require('../util/processLess');
gulp.task('less', function () {
var stream = new MergeStream();
_.forEach(config.sources.less, function (group) {
stream.add( processLess(group.files, group.out) );
});
return stream;
});

View File

@@ -0,0 +1,26 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var karmaServer = require('karma').Server;
/**************************
* Build tests
**************************/
// Karma test
gulp.task('test:unit', function() {
new karmaServer({
configFile: __dirname + "/test/config/karma.conf.js",
keepalive: true
})
.start();
});
gulp.task('test:e2e', function() {
new karmaServer({
configFile: __dirname + "/test/config/e2e.js",
keepalive: true
})
.start();
});

View File

@@ -0,0 +1,25 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
gulp.task('views', function () {
var stream = new MergeStream();
_.forEach(config.sources.views, function (group) {
console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder)
stream.add (
gulp.src(group.files)
.pipe( gulp.dest(config.root + config.targets.views + group.folder) )
);
});
return stream;
});

View File

@@ -0,0 +1,58 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
var processJs = require('../util/processJs');
var watch = require('gulp-watch');
gulp.task('watch', function () {
var stream = new MergeStream();
var watchInterval = 500;
//Setup a watcher for all groups of javascript files
_.forEach(config.sources.js, function (group) {
if(group.watch !== false){
stream.add(
watch(group.files, { ignoreInitial: true, interval: watchInterval }, function (file) {
console.info(file.path + " has changed, added to: " + group.out);
processJs(group.files, group.out);
})
);
}
});
stream.add(
//watch all less files and trigger the less task
watch(config.sources.globs.less, { ignoreInitial: true, interval: watchInterval }, function () {
gulp.run(['less']);
})
);
//watch all views - copy single file changes
stream.add(
watch(config.sources.globs.views, { interval: watchInterval })
.pipe(gulp.dest(config.root + config.targets.views))
);
//watch all app js files that will not be merged - copy single file changes
stream.add(
watch(config.sources.globs.js, { interval: watchInterval })
.pipe(gulp.dest(config.root + config.targets.js))
);
return stream;
});

View File

@@ -0,0 +1,18 @@
'use strict';
var notify = require('gulp-notify');
module.exports = function(error) {
var args = Array.prototype.slice.call(arguments);
// Send error to notification center with gulp-notify
notify.onError({
title: 'Compile Error',
message: '<%= error.message %>'
}).apply(this, args);
// Keep gulp from hanging on this task
this.emit('end');
};

View File

@@ -0,0 +1,32 @@
var config = require('../config');
var gulp = require('gulp');
var eslint = require('gulp-eslint');
var babel = require("gulp-babel");
var sort = require('gulp-sort');
var concat = require('gulp-concat');
var wrap = require("gulp-wrap-js");
module.exports = function(files, out) {
var task = gulp.src(files);
if (global.isProd === true) {
// check for js errors
task = task.pipe(eslint());
// outputs the lint results to the console
task = task.pipe(eslint.format());
}
// sort files in stream by path or any custom sort comparator
task = task.pipe(babel())
.pipe(sort())
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
.pipe(gulp.dest(config.root + config.targets.js));
return task;
};

View File

@@ -0,0 +1,33 @@
var config = require('../config');
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var less = require('gulp-less');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');
var cleanCss = require("gulp-clean-css");
var rename = require('gulp-rename');
module.exports = function(files, out) {
var processors = [
autoprefixer,
cssnano({zindex: false})
];
var task = gulp.src(files)
.pipe(less());
if (global.isProd === true) {
task = task.pipe(cleanCss());
}
task = task.pipe(postcss(processors))
.pipe(rename(out))
.pipe(gulp.dest(config.root + config.targets.css));
return task;
};

View File

@@ -0,0 +1,9 @@
'use strict';
var path = require('path');
// Filters out non .js files. Prevents
// accidental inclusion of possible hidden files
module.exports = function(name) {
return /(\.(js)$)/i.test(path.extname(name));
};

603
src/Umbraco.Web.UI.Client/gulpfile.js Normal file → Executable file
View File

@@ -1,587 +1,16 @@
var gulp = require('gulp');
var watch = require('gulp-watch');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var wrap = require("gulp-wrap-js");
var sort = require('gulp-sort');
var connect = require('gulp-connect');
var open = require('gulp-open');
var babel = require("gulp-babel");
var runSequence = require('run-sequence');
var imagemin = require('gulp-imagemin');
var _ = require('lodash');
var MergeStream = require('merge-stream');
// js
var eslint = require('gulp-eslint');
//Less + css
var postcss = require('gulp-postcss');
var less = require('gulp-less');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');
var cleanCss = require("gulp-clean-css");
// Documentation
var gulpDocs = require('gulp-ngdocs');
// Testing
var karmaServer = require('karma').Server;
/***************************************************************
Helper functions
***************************************************************/
function processJs(files, out) {
return gulp.src(files)
// check for js errors
.pipe(eslint())
// outputs the lint results to the console
.pipe(eslint.format())
// sort files in stream by path or any custom sort comparator
.pipe(babel())
.pipe(sort())
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
.pipe(gulp.dest(root + targets.js));
console.log(out + " compiled");
}
function processLess(files, out) {
var processors = [
autoprefixer,
cssnano({zindex: false})
];
return gulp.src(files)
.pipe(less())
.pipe(cleanCss())
.pipe(postcss(processors))
.pipe(rename(out))
.pipe(gulp.dest(root + targets.css));
console.log(out + " compiled");
}
/***************************************************************
Paths and destinations
Each group is iterated automatically in the setup tasks below
***************************************************************/
var sources = {
//less files used by backoffice and preview
//processed in the less task
less: {
installer: { files: ["src/less/installer.less"], out: "installer.css" },
nonodes: { files: ["src/less/pages/nonodes.less"], out: "nonodes.style.min.css"},
preview: { files: ["src/less/canvas-designer.less"], out: "canvasdesigner.css" },
umbraco: { files: ["src/less/belle.less"], out: "umbraco.css" }
},
//js files for backoffie
//processed in the js task
js: {
preview: { files: ["src/preview/**/*.js"], out: "umbraco.preview.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" },
resources: { files: ["src/common/resources/**/*.js"], out: "umbraco.resources.js" },
services: { files: ["src/common/services/**/*.js"], out: "umbraco.services.js" },
security: { files: ["src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
},
//selectors for copying all views into the build
//processed in the views task
views:{
umbraco: {files: ["src/views/**/*.html"], folder: ""},
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
},
//globs for file-watching
globs:{
views: "./src/views/**/*.html",
less: "./src/less/**/*.less",
js: "./src/*.js",
lib: "./lib/**/*",
assets: "./src/assets/**"
}
};
var root = "../Umbraco.Web.UI/Umbraco/";
var targets = {
js: "js/",
lib: "lib/",
views: "views/",
css: "assets/css/",
assets: "assets/"
};
/**************************
* Main tasks for the project to prepare backoffice files
**************************/
// Build - build the files ready for production
gulp.task('build', function(cb) {
runSequence(["dependencies", "js", "less", "views"], cb);
});
// Dev - build the files ready for development and start watchers
gulp.task('dev', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});
// Docserve - build and open the back office documentation
gulp.task('docserve', function(cb) {
runSequence('docs', 'connect:docs', 'open:docs', cb);
});
/**************************
* Task processes and copies all dependencies, either installed by npm or stored locally in the project
**************************/
gulp.task('dependencies', function () {
//as we do multiple things in this task, we merge the multiple streams
var stream = new MergeStream();
// Pick the dependencies we need from each package
// so we don't just ship with a lot of files that aren't needed
const nodeModules = [
{
"name": "ace-builds",
"src": [
"./node_modules/ace-builds/src-min-noconflict/ace.js",
"./node_modules/ace-builds/src-min-noconflict/ext-language_tools.js",
"./node_modules/ace-builds/src-min-noconflict/ext-searchbox.js",
"./node_modules/ace-builds/src-min-noconflict/ext-settings_menu.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/text.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/javascript.js",
"./node_modules/ace-builds/src-min-noconflict/snippets/css.js",
"./node_modules/ace-builds/src-min-noconflict/theme-chrome.js",
"./node_modules/ace-builds/src-min-noconflict/mode-razor.js",
"./node_modules/ace-builds/src-min-noconflict/mode-javascript.js",
"./node_modules/ace-builds/src-min-noconflict/mode-css.js",
"./node_modules/ace-builds/src-min-noconflict/worker-javascript.js",
"./node_modules/ace-builds/src-min-noconflict/worker-css.js"
],
"base": "./node_modules/ace-builds"
},
{
"name": "angular",
"src": ["./node_modules/angular/angular.js"],
"base": "./node_modules/angular"
},
{
"name": "angular-cookies",
"src": ["./node_modules/angular-cookies/angular-cookies.js"],
"base": "./node_modules/angular-cookies"
},
{
"name": "angular-dynamic-locale",
"src": [
"./node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js",
"./node_modules/angular-dynamic-locale/dist/tmhDynamicLocale.min.js.map"
],
"base": "./node_modules/angular-dynamic-locale/dist"
},
{
"name": "angular-sanitize",
"src": ["./node_modules/angular-sanitize/angular-sanitize.js"],
"base": "./node_modules/angular-sanitize"
},
{
"name": "angular-touch",
"src": ["./node_modules/angular-touch/angular-touch.js"],
"base": "./node_modules/angular-touch"
},
{
"name": "angular-ui-sortable",
"src": ["./node_modules/angular-ui-sortable/dist/sortable.js"],
"base": "./node_modules/angular-ui-sortable/dist"
},
{
"name": "angular-route",
"src": ["./node_modules/angular-route/angular-route.js"],
"base": "./node_modules/angular-route"
},
{
"name": "angular-animate",
"src": ["./node_modules/angular-animate/angular-animate.js"],
"base": "./node_modules/angular-animate"
},
{
"name": "angular-i18n",
"src": [
"./node_modules/angular-i18n/angular-i18n.js",
"./node_modules/angular-i18n/angular-locale_*.js"
],
"base": "./node_modules/angular-i18n"
},
{
"name": "angular-local-storage",
"src": [
"./node_modules/angular-local-storage/dist/angular-local-storage.min.js",
"./node_modules/angular-local-storage/dist/angular-local-storage.min.js.map"
],
"base": "./node_modules/angular-local-storage/dist"
},
{
"name": "angular-messages",
"src": ["./node_modules/angular-messages/angular-messages.js"],
"base": "./node_modules/angular-messages"
},
{
"name": "angular-mocks",
"src": ["./node_modules/angular-mocks/angular-mocks.js"],
"base": "./node_modules/angular-mocks"
},
{
"name": "animejs",
"src": ["./node_modules/animejs/anime.min.js"],
"base": "./node_modules/animejs"
},
{
"name": "bootstrap-social",
"src": ["./node_modules/bootstrap-social/bootstrap-social.css"],
"base": "./node_modules/bootstrap-social"
},
{
"name": "angular-chart.js",
"src": ["./node_modules/angular-chart.js/dist/angular-chart.min.js"],
"base": "./node_modules/angular-chart.js/dist"
},
{
"name": "chart.js",
"src": ["./node_modules/chart.js/dist/chart.min.js"],
"base": "./node_modules/chart.js/dist"
},
{
"name": "clipboard",
"src": ["./node_modules/clipboard/dist/clipboard.min.js"],
"base": "./node_modules/clipboard/dist"
},
{
"name": "jsdiff",
"src": ["./node_modules/diff/dist/diff.min.js"],
"base": "./node_modules/diff/dist"
},
{
"name": "flatpickr",
"src": [
"./node_modules/flatpickr/dist/flatpickr.js",
"./node_modules/flatpickr/dist/flatpickr.css"
],
"base": "./node_modules/flatpickr/dist"
},
{
"name": "font-awesome",
"src": [
"./node_modules/font-awesome/fonts/*",
"./node_modules/font-awesome/css/font-awesome.min.css"
],
"base": "./node_modules/font-awesome"
},
{
"name": "jquery",
"src": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/jquery/dist/jquery.min.map"
],
"base": "./node_modules/jquery/dist"
},
{
"name": "jquery-ui",
"src": ["./node_modules/jquery-ui-dist/jquery-ui.min.js"],
"base": "./node_modules/jquery-ui-dist"
},
{
"name": "jquery-ui-touch-punch",
"src": ["./node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js"],
"base": "./node_modules/jquery-ui-touch-punch"
},
{
"name": "lazyload-js",
"src": ["./node_modules/lazyload-js/lazyload.min.js"],
"base": "./node_modules/lazyload-js"
},
{
"name": "moment",
"src": ["./node_modules/moment/min/moment.min.js"],
"base": "./node_modules/moment/min"
},
{
"name": "moment",
"src": ["./node_modules/moment/locale/*.js"],
"base": "./node_modules/moment/locale"
},
{
"name": "ng-file-upload",
"src": ["./node_modules/ng-file-upload/dist/ng-file-upload.min.js"],
"base": "./node_modules/ng-file-upload/dist"
},
{
"name": "nouislider",
"src": [
"./node_modules/nouislider/distribute/nouislider.min.js",
"./node_modules/nouislider/distribute/nouislider.min.css"
],
"base": "./node_modules/nouislider/distribute"
},
{
"name": "signalr",
"src": ["./node_modules/signalr/jquery.signalR.js"],
"base": "./node_modules/signalr"
},
{
"name": "spectrum",
"src": [
"./node_modules/spectrum-colorpicker/spectrum.js",
"./node_modules/spectrum-colorpicker/spectrum.css"
],
"base": "./node_modules/spectrum-colorpicker"
},
{
"name": "tinymce",
"src": [
"./node_modules/tinymce/tinymce.min.js",
"./node_modules/tinymce/plugins/**",
"./node_modules/tinymce/skins/**",
"./node_modules/tinymce/themes/**"
],
"base": "./node_modules/tinymce"
},
{
"name": "typeahead.js",
"src": ["./node_modules/typeahead.js/dist/typeahead.bundle.min.js"],
"base": "./node_modules/typeahead.js/dist"
},
{
"name": "underscore",
"src": ["node_modules/underscore/underscore-min.js"],
"base": "./node_modules/underscore"
}
];
// add streams for node modules
nodeModules.forEach(module => {
stream.add(
gulp.src(module.src,
{ base: module.base })
.pipe(gulp.dest(root + targets.lib + "/" + module.name))
);
});
//copy over libs which are not on npm (/lib)
stream.add(
gulp.src(sources.globs.lib)
.pipe(gulp.dest(root + targets.lib))
);
//Copies all static assets into /root / assets folder
//css, fonts and image files
stream.add(
gulp.src(sources.globs.assets)
.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
{removeViewBox: true},
{cleanupIDs: false}
]
})
]))
.pipe(gulp.dest(root + targets.assets))
);
// Copies all the less files related to the preview into their folder
//these are not pre-processed as preview has its own less combiler client side
stream.add(
gulp.src("src/canvasdesigner/editors/*.less")
.pipe(gulp.dest(root + targets.assets + "/less"))
);
// Todo: check if we need these fileSize
stream.add(
gulp.src("src/views/propertyeditors/grid/config/*.*")
.pipe(gulp.dest(root + targets.views + "/propertyeditors/grid/config"))
);
stream.add(
gulp.src("src/views/dashboard/default/*.jpg")
.pipe(gulp.dest(root + targets.views + "/dashboard/default"))
);
return stream;
});
/**************************
* Copies all angular JS files into their seperate umbraco.*.js file
**************************/
gulp.task('js', function () {
//we run multiple streams, so merge them all together
var stream = new MergeStream();
stream.add(
gulp.src(sources.globs.js)
.pipe(gulp.dest(root + targets.js))
);
_.forEach(sources.js, function (group) {
stream.add (processJs(group.files, group.out) );
});
return stream;
});
gulp.task('less', function () {
var stream = new MergeStream();
_.forEach(sources.less, function (group) {
stream.add( processLess(group.files, group.out) );
});
return stream;
});
gulp.task('views', function () {
var stream = new MergeStream();
_.forEach(sources.views, function (group) {
console.log("copying " + group.files + " to " + root + targets.views + group.folder)
stream.add (
gulp.src(group.files)
.pipe( gulp.dest(root + targets.views + group.folder) )
);
});
return stream;
});
gulp.task('watch', function () {
var stream = new MergeStream();
var watchInterval = 500;
//Setup a watcher for all groups of javascript files
_.forEach(sources.js, function (group) {
if(group.watch !== false){
stream.add(
watch(group.files, { ignoreInitial: true, interval: watchInterval }, function (file) {
console.info(file.path + " has changed, added to: " + group.out);
processJs(group.files, group.out);
})
);
}
});
stream.add(
//watch all less files and trigger the less task
watch(sources.globs.less, { ignoreInitial: true, interval: watchInterval }, function () {
gulp.run(['less']);
})
);
//watch all views - copy single file changes
stream.add(
watch(sources.globs.views, { interval: watchInterval })
.pipe(gulp.dest(root + targets.views))
);
//watch all app js files that will not be merged - copy single file changes
stream.add(
watch(sources.globs.js, { interval: watchInterval })
.pipe(gulp.dest(root + targets.js))
);
return stream;
});
/**************************
* Build Backoffice UI API documentation
**************************/
gulp.task('docs', [], function (cb) {
var options = {
html5Mode: false,
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
dest: 'docs/api',
styles: ['docs/umb-docs.css'],
image: "https://our.umbraco.com/assets/images/logo.svg"
}
return gulpDocs.sections({
api: {
glob: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
api: true,
title: 'API Documentation'
}
})
.pipe(gulpDocs.process(options))
.pipe(gulp.dest('docs/api'));
cb();
});
gulp.task('connect:docs', function (cb) {
connect.server({
root: 'docs/api',
livereload: true,
fallback: 'docs/api/index.html',
port: 8880
});
cb();
});
gulp.task('open:docs', function (cb) {
var options = {
uri: 'http://localhost:8880/index.html'
};
gulp.src(__filename)
.pipe(open(options));
cb();
});
/**************************
* Build tests
**************************/
// Karma test
gulp.task('test:unit', function() {
new karmaServer({
configFile: __dirname + "/test/config/karma.conf.js",
keepalive: true
})
.start();
});
gulp.task('test:e2e', function() {
new karmaServer({
configFile: __dirname + "/test/config/e2e.js",
keepalive: true
})
.start();
});
'use strict';
/*
* gulpfile.js
* ===========
* Rather than manage one giant configuration file responsible
* for creating multiple tasks, each task has been broken out into
* its own file in gulp/tasks. Any file in that folder gets automatically
* required by the loop in ./gulp/index.js (required below).
*
* To add a new task, simply add a new task file to gulp/tasks.
*/
global.isProd = true;
require('./gulp');

View File

@@ -5115,6 +5115,12 @@
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
"dev": true
},
"fs": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz",
"integrity": "sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg=",
"dev": true
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -6119,6 +6125,12 @@
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
"dev": true
},
"growly": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"dev": true
},
"gulp": {
"version": "3.9.1",
"resolved": "http://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz",
@@ -6916,6 +6928,102 @@
}
}
},
"gulp-notify": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz",
"integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==",
"dev": true,
"requires": {
"ansi-colors": "^1.0.1",
"fancy-log": "^1.3.2",
"lodash.template": "^4.4.0",
"node-notifier": "^5.2.1",
"node.extend": "^2.0.0",
"plugin-error": "^0.1.2",
"through2": "^2.0.3"
},
"dependencies": {
"arr-diff": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
"integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
"dev": true,
"requires": {
"arr-flatten": "^1.0.1",
"array-slice": "^0.2.3"
}
},
"arr-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
"integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=",
"dev": true
},
"array-slice": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
"integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
"dev": true
},
"extend-shallow": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
"integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
"dev": true,
"requires": {
"kind-of": "^1.1.0"
}
},
"kind-of": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
"dev": true
},
"lodash.template": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
"integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=",
"dev": true,
"requires": {
"lodash._reinterpolate": "~3.0.0",
"lodash.templatesettings": "^4.0.0"
}
},
"lodash.templatesettings": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz",
"integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=",
"dev": true,
"requires": {
"lodash._reinterpolate": "~3.0.0"
}
},
"node.extend": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz",
"integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==",
"dev": true,
"requires": {
"has": "^1.0.3",
"is": "^3.2.1"
}
},
"plugin-error": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
"integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
"dev": true,
"requires": {
"ansi-cyan": "^0.1.1",
"ansi-red": "^0.1.1",
"arr-diff": "^1.0.1",
"arr-union": "^2.0.1",
"extend-shallow": "^1.1.2"
}
}
}
},
"gulp-open": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/gulp-open/-/gulp-open-3.0.1.tgz",
@@ -9368,6 +9476,18 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"node-notifier": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.3.0.tgz",
"integrity": "sha512-AhENzCSGZnZJgBARsUjnQ7DnZbzyP+HxlVXuD0xqAnvL8q+OqtSX7lGg9e8nHzwXkMMXNdVeqq4E2M3EUAqX6Q==",
"dev": true,
"requires": {
"growly": "^1.3.0",
"semver": "^5.5.0",
"shellwords": "^0.1.1",
"which": "^1.3.0"
}
},
"node-releases": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.5.tgz",
@@ -14124,6 +14244,12 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"shellwords": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
"dev": true
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",

View File

@@ -2,7 +2,10 @@
"private": true,
"scripts": {
"test": "karma start test/config/karma.conf.js --singlerun",
"build": "gulp"
"build": "gulp build",
"dev": "gulp dev",
"fastdev": "gulp fastdev",
"docs": "gulp docs"
},
"dependencies": {
"ace-builds": "1.4.2",
@@ -62,6 +65,7 @@
"gulp-watch": "5.0.1",
"gulp-wrap": "0.14.0",
"gulp-wrap-js": "0.4.1",
"gulp-notify": "^3.0.0",
"jasmine-core": "3.3.0",
"karma": "3.1.1",
"karma-jasmine": "2.0.1",
@@ -70,6 +74,7 @@
"lodash": "4.17.11",
"marked": "^0.5.2",
"merge-stream": "1.0.1",
"run-sequence": "2.2.1"
"run-sequence": "2.2.1",
"fs": "0.0.2"
}
}

View File

@@ -215,9 +215,15 @@
* @param {any} selectedVariant
*/
function openSplitView(selectedVariant) {
var selectedCulture = selectedVariant.language.culture;
//Find the whole variant model based on the culture that was chosen
var variant = _.find(vm.content.variants, function (v) {
return v.language.culture === selectedCulture;
});
insertVariantEditor(vm.editors.length, initVariant(variant, vm.editors.length));
//only the content app can be selected since no other apps are shown, and because we copy all of these apps
//to the "editors" we need to update this across all editors
for (var e = 0; e < vm.editors.length; e++) {
@@ -233,13 +239,6 @@
}
}
//Find the whole variant model based on the culture that was chosen
var variant = _.find(vm.content.variants, function (v) {
return v.language.culture === selectedCulture;
});
insertVariantEditor(vm.editors.length, initVariant(variant, vm.editors.length));
//TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
editor.collapsed = true;
editor.loading = true;
@@ -260,6 +259,8 @@
vm.editors.splice(editorIndex, 1);
//remove variant from open variants
vm.openVariants.splice(editorIndex, 1);
//update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)
$location.search("cculture", vm.openVariants[0]);
splitViewChanged();
}, 400);
}
@@ -270,7 +271,7 @@
* @param {any} editorIndex The index of the editor being changed
*/
function selectVariant(variant, editorIndex) {
// prevent variants already open in a split view to be opened
if(vm.openVariants.indexOf(variant.language.culture) !== -1) {
return;

View File

@@ -44,6 +44,7 @@ the directive will use {@link umbraco.directives.directive:umbLockedField umbLoc
@param {string} alias (<code>binding</code>): The model where the alias is bound.
@param {string} aliasFrom (<code>binding</code>): The model to generate the alias from.
@param {string} validationPosition (<code>binding</code>): The position of the validation. Set to <code>'left'</code> or <code>'right'</code>.
@param {boolean=} enableLock (<code>binding</code>): Set to <code>true</code> to add a lock next to the alias from where it can be unlocked and changed.
**/
@@ -57,6 +58,7 @@ angular.module("umbraco.directives")
alias: '=',
aliasFrom: '=',
enableLock: '=?',
validationPosition: '=?',
serverValidationField: '@'
},
link: function (scope, element, attrs, ctrl) {

View File

@@ -40,6 +40,7 @@ Use this directive to render a value with a lock next to it. When the lock is cl
@param {boolean=} locked (<code>binding</code>): <Code>true</code> by default. Set to <code>false</code> to unlock the text.
@param {string=} placeholderText (<code>binding</code>): If ngModel is empty this text will be shown.
@param {string=} regexValidation (<code>binding</code>): Set a regex expression for validation of the field.
@param {string} validationPosition (<code>binding</code>): The position of the validation. Set to <code>'left'</code> or <code>'right'</code>.
@param {string=} serverValidationField (<code>attribute</code>): Set a server validation field.
**/
@@ -70,7 +71,11 @@ Use this directive to render a value with a lock next to it. When the lock is cl
// if locked state is not defined as an attr set default state
if (scope.placeholderText === undefined || scope.placeholderText === null) {
scope.placeholderText = "Enter value...";
}
}
if (scope.validationPosition === undefined || scope.validationPosition === null) {
scope.validationPosition = "left";
}
}
@@ -93,9 +98,10 @@ Use this directive to render a value with a lock next to it. When the lock is cl
templateUrl: 'views/components/umb-locked-field.html',
scope: {
ngModel: "=",
locked: "=?",
locked: "=?",
placeholderText: "=?",
regexValidation: "=?",
regexValidation: "=?",
validationPosition: "=?",
serverValidationField: "@"
},
link: link

View File

@@ -11,24 +11,24 @@ angular.module('umbraco.mocks').
var menu = [
{ name: "Create", cssclass: "plus", alias: "create", metaData: {} },
{ seperator: true, name: "Delete", cssclass: "remove", alias: "delete", metaData: {} },
{ separator: true, name: "Delete", cssclass: "remove", alias: "delete", metaData: {} },
{ name: "Move", cssclass: "move", alias: "move", metaData: {} },
{ name: "Copy", cssclass: "copy", alias: "copy", metaData: {} },
{ name: "Sort", cssclass: "sort", alias: "sort", metaData: {} },
{ seperator: true, name: "Publish", cssclass: "globe", alias: "publish", metaData: {} },
{ separator: true, name: "Publish", cssclass: "globe", alias: "publish", metaData: {} },
{ name: "Rollback", cssclass: "undo", alias: "rollback", metaData: {} },
{ seperator: true, name: "Permissions", cssclass: "lock", alias: "permissions", metaData: {} },
{ separator: true, name: "Permissions", cssclass: "lock", alias: "permissions", metaData: {} },
{ name: "Audit Trail", cssclass: "time", alias: "audittrail", metaData: {} },
{ name: "Notifications", cssclass: "envelope", alias: "notifications", metaData: {} },
{ seperator: true, name: "Hostnames", cssclass: "home", alias: "hostnames", metaData: {} },
{ separator: true, name: "Hostnames", cssclass: "home", alias: "hostnames", metaData: {} },
{ name: "Public Access", cssclass: "group", alias: "publicaccess", metaData: {} },
{ seperator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} },
{ separator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} },
{ seperator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyrecyclebin", metaData: {} }
{ separator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyrecyclebin", metaData: {} }
];
var result = {
@@ -94,7 +94,7 @@ angular.module('umbraco.mocks').
jsAction: "umbracoMenuActions.CreateChildEntity"
}
},
{ seperator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} }
{ separator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} }
];
return [200, menu, null];

View File

@@ -453,13 +453,11 @@ angular.module('umbraco.mocks').
"notifications_notifications": "Notifications",
"packager_chooseLocalPackageText": " Choose Package from your machine, by clicking the Browse<br /> button and locating the package. Umbraco packages usually have a '.umb' or '.zip' extension. ",
"packager_packageAuthor": "Author",
"packager_packageDemonstration": "Demonstration",
"packager_packageDocumentation": "Documentation",
"packager_packageMetaData": "Package meta data",
"packager_packageName": "Package name",
"packager_packageNoItemsHeader": "Package doesn't contain any items",
"packager_packageNoItemsText": "This package file doesn't contain any items to uninstall.<br/><br/> You can safely remove this from the system by clicking 'uninstall package' below.",
"packager_packageNoUpgrades": "No upgrades available",
"packager_packageOptions": "Package options",
"packager_packageReadme": "Package readme",
"packager_packageRepository": "Package repository",
@@ -468,12 +466,7 @@ angular.module('umbraco.mocks').
"packager_packageUninstalledText": "The package was successfully uninstalled",
"packager_packageUninstallHeader": "Uninstall package",
"packager_packageUninstallText": "You can unselect items you do not wish to remove, at this time, below. When you click 'confirm uninstall' all checked-off items will be removed.<br /> <span style='color: Red; font-weight: bold;'>Notice:</span> any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, so uninstall with caution. If in doubt, contact the package author.",
"packager_packageUpgradeDownload": "Download update from the repository",
"packager_packageUpgradeHeader": "Upgrade package",
"packager_packageUpgradeInstructions": "Upgrade instructions",
"packager_packageUpgradeText": " There's an upgrade available for this package. You can download it directly from the Umbraco package repository.",
"packager_packageVersion": "Package version",
"packager_viewPackageWebsite": "View package website",
"paste_doNothing": "Paste with full formatting (Not recommended)",
"paste_errorMessage": "The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web.",
"paste_removeAll": "Paste as raw text without any formatting at all",
@@ -640,7 +633,7 @@ angular.module('umbraco.mocks').
"templateEditor_urlEncodeHelp": "Will format special characters in URLs",
"templateEditor_usedIfAllEmpty": "Will only be used when the field values above are empty",
"templateEditor_usedIfEmpty": "This field will only be used if the primary field is empty",
"templateEditor_withTime": "Yes, with time. Seperator: ",
"templateEditor_withTime": "Yes, with time. Separator: ",
"translation_details": "Translation details",
"translation_DownloadXmlDTD": "Download xml DTD",
"translation_fields": "Fields",

View File

@@ -175,7 +175,17 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"packageApiBaseUrl",
"GetCreatedPackageById",
[{ id: id }])),
{ id: id })),
'Failed to get package');
},
getInstalledById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"packageApiBaseUrl",
"GetInstalledPackageById",
{ id: id })),
'Failed to get package');
},

View File

@@ -421,7 +421,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
*/
reBindChangedProperties: function (origContent, savedContent) {
//TODO: We should probably split out this logic to deal with media/members seperately to content
//TODO: We should probably split out this logic to deal with media/members separately to content
//a method to ignore built-in prop changes
var shouldIgnore = function (propName) {

View File

@@ -125,6 +125,8 @@ body.touch .umb-tree {
position: inherit;
display: inherit;
list-style: none;
h6 {
padding: 10px 0 10px 20px;
font-weight: inherit;
@@ -137,7 +139,15 @@ body.touch .umb-tree {
}
&-item {
padding-left: 20px;
padding: 4px 0;
&:hover {
background-color: @gray-10;
}
&-link {
display: block;
}
}
&-link {

View File

@@ -28,7 +28,7 @@
color: @black;
}
.umb-breadcrumbs__seperator {
.umb-breadcrumbs__separator {
position: relative;
top: 1px;
margin-left: 5px;
@@ -41,4 +41,4 @@ input.umb-breadcrumbs__add-ancestor {
margin-top: -2px;
margin-left: 3px;
width: 100px;
}
}

View File

@@ -38,6 +38,7 @@ input.umb-locked-field__input {
transition: color 0.25s;
padding: 0;
height: auto;
max-width: 300px;
}
input.umb-locked-field__input:focus {

View File

@@ -1,46 +1,67 @@
.umb-validation-label {
position: absolute;
top: 27px;
width: 200px;
padding: 1px 5px;
background: @red;
color: @white;
font-size: 11px;
line-height: 1.5em;
position: absolute;
top: 27px;
min-width: 100px;
max-width: 200px;
padding: 1px 5px;
background: @red;
color: @white;
font-size: 11px;
line-height: 1.5em;
}
.umb-validation-label:after {
bottom: 100%;
left: 10px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: @red;
border-width: 4px;
margin-left: -4px;
bottom: 100%;
left: 10px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: @red;
border-width: 4px;
margin-left: -4px;
}
.umb-validation-label.-left {
left: 0;
right: auto;
&:after {
left: 10px;
right: auto;
}
}
.umb-validation-label.-right {
right: 0;
left: auto;
&:after {
right: 10px;
left: auto;
}
}
.umb-validation-label.-arrow-left {
margin-left: 10px;
margin-left: 10px;
}
.umb-validation-label.-arrow-left:after {
right: 100%;
top: 50%;
left: auto;
bottom: auto;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-right-color: @red;
border-width: 4px;
margin-top: -4px;
right: 100%;
top: 50%;
left: auto;
bottom: auto;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-right-color: @red;
border-width: 4px;
margin-top: -4px;
}

View File

@@ -373,10 +373,6 @@
margin-bottom: 0;
}
.umb-panel-header-alias .umb-validation-label:after {
visibility: hidden;
}
.umb-panel-header-alias .umb-locked-field:after {
display: none;
}

View File

@@ -149,14 +149,22 @@ app.config(function ($routeProvider) {
.when('/:section/:tree/:method?', {
//This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
template: "<div ng-include='templateUrl'></div>",
//This controller will execute for this route, then we replace the template dynamnically based on the current tree.
controller: function ($scope, $route, $routeParams, treeService) {
//This controller will execute for this route, then we replace the template dynamically based on the current tree.
controller: function ($scope, $routeParams, treeService) {
if (!$routeParams.method) {
$scope.templateUrl = "views/common/dashboard.html";
return;
}
// Here we need to figure out if this route is for a package tree and if so then we need
//special case for the package section
var packagePages = ["edit", "options"];
if ($routeParams.section.toLowerCase() === "packages" && $routeParams.tree.toLowerCase() === "packages" && packagePages.indexOf($routeParams.method.toLowerCase()) === -1) {
$scope.templateUrl = "views/packages/overview.html";
return;
}
// Here we need to figure out if this route is for a user's package tree and if so then we need
// to change it's convention view path to:
// /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html

View File

@@ -33,31 +33,51 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
$scope.showTarget = $scope.model.hideTarget !== true;
// this ensures that we only sync the tree once and only when it's ready
var oneTimeTreeSync = {
executed: false,
treeReady: false,
sync: function () {
// don't run this if:
// - it was already run once
// - the tree isn't ready yet
// - the model path hasn't been loaded yet
if (this.executed || !this.treeReady || !($scope.model.target && $scope.model.target.path)) {
return;
}
this.executed = true;
// sync the tree to the model path
$scope.dialogTreeApi.syncTree({
path: $scope.model.target.path,
tree: "content"
});
}
};
if (dialogOptions.currentTarget) {
$scope.model.target = dialogOptions.currentTarget;
// clone the current target so we don't accidentally update the caller's model while manipulating $scope.model.target
$scope.model.target = angular.copy(dialogOptions.currentTarget);
//if we have a node ID, we fetch the current node to build the form data
if ($scope.model.target.id || $scope.model.target.udi) {
//will be either a udi or an int
var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
if (!$scope.model.target.path) {
// is it a content link?
if (!$scope.model.target.isMedia) {
// get the content path
entityResource.getPath(id, "Document").then(function (path) {
$scope.model.target.path = path;
//now sync the tree to this path
$scope.dialogTreeApi.syncTree({
path: $scope.model.target.path,
tree: "content"
});
oneTimeTreeSync.sync();
});
// get the content properties to build the anchor name list
contentResource.getById(id).then(function (resp) {
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0];
});
}
// if a link exists, get the properties to build the anchor name list
contentResource.getById(id).then(function (resp) {
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0];
});
} else if ($scope.model.target.url.length) {
// a url but no id/udi indicates an external link - trim the url to remove the anchor/qs
// only do the substring if there's a # or a ?
@@ -72,6 +92,11 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
$scope.anchorValues = dialogOptions.anchors;
}
function treeLoadedHandler(args) {
oneTimeTreeSync.treeReady = true;
oneTimeTreeSync.sync();
}
function nodeSelectHandler(args) {
if (args && args.event) {
args.event.preventDefault();
@@ -127,6 +152,12 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
$scope.model.target.url = mediaHelper.resolveFile(media);
editorService.close();
// make sure the content tree has nothing highlighted
$scope.dialogTreeApi.syncTree({
path: "-1",
tree: "content"
});
},
close: function() {
editorService.close();
@@ -146,7 +177,7 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, {
nodeSelectHandler({
event: evt,
node: result
});
@@ -159,6 +190,7 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
};
$scope.onTreeInit = function () {
$scope.dialogTreeApi.callbacks.treeLoaded(treeLoadedHandler);
$scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler);
$scope.dialogTreeApi.callbacks.treeNodeExpanded(nodeExpandedHandler);
}
@@ -166,7 +198,7 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, {
nodeSelectHandler({
node: node
});
};

View File

@@ -55,12 +55,12 @@
<ul class="umb-breadcrumbs">
<li ng-hide="startNodeId != -1" class="umb-breadcrumbs__ancestor">
<a href ng-click="gotoFolder()" prevent-default>Media</a>
<span class="umb-breadcrumbs__seperator">&#47;</span>
<span class="umb-breadcrumbs__separator">&#47;</span>
</li>
<li ng-repeat="item in path" class="umb-breadcrumbs__ancestor">
<a href ng-click="gotoFolder(item)" prevent-default>{{item.name}}</a>
<span class="umb-breadcrumbs__seperator">&#47;</span>
<span class="umb-breadcrumbs__separator">&#47;</span>
</li>
<li class="umb-breadcrumbs__ancestor" ng-show="!lockedFolder">

View File

@@ -54,6 +54,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
vm.toggleLanguageSelector = toggleLanguageSelector;
vm.selectLanguage = selectLanguage;
vm.onSearchResults = onSearchResults;
vm.selectResult = selectResult;
vm.hideSearch = hideSearch;
vm.closeMiniListView = closeMiniListView;
vm.selectListViewNode = selectListViewNode;

View File

@@ -5,7 +5,7 @@
<div class='umb-modalcolumn-body'>
<ul class="umb-actions">
<li data-element="action-{{action.alias}}" ng-click="executeMenuItem(action)" class="umb-action" ng-class="{sep:action.seperator, '-opens-dialog': action.opensDialog}" ng-repeat="action in menuActions">
<li data-element="action-{{action.alias}}" ng-click="executeMenuItem(action)" class="umb-action" ng-class="{sep:action.separator, '-opens-dialog': action.opensDialog}" ng-repeat="action in menuActions">
<a class="umb-action-link" prevent-default>
<i class="icon icon-{{action.cssclass}}"></i>
<span class="menu-label">{{action.name}}</span>

View File

@@ -7,15 +7,11 @@
<umb-box-content class="block-form">
<ul class="nav nav-stacked" style="margin-bottom: 0;">
<li ng-repeat="url in currentUrls">
<span ng-if="url.isUrl">
<a href="{{url.text}}" target="_blank" ng-if="url.isUrl">
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
<a href="{{url.text}}" target="_blank">
<i class="icon icon-window-popin"></i>
<span>{{url.text}}</span>
</a>
</span>
<i class="icon icon-out"></i>
<span>{{url.text}}</span>
</a>
<div ng-if="!url.isUrl" style="margin-top: 4px;">
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>

View File

@@ -7,9 +7,9 @@
<!-- use callback to handle click -->
<a ng-if="!$last && allowOnOpen" href="#" ng-click="open(ancestor)" class="umb-breadcrumbs__ancestor-link" title="{{ancestor.name}}" prevent-default>{{ancestor.name}}</a>
<span ng-if="!$last" class="umb-breadcrumbs__seperator">&#47;</span>
<span ng-if="!$last" class="umb-breadcrumbs__separator">&#47;</span>
<span class="umb-breadcrumbs__ancestor-text" ng-if="$last" title="{{ancestor.name}}">{{ancestor.name}}</span>
</li>
</ul>
</ul>

View File

@@ -45,6 +45,7 @@
alias="$parent.alias"
alias-from="$parent.name"
enable-lock="true"
validation-position="'right'"
server-validation-field="Alias">
</umb-generate-alias>

View File

@@ -9,7 +9,7 @@
</umb-button>
<umb-dropdown ng-if="dropdown.isOpen" class="umb-actions" on-close="dropdown.isOpen = false">
<umb-dropdown-item class="umb-action" ng-class="{'sep':action.seperatorm, '-opens-dialog': action.opensDialog}" ng-repeat="action in actions">
<umb-dropdown-item class="umb-action" ng-class="{'sep':action.separatorm, '-opens-dialog': action.opensDialog}" ng-repeat="action in actions">
<a href="" ng-click="executeMenuItem(action)" prevent-default>
<i class="icon icon-{{action.cssclass}}"></i>
<span class="menu-label">{{action.name}}</span>

View File

@@ -5,16 +5,16 @@
<umb-box-content class="block-form">
<umb-empty-state
ng-if="!nodeUrl || !nodeUrl.isUrl"
ng-if="!nodeUrl"
size="small">
<localize key="content_noMediaLink"></localize>
</umb-empty-state>
<ul ng-if="nodeUrl && nodeUrl.isUrl" class="nav nav-stacked" style="margin-bottom: 0;">
<ul ng-if="nodeUrl" class="nav nav-stacked" style="margin-bottom: 0;">
<li>
<a href="{{nodeUrl.text}}" target="_blank">
<i class="icon icon-window-popin"></i>
<span>{{nodeUrl.text}}</span>
<a href="{{nodeUrl}}" target="_blank">
<i class="icon icon-out"></i>
<span>{{nodeUrl}}</span>
</a>
</li>
</ul>

View File

@@ -6,9 +6,9 @@
<ul class="umb-tree">
<li class="root">
<ul class="umb-search-group">
<li ng-repeat="result in results">
<div style="padding-left: 20px" ng-class="{'umb-tree-node-checked' : result.selected}">
<a class="umb-search-group-item-link" ng-class="{first:$first}" ng-click="selectResultCallback($event, result)">
<li class="umb-search-group-item" ng-repeat="result in results">
<div ng-class="{'umb-tree-node-checked' : result.selected}">
<a href class="umb-search-group-item-link" ng-class="{first:$first}" ng-click="selectResultCallback($event, result)">
<div class="umb-search-group-item-name">
<i class="icon umb-tree-icon sprTree {{result.icon}}"></i>
{{result.name}}

View File

@@ -5,8 +5,8 @@
locked="locked"
ng-model="alias"
placeholder-text="placeholderText"
validation-position="validationPosition"
server-validation-field="{{serverValidationField}}">
</umb-locked-field>
</div>
</div>

View File

@@ -29,9 +29,22 @@
</div>
<div ng-messages="lockedFieldForm.lockedField.$error" show-validation-on-submit>
<div class="umb-validation-label" ng-message="required"><localize key="general_required">Required</localize> <localize key="content_alias">alias</localize></div>
<div ng-if="regexValidation.length > 0" class="umb-validation-label" ng-message="valRegex"><localize key="general_invalid">Invalid</localize> <localize key="content_alias">alias</localize></div>
<div ng-if="serverValidationField.length > 0" class="umb-validation-label" ng-message="valServerField">{{lockedFieldForm.lockedField.errorMsg}}</div>
<div class="umb-validation-label"
ng-class="{ '-left': validationPosition === 'left', '-right': validationPosition === 'right' }"
ng-message="required">
<localize key="general_required">Required</localize> <localize key="content_alias">alias</localize>
</div>
<div class="umb-validation-label"
ng-class="{ '-left': validationPosition === 'left', '-right': validationPosition === 'right' }"
ng-if="regexValidation.length > 0"
ng-message="valRegex">
<localize key="general_invalid">Invalid</localize> <localize key="content_alias">alias</localize>
</div>
<div class="umb-validation-label"
ng-class="{ '-left': validationPosition === 'left', '-right': validationPosition === 'right' }"
ng-if="serverValidationField.length > 0"
ng-message="valServerField">{{lockedFieldForm.lockedField.errorMsg}}
</div>
</div>
</ng-form>

View File

@@ -1,26 +1,91 @@
angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController",
function ($scope, relationResource, mediaResource, navigationService, appState, treeService, localizationService) {
function ($scope, relationResource, mediaResource, entityResource, navigationService, appState, treeService, userService) {
$scope.source = _.clone($scope.currentNode);
$scope.error = null;
$scope.success = false;
$scope.loading = true;
$scope.moving = false;
$scope.success = false;
$scope.dialogTreeApi = {};
$scope.searchInfo = {
showSearch: false,
results: [],
selectedSearchResults: []
}
$scope.treeModel = {
hideHeader: false
}
userService.getCurrentUser().then(function (userData) {
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
});
function nodeSelectHandler(args) {
if (args && args.event) {
args.event.preventDefault();
args.event.stopPropagation();
}
if ($scope.target) {
//un-select if there's a current one selected
$scope.target.selected = false;
}
$scope.target = args.node;
$scope.target.selected = true;
}
function nodeExpandedHandler(args) {
// open mini list view for list views
if (args.node.metaData.isContainer) {
openMiniListView(args.node);
}
}
$scope.hideSearch = function () {
$scope.searchInfo.showSearch = false;
$scope.searchInfo.results = [];
}
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
};
//callback when there are search results
$scope.onSearchResults = function (results) {
$scope.searchInfo.results = results;
$scope.searchInfo.showSearch = true;
};
$scope.onTreeInit = function () {
$scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler);
$scope.dialogTreeApi.callbacks.treeNodeExpanded(nodeExpandedHandler);
}
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
};
$scope.closeMiniListView = function () {
$scope.miniListView = undefined;
};
function openMiniListView(node) {
$scope.miniListView = node;
}
relationResource.getByChildId($scope.source.id, "relateParentDocumentOnDelete").then(function (data) {
$scope.loading = false;
if (!data.length) {
localizationService.localizeMany(["recycleBin_itemCannotBeRestored", "recycleBin_noRestoreRelation"])
.then(function(values) {
$scope.success = false;
$scope.error = {
errorMsg: values[0],
data: {
Message: values[1]
}
}
});
$scope.moving = true;
return;
}
@@ -30,40 +95,31 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController",
$scope.target = { id: -1, name: "Root" };
} else {
$scope.loading = true;
mediaResource.getById($scope.relation.parentId).then(function (data) {
$scope.loading = false;
$scope.target = data;
// make sure the target item isn't in the recycle bin
if ($scope.target.path.indexOf("-21") !== -1) {
localizationService.localizeMany(["recycleBin_itemCannotBeRestored", "recycleBin_restoreUnderRecycled"])
.then(function (values) {
$scope.success = false;
$scope.error = {
errorMsg: values[0],
data: {
Message: values[1].replace('%0%', $scope.target.name)
}
}
});
$scope.success = false;
}
$scope.loading = true;
}, function (err) {
$scope.success = false;
$scope.error = err;
entityResource.getById($scope.relation.parentId, "media").then(function (data) {
$scope.loading = false;
});
$scope.target = data;
// make sure the target item isn't in the recycle bin
if ($scope.target.path.indexOf("-21") !== -1) {
$scope.moving = true;
$scope.target = null;
}
}, function (err) {
$scope.loading = false;
$scope.error = err;
});
}
}, function (err) {
$scope.success = false;
$scope.error = err;
$scope.loading = false;
$scope.error = err;
});
$scope.restore = function () {
$scope.loading = true;
$scope.loading = true;
// this code was copied from `content.move.controller.js`
mediaResource.move({ parentId: $scope.target.id, id: $scope.source.id })
.then(function (path) {
@@ -89,9 +145,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController",
});
}, function (err) {
$scope.success = false;
$scope.error = err;
$scope.loading = false;
$scope.error = err;
});
};

View File

@@ -1,34 +1,87 @@
<div ng-controller="Umbraco.Editors.Media.RestoreController">
<div class="umb-dialog-body">
<umb-pane>
<div class="umb-dialog-body">
<umb-pane>
<umb-load-indicator ng-show="loading">
</umb-load-indicator>
<umb-load-indicator
ng-show="loading">
</umb-load-indicator>
<div ng-show="error">
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.Message}}</div>
</div>
</div>
<p class="abstract" ng-hide="loading || error != null || success">
<localize key="actions_restore">Restore</localize> <strong>{{source.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>?
</p>
<div ng-show="success">
<div class="alert alert-success">
<strong>{{source.name}}</strong>
<span ng-hide="moving"><localize key="recycleBin_wasRestored">was restored under</localize></span>
<span ng-show="moving"><localize key="editdatatype_wasMoved">was moved underneath</localize></span>
<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="close()">Ok</button>
</div>
<div ng-show="error">
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.Message}}</div>
</div>
</div>
<div ng-hide="moving || loading || success">
<div ng-show="success">
<div class="alert alert-success">
<strong>{{source.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize> <strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="close()"><localize key="general_ok">Ok</localize></button>
</div>
<p class="abstract" ng-hide="error || success">
<localize key="actions_restore">Restore</localize> <strong>{{source.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>?
</p>
</umb-pane>
</div>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || success">
<a class="btn btn-link" ng-click="close()"><localize key="general_cancel">Cancel</localize></a>
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button>
</div>
<div ng-hide="!moving || loading || success">
<div>
<div class="alert alert-info">
<div><strong><localize key="recycleBin_itemCannotBeRestored">Cannot automatically restore this item</localize></strong></div>
<div><localize key="recycleBin_itemCannotBeRestoredHelpText">There is no location where this item can be automatically restored. You can move the item manually using the tree below.</localize></div>
</div>
</div>
<div ng-hide="miniListView">
<umb-tree-search-box hide-search-callback="hideSearch"
search-callback="onSearchResults"
show-search="{{searchInfo.showSearch}}"
section="media">
</umb-tree-search-box>
<br />
<umb-tree-search-results ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div ng-hide="searchInfo.showSearch">
<umb-tree section="media"
hideheader="{{treeModel.hideHeader}}"
hideoptions="true"
isdialog="true"
api="dialogTreeApi"
on-init="onTreeInit()"
enablelistviewexpand="true"
enablecheckboxes="true">
</umb-tree>
</div>
</div>
<umb-mini-list-view ng-if="miniListView"
node="miniListView"
entity-type="Document"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>
</umb-pane>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || moving || success">
<a class="btn btn-link" ng-click="close()"><localize key="general_cancel">Cancel</localize></a>
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || !moving || success">
<a class="btn btn-link" ng-click="close()"><localize key="general_cancel">Cancel</localize></a>
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null" ng-disabled="!target"><localize key="actions_move">Move</localize></button>
</div>
</div>

View File

@@ -1,7 +1,7 @@
(function () {
"use strict";
function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, stylesheetResource, languageResource, packageResource, dictionaryResource, editorService, formHelper) {
function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, packageResource, editorService, formHelper) {
const vm = this;
@@ -19,8 +19,8 @@
vm.openContentPicker = openContentPicker;
vm.openFilePicker = openFilePicker;
vm.removeFile = removeFile;
vm.openControlPicker = openControlPicker;
vm.removeControl = removeControl;
vm.openViewPicker = openViewPicker;
vm.removePackageView = removePackageView;
vm.downloadFile = downloadFile;
const packageId = $routeParams.id;
@@ -73,7 +73,7 @@
});
// get all stylesheets
stylesheetResource.getAll().then(stylesheets => {
entityResource.getAll("Stylesheet").then(stylesheets => {
vm.stylesheets = stylesheets;
});
@@ -87,7 +87,7 @@
});
// get all languages
languageResource.getAll().then(languages => {
entityResource.getAll("Language").then(languages => {
// a package stores the id as a string so we
// need to convert all ids to string for comparison
languages.forEach(language => {
@@ -97,7 +97,7 @@
});
// get all dictionary items
dictionaryResource.getList().then(dictionaryItems => {
entityResource.getAll("DictionaryItem").then(dictionaryItems => {
// a package stores the id as a string so we
// need to convert all ids to string for comparison
dictionaryItems.forEach(dictionaryItem => {
@@ -130,7 +130,7 @@
}
function back() {
$location.path("packages/packages/overview").search('create', null);;
$location.path("packages/packages/created").search("create", null).search("packageId", null);
}
function createOrUpdatePackage(editPackageForm) {
@@ -148,7 +148,7 @@
if (create) {
//if we are creating, then redirect to the correct url and reload
$location.path("packages/packages/edit/" + vm.package.id).search("subview", "created").search("create", null);
$location.path("packages/packages/edit/" + vm.package.id).search("create", null);
//don't add a browser history for this
$location.replace();
}
@@ -220,16 +220,16 @@
vm.package.files.splice(index, 1);
}
function openControlPicker() {
function openViewPicker() {
const controlPicker = {
title: "Select control",
title: "Select view",
section: "settings",
treeAlias: "files",
entityType: "file",
onlyInitialized: false,
select: function(node) {
const id = unescape(node.id);
vm.package.loadControl = id;
vm.package.packageView = id;
editorService.close();
},
close: function() {
@@ -239,8 +239,8 @@
editorService.treePicker(controlPicker);
}
function removeControl() {
vm.package.loadControl = null;
function removePackageView() {
vm.package.packageView = null;
}
onInit();

View File

@@ -255,23 +255,23 @@
</umb-control-group>
<umb-control-group
label="Control after installation"
description="Load control after installation (ex: /usercontrols/installer.ascx)">
label="Package options view"
description="Load this view after installation (ex: App_Plugins/MyApp/MyPackageOptions.html). It can be used to configure your package at any time by clicking Options on the installed package listing">
<umb-node-preview
ng-if="vm.package.loadControl"
ng-if="vm.package.packageView"
icon="'icon-box'"
name="vm.package.loadControl"
name="vm.package.packageView"
allow-edit="true"
allow-remove="true"
on-edit="vm.openControlPicker()"
on-remove="vm.removeControl()">
on-edit="vm.openViewPicker()"
on-remove="vm.removePackageView()">
</umb-node-preview>
<a href=""
ng-if="!vm.package.loadControl"
ng-show="!vm.package.packageView"
class="umb-node-preview-add"
ng-click="vm.openControlPicker()"
ng-click="vm.openViewPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>

View File

@@ -0,0 +1,47 @@
(function () {
"use strict";
function OptionsController($scope, $location, $routeParams, packageResource, umbRequestHelper) {
const vm = this;
vm.showBackButton = true;
vm.loading = true;
vm.back = back;
const packageId = $routeParams.id;
function onInit() {
packageResource.getInstalledById(packageId).then(pck => {
vm.package = pck;
//set the $scope too, packages can then access this if they wanted from their own scope or parent scope
$scope.package = pck;
vm.loading = false;
//make sure the packageView is formatted as a virtual path
pck.packageView = pck.packageView.startsWith("/~")
? pck.packageView
: pck.packageView.startsWith("/")
? "~" + pck.packageView
: "~/" + pck.packageView;
pck.packageView = umbRequestHelper.convertVirtualToAbsolutePath(pck.packageView);
});
}
function back() {
$location.path("packages/packages/installed").search("packageId", null);
}
onInit();
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.OptionsController", OptionsController);
})();

View File

@@ -0,0 +1,40 @@
<div data-element="editor-packages" ng-controller="Umbraco.Editors.Packages.OptionsController as vm">
<form name="editPackageForm" no-validate val-form-manager>
<umb-editor-view>
<umb-editor-header
name="vm.package.name"
name-locked="true"
on-back="vm.back()"
show-back-button="vm.showBackButton"
hide-icon="true"
hide-description="true"
hide-alias="true">
</umb-editor-header>
<umb-editor-container class="form-horizontal">
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<p ng-if="!vm.package.packageView">
This package has no configuration view
</p>
<div ng-if="vm.package.packageView"
ng-include="vm.package.packageView"></div>
</umb-editor-container>
<!--A footer is nice but the developer can't interact with it, hrmmm-->
<!--<umb-editor-footer>
<umb-editor-footer-content-right>
</umb-editor-footer-content-right>
</umb-editor-footer>-->
</umb-editor-view>
</form>
</div>

View File

@@ -1,30 +1,29 @@
(function () {
"use strict";
function PackagesOverviewController($scope, $location, localStorageService) {
function PackagesOverviewController($scope, $location, $routeParams, localStorageService) {
//Hack!
// if there is a cookie value for packageInstallUri then we need to redirect there,
// if there is a local storage value for packageInstallData then we need to redirect there,
// the issue is that we still have webforms and we cannot go to a hash location and then window.reload
// because it will double load it.
// we will refresh and then navigate there.
let installPackageUri = localStorageService.get("packageInstallUri");
let packageUri = $location.search().subview;
let packageInstallData = localStorageService.get("packageInstallData");
let packageUri = $routeParams.method;
if (installPackageUri) {
localStorageService.remove("packageInstallUri");
if (packageInstallData) {
localStorageService.remove("packageInstallData");
}
if (installPackageUri && installPackageUri !== "installed") {
//navigate to the custom installer screen, if it is just "installed", then we'll
//show the installed view
$location.path(installPackageUri).search("");
if (packageInstallData && packageInstallData !== "installed" && packageInstallData.postInstallationPath) {
//navigate to the custom installer screen, if it is just "installed" it means there is no custom installer screen
$location.path(packageInstallData.postInstallationPath).search("packageId", packageInstallData.id);
}
else {
var vm = this;
packageUri = installPackageUri ? installPackageUri : packageUri; //use the path stored in storage over the one in the current path
packageUri = packageInstallData ? packageInstallData : packageUri; //use the path stored in storage over the one in the current path
vm.page = {};
vm.page.name = "Packages";
@@ -33,10 +32,10 @@
"name": "Packages",
"icon": "icon-cloud",
"view": "views/packages/views/repo.html",
"active": !packageUri || packageUri === "navigation",
"active": !packageUri || packageUri === "repo",
"alias": "umbPackages",
"action": function() {
$location.search("subview", "navigation");
"action": function () {
$location.path("/packages/packages/repo");
}
},
{
@@ -45,8 +44,8 @@
"view": "views/packages/views/installed.html",
"active": packageUri === "installed",
"alias": "umbInstalled",
"action": function() {
$location.search("subview", "installed");
"action": function () {
$location.path("/packages/packages/installed");
}
},
{
@@ -55,8 +54,8 @@
"view": "views/packages/views/install-local.html",
"active": packageUri === "local",
"alias": "umbInstallLocal",
"action": function() {
$location.search("subview", "local");
"action": function () {
$location.path("/packages/packages/local");
}
},
{
@@ -65,8 +64,8 @@
"view": "views/packages/views/created.html",
"active": packageUri === "created",
"alias": "umbCreatedPackages",
"action": function() {
$location.search("subview", "created");
"action": function () {
$location.path("/packages/packages/created");
}
}
];

View File

@@ -184,14 +184,8 @@
installError)
.then(function (result) {
if (result.postInstallationPath) {
//Put the redirect Uri in a cookie so we can use after reloading
localStorageService.set("packageInstallUri", result.postInstallationPath);
}
else {
//set to a constant value so it knows to just go to the installed view
localStorageService.set("packageInstallUri", "installed");
}
//Put the package data in local storage so we can use after reloading
localStorageService.set("packageInstallData", result);
vm.installState.status = labels.installStateCompleted;
vm.installCompleted = true;

View File

@@ -7,6 +7,7 @@
vm.confirmUninstall = confirmUninstall;
vm.uninstallPackage = uninstallPackage;
vm.packageOptions = packageOptions;
vm.state = "list";
vm.installState = {
status: ""
@@ -34,6 +35,11 @@
});
}
function packageOptions(pck) {
$location.path("packages/packages/options/" + pck.id)
.search("packageId", null); //ensure the installId flag is gone, it's only available on first install
}
function confirmUninstall(pck) {
vm.state = "packageDetails";
vm.package = pck;
@@ -51,7 +57,7 @@
vm.installState.progress = "100";
//set this flag so that on refresh it shows the installed packages list
localStorageService.set("packageInstallUri", "installed");
localStorageService.set("packageInstallData", "installed");
//reload on next digest (after cookie)
$timeout(function () {

View File

@@ -23,12 +23,19 @@
</div>
</td>
<td style="text-align: right;">
<umb-button
type="button"
button-style="danger"
size="xxs"
label-key="packager_packageUninstallHeader"
action="vm.confirmUninstall(installedPackage)">
<umb-button ng-show="installedPackage.packageView"
type="button"
button-style="info"
size="xxs"
label-key="packager_packageOptions"
action="vm.packageOptions(installedPackage)">
</umb-button>
<umb-button type="button"
button-style="danger"
size="xxs"
label-key="packager_packageUninstallHeader"
action="vm.confirmUninstall(installedPackage)">
</umb-button>
</td>
</tr>

View File

@@ -257,10 +257,8 @@
error)
.then(function (result) {
if (result.postInstallationPath) {
//Put the redirect Uri in a cookie so we can use after reloading
localStorageService.set("packageInstallUri", result.postInstallationPath);
}
//Put the package data in local storage so we can use after reloading
localStorageService.set("packageInstallData", result);
vm.installState.status = labels.installStateCompleted;
vm.installCompleted = true;

View File

@@ -437,7 +437,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
view: "views/propertyeditors/listview/overlays/listviewpublish.html",
submitButtonLabelKey: "actions_publish",
submit: function (model) {
// create a comma seperated array of selected cultures
// create a comma separated array of selected cultures
let selectedCultures = [];
if (model.languages && model.languages.length > 0) {
model.languages.forEach(language => {
@@ -492,7 +492,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
submitButtonLabelKey: "actions_unpublish",
submit: function (model) {
// create a comma seperated array of selected cultures
// create a comma separated array of selected cultures
let selectedCultures = [];
if (model.languages && model.languages.length > 0) {
model.languages.forEach(language => {