Merge remote-tracking branch 'upstream/dev-v7' into dev-v7-U4-9571

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/tree.less
This commit is contained in:
Bjarne Fyrstenborg
2017-05-24 09:40:14 +02:00
1256 changed files with 47761 additions and 19776 deletions

View File

@@ -25,9 +25,10 @@
"jquery-migrate": "1.4.0",
"angular-dynamic-locale": "0.1.28",
"ng-file-upload": "~7.3.8",
"tinymce": "~4.1.10",
"tinymce": "~4.5.3",
"codemirror": "~5.3.0",
"angular-local-storage": "~0.2.3",
"moment": "~2.10.3"
"moment": "~2.10.3",
"ace-builds": "^1.2.3"
}
}

View File

@@ -11,17 +11,17 @@ module.exports = function (grunt) {
//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:canvasdesigner', 'postcss', 'copy:canvasdesigner', 'copy:assets', 'copy:vs']);
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:canvasdesigner', 'postcss', 'bower-install-simple', 'bower', 'copy', 'clean:post']);
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', 'postcss', 'bower-install-simple', 'bower', 'copy']);
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']);
@@ -293,6 +293,16 @@ module.exports = function (grunt) {
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':
@@ -394,7 +404,7 @@ module.exports = function (grunt) {
tutorials: {
src: [],
title: ''
}
}
},
eslint:{
@@ -524,7 +534,26 @@ module.exports = function (grunt) {
'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',
]
}
}
}
},

View File

@@ -0,0 +1,161 @@
.ace-chrome .ace_gutter {
background: white !important;
color: #ccc !important;
overflow : hidden;
}
.ace-chrome .ace_print-margin {
}
.ace-chrome {
background-color: #FFFFFF;
color: black;
}
.ace-chrome .ace_cursor {
color: black;
}
.ace-chrome .ace_invisible {
color: rgb(191, 191, 191);
}
.ace-chrome .ace_constant.ace_buildin {
color: rgb(88, 72, 246);
}
.ace-chrome .ace_constant.ace_language {
color: rgb(88, 92, 246);
}
.ace-chrome .ace_constant.ace_library {
color: rgb(6, 150, 14);
}
.ace-chrome .ace_invalid {
background-color: rgb(153, 0, 0);
color: white;
}
.ace_punctuation.ace_short.ace_razor{
background: yellow;
}
.ace-chrome .ace_fold {
}
.ace-chrome .ace_support.ace_function {
color: rgb(60, 76, 114);
}
.ace-chrome .ace_support.ace_constant {
color: rgb(6, 150, 14);
}
.ace-chrome .ace_support.ace_type,
.ace-chrome .ace_support.ace_class
.ace-chrome .ace_support.ace_other {
color: rgb(109, 121, 222);
}
.ace-chrome .ace_variable.ace_parameter {
font-style:italic;
color:#FD971F;
}
.ace-chrome .ace_keyword.ace_operator {
color: rgb(104, 118, 135);
}
.ace-chrome .ace_comment {
color: #236e24;
}
.ace-chrome .ace_comment.ace_doc {
color: #236e24;
}
.ace-chrome .ace_comment.ace_doc.ace_tag {
color: #236e24;
}
.ace-chrome .ace_constant.ace_numeric {
color: rgb(0, 0, 205);
}
.ace-chrome .ace_variable {
color: rgb(49, 132, 149);
}
.ace-chrome .ace_xml-pe {
color: rgb(104, 104, 91);
}
.ace-chrome .ace_entity.ace_name.ace_function {
color: #0000A2;
}
.ace-chrome .ace_heading {
color: rgb(12, 7, 255);
}
.ace-chrome .ace_list {
color:rgb(185, 6, 144);
}
.ace-chrome .ace_marker-layer .ace_selection {
background: rgb(181, 213, 255);
}
.ace-chrome .ace_marker-layer .ace_step {
background: rgb(252, 255, 0);
}
.ace-chrome .ace_marker-layer .ace_stack {
background: rgb(164, 229, 101);
}
.ace-chrome .ace_marker-layer .ace_bracket {
margin: -1px 0 0 -1px;
border: 1px solid rgb(192, 192, 192);
}
.ace-chrome .ace_marker-layer .ace_active-line {
background: rgba(0, 0, 0, 0.07) !important;
}
.ace-chrome .ace_gutter-active-line {
background: rgba(0, 0, 0, 0.07) !important;
}
.ace-chrome .ace_marker-layer .ace_selected-word {
background: rgb(250, 250, 255);
border: 1px solid rgb(200, 200, 250);
}
.ace-chrome .ace_storage,
.ace-chrome .ace_keyword,
.ace-chrome .ace_meta.ace_tag {
color: rgb(147, 15, 128);
}
.ace-chrome .ace_string.ace_regex {
color: rgb(255, 0, 0)
}
.ace-chrome .ace_string {
color: #1A1AA6;
}
.ace-chrome .ace_entity.ace_other.ace_attribute-name {
color: #994409;
}
.ace-chrome .ace_indent-guide {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;
}
.ace-chrome .ace_razor {
background: yellow;
}

View File

