diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/TestDrivenDevFlow.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Test-Driven-DevFlow.ngdoc similarity index 97% rename from src/Umbraco.Web.UI.Client/docs/src/tutorials/TestDrivenDevFlow.ngdoc rename to src/Umbraco.Web.UI.Client/docs/src/tutorials/Test-Driven-DevFlow.ngdoc index f49dda87bb..a59f52bbf5 100644 --- a/src/Umbraco.Web.UI.Client/docs/src/tutorials/TestDrivenDevFlow.ngdoc +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Test-Driven-DevFlow.ngdoc @@ -1,84 +1,84 @@ -@ngdoc overview -@name Test-driven developement flow -@description - -##Overview - -_This document tries to outline what is required to have a test-driven setup for -angular developement in Umbraco 7. It goes through the setup process as well as how -to add new services that requires mocking as well as how to use grunt to run tests automaticly._ - -##Setup -Make sure to have all the node dependencies in order when you start, these are updated regularly in case we need to go to a new version of a dependency, or new dependencies are added. - -Simply run open a terminal / cmd in the Umbraco.Web.Ui.Client folder and run: - - npm install - -This should setup the entire grunt,karma and jsint setup we use for tests and pruning. - -##Automated testing -To start working on the client files, and have them automaticly built and merged into the client project, as well as the VS project, simply run the command - - grunt dev - -This will start a webserver on :8080 and tell karma to run tests every time a .js or .less file is changed. -After linting and tests have passed, all the client files are copied to umrbaco.web.ui/umbraco folder, so it also keeps the server project uptodate on any client changes. This should all happen in the background. - -##Adding a new service -The process for adding or modifying a service should always be based on passed tests. So if we need to change the footprint of the contentservice, and the way any controller calls this service, we need to make sure the tests passes with our mocked services. - -This ensures 3 things: -- we test our controllers -- we test our services -- we always have mocked data available, if you want to run the client without IIS - - -###Example: -We add a service for fetching macros from the database, the initial implementation should happen of this service should happen in `/src/common/resources/macro.resource.js` - -The macro.resource.js calls `$http` as normal, but no server implementation should be needed at this point. - -Next, we describe how the rest service should return data, this is done in /common/mocks/umbraco.httpbackend.js, where we can define what data a certain url -would return. - -So in the case of getting tree items we define: - - $httpBackend - .whenGET( urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetApplicationTrees') ) - .respond(returnApplicationTrees); - -The `returnApplicationTrees` function then looks like this: - - function returnApplicationTrees(status, data, headers){ - var app = getParameterByName(data, "application"); - var tree = _backendData.tree.getApplication(app); - return [200, tree, null]; - } - -It returns an array of 3 items, the http status code, the expected data, and finally it can return a collection of http headers. - - _backendData.tree.getApplication(app); - -Refers to a helper method in `umbraco.httpbackend.helper.js` which contains all the helper methods we -use to return static json. - -###In short -So to add a service, which requires data from the server we should: - -- add the .service.js as normal -- add the .resource.js as normal -- call $http as normal -- define the response data in umbraco.httpbackend.helper.js -- define the url in umbraco.httpbackend.js - -###ServerVariables -There is a static servervariables file in /mocks which describes the urls used by the rest service, this is currently needed as we dont have this set as a angular service, and no real conventions for these urls yet. Longer-term it would be great to have a urlBuilder which could do - - urlService.url("contentTypes", "GetAllowedChildren"); - //would return ///contentyTypes/getAllowedChildren - -But for now, they are set in the servervariables file. - - - +@ngdoc overview +@name Test-driven developement flow +@description + +##Overview + +_This document tries to outline what is required to have a test-driven setup for +angular developement in Umbraco 7. It goes through the setup process as well as how +to add new services that requires mocking as well as how to use grunt to run tests automaticly._ + +##Setup +Make sure to have all the node dependencies in order when you start, these are updated regularly in case we need to go to a new version of a dependency, or new dependencies are added. + +Simply run open a terminal / cmd in the Umbraco.Web.Ui.Client folder and run: + + npm install + +This should setup the entire grunt,karma and jsint setup we use for tests and pruning. + +##Automated testing +To start working on the client files, and have them automaticly built and merged into the client project, as well as the VS project, simply run the command + + grunt dev + +This will start a webserver on :8080 and tell karma to run tests every time a .js or .less file is changed. +After linting and tests have passed, all the client files are copied to umrbaco.web.ui/umbraco folder, so it also keeps the server project uptodate on any client changes. This should all happen in the background. + +##Adding a new service +The process for adding or modifying a service should always be based on passed tests. So if we need to change the footprint of the contentservice, and the way any controller calls this service, we need to make sure the tests passes with our mocked services. + +This ensures 3 things: +- we test our controllers +- we test our services +- we always have mocked data available, if you want to run the client without IIS + + +###Example: +We add a service for fetching macros from the database, the initial implementation should happen of this service should happen in `/src/common/resources/macro.resource.js` + +The macro.resource.js calls `$http` as normal, but no server implementation should be needed at this point. + +Next, we describe how the rest service should return data, this is done in /common/mocks/umbraco.httpbackend.js, where we can define what data a certain url +would return. + +So in the case of getting tree items we define: + + $httpBackend + .whenGET( urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetApplicationTrees') ) + .respond(returnApplicationTrees); + +The `returnApplicationTrees` function then looks like this: + + function returnApplicationTrees(status, data, headers){ + var app = getParameterByName(data, "application"); + var tree = _backendData.tree.getApplication(app); + return [200, tree, null]; + } + +It returns an array of 3 items, the http status code, the expected data, and finally it can return a collection of http headers. + + _backendData.tree.getApplication(app); + +Refers to a helper method in `umbraco.httpbackend.helper.js` which contains all the helper methods we +use to return static json. + +###In short +So to add a service, which requires data from the server we should: + +- add the .service.js as normal +- add the .resource.js as normal +- call $http as normal +- define the response data in umbraco.httpbackend.helper.js +- define the url in umbraco.httpbackend.js + +###ServerVariables +There is a static servervariables file in /mocks which describes the urls used by the rest service, this is currently needed as we dont have this set as a angular service, and no real conventions for these urls yet. Longer-term it would be great to have a urlBuilder which could do + + urlService.url("contentTypes", "GetAllowedChildren"); + //would return ///contentyTypes/getAllowedChildren + +But for now, they are set in the servervariables file. + + + diff --git a/src/Umbraco.Web.UI.Client/gruntFile.js b/src/Umbraco.Web.UI.Client/gruntFile.js index e472fe30d0..269b4545f7 100644 --- a/src/Umbraco.Web.UI.Client/gruntFile.js +++ b/src/Umbraco.Web.UI.Client/gruntFile.js @@ -5,9 +5,10 @@ module.exports = function (grunt) { grunt.registerTask('dev', ['jshint:dev', 'build', 'webserver', 'open:dev', 'watch']); //run by the watch task - grunt.registerTask('watch-js', ['jshint:dev','concat','copy:app','copy:mocks','copy:app','karma:unit', 'copy:vs']); + grunt.registerTask('watch-js', ['jshint:dev','concat','copy:app','copy:mocks','copy:packages','karma:unit', 'copy:vs']); grunt.registerTask('watch-less', ['recess:build','copy:assets','copy:vs']); grunt.registerTask('watch-html', ['copy:views', 'copy:vs']); + grunt.registerTask('watch-packages', ['copy:packages']); grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']); //triggered from grunt dev or grunt @@ -226,6 +227,11 @@ module.exports = function (grunt) { html: { files: ['src/views/**/*.html', 'src/*.html'], tasks:['watch-html','timestamp'] + }, + + packages: { + files: 'src/packages/**/*.*', + tasks: ['watch-packages', 'timestamp'], } }, diff --git a/src/Umbraco.Web.UI.Client/src/app.dev.js b/src/Umbraco.Web.UI.Client/src/app.dev.js index 4e164751bf..3cd4ef1df6 100644 --- a/src/Umbraco.Web.UI.Client/src/app.dev.js +++ b/src/Umbraco.Web.UI.Client/src/app.dev.js @@ -9,7 +9,6 @@ var app = angular.module('umbraco', [ 'ui.sortable' ]); - /* For Angular 1.2: we need to load in Route, animate and touch seperately 'ngRoute', 'ngAnimate', diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index d72a5c74be..5e6d5c2ea4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -21,7 +21,7 @@ angular.module('umbraco.services') //TODO: would be nicer to set all of the options here first instead of implicitly below! var ui = {}; - + function setMode(mode) { switch (mode) { case 'tree': @@ -30,7 +30,7 @@ angular.module('umbraco.services') ui.showContextMenuDialog = false; ui.stickyNavigation = false; - $("#search-form input").focus(); + $("#search-form input").focus(); break; case 'menu': ui.showNavigation = true; @@ -145,7 +145,7 @@ angular.module('umbraco.services') if(!service.active){ service.hideTree(); } - }, 700); + }, 300); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/less/grid.less b/src/Umbraco.Web.UI.Client/src/less/grid.less index 1aac0435b8..513a0be73e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/grid.less @@ -5,7 +5,6 @@ html, body { height: 100%; - overflow: hidden; } body { @@ -101,7 +100,6 @@ body { left: 0px; right: 0px; padding-top: 100px; - background: green; } @@ -115,10 +113,15 @@ body { } #tree { - padding: 15px 0px 20px 0px; + padding: 0px; position: relative; z-index: 100 !important; height: 100%; + overflow: auto; +} + +#tree .umb-tree { + padding: 15px 0px 20px 0px; } #search-results { diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index 68e3642360..8bf7448e8f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -73,8 +73,8 @@ ul.sections li i { height: 30px } -ul.sections li.current i { - color: @blue; +ul.sections li.current { + border-left: 4px @orange solid; } ul.sections li i.traycontent { diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less index 747238f94d..7fae143b47 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/tree.less @@ -6,6 +6,7 @@ min-width: 100%; width: auto; } + .umb-tree li { display: block; min-width: 100%; diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 74a84a3351..9a04612402 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -89,8 +89,10 @@ @btnBackgroundHighlight: #f2f2f2; @btnBorder: #ccc; -@btnPrimaryBackground: #297aff; -@btnPrimaryBackgroundHighlight: #297aff; +//@btnPrimaryBackground: #297aff; +//@btnPrimaryBackgroundHighlight: #297aff; +@btnPrimaryBackground: #53a93f; +@btnPrimaryBackgroundHighlight: #53a93f; @btnInfoBackground: #5bc0de; @btnInfoBackgroundHighlight: #2f96b4; diff --git a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.controller.js index 6b90b712b0..f4bdf66e7b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.controller.js @@ -5,6 +5,11 @@ function ($scope,assetsService) { //tell the assets service to load the markdown.editor libs from the markdown editors //plugin folder + + if($scope.model.value === null || $scope.model.value === ""){ + $scope.model.value = $scope.model.config.defaultValue; + } + assetsService .load([ "/app_plugins/markdowneditor/lib/markdown.converter.js", diff --git a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.html b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.html index 4a2ae9d1e8..4321b46a56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.html +++ b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/markdowneditor.html @@ -2,5 +2,6 @@
-
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/package.manifest b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/package.manifest index df4a8a2da9..dc3c7bb77b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/package.manifest +++ b/src/Umbraco.Web.UI.Client/src/packages/MarkdownEditor/package.manifest @@ -5,7 +5,23 @@ name: "Markdown editor", editor: { view: "~/App_Plugins/MarkDownEditor/markdowneditor.html" - } + }, + preValueEditor: { + fields: [ + { + label: "Preview", + description: "Display a live preview", + key: "preview", + view: "boolean" + }, + { + label: "Default value", + description: "If value is blank, the editor will show this", + key: "defaultValue", + view: "textarea" + } + ] + } } ] , diff --git a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.html b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.html index 0ab89bc058..5c091db78c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.html +++ b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.html @@ -1,18 +1,17 @@ -
+ \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.js b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.js index b345d6f9e0..5fd88a98b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.js +++ b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/mntp/mntp.js @@ -3,40 +3,56 @@ angular.module('umbraco') .controller("uComponents.Editors.MNTPController", - function($scope, dialogService, entityResource){ - $scope.ids = $scope.model.value.split(','); - $scope.renderModel = []; - - entityResource.getByIds($scope.ids).then(function(data){ - $(data).each(function(i, item){ - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + function($scope, dialogService, entityResource, $log, iconHelper){ + $scope.ids = $scope.model.value.split(','); + $scope.renderModel = []; + $scope.multipicker = $scope.model.config.multi === "True"; + + entityResource.getByIds($scope.ids).then(function(data){ + $(data).each(function(i, item){ + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + }); }); - }); - $scope.openContentPicker =function(){ - var d = dialogService.contentPicker({scope: $scope, callback: populate}); - }; + $scope.openContentPicker =function(){ + var d = dialogService.contentPicker({scope: $scope, multipicker: $scope.multipicker, callback: populate}); + }; - $scope.remove =function(index){ - $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = $scope.ids.join(); - }; + $scope.remove =function(index){ + $scope.renderModel.splice(index, 1); + $scope.ids.splice(index, 1); + $scope.model.value = trim($scope.ids.join(), ","); + }; - $scope.add =function(item){ + $scope.add =function(item){ + if($scope.ids.indexOf(item.id) < 0){ + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + $scope.ids.push(item.id); + $scope.model.value = trim($scope.ids.join(), ","); + } + }; - if($scope.ids.indexOf(item.id) < 0){ - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}) - $scope.ids.push(item.id); + $scope.clear = function(){ + $scope.ids = []; + $scope.model.value = ""; + $scope.renderModel = []; + } - $scope.model.value = $scope.ids.join(); - } - }; + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); + return str.replace(rgxtrim, ''); + } - - function populate(data){ - $(data.selection).each(function(i, item){ - $scope.add(item); - }); - } -}); \ No newline at end of file + function populate(data){ + if(data.selection && angular.isArray(data.selection)){ + $(data.selection).each(function(i, item){ + $scope.add(item); + }); + }else{ + $scope.clear(); + $scope.add(data); + } + } + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/package.manifest b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/package.manifest index c7544dc3f4..379c3b82ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/package.manifest +++ b/src/Umbraco.Web.UI.Client/src/packages/propertyeditors/package.manifest @@ -5,7 +5,17 @@ name: "Multinode treepicker", editor: { view: "~/App_Plugins/propertyeditors/mntp/mntp.html" - } + }, + + preValueEditor: { + fields: [ + { + label: "Multipicker", + key: "multi", + view: "boolean" + } + ] + } }, { id: "71b8ad1a-8dc2-425c-b6b8-faa158075e63", diff --git a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js index 38d23aa8af..16f86f7eb5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js @@ -62,13 +62,14 @@ function MainController($scope, $routeParams, $rootScope, $timeout, $http, $log, $scope.user = data.user; - var url = "http://da.gravatar.com/" + $scope.user.emailHash + ".json"; +/* + var url = "http://www.gravatar.com/avatar/" + $scope.user.emailHash + ".json?404=404"; $http.jsonp(url).then(function(response){ - $log.log(response); + $log.log("found: " + response); }, function(data){ $log.log(data); }); - +*/ /* if($scope.user.avatar){ diff --git a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js index 80f51e7604..aa6d58911b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js @@ -9,12 +9,16 @@ * * @param {navigationService} navigationService A reference to the navigationService */ -function NavigationController($scope,$rootScope, $location, $log, navigationService, keyboardService, dialogService, historyService, sectionResource, angularHelper) { +function NavigationController($scope,$rootScope, $location, $log, $routeParams, navigationService, keyboardService, dialogService, historyService, sectionResource, angularHelper) { //Put the navigation service on this scope so we can use it's methods/properties in the view. // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one. $scope.nav = navigationService; + $scope.routeParams = $routeParams; + $scope.$watch("routeParams.section", function (newVal, oldVal) { + $scope.currentSection = newVal; + }); //trigger search with a hotkey: keyboardService.bind("ctrl+shift+s", function(){ diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html index de2bf70687..aaae18c74f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html @@ -8,7 +8,7 @@ -
  • +
  • - Help + {{currentSection}}
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html new file mode 100644 index 0000000000..c709cfe232 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html @@ -0,0 +1,3 @@ + + +{{model | json}} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html new file mode 100644 index 0000000000..e6c0177924 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js index 174e3816bf..4f75eca119 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js @@ -1,5 +1,4 @@ function booleanEditorController($scope, $rootScope, assetsService) { - $scope.renderModel = { value: false }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 14deda74c2..c07baa10f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,5 +1,5 @@ -
    -