@@ -172,8 +172,17 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
//locallink detection, we do this here, to avoid poluting the dialogservice
//so the dialog service can just expect to get a node-like structure
if(currentTarget.url.indexOf("localLink:") > 0){
currentTarget.id = currentTarget.url.substring(currentTarget.url.indexOf(":")+1,currentTarget.url.length-1);
if (currentTarget.url.indexOf("localLink:") > 0) {
var linkId = currentTarget.url.substring(currentTarget.url.indexOf(":") + 1, currentTarget.url.length - 1);
//we need to check if this is an INT or a UDI
var parsedIntId = parseInt(linkId, 10);
if (isNaN(parsedIntId)) {
//it's a UDI
currentTarget.udi = linkId;
}
else {
currentTarget.id = linkId;
}
}
}
@@ -182,27 +191,34 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
callback: function (data) {
if (data) {
var href = data.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = data.udi ? true : false;
var id = hasUdi ? data.udi : (data.id ? data.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null
};
if (hasUdi) {
a["data-udi"] = data.udi;
}
else if (data.id) {
a["data-id"] = data.id;
}
}
function insertLink() {
if (anchorElm) {
dom.setAttribs(anchorElm, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
dom.setAttribs(anchorElm, createElemAttributes());
selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -212,9 +228,11 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(data.id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
href = "/{localLink:" + data.id + "}";
insertLink();
if (id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
}

View File

@@ -1,7 +1,8 @@
body.mce-content-body {
background-color: #fff;
font-family: Verdana,Arial,Helvetica,sans-serif;
font-size: 11px;
font-size: 14px;
line-height: 1.5em;
scrollbar-3dlight-color: #f0f0ee;
scrollbar-arrow-color: #676662;
scrollbar-base-color: #f0f0ee;

View File

@@ -1,17 +0,0 @@
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: url('../fonts/opensans/OpenSans-Regular-webfont.eot');
src: local('Open Sans'), local('OpenSans'), url('../fonts/opensans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/opensans/OpenSans-Regular-webfont.ttf') format('truetype'), url('../fonts/opensans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
src: url('../fonts/opensans/OpenSans-Semibold-webfont.eot');
src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url('../fonts/opensans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/opensans/OpenSans-Semibold-webfont.ttf') format('truetype'), url('../fonts/opensans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg');
}
abbr,address,article,aside,audio,b,blockquote,body,canvas,caption,cite,code,dd,del,details,dfn,div,dl,dt,em,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,p,pre,q,samp,section,small,span,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,ul,var,video{margin:0;padding:0;outline:0;border:0;background:0 0;vertical-align:baseline;font-size:100%}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}nav ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}a{margin:0;padding:0;background:0 0;vertical-align:baseline;font-size:100%}ins{background-color:#ff9;color:#000;text-decoration:none}mark{background-color:#ff9;color:#000;font-weight:700;font-style:italic}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help}table{border-spacing:0;border-collapse:collapse}hr{display:block;margin:1em 0;padding:0;height:1px;border:0;border-top:1px solid #ccc}input,select{vertical-align:middle}html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{height:100%;width:100%;color:#fff;font-family:'Open Sans',sans-serif;font-weight:400;font-size:.9375em;line-height:1.5}h1{font-size:1.7em;margin:40px auto 10px;font-weight:700}h2{font-size:1.35em;margin:0 auto .4em;font-weight:700}h3{font-size:1em;font-weight:400;font-style:italic}p{font-size:1em;line-height:1.6}p+a{margin-top:1rem;display:inline-block}a,a:active,a:visited{text-decoration:none}.cta{margin:4.5em auto 1.5em;padding-bottom:4.5em}.button,.button:visited{padding:.9375em 1.875em;border-radius:.1em;font-weight:600;font-size:1.2em;background:#2e99f4;color:#fff;display:inline-block;border:none;transition:all 200ms ease-in-out}.button:hover,.button:visited:hover{border-bottom:none;background:#0c80e3}section{background:url(../img/nonodesbg.jpg) center center/cover;height:100%;width:100%;display:table;padding:3em 1.75em}section a,section a:focus,section a:visited{color:#46a5f5;font-size:1.1625em;border-bottom:1px solid transparent;transition:border-bottom 100ms ease-in-out}section a:focus:hover,section a:hover,section a:visited:hover{border-bottom:1px solid}section:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:rgba(0,0,0,.17);background:linear-gradient(45deg,rgba(85,98,112,.1) 10%,rgba(255,107,107,.1) 95%);z-index:0}section article{display:table-cell;vertical-align:middle;margin:0 auto;text-align:center;position:relative;z-index:100}section article>div{max-width:60em;margin:0 auto}section .logo{background:url(../img/logo.png) no-repeat;width:91px;height:91px;margin:0 auto}section .row{overflow:hidden}section .row .col{text-align:left;width:100%}section .row .col:nth-child(2){margin-top:3em}@media screen and (min-width:48em){body,html{font-size:1em}h1{font-size:2.5em;margin:70px auto 0;letter-spacing:.5px}h2{font-size:1.4375em;margin:0 auto 1em}h3{font-size:1.2em}a{font-size:1.1rem;font-weight:600}p+a{margin-top:3rem}.cta{margin:7.5em auto 2.5em;border-bottom:1px solid rgba(255,255,255,.5);padding-bottom:7.5em}section{padding:0 15px}section .row .col{float:left;width:50%;padding-right:5%;display:inline-block}section .row .col:nth-child(2){padding-right:0;padding-left:5%;margin-top:0}.button{font-size:1.1625em}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 273 KiB

View File

@@ -20,7 +20,10 @@
<div class="fix-left-menu selected">
<div class="avatar">
<img ng-src="../assets/img/application/logo.png">
<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}">

View File

@@ -40,27 +40,50 @@ Use this directive to generate a list of breadcrumbs.
@param {array} ancestors Array of ancestors
@param {string} entityType The content entity type (member, media, content).
@param {callback} Callback when an ancestor is clicked. It will override the default link behaviour.
**/
(function() {
'use strict';
(function () {
'use strict';
function BreadcrumbsDirective() {
function BreadcrumbsDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
scope: {
ancestors: "=",
entityType: "@"
}
};
function link(scope, el, attr, ctrl) {
return directive;
scope.allowOnOpen = false;
}
scope.open = function(ancestor) {
if(scope.onOpen && scope.allowOnOpen) {
scope.onOpen({'ancestor': ancestor});
}
};
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
function onInit() {
if ("onOpen" in attr) {
scope.allowOnOpen = true;
}
}
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
scope: {
ancestors: "=",
entityType: "@",
onOpen: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
})();

View File

@@ -254,6 +254,7 @@ Use this directive to construct a header inside the main editor window.
hideAlias: "@",
description: "=",
hideDescription: "@",
descriptionLocked: "@",
navigation: "="
},
link: link

View File

@@ -163,13 +163,13 @@ angular.module('umbraco.directives')
}
// ignore clicks on dialog from old dialog service
var oldDialog = $(el).parents("#old-dialog-service");
var oldDialog = $(event.target).parents("#old-dialog-service");
if (oldDialog.length === 1) {
return;
}
// ignore clicks in tinyMCE dropdown(floatpanel)
var floatpanel = $(el).parents(".mce-floatpanel");
var floatpanel = $(event.target).closest(".mce-floatpanel");
if (floatpanel.length === 1) {
return;
}

View File

@@ -105,12 +105,14 @@ angular.module("umbraco.directives")
});
//// INIT /////
$image.load(function(){
$timeout(function(){
setDimensions();
scope.loaded = true;
scope.onImageLoaded();
});
$image.load(function() {
$timeout(function() {
setDimensions();
scope.loaded = true;
if (angular.isFunction(scope.onImageLoaded)) {
scope.onImageLoaded();
}
});
});
$(window).on('resize.umbImageGravity', function(){

View File

@@ -5,7 +5,27 @@ angular.module("umbraco.directives")
* @name umbraco.directives.directive:localize
* @restrict EA
* @function
* @description Localize directive
* @description
* <div>
* <strong>Component</strong><br />
* Localize a specific token to put into the HTML as an item
* </div>
* <div>
* <strong>Attribute</strong><br />
* Add a HTML attribute to an element containing the HTML attribute name you wish to localise
* Using the format of '@section_key' or 'section_key'
* </div>
* ##Usage
* <pre>
* <!-- Component -->
* <localize key="general_close">Close</localize>
* <localize key="section_key">Fallback value</localize>
*
* <!-- Attribute -->
* <input type="text" localize="placeholder" placeholder="@placeholders_entername" />
* <input type="text" localize="placeholder,title" title="@section_key" placeholder="@placeholders_entername" />
* <div localize="title" title="@section_key"></div>
* </pre>
**/
.directive('localize', function ($log, localizationService) {
return {
@@ -28,6 +48,7 @@ angular.module("umbraco.directives")
return {
restrict: 'A',
link: function (scope, element, attrs) {
//Support one or more attribute properties to update
var keys = attrs.localize.split(',');
angular.forEach(keys, function(value, key){
@@ -35,13 +56,15 @@ angular.module("umbraco.directives")
if(attr){
if(attr[0] === '@'){
var t = localizationService.tokenize(attr.substring(1), scope);
localizationService.localize(t.key, t.tokens).then(function(val){
element.attr(value, val);
});
//If the translation key starts with @ then remove it
attr = attr.substring(1);
}
var t = localizationService.tokenize(attr, scope);
localizationService.localize(t.key, t.tokens).then(function(val){
element.attr(value, val);
});
}
});
}

View File

@@ -478,8 +478,10 @@ Opens an overlay to show a custom YSOD. </br>
numberOfOverlays = overlayHelper.getNumberOfOverlays();
if(numberOfOverlays === overlayNumber) {
scope.closeOverLay();
if (numberOfOverlays === overlayNumber) {
scope.$apply(function () {
scope.closeOverLay();
});
}
event.preventDefault();

View File

@@ -43,7 +43,8 @@
templateUrl: "views/components/tabs/umb-tabs-nav.html",
scope: {
model: "=",
tabdrop: "="
tabdrop: "=",
idSuffix: "@"
},
link: link
};

View File

@@ -17,11 +17,13 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
hideheader: '@',
cachekey: '@',
isdialog: '@',
onlyinitialized: '@',
//Custom query string arguments to pass in to the tree as a string, example: "startnodeid=123&something=value"
customtreeparams: '@',
eventhandler: '=',
enablecheckboxes: '@',
enablelistviewsearch: '@'
enablelistviewsearch: '@',
enablelistviewexpand: '@'
},
compile: function(element, attrs) {
@@ -35,7 +37,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
'<a class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)"><i></i><i></i><i></i></a>' +
'</div>';
template += '<ul>' +
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'<umb-tree-item ng-repeat="child in tree.root.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'</ul>' +
'</li>' +
'</ul>';
@@ -251,7 +253,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
deleteAnimations = false;
//default args
var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false };
var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false, onlyinitialized: scope.onlyinitialized };
//add the extra query string params if specified
if (scope.customtreeparams) {
@@ -304,7 +306,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
scope.selectEnabledNodeClass = function (node) {
return node ?
node.selected ?
'icon umb-tree-icon sprTree icon-check blue temporary' :
'icon umb-tree-icon sprTree icon-check green temporary' :
'' :
'';
};

View File

@@ -27,6 +27,7 @@ angular.module("umbraco.directives")
section: '@',
eventhandler: '=',
currentNode: '=',
enablelistviewexpand: '@',
node: '=',
tree: '='
},
@@ -38,7 +39,7 @@ angular.module("umbraco.directives")
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" >' +
//NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded, \'icon-navigation-down\': node.expanded}" ng-click="load(node)">&nbsp;</ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded || node.metaData.isContainer, \'icon-navigation-down\': node.expanded && !node.metaData.isContainer}" ng-click="load(node)">&nbsp;</ins>' +
'<i class="icon umb-tree-icon sprTree" ng-click="select(node, $event)"></i>' +
'<a href="#/{{node.routePath}}" ng-click="select(node, $event)"></a>' +
//NOTE: These are the 'option' elipses
@@ -74,11 +75,12 @@ angular.module("umbraco.directives")
//toggle visibility of last 'ins' depending on children
//visibility still ensure the space is "reserved", so both nodes with and without children are aligned.
if (!node.hasChildren) {
element.find("ins").last().css("visibility", "hidden");
if (node.hasChildren || node.metaData.isContainer && scope.enablelistviewexpand === "true") {
element.find("ins").last().css("visibility", "visible");
}
else {
element.find("ins").last().css("visibility", "visible");
element.find("ins").last().css("visibility", "hidden");
}
var icon = element.find("i:first");
@@ -192,7 +194,7 @@ angular.module("umbraco.directives")
emits treeNodeCollapsing event if already expanded and treeNodeExpanding if collapsed
*/
scope.load = function (node) {
if (node.expanded) {
if (node.expanded && !node.metaData.isContainer) {
deleteAnimations = false;
emitEvent("treeNodeCollapsing", { tree: scope.tree, node: node, element: element });
node.expanded = false;
@@ -227,7 +229,7 @@ angular.module("umbraco.directives")
setupNodeDom(scope.node, scope.tree);
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
var newElement = angular.element(template);
$compile(newElement)(scope);
element.append(newElement);

View File

@@ -0,0 +1,342 @@
(function() {
'use strict';
function AceEditorDirective(umbAceEditorConfig, assetsService, angularHelper) {
/**
* Sets editor options such as the wrapping mode or the syntax checker.
*
* The supported options are:
*
* <ul>
* <li>showGutter</li>
* <li>useWrapMode</li>
* <li>onLoad</li>
* <li>theme</li>
* <li>mode</li>
* </ul>
*
* @param acee
* @param session ACE editor session
* @param {object} opts Options to be set
*/
var setOptions = function(acee, session, opts) {
// sets the ace worker path, if running from concatenated
// or minified source
if (angular.isDefined(opts.workerPath)) {
var config = window.ace.require('ace/config');
config.set('workerPath', opts.workerPath);
}
// ace requires loading
if (angular.isDefined(opts.require)) {
opts.require.forEach(function(n) {
window.ace.require(n);
});
}
// Boolean options
if (angular.isDefined(opts.showGutter)) {
acee.renderer.setShowGutter(opts.showGutter);
}
if (angular.isDefined(opts.useWrapMode)) {
session.setUseWrapMode(opts.useWrapMode);
}
if (angular.isDefined(opts.showInvisibles)) {
acee.renderer.setShowInvisibles(opts.showInvisibles);
}
if (angular.isDefined(opts.showIndentGuides)) {
acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
}
if (angular.isDefined(opts.useSoftTabs)) {
session.setUseSoftTabs(opts.useSoftTabs);
}
if (angular.isDefined(opts.showPrintMargin)) {
acee.setShowPrintMargin(opts.showPrintMargin);
}
// commands
if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
acee.commands.addCommands([{
name: 'unfind',
bindKey: {
win: 'Ctrl-F',
mac: 'Command-F'
},
exec: function() {
return false;
},
readOnly: true
}]);
}
// Basic options
if (angular.isString(opts.theme)) {
acee.setTheme('ace/theme/' + opts.theme);
}
if (angular.isString(opts.mode)) {
session.setMode('ace/mode/' + opts.mode);
}
// Advanced options
if (angular.isDefined(opts.firstLineNumber)) {
if (angular.isNumber(opts.firstLineNumber)) {
session.setOption('firstLineNumber', opts.firstLineNumber);
} else if (angular.isFunction(opts.firstLineNumber)) {
session.setOption('firstLineNumber', opts.firstLineNumber());
}
}
// advanced options
var key, obj;
if (angular.isDefined(opts.advanced)) {
for (key in opts.advanced) {
// create a javascript object with the key and value
obj = {
name: key,
value: opts.advanced[key]
};
// try to assign the option to the ace editor
acee.setOption(obj.name, obj.value);
}
}
// advanced options for the renderer
if (angular.isDefined(opts.rendererOptions)) {
for (key in opts.rendererOptions) {
// create a javascript object with the key and value
obj = {
name: key,
value: opts.rendererOptions[key]
};
// try to assign the option to the ace editor
acee.renderer.setOption(obj.name, obj.value);
}
}
// onLoad callbacks
angular.forEach(opts.callbacks, function(cb) {
if (angular.isFunction(cb)) {
cb(acee);
}
});
};
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 () {
if (angular.isUndefined(window.ace)) {
throw new Error('ui-ace need ace to work... (o rly?)');
} else {
// init editor
init();
}
});
function init() {
/**
* Corresponds the umbAceEditorConfig ACE configuration.
* @type object
*/
var options = umbAceEditorConfig.ace || {};
/**
* umbAceEditorConfig merged with user options via json in attribute or data binding
* @type object
*/
var opts = angular.extend({}, options, scope.umbAceEditor);
//load ace libraries here...
/**
* ACE editor
* @type object
*/
var acee = window.ace.edit(el[0]);
acee.$blockScrolling = Infinity;
/**
* ACE editor session.
* @type object
* @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
*/
var session = acee.getSession();
/**
* Reference to a change listener created by the listener factory.
* @function
* @see listenerFactory.onChange
*/
var onChangeListener;
/**
* Reference to a blur listener created by the listener factory.
* @function
* @see listenerFactory.onBlur
*/
var onBlurListener;
/**
* Calls a callback by checking its existing. The argument list
* is variable and thus this function is relying on the arguments
* object.
* @throws {Error} If the callback isn't a function
*/
var executeUserCallback = function() {
/**
* The callback function grabbed from the array-like arguments
* object. The first argument should always be the callback.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {*}
*/
var callback = arguments[0];
/**
* Arguments to be passed to the callback. These are taken
* from the array-like arguments object. The first argument
* is stripped because that should be the callback function.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {Array}
*/
var args = Array.prototype.slice.call(arguments, 1);
if (angular.isDefined(callback)) {
scope.$evalAsync(function() {
if (angular.isFunction(callback)) {
callback(args);
} else {
throw new Error('ui-ace use a function as callback.');
}
});
}
};
/**
* Listener factory. Until now only change listeners can be created.
* @type object
*/
var listenerFactory = {
/**
* Creates a change listener which propagates the change event
* and the editor session to the callback from the user option
* onChange. It might be exchanged during runtime, if this
* happens the old listener will be unbound.
*
* @param callback callback function defined in the user options
* @see onChangeListener
*/
onChange: function(callback) {
return function(e) {
var newValue = session.getValue();
angularHelper.safeApply(scope, function () {
scope.model = newValue;
});
executeUserCallback(callback, e, acee);
};
},
/**
* Creates a blur listener which propagates the editor session
* to the callback from the user option onBlur. It might be
* exchanged during runtime, if this happens the old listener
* will be unbound.
*
* @param callback callback function defined in the user options
* @see onBlurListener
*/
onBlur: function(callback) {
return function() {
executeUserCallback(callback, acee);
};
}
};
attr.$observe('readonly', function(value) {
acee.setReadOnly(!!value || value === '');
});
// Value Blind
if(scope.model) {
session.setValue(scope.model);
}
// Listen for option updates
var updateOptions = function(current, previous) {
if (current === previous) {
return;
}
opts = angular.extend({}, options, scope.umbAceEditor);
opts.callbacks = [opts.onLoad];
if (opts.onLoad !== options.onLoad) {
// also call the global onLoad handler
opts.callbacks.unshift(options.onLoad);
}
// EVENTS
// unbind old change listener
session.removeListener('change', onChangeListener);
// bind new change listener
onChangeListener = listenerFactory.onChange(opts.onChange);
session.on('change', onChangeListener);
// unbind old blur listener
//session.removeListener('blur', onBlurListener);
acee.removeListener('blur', onBlurListener);
// bind new blur listener
onBlurListener = listenerFactory.onBlur(opts.onBlur);
acee.on('blur', onBlurListener);
setOptions(acee, session, opts);
};
scope.$watch(scope.umbAceEditor, updateOptions, /* deep watch */ true);
// set the options here, even if we try to watch later, if this
// line is missing things go wrong (and the tests will also fail)
updateOptions(options);
el.on('$destroy', function() {
acee.session.$stopWorker();
acee.destroy();
});
scope.$watch(function() {
return [el[0].offsetWidth, el[0].offsetHeight];
}, function() {
acee.resize();
acee.renderer.updateFull();
}, true);
}
}
var directive = {
restrict: 'EA',
scope: {
"umbAceEditor": "=",
"model": "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives')
.constant('umbAceEditorConfig', {})
.directive('umbAceEditor', AceEditorDirective);
})();

View File

@@ -0,0 +1,179 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbDateTimePicker
@restrict E
@scope
@description
<b>Added in Umbraco version 7.6</b>
This directive is a wrapper of the bootstrap datetime picker version 3.1.3. Use it to render a date time picker.
For extra details about options and events take a look here: http://eonasdan.github.io/bootstrap-datetimepicker/
Use this directive to render a date time picker
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-date-time-picker
options="vm.config"
on-change="vm.datePickerChange(event)"
on-error="vm.datePickerError(event)">
</umb-date-time-picker>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.date = "";
vm.config = {
pickDate: true,
pickTime: true,
useSeconds: true,
format: "YYYY-MM-DD HH:mm:ss",
icons: {
time: "icon-time",
date: "icon-calendar",
up: "icon-chevron-up",
down: "icon-chevron-down"
}
};
vm.datePickerChange = datePickerChange;
vm.datePickerError = datePickerError;
function datePickerChange(event) {
// handle change
if(event.date && event.date.isValid()) {
var date = event.date.format(vm.datePickerConfig.format);
}
}
function datePickerError(event) {
// handle error
}
}
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@param {object} options (<code>binding</code>): Config object for the date picker.
@param {callback} onHide (<code>callback</code>): Hide callback.
@param {callback} onShow (<code>callback</code>): Show callback.
@param {callback} onChange (<code>callback</code>): Change callback.
@param {callback} onError (<code>callback</code>): Error callback.
@param {callback} onUpdate (<code>callback</code>): Update callback.
**/
(function () {
'use strict';
function DateTimePickerDirective(assetsService) {
function link(scope, element, attrs, ctrl) {
function onInit() {
// load css file for the date picker
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css');
// load the js file for the date picker
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js').then(function () {
// init date picker
initDatePicker();
});
}
function onHide(event) {
if (scope.onHide) {
scope.$apply(function(){
// callback
scope.onHide({event: event});
});
}
}
function onShow() {
if (scope.onShow) {
scope.$apply(function(){
// callback
scope.onShow();
});
}
}
function onChange(event) {
if (scope.onChange && event.date && event.date.isValid()) {
scope.$apply(function(){
// callback
scope.onChange({event: event});
});
}
}
function onError(event) {
if (scope.onError) {
scope.$apply(function(){
// callback
scope.onError({event:event});
});
}
}
function onUpdate(event) {
if (scope.onUpdate) {
scope.$apply(function(){
// callback
scope.onUpdate({event: event});
});
}
}
function initDatePicker() {
// Open the datepicker and add a changeDate eventlistener
element
.datetimepicker(scope.options)
.on("dp.hide", onHide)
.on("dp.show", onShow)
.on("dp.change", onChange)
.on("dp.error", onError)
.on("dp.update", onUpdate);
}
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-date-time-picker.html',
scope: {
options: "=",
onHide: "&",
onShow: "&",
onChange: "&",
onError: "&",
onUpdate: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbDateTimePicker', DateTimePickerDirective);
})();

View File

@@ -87,7 +87,8 @@ Use this directive to generate a list of folders presented as a flexbox grid.
scope.clickFolder = function(folder, $event, $index) {
if(scope.onClick) {
scope.onClick(folder, $event, $index);
scope.onClick(folder, $event, $index);
$event.stopPropagation();
}
};

View File

@@ -107,34 +107,76 @@ When this combination is hit an overview is opened with shortcuts based on the m
@param {object} model keyboard shortcut model. See description and example above.
**/
(function() {
'use strict';
(function () {
'use strict';
function KeyboardShortcutsOverviewDirective() {
function KeyboardShortcutsOverviewDirective(platformService) {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
scope.shortcutOverlay = false;
var eventBindings = [];
var isMac = platformService.isMac();
scope.toggleShortcutsOverlay = function() {
scope.shortcutOverlay = !scope.shortcutOverlay;
};
scope.toggleShortcutsOverlay = function () {
scope.showOverlay = !scope.showOverlay;
scope.onToggle();
};
function onInit() {
angular.forEach(scope.model, function (shortcutGroup) {
angular.forEach(shortcutGroup.shortcuts, function (shortcut) {
shortcut.platformKeys = [];
// get shortcut keys for mac
if (isMac && shortcut.keys && shortcut.keys.mac) {
shortcut.platformKeys = shortcut.keys.mac;
// get shortcut keys for windows
} else if (!isMac && shortcut.keys && shortcut.keys.win) {
shortcut.platformKeys = shortcut.keys.win;
// get default shortcut keys
} else if (shortcut.keys && shortcut && shortcut.keys.length > 0) {
shortcut.platformKeys = shortcut.keys;
}
});
});
}
onInit();
eventBindings.push(scope.$watch('model', function(newValue, oldValue){
if (newValue !== oldValue) {
onInit();
}
}));
// clean up
scope.$on('$destroy', function () {
// unbind watchers
for (var e in eventBindings) {
eventBindings[e]();
}
});
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html',
link: link,
scope: {
model: "=",
onToggle: "&",
showOverlay: "=?"
}
};
return directive;
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html',
link: link,
scope: {
model: "="
}
};
return directive;
}
angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective);
angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective);
})();

View File

@@ -7,7 +7,7 @@
* Used on a button to launch a mini content editor editor dialog
**/
angular.module("umbraco.directives")
.directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) {
.directive('umbLaunchMiniEditor', function (miniEditorHelper) {
return {
restrict: 'A',
replace: false,
@@ -16,69 +16,8 @@ angular.module("umbraco.directives")
},
link: function(scope, element, attrs) {
var launched = false;
element.click(function() {
if (launched === true) {
return;
}
launched = true;
//We need to store the current files selected in the file manager locally because the fileManager
// is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
// and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
// clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
var currFiles = _.groupBy(fileManager.getFiles(), "alias");
fileManager.clearFiles();
//We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
// any property editors that are working with editorState get given the correct entity, otherwise strange things will
// start happening.
var currEditorState = editorState.getCurrent();
dialogService.open({
template: "views/common/dialogs/content/edit.html",
id: scope.node.id,
closeOnSave: true,
tabFilter: ["Generic properties"],
callback: function (data) {
//set the node name back
scope.node.name = data.name;
//reset the fileManager to what it was
fileManager.clearFiles();
_.each(currFiles, function (val, key) {
fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
});
//reset the editor state
editorState.set(currEditorState);
//Now we need to check if the content item that was edited was actually the same content item
// as the main content editor and if so, update all property data
if (data.id === currEditorState.id) {
var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
}
launched = false;
},
closeCallback: function () {
//reset the fileManager to what it was
fileManager.clearFiles();
_.each(currFiles, function (val, key) {
fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
});
//reset the editor state
editorState.set(currEditorState);
launched = false;
}
});
miniEditorHelper.launchMiniEditor(scope.node);
});
}

View File

@@ -247,6 +247,7 @@ Use this directive to generate a thumbnail grid of media items.
scope.clickItem = function(item, $event, $index) {
if (scope.onClick) {
scope.onClick(item, $event, $index);
$event.stopPropagation();
}
};

View File

@@ -0,0 +1,216 @@
(function () {
'use strict';
function MiniListViewDirective(entityResource, iconHelper) {
function link(scope, el, attr, ctrl) {
scope.search = "";
scope.miniListViews = [];
scope.breadcrumb = [];
var miniListViewsHistory = [];
var goingForward = true;
var skipAnimation = true;
function onInit() {
open(scope.node);
}
function open(node) {
// convert legacy icon for node
if(node && node.icon) {
node.icon = iconHelper.convertFromLegacyIcon(node.icon);
}
goingForward = true;
var miniListView = {
node: node,
loading: true,
pagination: {
pageSize: 10,
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder",
orderBySystemField: true
}
};
// clear and push mini list view in dom so we only render 1 view
scope.miniListViews = [];
scope.miniListViews.push(miniListView);
// store in history so we quickly can navigate back
miniListViewsHistory.push(miniListView);
// get children
getChildrenForMiniListView(miniListView);
makeBreadcrumb();
}
function getChildrenForMiniListView(miniListView) {
// start loading animation list view
miniListView.loading = true;
entityResource.getPagedChildren(miniListView.node.id, scope.entityType, miniListView.pagination)
.then(function (data) {
// update children
miniListView.children = data.items;
_.each(miniListView.children, function(c) {
// convert legacy icon for node
if(c.icon) {
c.icon = iconHelper.convertFromLegacyIcon(c.icon);
}
// set published state for content
if (c.metaData) {
c.hasChildren = c.metaData.HasChildren;
if(scope.entityType === "Document") {
c.published = c.metaData.IsPublished;
}
}
});
// update pagination
miniListView.pagination.totalItems = data.totalItems;
miniListView.pagination.totalPages = data.totalPages;
// stop load indicator
miniListView.loading = false;
});
}
scope.openNode = function(event, node) {
open(node);
event.stopPropagation();
};
scope.selectNode = function(node) {
if(scope.onSelect) {
scope.onSelect({'node': node});
}
};
/* Pagination */
scope.goToPage = function(pageNumber, miniListView) {
// set new page number
miniListView.pagination.pageNumber = pageNumber;
// get children
getChildrenForMiniListView(miniListView);
};
/* Breadcrumb */
scope.clickBreadcrumb = function(ancestor) {
var found = false;
goingForward = false;
angular.forEach(miniListViewsHistory, function(historyItem, index){
// We need to make sure we can compare the two id's.
// Some id's are integers and others are strings.
// Members have string ids like "all-members".
if(historyItem.node.id.toString() === ancestor.id.toString()) {
// load the list view from history
scope.miniListViews = [];
scope.miniListViews.push(historyItem);
// clean up history - remove all children after
miniListViewsHistory.splice(index + 1, miniListViewsHistory.length);
found = true;
}
});
if(!found) {
// if we can't find the view in the history - close the list view
scope.exitMiniListView();
}
// update the breadcrumb
makeBreadcrumb();
};
scope.showBackButton = function() {
// don't show the back button if the start node is a list view
if(scope.node.metaData && scope.node.metaData.IsContainer || scope.node.isContainer) {
return false;
} else {
return true;
}
};
scope.exitMiniListView = function() {
miniListViewsHistory = [];
scope.miniListViews = [];
if(scope.onClose) {
scope.onClose();
}
};
function makeBreadcrumb() {
scope.breadcrumb = [];
angular.forEach(miniListViewsHistory, function(historyItem){
scope.breadcrumb.push(historyItem.node);
});
}
/* Search */
scope.searchMiniListView = function(search, miniListView) {
// set search value
miniListView.pagination.filter = search;
// reset pagination
miniListView.pagination.pageNumber = 1;
// start loading animation list view
miniListView.loading = true;
searchMiniListView(miniListView);
};
var searchMiniListView = _.debounce(function (miniListView) {
scope.$apply(function () {
getChildrenForMiniListView(miniListView);
});
}, 500);
/* Animation */
scope.getMiniListViewAnimation = function() {
// disable the first "slide-in-animation"" if the start node is a list view
if(scope.node.metaData && scope.node.metaData.IsContainer && skipAnimation || scope.node.isContainer && skipAnimation) {
skipAnimation = false;
return;
}
if(goingForward) {
return 'umb-mini-list-view--forward';
} else {
return 'umb-mini-list-view--backwards';
}
};
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-mini-list-view.html',
scope: {
node: "=",
entityType: "@",
startNodeId: "=",
onSelect: "&",
onClose: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbMiniListView', MiniListViewDirective);
})();

View File

@@ -0,0 +1,122 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbNodePreview
@restrict E
@scope
@description
<strong>Added in Umbraco v. 7.6:</strong> Use this directive to render a node preview.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.NodePreviewController as vm">
<div ui-sortable ng-model="vm.nodes">
<umb-node-preview
ng-repeat="node in vm.nodes"
icon="node.icon"
name="node.name"
published="node.published"
description="node.description"
sortable="vm.sortable"
allow-remove="vm.allowRemove"
allow-open="vm.allowOpen"
on-remove="vm.remove($index, vm.nodes)"
on-open="vm.open(node)">
</umb-node-preview>
</div>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.allowRemove = true;
vm.allowOpen = true;
vm.sortable = true;
vm.nodes = [
{
"icon": "icon-document",
"name": "My node 1",
"published": true,
"description": "A short description of my node"
},
{
"icon": "icon-document",
"name": "My node 2",
"published": true,
"description": "A short description of my node"
}
];
vm.remove = remove;
vm.open = open;
function remove(index, nodes) {
alert("remove node");
}
function open(node) {
alert("open node");
}
}
angular.module("umbraco").controller("My.NodePreviewController", Controller);
})();
</pre>
@param {string} icon (<code>binding</code>): The node icon.
@param {string} name (<code>binding</code>): The node name.
@param {boolean} published (<code>binding</code>): The node pusblished state.
@param {string} description (<code>binding</code>): A short description.
@param {boolean} sortable (<code>binding</code>): Will add a move cursor on the node preview. Can used in combination with ui-sortable.
@param {boolean} allowRemove (<code>binding</code>): Show/Hide the remove button.
@param {boolean} allowOpen (<code>binding</code>): Show/Hide the open button.
@param {function} onRemove (<code>expression</code>): Callback function when the remove button is clicked.
@param {function} onOpen (<code>expression</code>): Callback function when the open button is clicked.
**/
(function () {
'use strict';
function NodePreviewDirective() {
function link(scope, el, attr, ctrl) {
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-node-preview.html',
scope: {
icon: "=?",
name: "=",
description: "=?",
published: "=?",
sortable: "=?",
allowOpen: "=?",
allowRemove: "=?",
onOpen: "&?",
onRemove: "&?"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbNodePreview', NodePreviewDirective);
})();

View File

@@ -134,25 +134,40 @@ Use this directive to generate a pagination.
}
scope.next = function() {
if (scope.onNext && scope.pageNumber < scope.totalPages) {
scope.pageNumber++;
scope.onNext(scope.pageNumber);
}
scope.next = function () {
if (scope.pageNumber < scope.totalPages) {
scope.pageNumber++;
if (scope.onNext) {
scope.onNext(scope.pageNumber);
}
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
scope.prev = function(pageNumber) {
if (scope.onPrev && scope.pageNumber > 1) {
scope.pageNumber--;
scope.onPrev(scope.pageNumber);
}
scope.prev = function (pageNumber) {
if (scope.pageNumber > 1) {
scope.pageNumber--;
if (scope.onPrev) {
scope.onPrev(scope.pageNumber);
}
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
scope.goToPage = function(pageNumber) {
if(scope.onGoToPage) {
scope.pageNumber = pageNumber + 1;
scope.onGoToPage(scope.pageNumber);
}
scope.goToPage = function (pageNumber) {
scope.pageNumber = pageNumber + 1;
if (scope.onGoToPage) {
scope.onGoToPage(scope.pageNumber);
}
if (scope.onChange) {
if (scope.onChange) {
scope.onChange({ "pageNumber": scope.pageNumber });
}
}
};
var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
@@ -176,7 +191,8 @@ Use this directive to generate a pagination.
totalPages: "=",
onNext: "=",
onPrev: "=",
onGoToPage: "="
onGoToPage: "=",
onChange: "&"
},
link: link
};

View File

@@ -36,119 +36,119 @@ Use this directive make an element sticky and follow the page when scrolling.
@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
**/
(function() {
'use strict';
(function () {
'use strict';
function StickyBarDirective($rootScope) {
function StickyBarDirective($rootScope) {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
var bar = $(el);
var scrollableContainer = null;
var clonedBar = null;
var cloneIsMade = false;
var barTop = bar.context.offsetTop;
var bar = $(el);
var scrollableContainer = null;
var clonedBar = null;
var cloneIsMade = false;
function activate() {
function activate() {
if (attr.scrollableContainer) {
scrollableContainer = $(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
if (attr.scrollableContainer) {
scrollableContainer = $(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
$(window).on('resize.umbStickyBar', determineVisibility);
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
$(window).on('resize.umbStickyBar', determineVisibility);
scope.$on('$destroy', function() {
scrollableContainer.off('.umbStickyBar');
$(window).off('.umbStickyBar');
});
}
function determineVisibility() {
var scrollTop = scrollableContainer.scrollTop();
if (scrollTop > barTop) {
if (!cloneIsMade) {
createClone();
clonedBar.css({
'visibility': 'visible'
});
} else {
calculateSize();
}
} else {
if (cloneIsMade) {
//remove cloned element (switched places with original on creation)
bar.remove();
bar = clonedBar;
clonedBar = null;
bar.removeClass('-umb-sticky-bar');
bar.css({
position: 'relative',
'width': 'auto',
'height': 'auto',
'z-index': 'auto',
'visibility': 'visible'
});
cloneIsMade = false;
}
scope.$on('$destroy', function () {
scrollableContainer.off('.umbStickyBar');
$(window).off('.umbStickyBar');
});
}
}
function determineVisibility() {
function calculateSize() {
clonedBar.css({
width: bar.outerWidth(),
height: bar.height()
});
}
var barTop = bar[0].offsetTop;
var scrollTop = scrollableContainer.scrollTop();
function createClone() {
//switch place with cloned element, to keep binding intact
clonedBar = bar;
bar = clonedBar.clone();
clonedBar.after(bar);
clonedBar.addClass('-umb-sticky-bar');
clonedBar.css({
'position': 'fixed',
'z-index': 500,
'visibility': 'hidden'
});
if (scrollTop > barTop) {
cloneIsMade = true;
calculateSize();
if (!cloneIsMade) {
}
createClone();
activate();
clonedBar.css({
'visibility': 'visible'
});
}
} else {
var directive = {
restrict: 'A',
link: link
};
calculateSize();
return directive;
}
}
angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective);
} else {
if (cloneIsMade) {
//remove cloned element (switched places with original on creation)
bar.remove();
bar = clonedBar;
clonedBar = null;
bar.removeClass('-umb-sticky-bar');
bar.css({
position: 'relative',
'width': 'auto',
'height': 'auto',
'z-index': 'auto',
'visibility': 'visible'
});
cloneIsMade = false;
}
}
}
function calculateSize() {
clonedBar.css({
width: bar.outerWidth(),
height: bar.height()
});
}
function createClone() {
//switch place with cloned element, to keep binding intact
clonedBar = bar;
bar = clonedBar.clone();
clonedBar.after(bar);
clonedBar.addClass('-umb-sticky-bar');
clonedBar.css({
'position': 'fixed',
'z-index': 500,
'visibility': 'hidden'
});
cloneIsMade = true;
calculateSize();
}
activate();
}
var directive = {
restrict: 'A',
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective);
})();

View File

@@ -1,7 +1,7 @@
(function () {
'use strict';
function TableDirective() {
function TableDirective(iconHelper) {
function link(scope, el, attr, ctrl) {
@@ -43,6 +43,10 @@
}
};
scope.getIcon = function (entry) {
return iconHelper.convertFromLegacyIcon(entry.icon);
};
}
var directive = {

View File

@@ -9,7 +9,7 @@ function valServerField(serverValidationManager) {
return {
require: 'ngModel',
restrict: "A",
link: function (scope, element, attr, ctrl) {
link: function (scope, element, attr, ngModel) {
var fieldName = null;
var eventBindings = [];
@@ -23,23 +23,25 @@ function valServerField(serverValidationManager) {
// resubmitted. So once a field is changed that has a server error assigned to it
// we need to re-validate it for the server side validator so the user can resubmit
// the form. Of course normal client-side validators will continue to execute.
eventBindings.push(scope.$watch('ngModel', function(newValue){
if (ctrl.$invalid) {
ctrl.$setValidity('valServerField', true);
eventBindings.push(scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue){
if (ngModel.$invalid) {
ngModel.$setValidity('valServerField', true);
}
}));
//subscribe to the server validation changes
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
if (!isValid) {
ctrl.$setValidity('valServerField', false);
ngModel.$setValidity('valServerField', false);
//assign an error msg property to the current validator
ctrl.errorMsg = fieldErrors[0].errorMsg;
ngModel.errorMsg = fieldErrors[0].errorMsg;
}
else {
ctrl.$setValidity('valServerField', true);
ngModel.$setValidity('valServerField', true);
//reset the error message
ctrl.errorMsg = "";
ngModel.errorMsg = "";
}
});

View File

@@ -343,8 +343,13 @@ angular.module('umbraco.mocks').
{
results.push(decodeURIComponent(match[1].replace(/\+/g, " ")));
}
return results;
},
getObjectPropertyFromJsonString: function(data, name) {
var obj = JSON.parse(data);
return obj[name];
}
};
}]);

View File

@@ -21,8 +21,9 @@ angular.module('umbraco.mocks').
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var ids = mocksUtils.getParametersByName(data, "ids") || [1234, 23324, 2323, 23424];
var nodes = [];
$(ids).each(function (i, id) {
@@ -33,6 +34,33 @@ angular.module('umbraco.mocks').
return [200, nodes, null];
}
function returnEntitybyIdsPost(method, url, data, headers) {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
var ids = mocksUtils.getObjectPropertyFromJsonString(data, "ids") || [1234, 23324, 2323, 23424];
var nodes = [];
$(ids).each(function (i, id) {
var _id = parseInt(id, 10);
nodes.push(mocksUtils.getMockEntity(_id));
});
return [200, nodes, null];
}
function returnEntityUrl() {
if (!mocksUtils.checkAuth()) {
return [401, null, null];
}
return [200, "url", null];
}
return {
register: function () {
@@ -41,6 +69,10 @@ angular.module('umbraco.mocks').
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetByIds'))
.respond(returnEntitybyIds);
$httpBackend
.whenPOST(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetByIds'))
.respond(returnEntitybyIdsPost);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetAncestors'))
.respond(returnEntitybyIds);
@@ -48,6 +80,10 @@ angular.module('umbraco.mocks').
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetById?'))
.respond(returnEntitybyId);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetUrl?'))
.respond(returnEntityUrl);
}
};
}]);

View File

@@ -13,6 +13,41 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
return {
get2FAProviders: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"Get2FAProviders")),
'Could not retrive two factor provider info');
},
send2FACode: function (provider) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"PostSend2FACode"),
angular.toJson(provider)),
'Could not send code');
},
verify2FACode: function (provider, code) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"PostVerify2FACode"),
{
code: code,
provider: provider
}),
'Could not verify code');
},
/**
* @ngdoc method
* @name umbraco.resources.authResource#performLogin

View File

@@ -0,0 +1,251 @@
/**
* @ngdoc service
* @name umbraco.resources.codefileResource
* @description Loads in data for files that contain code such as js scripts, partial views and partial view macros
**/
function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
return {
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#getByPath
* @methodOf umbraco.resources.codefileResource
*
* @description
* Gets a codefile item with a given path
*
* ##usage
* <pre>
* codefileResource.getByPath('scripts', 'oooh-la-la.js')
* .then(function(codefile) {
* alert('its here!');
* });
* </pre>
*
* <pre>
* codefileResource.getByPath('partialView', 'Grid%2fEditors%2fBase.cshtml')
* .then(function(codefile) {
* alert('its here!');
* });
* </pre>
*
* @param {type} the type of script (partialView, partialViewMacro, script)
* @param {virtualpath} the virtual path of the script
* @returns {Promise} resourcePromise object.
*
*/
getByPath: function (type, virtualpath) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"GetByPath",
[{ type: type }, {virtualPath: virtualpath }])),
"Failed to retrieve data for " + type + " from virtual path " + virtualpath);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#getByAlias
* @methodOf umbraco.resources.codefileResource
*
* @description
* Gets a template item with a given alias
*
* ##usage
* <pre>
* codefileResource.getByAlias("upload")
* .then(function(template) {
* alert('its here!');
* });
* </pre>
*
* @param {String} alias Alias of template to retrieve
* @returns {Promise} resourcePromise object.
*
*/
getByAlias: function (alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetByAlias",
[{ alias: alias }])),
"Failed to retrieve data for template with alias: " + alias);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#deleteByPath
* @methodOf umbraco.resources.codefileResource
*
* @description
* Deletes a codefile with a given type & path
*
* ##usage
* <pre>
* codefileResource.deleteByPath('scripts', 'oooh-la-la.js')
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* <pre>
* codefileResource.deleteByPath('partialViews', 'Grid%2fEditors%2fBase.cshtml')
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {type} the type of script (partialViews, partialViewMacros, scripts)
* @param {virtualpath} the virtual path of the script
* @returns {Promise} resourcePromise object.
*
*/
deleteByPath: function (type, virtualpath) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"Delete",
[{ type: type }, { virtualPath: virtualpath}])),
"Failed to delete item: " + virtualpath);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#save
* @methodOf umbraco.resources.codefileResource
*
* @description
* Saves or update a codeFile
*
* ##usage
* <pre>
* codefileResource.save(codeFile)
* .then(function(codeFile) {
* alert('its saved!');
* });
* </pre>
*
* @param {Object} template object to save
* @returns {Promise} resourcePromise object.
*
*/
save: function (codeFile) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"PostSave"),
codeFile),
"Failed to save data for code file " + codeFile.virtualPath);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#getSnippets
* @methodOf umbraco.resources.codefileResource
*
* @description
* Gets code snippets for a given file type
*
* ##usage
* <pre>
* codefileResource.getSnippets("partialViews")
* .then(function(snippets) {
* alert('its here!');
* });
* </pre>
*
* @param {string} file type: (partialViews, partialViewMacros)
* @returns {Promise} resourcePromise object.
*
*/
getSnippets: function (fileType) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"GetSnippets?type=" + fileType )),
"Failed to get snippet for" + fileType);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#getScaffold
* @methodOf umbraco.resources.codefileResource
*
* @description
* Returns a scaffold of an empty codefile item.
*
* The scaffold is used to build editors for code file editors that has not yet been populated with data.
*
* ##usage
* <pre>
* codefileResource.getScaffold("partialViews", "Breadcrumb")
* .then(function(data) {
* alert('its here!');
* });
* </pre>
*
* @param {string} File type: (scripts, partialViews, partialViewMacros).
* @param {string} Snippet name (Ex. Breadcrumb).
* @returns {Promise} resourcePromise object.
*
*/
getScaffold: function (type, id, snippetName) {
var queryString = "?type=" + type + "&id=" + id;
if (snippetName) {
queryString += "&snippetName=" + snippetName;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"GetScaffold" + queryString)),
"Failed to get scaffold for" + type);
},
/**
* @ngdoc method
* @name umbraco.resources.codefileResource#createContainer
* @methodOf umbraco.resources.codefileResource
*
* @description
* Creates a container/folder
*
* ##usage
* <pre>
* codefileResource.createContainer("partialViews", "folder%2ffolder", "folder")
* .then(function(data) {
* alert('its here!');
* });
* </pre>
*
* @param {string} File type: (scripts, partialViews, partialViewMacros).
* @param {string} Parent Id: url encoded path
* @param {string} Container name
* @returns {Promise} resourcePromise object.
*
*/
createContainer: function(type, parentId, name) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"PostCreateContainer",
{ type: type, parentId: parentId, name: encodeURIComponent(name) })),
'Failed to create a folder under parent id ' + parentId);
}
};
}
angular.module("umbraco.resources").factory("codefileResource", codefileResource);

View File

@@ -477,20 +477,20 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetChildren",
[
{ id: parentId },
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: toBool(options.orderBySystemField) },
{ filter: options.filter }
])),
'Failed to retrieve children for content item ' + parentId);
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetChildren",
{
id: parentId,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
orderBySystemField: toBool(options.orderBySystemField),
filter: options.filter
})),
'Failed to retrieve children for content item ' + parentId);
},
/**

View File

@@ -92,6 +92,16 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
'Failed to retrieve property type aliases');
},
getAllStandardFields: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentTypeApiBaseUrl",
"GetAllStandardFields")),
'Failed to retrieve standard fields');
},
getPropertyTypeScaffold : function (id) {
return umbRequestHelper.resourcePromise(
$http.get(

View File

@@ -38,6 +38,10 @@ function entityResource($q, $http, umbRequestHelper) {
getSafeAlias: function (value, camelCase) {
if (!value) {
return "";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -56,7 +60,7 @@ function entityResource($q, $http, umbRequestHelper) {
*
* ##usage
* <pre>
* entityResource.getPath(id)
* entityResource.getPath(id, type)
* .then(function(pathArray) {
* alert('its here!');
* });
@@ -68,6 +72,11 @@ function entityResource($q, $http, umbRequestHelper) {
*
*/
getPath: function (id, type) {
if (id === -1 || id === "-1") {
return "-1";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -77,6 +86,42 @@ function entityResource($q, $http, umbRequestHelper) {
'Failed to retrieve path for id:' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getUrl
* @methodOf umbraco.resources.entityResource
*
* @description
* Returns a url, given a node ID and type
*
* ##usage
* <pre>
* entityResource.getUrl(id, type)
* .then(function(url) {
* alert('its here!');
* });
* </pre>
*
* @param {Int} id Id of node to return the public url to
* @param {string} type Object type name
* @returns {Promise} resourcePromise object containing the url.
*
*/
getUrl: function (id, type) {
if (id === -1 || id === "-1") {
return "";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetUrl",
[{ id: id }, {type: type }])),
'Failed to retrieve url for id:' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getById
@@ -100,14 +145,19 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getById: function (id, type) {
getById: function (id, type) {
if (id === -1 || id === "-1") {
return null;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetById",
[{ id: id}, {type: type }])),
'Failed to retrieve entity data for id ' + id);
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetById",
[{ id: id }, { type: type }])),
'Failed to retrieve entity data for id ' + id);
},
/**
@@ -135,24 +185,17 @@ function entityResource($q, $http, umbRequestHelper) {
*/
getByIds: function (ids, type) {
var query = "";
_.each(ids, function(item) {
query += "ids=" + item + "&";
});
// if ids array is empty we need a empty variable in the querystring otherwise the service returns a error
if (ids.length === 0) {
query += "ids=&";
}
query += "type=" + type;
var query = "type=" + type;
return umbRequestHelper.resourcePromise(
$http.get(
$http.post(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetByIds",
query)),
query),
{
ids: ids
}),
'Failed to retrieve entity data for ids ' + ids);
},
@@ -261,18 +304,19 @@ function entityResource($q, $http, umbRequestHelper) {
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getAncestors
* @name umbraco.resources.entityResource#getChildren
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets children entities for a given item
*
*
* @param {Int} parentid id of content item to return children of
* @param {string} type Object type name
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getChildren: function (id, type) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -281,6 +325,146 @@ function entityResource($q, $http, umbRequestHelper) {
[{ id: id }, { type: type }])),
'Failed to retrieve child data for id ' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getPagedChildren
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets paged children of a content item with a given id
*
* ##usage
* <pre>
* entityResource.getPagedChildren(1234, "Content", {pageSize: 10, pageNumber: 2})
* .then(function(contentArray) {
* var children = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Int} parentid id of content item to return children of
* @param {string} type Object type name
* @param {Object} options optional options object
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 1
* @param {Int} options.pageNumber if paging data, current page index, default = 100
* @param {String} options.filter if provided, query will only return those with names matching the filter
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
* @param {String} options.orderBy property to order items by, default: `SortOrder`
* @returns {Promise} resourcePromise object containing an array of content items.
*
*/
getPagedChildren: function (parentId, type, options) {
var defaults = {
pageSize: 1,
pageNumber: 100,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder"
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetPagedChildren",
{
id: parentId,
type: type,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: encodeURIComponent(options.filter)
}
)),
'Failed to retrieve child data for id ' + parentId);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getPagedDescendants
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets paged descendants of a content item with a given id
*
* ##usage
* <pre>
* entityResource.getPagedDescendants(1234, "Content", {pageSize: 10, pageNumber: 2})
* .then(function(contentArray) {
* var children = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Int} parentid id of content item to return descendants of
* @param {string} type Object type name
* @param {Object} options optional options object
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 1
* @param {Int} options.pageNumber if paging data, current page index, default = 100
* @param {String} options.filter if provided, query will only return those with names matching the filter
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
* @param {String} options.orderBy property to order items by, default: `SortOrder`
* @returns {Promise} resourcePromise object containing an array of content items.
*
*/
getPagedDescendants: function (parentId, type, options) {
var defaults = {
pageSize: 1,
pageNumber: 100,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder"
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetPagedDescendants",
{
id: parentId,
type: type,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: encodeURIComponent(options.filter)
}
)),
'Failed to retrieve child data for id ' + parentId);
},
/**
* @ngdoc method

View File

@@ -2,13 +2,13 @@
* @ngdoc service
* @name umbraco.resources.macroResource
* @description Deals with data for macros
*
*
**/
function macroResource($q, $http, umbRequestHelper) {
//the factory object returned
return {
/**
* @ngdoc method
* @name umbraco.resources.macroResource#getMacroParameters
@@ -20,7 +20,7 @@ function macroResource($q, $http, umbRequestHelper) {
* @param {int} macroId The macro id to get parameters for
*
*/
getMacroParameters: function (macroId) {
getMacroParameters: function (macroId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -29,7 +29,7 @@ function macroResource($q, $http, umbRequestHelper) {
[{ macroId: macroId }])),
'Failed to retrieve macro parameters for macro with id ' + macroId);
},
/**
* @ngdoc method
* @name umbraco.resources.macroResource#getMacroResult
@@ -55,6 +55,27 @@ function macroResource($q, $http, umbRequestHelper) {
macroParams: macroParamDictionary
}),
'Failed to retrieve macro result for macro with alias ' + macroAlias);
},
/**
*
* @param {} filename
* @returns {}
*/
createPartialViewMacroWithFile: function(virtualPath, filename) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"macroApiBaseUrl",
"CreatePartialViewMacroWithFile"), {
virtualPath: virtualPath,
filename: filename
}
),
'Failed to create macro "' + filename + '"'
);
}
};
}

View File

@@ -485,7 +485,49 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
"mediaApiBaseUrl",
"EmptyRecycleBin")),
'Failed to empty the recycle bin');
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#search
* @methodOf umbraco.resources.mediaResource
*
* @description
* Empties the media recycle bin
*
* ##usage
* <pre>
* mediaResource.search("my search", 1, 100, -1)
* .then(function(searchResult) {
* alert('it's here!');
* });
* </pre>
*
* @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
* @returns {Promise} resourcePromise object.
*
*/
search: function (query, pageNumber, pageSize, searchFrom) {
var args = [
{ "query": query },
{ "pageNumber": pageNumber },
{ "pageSize": pageSize },
{ "searchFrom": searchFrom }
];
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"Search",
args)),
'Failed to retrieve media items for search: ' + query);
}
};
}

View File

@@ -19,14 +19,14 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
_.each(filterContentTypes, function (item) {
query += "filterContentTypes=" + item + "&";
});
// if filterContentTypes array is empty we need a empty variable in the querystring otherwise the service returns a error
// if filterContentTypes array is empty we need a empty variable in the querystring otherwise the service returns a error
if (filterContentTypes.length === 0) {
query += "filterContentTypes=&";
}
_.each(filterPropertyTypes, function (item) {
query += "filterPropertyTypes=" + item + "&";
});
// if filterPropertyTypes array is empty we need a empty variable in the querystring otherwise the service returns a error
// if filterPropertyTypes array is empty we need a empty variable in the querystring otherwise the service returns a error
if (filterPropertyTypes.length === 0) {
query += "filterPropertyTypes=&";
}

View File

@@ -5,15 +5,14 @@
**/
function ourPackageRepositoryResource($q, $http, umbDataFormatter, umbRequestHelper) {
//var baseurl = "http://localhost:24292/webapi/packages/v1";
var baseurl = "https://our.umbraco.org/webapi/packages/v1";
var baseurl = Umbraco.Sys.ServerVariables.umbracoUrls.packagesRestApiBaseUrl;
return {
getDetails: function (packageId) {
return umbRequestHelper.resourcePromise(
$http.get(baseurl + "/" + packageId),
$http.get(baseurl + "/" + packageId + "?version=" + Umbraco.Sys.ServerVariables.application.version),
'Failed to get package details');
},

View File

@@ -0,0 +1,196 @@
/**
* @ngdoc service
* @name umbraco.resources.templateResource
* @description Loads in data for templates
**/
function templateResource($q, $http, umbDataFormatter, umbRequestHelper) {
return {
/**
* @ngdoc method
* @name umbraco.resources.templateResource#getById
* @methodOf umbraco.resources.templateResource
*
* @description
* Gets a template item with a given id
*
* ##usage
* <pre>
* templateResource.getById(1234)
* .then(function(template) {
* alert('its here!');
* });
* </pre>
*
* @param {Int} id id of template to retrieve
* @returns {Promise} resourcePromise object.
*
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetById",
[{ id: id }])),
"Failed to retrieve data for template id " + id);
},
/**
* @ngdoc method
* @name umbraco.resources.templateResource#getByAlias
* @methodOf umbraco.resources.templateResource
*
* @description
* Gets a template item with a given alias
*
* ##usage
* <pre>
* templateResource.getByAlias("upload")
* .then(function(template) {
* alert('its here!');
* });
* </pre>
*
* @param {String} alias Alias of template to retrieve
* @returns {Promise} resourcePromise object.
*
*/
getByAlias: function (alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetByAlias",
[{ alias: alias }])),
"Failed to retrieve data for template with alias: " + alias);
},
/**
* @ngdoc method
* @name umbraco.resources.templateResource#getAll
* @methodOf umbraco.resources.templateResource
*
* @description
* Gets all templates
*
* ##usage
* <pre>
* templateResource.getAll()
* .then(function(templates) {
* alert('its here!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object.
*
*/
getAll: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetAll")),
"Failed to retrieve data");
},
/**
* @ngdoc method
* @name umbraco.resources.templateResource#getScaffold
* @methodOf umbraco.resources.templateResource
*
* @description
* Returns a scaffold of an empty template item
*
* The scaffold is used to build editors for templates that has not yet been populated with data.
*
* ##usage
* <pre>
* templateResource.getScaffold()
* .then(function(template) {
* alert('its here!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object containing the template scaffold.
*
*/
getScaffold: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetScaffold",
[{ id: id }] )),
"Failed to retrieve data for empty template");
},
/**
* @ngdoc method
* @name umbraco.resources.templateResource#deleteById
* @methodOf umbraco.resources.templateResource
*
* @description
* Deletes a template with a given id
*
* ##usage
* <pre>
* templateResource.deleteById(1234)
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {Int} id id of template to delete
* @returns {Promise} resourcePromise object.
*
*/
deleteById: function(id) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"DeleteById",
[{ id: id }])),
"Failed to delete item " + id);
},
/**
* @ngdoc method
* @name umbraco.resources.templateResource#save
* @methodOf umbraco.resources.templateResource
*
* @description
* Saves or update a template
*
* ##usage
* <pre>
* templateResource.save(template)
* .then(function(template) {
* alert('its saved!');
* });
* </pre>
*
* @param {Object} template object to save
* @returns {Promise} resourcePromise object.
*
*/
save: function (template) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"PostSave"),
template),
"Failed to save data for template id " + template.id);
}
};
}
angular.module("umbraco.resources").factory("templateResource", templateResource);

View File

@@ -0,0 +1,151 @@
/**
* @ngdoc service
* @name umbraco.resources.templateQueryResource
* @function
*
* @description
* Used by the query builder
*/
(function () {
'use strict';
function templateQueryResource($http, umbRequestHelper) {
/**
* @ngdoc function
* @name umbraco.resources.templateQueryResource#getAllowedProperties
* @methodOf umbraco.resources.templateQueryResource
* @function
*
* @description
* Called to get allowed properties
* ##usage
* <pre>
* templateQueryResource.getAllowedProperties()
* .then(function(response) {
*
* });
* </pre>
*/
function getAllowedProperties() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateQueryApiBaseUrl",
"GetAllowedProperties")),
'Failed to retrieve properties');
}
/**
* @ngdoc function
* @name umbraco.resources.templateQueryResource#getContentTypes
* @methodOf umbraco.resources.templateQueryResource
* @function
*
* @description
* Called to get content types
* ##usage
* <pre>
* templateQueryResource.getContentTypes()
* .then(function(response) {
*
* });
* </pre>
*/
function getContentTypes() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateQueryApiBaseUrl",
"GetContentTypes")),
'Failed to retrieve content types');
}
/**
* @ngdoc function
* @name umbraco.resources.templateQueryResource#getFilterConditions
* @methodOf umbraco.resources.templateQueryResource
* @function
*
* @description
* Called to the filter conditions
* ##usage
* <pre>
* templateQueryResource.getFilterConditions()
* .then(function(response) {
*
* });
* </pre>
*/
function getFilterConditions() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateQueryApiBaseUrl",
"GetFilterConditions")),
'Failed to retrieve filter conditions');
}
/**
* @ngdoc function
* @name umbraco.resources.templateQueryResource#postTemplateQuery
* @methodOf umbraco.resources.templateQueryResource
* @function
*
* @description
* Called to get content types
* ##usage
* <pre>
* var query = {
* contentType: {
* name: "Everything"
* },
* source: {
* name: "My website"
* },
* filters: [
* {
* property: undefined,
* operator: undefined
* }
* ],
* sort: {
* property: {
* alias: "",
* name: "",
* },
* direction: "ascending"
* }
* };
*
* templateQueryResource.postTemplateQuery(query)
* .then(function(response) {
*
* });
* </pre>
* @param {object} query Query to build result
*/
function postTemplateQuery(query) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"templateQueryApiBaseUrl",
"PostTemplateQuery"),
query),
'Failed to retrieve query');
}
var resource = {
getAllowedProperties: getAllowedProperties,
getContentTypes: getContentTypes,
getFilterConditions: getFilterConditions,
postTemplateQuery: postTemplateQuery
};
return resource;
}
angular.module('umbraco.resources').factory('templateQueryResource', templateQueryResource);
})();

View File

@@ -51,10 +51,15 @@ function treeResource($q, $http, umbRequestHelper) {
if (!options.isDialog) {
options.isDialog = false;
}
//create the query string for the tree request, these are the mandatory options:
var query = "application=" + options.section + "&tree=" + options.tree + "&isDialog=" + options.isDialog;
//if you need to load a not initialized tree set this value to false - default is true
if (options.onlyinitialized) {
query += "&onlyInitialized=" + options.onlyinitialized;
}
//the options can contain extra query string parameters
if (options.queryString) {
query += "&" + options.queryString;

View File

@@ -27,7 +27,7 @@ angular.module('umbraco.security.interceptor')
var headers = config.headers ? config.headers : {};
//Here we'll check if we should ignore the error (either based on the original header set or the request configuration)
if (headers["x-umb-ignore-error"] === "ignore" || config.umbIgnoreErrors === true) {
if (headers["x-umb-ignore-error"] === "ignore" || config.umbIgnoreErrors === true || (angular.isArray(config.umbIgnoreStatus) && config.umbIgnoreStatus.indexOf(originalResponse.status) !== -1)) {
//exit/ignore
return promise;
}

View File

@@ -522,6 +522,26 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return true;
}
return false;
},
/**
* @ngdoc function
* @name umbraco.services.contentEditingHelper#redirectToRenamedContent
* @methodOf umbraco.services.contentEditingHelper
* @function
*
* @description
* For some editors like scripts or entites that have names as ids, these names can change and we need to redirect
* to their new paths, this is helper method to do that.
*/
redirectToRenamedContent: function (id) {
//clear the query strings
$location.search("");
//change to new path
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
//don't add a browser history for this
$location.replace();
return true;
}
};
}

View File

@@ -0,0 +1,29 @@
(function() {
'use strict';
function entityHelper() {
function getEntityTypeFromSection(section) {
if (section === "member") {
return "Member";
}
else if (section === "media") {
return "Media";
} else {
return "Document";
}
}
////////////
var service = {
getEntityTypeFromSection: getEntityTypeFromSection
};
return service;
}
angular.module('umbraco.services').factory('entityHelper', entityHelper);
})();

View File

@@ -135,8 +135,13 @@ angular.module('umbraco.services')
*
* @description
* Checks the dictionary for a localized resource string
* @param {String} value the area/key to localize
* @param {Array} tokens if specified this array will be sent as parameter values
* @param {String} value the area/key to localize in the format of 'section_key'
* alternatively if no section is set such as 'key' then we assume the key is to be looked in
* the 'general' section
*
* @param {Array} tokens if specified this array will be sent as parameter values
* This replaces %0% and %1% etc in the dictionary key value with the passed in strings
*
* @returns {String} localized resource string
*/
localize: function (value, tokens) {
@@ -146,6 +151,143 @@ angular.module('umbraco.services')
});
},
/**
* @ngdoc method
* @name umbraco.services.localizationService#localizeMany
* @methodOf umbraco.services.localizationService
*
* @description
* Checks the dictionary for multipe localized resource strings at once, preventing the need for nested promises
* with localizationService.localize
*
* ##Usage
* <pre>
* localizationService.localizeMany(["speechBubbles_templateErrorHeader", "speechBubbles_templateErrorText"]).then(function(data){
* var header = data[0];
* var message = data[1];
* notificationService.error(header, message);
* });
* </pre>
*
* @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
* alternatively if no section is set such as 'key' then we assume the key is to be looked in
* the 'general' section
*
* @returns {Array} An array of localized resource string in the same order
*/
localizeMany: function(keys) {
if(keys){
//The LocalizationService.localize promises we want to resolve
var promises = [];
for(var i = 0; i < keys.length; i++){
promises.push(service.localize(keys[i], undefined));
}
return $q.all(promises).then(function(localizedValues){
return localizedValues;
});
}
},
/**
* @ngdoc method
* @name umbraco.services.localizationService#concat
* @methodOf umbraco.services.localizationService
*
* @description
* Checks the dictionary for multipe localized resource strings at once & concats them to a single string
* Which was not possible with localizationSerivce.localize() due to returning a promise
*
* ##Usage
* <pre>
* localizationService.concat(["speechBubbles_templateErrorHeader", "speechBubbles_templateErrorText"]).then(function(data){
* var combinedText = data;
* });
* </pre>
*
* @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
* alternatively if no section is set such as 'key' then we assume the key is to be looked in
* the 'general' section
*
* @returns {String} An concatenated string of localized resource string passed into the function in the same order
*/
concat: function(keys) {
if(keys){
//The LocalizationService.localize promises we want to resolve
var promises = [];
for(var i = 0; i < keys.length; i++){
promises.push(service.localize(keys[i], undefined));
}
return $q.all(promises).then(function(localizedValues){
//Build a concat string by looping over the array of resolved promises/translations
var returnValue = "";
for(var i = 0; i < localizedValues.length; i++){
returnValue += localizedValues[i];
}
return returnValue;
});
}
},
/**
* @ngdoc method
* @name umbraco.services.localizationService#format
* @methodOf umbraco.services.localizationService
*
* @description
* Checks the dictionary for multipe localized resource strings at once & formats a tokenized message
* Which was not possible with localizationSerivce.localize() due to returning a promise
*
* ##Usage
* <pre>
* localizationService.format(["template_insert", "template_insertSections"], "%0% %1%").then(function(data){
* //Will return 'Insert Sections'
* var formattedResult = data;
* });
* </pre>
*
* @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
* alternatively if no section is set such as 'key' then we assume the key is to be looked in
* the 'general' section
*
* @param {String} message is the string you wish to replace containing tokens in the format of %0% and %1%
* with the localized resource strings
*
* @returns {String} An concatenated string of localized resource string passed into the function in the same order
*/
format: function(keys, message){
if(keys){
//The LocalizationService.localize promises we want to resolve
var promises = [];
for(var i = 0; i < keys.length; i++){
promises.push(service.localize(keys[i], undefined));
}
return $q.all(promises).then(function(localizedValues){
//Replace {0} and {1} etc in message with the localized values
for(var i = 0; i < localizedValues.length; i++){
var token = "%" + i + "%";
var regex = new RegExp(token, "g");
message = message.replace(regex, localizedValues[i]);
}
return message;
});
}
}
};
//This happens after login / auth and assets loading

View File

@@ -21,7 +21,9 @@ function mediaTypeHelper(mediaTypeResource, $q) {
},
getAllowedImagetypes: function (mediaId){
//TODO: This is horribly inneficient - why make one request per type!?
// Get All allowedTypes
return mediaTypeResource.getAllowedTypes(mediaId)
.then(function(types){

View File

@@ -0,0 +1,89 @@
(function () {
'use strict';
function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper, $q) {
var launched = false;
function launchMiniEditor(node) {
var deferred = $q.defer();
launched = true;
//We need to store the current files selected in the file manager locally because the fileManager
// is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
// and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
// clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
var currFiles = _.groupBy(fileManager.getFiles(), "alias");
fileManager.clearFiles();
//We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
// any property editors that are working with editorState get given the correct entity, otherwise strange things will
// start happening.
var currEditorState = editorState.getCurrent();
dialogService.open({
template: "views/common/dialogs/content/edit.html",
id: node.id,
closeOnSave: true,
tabFilter: ["Generic properties"],
callback: function (data) {
//set the node name back
node.name = data.name;
//reset the fileManager to what it was
fileManager.clearFiles();
_.each(currFiles, function (val, key) {
fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
});
//reset the editor state
editorState.set(currEditorState);
//Now we need to check if the content item that was edited was actually the same content item
// as the main content editor and if so, update all property data
if (data.id === currEditorState.id) {
var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
}
launched = false;
deferred.resolve(data);
},
closeCallback: function () {
//reset the fileManager to what it was
fileManager.clearFiles();
_.each(currFiles, function (val, key) {
fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
});
//reset the editor state
editorState.set(currEditorState);
launched = false;
deferred.reject();
}
});
return deferred.promise;
}
var service = {
launchMiniEditor: launchMiniEditor
};
return service;
}
angular.module('umbraco.services').factory('miniEditorHelper', miniEditorHelper);
})();

View File

@@ -0,0 +1,23 @@
(function() {
'use strict';
function platformService() {
function isMac() {
return navigator.platform.toUpperCase().indexOf('MAC')>=0;
}
////////////
var service = {
isMac: isMac
};
return service;
}
angular.module('umbraco.services').factory('platformService', platformService);
})();

View File

@@ -0,0 +1,186 @@
(function() {
'use strict';
function templateHelperService(localizationService) {
//crappy hack due to dictionary items not in umbracoNode table
function getInsertDictionarySnippet(nodeName) {
return "@Umbraco.GetDictionaryValue(\"" + nodeName + "\")";
}
function getInsertPartialSnippet(parentId, nodeName) {
var partialViewName = nodeName.replace(".cshtml", "");
if(parentId) {
partialViewName = parentId + "/" + partialViewName;
}
return "@Html.Partial(\"" + partialViewName + "\")";
}
function getQuerySnippet(queryExpression) {
var code = "\n@{\n" + "\tvar selection = " + queryExpression + ";\n}\n";
code += "<ul>\n" +
"\t@foreach(var item in selection){\n" +
"\t\t<li>\n" +
"\t\t\t<a href=\"@item.Url\">@item.Name</a>\n" +
"\t\t</li>\n" +
"\t}\n" +
"</ul>\n\n";
return code;
}
function getRenderBodySnippet() {
return "@RenderBody()";
}
function getRenderSectionSnippet(sectionName, mandatory) {
return "@RenderSection(\"" + sectionName + "\", " + mandatory + ")";
}
function getAddSectionSnippet(sectionName) {
return "@section " + sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n";
}
function getGeneralShortcuts(){
return {
"name": localizationService.localize("shortcuts_generalHeader"),
"shortcuts": [
{
"description": localizationService.localize("buttons_undo"),
"keys": [{ "key": "ctrl" }, { "key": "z" }]
},
{
"description": localizationService.localize("buttons_redo"),
"keys": [{ "key": "ctrl" }, { "key": "y" }]
},
{
"description": localizationService.localize("buttons_save"),
"keys": [{ "key": "ctrl" }, { "key": "s" }]
}
]
};
}
function getEditorShortcuts(){
return {
"name": localizationService.localize("shortcuts_editorHeader"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_commentLine"),
"keys": [{ "key": "ctrl" }, { "key": "/" }]
},
{
"description": localizationService.localize("shortcuts_removeLine"),
"keys": [{ "key": "ctrl" }, { "key": "d" }]
},
{
"description": localizationService.localize("shortcuts_copyLineUp"),
"keys": {
"win": [{ "key": "alt" }, { "key": "shift" }, { "key": "up" }],
"mac": [{ "key": "cmd" }, { "key": "alt" }, { "key": "up" }]
}
},
{
"description": localizationService.localize("shortcuts_copyLineDown"),
"keys": {
"win": [{ "key": "alt" }, { "key": "shift" }, { "key": "down" }],
"mac": [{ "key": "cmd" }, { "key": "alt" }, { "key": "down" }]
}
},
{
"description": localizationService.localize("shortcuts_moveLineUp"),
"keys": [{ "key": "alt" }, { "key": "up" }]
},
{
"description": localizationService.localize("shortcuts_moveLineDown"),
"keys": [{ "key": "alt" }, { "key": "down" }]
}
]
};
}
function getTemplateEditorShortcuts(){
return {
"name": "Umbraco", //No need to localise Umbraco is the same in all languages :)
"shortcuts": [
{
"description": localizationService.format(["template_insert", "template_insertPageField"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "v" }]
},
{
"description": localizationService.format(["template_insert", "template_insertPartialView"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
},
{
"description": localizationService.format(["template_insert", "template_insertDictionaryItem"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
},
{
"description": localizationService.format(["template_insert", "template_insertMacro"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "m" }]
},
{
"description": localizationService.localize("template_queryBuilder"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "q" }]
},
{
"description": localizationService.format(["template_insert", "template_insertSections"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "s" }]
},
{
"description": localizationService.localize("template_mastertemplate"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
}
]
};
}
function getPartialViewEditorShortcuts(){
return {
"name": "Umbraco", //No need to localise Umbraco is the same in all languages :)
"shortcuts": [
{
"description": localizationService.format(["template_insert", "template_insertPageField"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "v" }]
},
{
"description": localizationService.format(["template_insert", "template_insertDictionaryItem"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
},
{
"description": localizationService.format(["template_insert", "template_insertMacro"], "%0% %1%"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "m" }]
},
{
"description": localizationService.localize("template_queryBuilder"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "q" }]
}
]
};
}
////////////
var service = {
getInsertDictionarySnippet: getInsertDictionarySnippet,
getInsertPartialSnippet: getInsertPartialSnippet,
getQuerySnippet: getQuerySnippet,
getRenderBodySnippet: getRenderBodySnippet,
getRenderSectionSnippet: getRenderSectionSnippet,
getAddSectionSnippet: getAddSectionSnippet,
getGeneralShortcuts: getGeneralShortcuts,
getEditorShortcuts: getEditorShortcuts,
getTemplateEditorShortcuts: getTemplateEditorShortcuts,
getPartialViewEditorShortcuts: getPartialViewEditorShortcuts
};
return service;
}
angular.module('umbraco.services').factory('templateHelper', templateHelperService);
})();

View File

@@ -95,11 +95,20 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
if(selectedElm.nodeName === 'IMG'){
var img = $(selectedElm);
var hasUdi = img.attr("data-udi") ? true : false;
currentTarget = {
altText: img.attr("alt"),
url: img.attr("src"),
id: img.attr("rel")
url: img.attr("src")
};
if (hasUdi) {
currentTarget["udi"] = img.attr("data-udi");
}
else {
currentTarget["id"] = img.attr("rel");
}
}
userService.getCurrentUser().then(function(userData) {
@@ -115,13 +124,23 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertMediaInEditor: function(editor, img) {
if(img) {
var hasUdi = img.udi ? true : false;
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
rel: img.id,
'data-id': img.id,
src: (img.url) ? img.url : "nothing.jpg",
id: '__mcenew'
};
};
if (hasUdi) {
data["data-udi"] = img.udi;
}
else {
//Considering these fixed because UDI will now be used and thus
// we have no need for rel http://issues.umbraco.org/issue/U4-6228, http://issues.umbraco.org/issue/U4-6595
data["rel"] = img.id;
data["data-id"] = img.id;
}
editor.insertContent(editor.dom.createHTML('img', data));
@@ -672,8 +691,17 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
//locallink detection, we do this here, to avoid poluting the dialogservice
//so the dialog service can just expect to get a node-like structure
if(currentTarget.url.indexOf("localLink:") > 0){
currentTarget.id = currentTarget.url.substring(currentTarget.url.indexOf(":")+1,currentTarget.url.length-1);
if (currentTarget.url.indexOf("localLink:") > 0) {
var linkId = currentTarget.url.substring(currentTarget.url.indexOf(":") + 1, currentTarget.url.length - 1);
//we need to check if this is an INT or a UDI
var parsedIntId = parseInt(linkId, 10);
if (isNaN(parsedIntId)) {
//it's a UDI
currentTarget.udi = linkId;
}
else {
currentTarget.id = linkId;
}
}
}
@@ -716,27 +744,36 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertLinkInEditor: function(editor, target, anchorElm) {
var href = target.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = target.udi ? true : false;
var id = hasUdi ? target.udi : (target.id ? target.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null
};
if (hasUdi) {
a["data-udi"] = target.udi;
}
else if (target.id) {
a["data-id"] = target.id;
}
return a;
}
function insertLink() {
if (anchorElm) {
editor.dom.setAttribs(anchorElm, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
editor.dom.setAttribs(anchorElm, createElemAttributes());
editor.selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
}
else {
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -746,8 +783,10 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(target.id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
href = "/{localLink:" + target.id + "}";
if(id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
}

View File

@@ -4,6 +4,7 @@ angular.module('umbraco.services')
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;
@@ -25,7 +26,7 @@ angular.module('umbraco.services')
}
});
}
}
}
function onLoginDialogClose(success) {
loginDialog = null;
@@ -182,8 +183,7 @@ angular.module('umbraco.services')
/** 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
@@ -199,17 +199,18 @@ angular.module('umbraco.services')
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(function (data) {
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful:function (data) {
//when it's successful, return the user data
setCurrentUser(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;
});
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
},
/** Logs the user out

View File

@@ -26,10 +26,35 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService',
/** execute code on each successful route */
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
if(current.params.section){
$rootScope.locationTitle = current.params.section + " - " + $location.$$host;
var deployConfig = Umbraco.Sys.ServerVariables.deploy;
var deployEnv, deployEnvTitle;
if (deployConfig) {
deployEnv = Umbraco.Sys.ServerVariables.deploy.CurrentWorkspace;
deployEnvTitle = "(" + deployEnv + ") ";
}
if(current.params.section) {
//Uppercase the current section, content, media, settings, developer, forms
var currentSection = current.params.section.charAt(0).toUpperCase() + current.params.section.slice(1);
var baseTitle = currentSection + " - " + $location.$$host;
//Check deploy for Global Umbraco.Sys obj workspace
if(deployEnv){
$rootScope.locationTitle = deployEnvTitle + baseTitle;
}
else {
$rootScope.locationTitle = baseTitle;
}
}
else {
if(deployEnv) {
$rootScope.locationTitle = deployEnvTitle + "Umbraco - " + $location.$$host;
}
$rootScope.locationTitle = "Umbraco - " + $location.$$host;
}

View File

@@ -100,7 +100,7 @@
<input type="submit" ng-disabled="myForm.$invalid || checking" ng-class="{disabled:myForm.$invalid}"
value="Continue" class="btn btn-success" />
<button class="btn" ng-click="restart()">Go back</button>
<button class="btn btn-info" ng-click="restart()">Go back</button>
<span class="inline-help" ng-if="checking" ng-animate="'fade'">
Validating your database connection...

View File

@@ -5,10 +5,10 @@
</p>
<p>
To read a report of changes between your current version <strong>{{installer.current.model.currentVersion}}</strong> and this version your upgrading to <strong>{{installer.current.model.newVersion}}</strong>
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>
</p>
<p>
<a ng-href="{{installer.current.model.reportUrl}}" target="_blank" class="btn">View Report</a>
<a ng-href="{{installer.current.model.reportUrl}}" target="_blank" class="btn btn-info">View Report</a>
</p>
<p>

View File

@@ -46,16 +46,21 @@
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" id="subscribeToNewsLetter" name="subscribeToNewsLetter"
ng-model="installer.current.model.subscribeToNewsLetter" /> Keep me updated on Umbraco Versions, Security Bulletins and Community News</label>
<label>
<input
type="checkbox"
id="subscribeToNewsLetter"
name="subscribeToNewsLetter"
ng-model="installer.current.model.subscribeToNewsLetter" />
Keep me updated on Umbraco Versions, Security Bulletins and Community News
</label>
</div>
</div>
<div class="control-group" ng-class="{disabled:myForm.$invalid}">
<div class="controls">
<input type="submit" ng-disabled="myForm.$invalid" value="Install" class="btn btn-success" />
<a href class="btn btn-neutral control-customize" ng-disabled="myForm.$invalid" ng-click="validateAndForward()">Customize</a>
<a href class="btn btn-info control-customize" ng-disabled="myForm.$invalid" ng-click="validateAndForward()">Customize</a>
</div>
</div>

View File

@@ -11,7 +11,7 @@
margin-bottom: @baseLineHeight;
background-color: @warningBackground;
border: 1px solid @warningBorder;
.border-radius(@baseBorderRadius);
.border-radius(@alertBorderRadius);
}
.alert,
.alert h4,
@@ -67,9 +67,9 @@
}
.alert-form {
background-color: #ECECEC;
border: 1px solid @gray !important;
color: @gray;
background-color: @gray-10;
border: 1px solid @gray-3 !important;
color: @gray-3;
}
.alert-form.-no-border {
@@ -77,7 +77,7 @@
}
.alert-form h4 {
color: @gray;
color: @gray-3;
}

View File

@@ -19,6 +19,11 @@ body {
line-height: @baseLineHeight;
color: @textColor;
background-color: @bodyBackground;
// better font rendering
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@@ -142,7 +147,7 @@ body {
left: 0px;
right: 0px;
padding-top: 100px;
border-right: 1px solid @grayLight;
border-right: 1px solid @purple-l3;
z-index: 100;
}
@@ -197,8 +202,8 @@ body {
right: -5px;
top: 0;
bottom: 0;
background-color: @grayLighter;
border: solid 1px @grayLight;
background-color: @gray-10;
border: solid 1px @purple-l3;
border-top: none;
border-bottom: none;
position:absolute;

View File

@@ -111,17 +111,24 @@
@import "components/umb-empty-state.less";
@import "components/umb-property-editor.less";
@import "components/umb-iconpicker.less";
@import "components/umb-insert-code-box.less";
@import "components/umb-packages.less";
@import "components/umb-package-local-install.less";
@import "components/umb-lightbox.less";
@import "components/umb-avatar.less";
@import "components/umb-progress-bar.less";
@import "components/umb-querybuilder.less";
@import "components/umb-pagination.less";
@import "components/umb-mini-list-view.less";
@import "components/buttons/umb-button.less";
@import "components/buttons/umb-button-group.less";
@import "components/buttons/umb-era-button.less";
@import "components/notifications/umb-notifications.less";
@import "components/umb-file-dropzone.less";
@import "components/umb-node-preview.less";
@import "components/umb-mini-editor.less";
// Utilities
@import "utilities/_flexbox.less";
@@ -132,6 +139,7 @@
//page specific styles
@import "pages/document-type-editor.less";
@import "pages/login.less";
@import "pages/welcome-dashboard.less";
//used for property editors

View File

@@ -9,7 +9,7 @@
// Core
.btn {
display: inline-block;
padding: 4px 12px;
padding: 6px 14px;
margin-bottom: 0; // For input.btn
font-size: @baseFontSize;
line-height: @baseLineHeight;
@@ -18,14 +18,15 @@
cursor: pointer;
background: @btnBackground;
color: @black;
border: 1px solid @btnBorder;
border: none;
box-shadow: none;
border-radius: 3px;
// Hover/focus state
&:hover,
&:focus {
background: @btnBackgroundHighlight;
color: @grayDark;
color: @gray-4;
background-position: 0 -15px;
text-decoration: none;
@@ -64,14 +65,11 @@
-webkit-box-shadow:none;
}
.btn-group > .btn:first-child,
.btn-group > .btn:last-child,
.btn-group > .dropdown-toggle {
border-radius: 0;
.btn-group .btn.dropdown-toggle {
border-left-width: 1px;
border-left-style: solid;
}
// Button Sizes
// --------------------------------------------------
@@ -138,7 +136,7 @@ input[type="button"] {
// Round button
.btn-round{
font-size: 24px;
color: @gray;
color: @gray-3;
background: @white;
line-height: 32px;
@@ -167,6 +165,16 @@ input[type="button"] {
color: rgba(255,255,255,.75);
}
.btn-primary,
.btn-warning,
.btn-danger,
.btn-success,
.btn-info,
.btn-inverse,
.btn-neutral {
font-weight: bold;
}
// Set the backgrounds
// -------------------------
.btn-primary {
@@ -196,16 +204,16 @@ input[type="button"] {
// Neutral appears as lighter gray
.btn-neutral {
.buttonBackground(@btnNeutralBackground, @btnNeutralBackgroundHighlight);
color: #656565;
color: @gray-5;
// Hover/focus state
&:hover,
&:focus {
color: #656565;
color: @gray-5;
}
&.disabled,
&[disabled] {
color:#656565;
color: @gray-5;
.opacity(65);
}
@@ -217,11 +225,11 @@ input[type="button"] {
padding: 15px 50px;
font-size: 16px;
border: none;
background: @blue;
background: @green;
color: white;
&:hover {
background: #2b8ee3;
font-weight: bold;
&:hover {
background: @green-d1;
}
}
@@ -281,7 +289,7 @@ input[type="submit"].btn {
}
.btn-link[disabled]:hover,
.btn-link[disabled]:focus {
color: @grayDark;
color: @gray-4;
text-decoration: none;
}

View File

@@ -14,6 +14,7 @@ body {
overflow: hidden;
height: 100%;
width: 100%;
width: ~"(calc(~'100%' - ~'80px'))"; // 80px is the fixed left menu for toggling the different browser sizes
position: absolute;
padding: 0;
margin: 0;
@@ -270,10 +271,10 @@ a, a:hover{
height: 100%;
padding: 0;
margin-left: -80px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Lato", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 16px;
background: #1D1D1D;
background: #413659;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
@@ -282,8 +283,8 @@ a, a:hover{
.avatar {
text-align:center;
padding: 25px 0 29px 0;
border-bottom: 1px solid #343434;
padding: 27px 0 29px 0;
border-bottom: 1px solid #2E2246;
}
.help {
@@ -295,7 +296,7 @@ a, a:hover{
margin: 0;
font-size: 30px;
text-align: center;
color: #d9d9d9;
color: #D8D7D9;
opacity: 0.4;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
@@ -304,7 +305,7 @@ a, a:hover{
ul.sections {
display: block;
background: #1d1d1d;
background: #413659;
height: 100%;
position:absolute;
top: 90px;
@@ -320,7 +321,7 @@ ul.sections {
ul.sections li {
display: block;
border-left: 4px #1d1d1d solid;
border-left: 4px #413659 solid;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
@@ -328,7 +329,7 @@ ul.sections li {
.fix-left-menu ul.sections li a span,
.fix-left-menu ul.sections li a i {
color: #d9d9d9;
color: #8d869b;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
@@ -342,12 +343,11 @@ ul.sections li a {
margin: 0 0 0 -4px;
text-align: center;
text-decoration: none;
border-bottom: 1px solid #343434;
border-bottom: 1px solid #2E2246;
}
ul.sections li a i {
font-size: 30px;
opacity: 0.4;
}
ul.sections li a span {
@@ -357,8 +357,16 @@ ul.sections li a span {
opacity: 0.4;
}
ul.sections li.current {
background-color: #2E2246;
}
ul.sections li.current a i {
color: #ffffff;
}
ul.sections li.current, ul.sections li:hover {
border-left: 4px #f57020 solid;
border-left: 4px #00AEA2 solid;
}
.fix-left-menu:hover ul.sections li a span,
@@ -533,7 +541,7 @@ h4.panel-title {
.field-title {
float: left;
margin-right: 10px;
font-size: 11px;
font-size: 12px;
color: #d9d9d9;
}
@@ -651,7 +659,7 @@ h4.panel-title {
padding: 0;
margin-top: -9px;
margin-right: 1px;
font-size: 11px;
font-size: 12px;
color: #d9d9d9;
text-align: right;
background-color: transparent;
@@ -684,7 +692,7 @@ h4.panel-title {
}
.canvasdesigner select {
font-size: 11px;
font-size: 12px;
}
.canvasdesigner .sp-dd {
@@ -794,7 +802,7 @@ h4.panel-title {
background-color: #ffffff;
border-radius: 10px;
opacity: 1.0;
box-shadow: 0 0 0 29px #ECECEC, 0 0 0 30px #C4C4C4;
box-shadow: 0 0 0 29px #E9E9EB, 0 0 0 30px #D8D7D9;
transition: all 0.5s ease-in-out;
}

View File

@@ -1,10 +1,46 @@
.umb-button-group__toggle {
padding-left: 8px;
padding-right: 8px;
margin-left: -1px;
}
.umb-button-group__sub-buttons.-align-right {
right: 0;
left: auto;
}
.umb-button-group {
.umb-button__button {
border-radius: 3px 0px 0px 3px;
}
.umb-button-group__toggle {
border-radius: 0px 3px 3px 0;
}
}
// hack for umb-era-button
.umb-era-button-group {
display: flex;
.umb-era-button:first-child {
padding-right: 15px;
border-radius: 3px 0 0 3px;
}
.umb-era-button.umb-button-group__toggle {
padding-right: 10px;
padding-left: 10px;
border-radius: 0 3px 3px 0;
border-left-style: solid;
border-left-width: 1px;
border-left-color: rgba(0,0,0,0.1);
}
.umb-era-button.umb-button-group__toggle .caret {
margin: 0;
}
}

View File

@@ -1,16 +1,21 @@
.umb-button {
position: relative;
overflow: hidden;
display: inline;
}
.umb-button__button:focus {
outline: none;
}
.umb-button__button {
position: relative;
z-index: 1;
}
.umb-button__content {
opacity: 1;
transition: opacity 0.25s ease;
display: flex;
flex-wrap: wrap;
}

View File

@@ -0,0 +1,113 @@
.umb-era-button {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
height: 38px;
line-height: 1;
max-width: 100%;
padding: 0 18px;
color: #202129;
background-color: #edeeee;
text-decoration: none !important;
user-select: none;
white-space: nowrap;
overflow: hidden;
border-radius: 3px;
border: 0 none;
box-sizing: border-box;
cursor: pointer;
transition: background-color 80ms ease, color 80ms ease;
font-weight: bold;
}
.umb-era-button:hover,
.umb-era-button:active {
color: #484848;
background-color: #e1e2e2;
outline: none;
text-decoration: none;
}
.umb-era-button:focus {
outline: none;
}
.umb-era-button.-blue {
background: @blue;
color: white;
}
.umb-era-button.-blue:hover {
background-color: @blueDark;
}
.umb-era-button.-red {
background: @btnDangerBackground;
color: white;
}
.umb-era-button.-red:hover {
background-color: darken(@btnDangerBackground, 5%);
}
.umb-era-button.-green {
background: @green;
color: @white;
}
.umb-era-button.-green:hover {
background-color: @green-d1;
}
.umb-era-button.-link {
padding: 0;
background: transparent;
}
.umb-era-button.-link:hover {
background-color: transparent;
opacity: .6;
}
.umb-era-button.-inactive {
cursor: not-allowed;
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-inactive:hover {
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-full-width {
display: block;
width: 100%;
}
.umb-era-button.umb-button--s {
height: 30px;
font-size: 13px;
}
.umb-era-button.-white {
background-color: @white;
&:hover {
opacity: .9;
}
}
.umb-era-button.-text-black {
color: @black;
}
/* icons */
.umb-era-button i {
margin-right: 5px;
}

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