Upgrade completed!
"; } + //now that everything is done, we need to determine the version of SQL server that is executing + LogHelper.Info"+f+"");d.html("");d.append(a.contents());return d}var s={},w={value:{}},K={"angular.js":"http://code.angularjs.org/"+t.version.full+"/angular.min.js","angular-resource.js":"http://code.angularjs.org/"+t.version.full+"/angular-resource.min.js","angular-sanitize.js":"http://code.angularjs.org/"+t.version.full+"/angular-sanitize.min.js", +"angular-cookies.js":"http://code.angularjs.org/"+t.version.full+"/angular-cookies.min.js"};s.jsFiddle=function(d,f,a){return{terminal:!0,link:function(o,c,l){function b(a,c){return''}var r={html:"",css:"",js:""};t.forEach(l.jsFiddle.split(" "),function(a,c){var b=a.split(".")[1];r[b]+=b=="html"?c==0?"
+
+Umbraco 7 UI, codename "Belle" Built on AngularJS, YepNope and Twitter Bootstrap
+ +Slides from the initial demonstration of Belle done at the Umbraco DK Fest can be found here:
+ +http://rawgithub.com/umbraco/Belle/master/Presentation/index.html
+ +This won't require any database or setup, as everything is running through node. All you have to do is install +node and grunt on either windows or OSX and the entire setup is ready for you.
+ +We need node to run tests and automated less compiling and other automated tasks. go to http://nodejs.org. Node.js is a powerfull javascript engine, which allows us to run all our tests and tasks written in javascript locally.
+ +note: On windows you might need to restart explorer.exe to register node.
+ +Next we need to install all the required packages. This is done with the package tool, included with node.js, open /Umbraco.Belle.Client in cmd.exe or osx terminal and run the command:
+ +npm install
+
+
+this will fetch all needed packages to your local machine.
+ +Grunt is a task runner for node.js, and we use it for all automated tasks in the build process. For convenience we need to install it globally on your machine, so it can be used directly in cmd.exe or the terminal.
+ +So run the command:
+ +npm install grunt-cli -g
+
+
+note: On windows you might need to restart explorer.exe to register the grunt cmd.
+ +note: On OSX you might need to run:
+ +sudo npm install grunt-cli -g
+
+
+Now that you have node and grunt installed, you can open /Umbraco.Belle.Client in either cmd.exe or terminal and run:
grunt dev
+
+
+This will build the site, merge less files, run tests and create the /Build folder, and finally open the site in your +browser.
+ +The current app is built, following conventions from angularJs and bootstrap. To get started with the applicaton you will need to atleast know the basics of these frameworks
+ +autoScale
+(directive in module umbraco.directives
+)
+Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set +So if you only want to scale the div to 70 pixels from the bottom you pass "70"
<div auto-scale> + ... +</div>+as class
<div class="auto-scale"> + ... +</div>+
headline
+(directive in module umbraco.directives
+)
+<ANY headline> + ... +</ANY>+as class
<ANY class="headline"> + ... +</ANY>+
login
+(directive in module umbraco.directives
+)
+<login> +</login>+
preventDefault
+(directive in module umbraco.directives
+)
+<ANY prevent-default> + ... +</ANY>+as class
<ANY class="prevent-default"> + ... +</ANY>+
umbAvatar
+(directive in module umbraco.directives
+)
+<umb-avatar> +</umb-avatar>+
umbConfirm
+(directive in module umbraco.directives
+)
+A confirmation dialog
<umb-confirm> +</umb-confirm>+
umbContentName
+(directive in module umbraco.directives
+)
+Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
<umb-content-name> +</umb-content-name>+
umbEditor
+(directive in module umbraco.directives
+)
+<umb-editor> +</umb-editor>+
umbFileUpload
+(directive in module umbraco.directives
+)
+<umb-file-upload> +</umb-file-upload>+as attribute
<ANY umb-file-upload> + ... +</ANY>+
umbNavigation
+(directive in module umbraco.directives
+)
+<umb-navigation> +</umb-navigation>+
umbNotifications
+(directive in module umbraco.directives
+)
+<ANY umb-notifications> + ... +</ANY>+as class
<ANY class="umb-notifications"> + ... +</ANY>+
umbPanel
+(directive in module umbraco.directives
+)
+<umb-panel> +</umb-panel>+
umbProperty
+(directive in module umbraco.directives
+)
+<umb-property> +</umb-property>+
umbTab
+(directive in module umbraco.directives
+)
+<umb-tab> +</umb-tab>+
umbTabView
+(directive in module umbraco.directives
+)
+<umb-tab-view> +</umb-tab-view>+
umbTree
+(directive in module umbraco.directives
+)
+<umb-tree> +</umb-tree>+
umbTreeItem
+(directive in module umbraco.directives
+)
+Renders a list item, representing a single node in the tree. +Includes element to toggle children, and a menu toggling button
+ +note: This directive is only used internally in the umbTree directive
<li umb-tree-item> + ... +</li>+as class
<li class="umb-tree-item"> + ... +</li>+
valPropertyMsg
+(directive in module umbraco.directives
+)
+This directive is used to control the display of the property level validation message. +We will listen for server side validation changes +and when an error is detected for this property we'll show the error message
formController
+<textarea val-property-msg> + ... +</textarea>+
valRegex
+(directive in module umbraco.directives
+)
+A custom directive to allow for matching a value against a regex string. +NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string
<ANY val-regex> + ... +</ANY>+
valServer
+(directive in module umbraco.directives
+)
+This directive is used to associate a content property with a server-side validation response +so that the validators in angular are updated based on server-side feedback.
<ANY val-server> + ... +</ANY>+
valServerField
+(directive in module umbraco.directives
+)
+This directive is used to associate a content field (not user defined) with a server-side validation response +so that the validators in angular are updated based on server-side feedback.
<ANY val-server-field> + ... +</ANY>+
valShowValidation
+(directive in module umbraco.directives
+)
+Used to toggle the show-validation class on the element containing the form elements to validate. +This is used because we don't want to show validation messages until after the form is submitted and then reset +the process when the form is successful. We do this by listening to the current controller's saving and saved events.
<ANY val-show-validation> + ... +</ANY>+
valTab
+(directive in module umbraco.directives
+)
+Used to show validation warnings for a tab to indicate that the tab content has validations errors in its data.
<ANY val-tab> + ... +</ANY>+
valToggleMsg
+(directive in module umbraco.directives
+)
+This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ?
formController
+<input val-toggle-msg> + ... +</input>+
sectionMocks
+(service in module umbraco.mocks
+)
+Mocks data retrival for the sections
authResource
+(service in module umbraco.resources
+)
+Loads in data for authentication
contentResource
+(service in module umbraco.resources
+)
+Loads/saves in data for content
contentTypeResource
+(service in module umbraco.resources
+)
+Loads in data for content types
legacyResource
+(service in module umbraco.resources
+)
+Handles legacy dialog requests
mediaResource
+(service in module umbraco.resources
+)
+Loads in data for media
mediaTypeResource
+(service in module umbraco.resources
+)
+Loads in data for media types
sectionResource
+(service in module umbraco.resources
+)
+Loads in data for section
treeResource
+(service in module umbraco.resources
+)
+Loads in data for trees
angularHelper
+(service in module umbraco.services
+)
+Some angular helper/extension methods
Returns the current form object applied to the scope or null if one is not found
Returns a null angular FormController, mostly for use in unit tests +NOTE: This is actually the same construct as angular uses internally for creating a null form but they don't expose + any of this publicly to us, so we need to create our own.
formName {string}
+The form name to assign
This checks if a digest/apply is already occuring, if not it will force an apply call
In some situations we need to return a promise as a rejection, normally based on invalid data. This +is a wrapper to do that so we can save one writing a bit of code.
objReject {object}
+The object to send back with the promise rejection
This will validate that the current scope has an assigned form object, if it doesn't an exception is thrown, if +it does we return the form object.
assetsService
+(service in module umbraco.services
+)
+Promise-based utillity service to lazy-load client-side dependencies inside angular controllers.
+ +To use, simply inject the assetsService into any controller that needs it, and make +sure the umbraco.services module is accesible - which it should be by default.
+ +
+ angular.module("umbraco").controller("my.controller". function(assetsService){
+ assetsService.load(["script.js", "styles.css"], $scope).then(function(){
+ //this code executes when the dependencies are done loading
+ });
+ });
+
+
+You can also load individual files, which gives you greater control over what attibutes are passed to the file, as well as timeout
+ +
+ angular.module("umbraco").controller("my.controller". function(assetsService){
+ assetsService.loadJs("script.js", $scope, {charset: 'utf-8'}, 10000 }).then(function(){
+ //this code executes when the script is done loading
+ });
+ });
+
+
+For these cases, there are 2 individual methods, one for javascript, and one for stylesheets:
+ +
+ angular.module("umbraco").controller("my.controller". function(assetsService){
+ assetsService.loadCss("stye.css", $scope, {media: 'print'}, 10000 }).then(function(){
+ //loadcss cannot determine when the css is done loading, so this will trigger instantly
+ });
+ });
+ $q
+angularHelper
+Injects a collection of files, this can be a mixed collection of css and js files, the loader will determine how to load them
+ +Warning: if the collection of files contains a .css file, you will in some cases not receive a resolved promise, it is therefore prefered to use the individual loadCss and loadJs methods
pathArray {Array}
+string array of paths to the files to load
scope {Scope}
+optional scope to pass into the loader
{Promise}
+– Promise object which resolves when all the files has loaded
Injects a file as a stylesheet into the document head
path {String}
+path to the css file to load
scope {Scope}
+optional scope to pass into the loader
keyvalue {Object}
+collection of attributes to pass to the stylesheet element
timeout {Number}
+in milliseconds
{Promise}
+– Promise object which resolves when the file has loaded
Injects a file as a javascript into the document
path {String}
+path to the js file to load
scope {Scope}
+optional scope to pass into the loader
keyvalue {Object}
+collection of attributes to pass to the script element
timeout {Number}
+in milliseconds
{Promise}
+– Promise object which resolves when the file has loaded
contentEditingHelper
+(service in module umbraco.services
+)
+A helper service for content controllers when editing/creating/saving content.
Returns all propertes contained for the content item (since the normal model has properties contained inside of tabs)
A function to handle what happens when we have validation issues from the server side
A function to handle when saving a content item is successful. This will rebind the values of the model that have changed +ensure the notifications are displayed and that the appropriate events are fired. This will also check if we need to redirect +when we're creating new content.
A function to handle the validation (modelState) errors collection which will happen on a 403 error indicating validation errors +It's worth noting that when a 403 occurs, the data is still saved just never published, though this depends on if the entity is a new +entity and whether or not the data fulfils the absolute basic requirements like having a mandatory Name.
re-binds all changed property values to the origContent object from the newContent object and returns an array of changed properties.
Changes the location to be editing the newly created content after create was successful. +We need to decide if we need to redirect to edito mode or if we will remain in create mode. +We will only need to maintain create mode if we have not fulfilled the basic requirements for creating an entity which is at least having a name.
dialogService
+(service in module umbraco.services
+)
+Application-wide service for handling modals, overlays and dialogs +By default it injects the passed template url into a div to body of the document +And renders it, but does also support rendering items in an iframe, incase +serverside processing is needed, or its a non-angular page
+ +To use, simply inject the dialogService into any controller that needs it, and make +sure the umbraco.services module is accesible - which it should be by default.
+ +
+ var dialog = dialogService.open({template: 'path/to/page.html', show: true, callback: done});
+ functon done(data){
+ //The dialog has been submitted
+ // data contains whatever the dialog has selected / attached
+ }
+ $rootScope
+$compile
+$http
+$log
+$q
+$templateCache
+Closes a specific dialog
dialog {Object}
+the dialog object to close
Closes all dialogs
Opens a content picker tree in a modal, the callback returns an array of selected documents
options {Object}
+content picker dialog options object
options.scope {$scope}
+dialog scope
options.callback {Function}
+callback function
{Object}
+– modal object
Opens a mcaro picker in a modal, the callback returns a object representing the macro and it's parameters
options {Object}
+mediapicker dialog options object
options.scope {$scope}
+dialog scope
options.callback {Function}
+callback function
{Object}
+– modal object
Opens a media picker in a modal, the callback returns an array of selected media items
options {Object}
+mediapicker dialog options object
options.scope {$scope}
+dialog scope
options.callback {Function}
+callback function
{Object}
+– modal object
Opens a modal rendering a given template url.
options {Object}
+rendering options
options.container {DomElement}
+the DOM element to inject the modal into, by default set to body
options.callback {Function}
+function called when the modal is submitted
options.template {String}
+the url of the template
options.animation {String}
+animation csss class, by default set to "fade"
options.modalClass {String}
+modal css class, by default "umb-modal"
options.show {Bool}
+show the modal instantly
options.scope {Object}
+scope to attach the modal to, by default rootScope.new()
options.iframe {Bool}
+load template in an iframe, only needed for serverside templates
options.width {Int}
+set a width on the modal, only needed for iframes
options.inline {Bool}
+strips the modal from any animation and wrappers, used when you want to inject a dialog into an existing container
{Object}
+– modal object
Opens a dialog with a chosen property editor in, a value can be passed to the modal, and this value is returned in the callback
options {Object}
+mediapicker dialog options object
options.scope {$scope}
+dialog scope
options.callback {Function}
+callback function
editor {String}
+editor to use to edit a given value and return on callback
value {Object}
+value sent to the property editor
{Object}
+– modal object
Opens a dialog to show a custom YSOD
historyService
+(service in module umbraco.services
+)
+Service to handle the main application navigation history. Responsible for keeping track +of where a user navigates to, stores an icon, url and name in a collection, to make it easy +for the user to go back to a previous editor / action
+ +Note: only works with new angular-based editors, not legacy ones
+ +To use, simply inject the historyService into any controller that needs it, and make +sure the umbraco.services module is accesible - which it should be by default.
+ +
+ angular.module("umbraco").controller("my.controller". function(historyService){
+ historyService.add({
+ icon: "icon-class",
+ name: "Editing 'articles',
+ link: "/content/edit/1234"}
+ );
+ });
+ $rootScope
+$timeout
+angularHelper
+Adds a given history item to the users history collection.
item {Object}
+the history item
item.icon {String}
+icon css class for the list, ex: "icon-image", "icon-doc"
item.link {String}
+route to the editor, ex: "/content/edit/1234"
item.name {String}
+friendly name for the history listing
{Object}
+– history item object
Method to return the current history collection.
Removes a history item from the users history collection, given an index to remove from.
index {Int}
+index to remove item from
Removes all history items from the users history collection
{Array}
+– Array of history entries for the current user, newest items first
iconHelper
+(service in module umbraco.services
+)
+A helper service for dealing with icons, mostly dealing with legacy tree icons
legacyJsLoader
+(service in module umbraco.services
+)
+Used to lazy load in any JS dependencies that need to be manually loaded in
legacyJsLoader();+
navigationService
+(service in module umbraco.services
+)
+Service to handle the main application navigation. Responsible for invoking the tree +Section navigation and search, and maintain their state for the entire application lifetime
$rootScope
+$routeParams
+$log
+$location
+dialogService
+treeService
+sectionResource
+Changes the active section to a given section alias +If the navigation is 'sticky' this will load the associated tree +and load the dashboard related to the section
sectionAlias {string}
+The alias of the section
hides the currently open dialog
hides the search pane
Hides the tree by hiding the containing dom element
Shows the legacy iframe and loads in the content based on the source url
source {String}
+The URL to load into the iframe
Opens a dialog, for a given action on a given tree node +uses the dialogService to inject the selected action dialog +into #dialog div.umb-panel-body +the path to the dialog view is determined by: +"views/" + current tree + "/" + action alias + ".html" +The dialog controller will get passed a scope object that is created here. This scope +object may be injected as part of the args object, if one is not found then a new scope +is created. Regardless of whether a scope is created or re-used, a few properties and methods +will be added to it so that they can be used in any dialog controller: + scope.currentNode = the selected tree node + scope.currentAction = the selected menu item
args {Object}
+arguments passed to the function
args.scope {Scope}
+current scope passed to the dialog
args.action {Object}
+the clicked action containing name and alias
shows the search pane
Shows the tree for a given tree alias but turning on the containing dom element +only changes if the section is different from the current one
sectionAlias {string}
+The alias of the section the tree should load data from
Opens the user dialog, next to the sections navigation +template is located in views/common/dialogs/user.html
notificationsService
+(service in module umbraco.services
+)
+Application-wide service for handling notifications, the umbraco application +maintains a single collection of notications, which the UI watches for changes. +By default when a notication is added, it is automaticly removed 7 seconds after +This can be changed on add()
+ +To use, simply inject the notificationsService into any controller that needs it, and make +sure the umbraco.services module is accesible - which it should be by default.
+ +
+ notificationsService.success("Document Published", "hooraaaay for you!");
+ notificationsService.error("Document Failed", "booooh");
+ $rootScope
+$timeout
+angularHelper
+Adds a red error notication to the notications collection +This should be used when an operations fails and could not complete
headline {String}
+Headline of the notification
message {String}
+longer text for the notication, trimmed after 200 characters, which can then be exanded
{Object}
+– notification object
Method to return all notifications from the notifcations collection
Removes a notification from the notifcations collection at a given index
index {Int}
+index where the notication should be removed from
Removes all notifications from the notifcations collection
Shows a notification based on the object passed in, normally used to render notifications sent back from the server
{Object}
+– args notification object
Adds a green success notication to the notications collection +This should be used when an operations completes without errors
headline {String}
+Headline of the notification
message {String}
+longer text for the notication, trimmed after 200 characters, which can then be exanded
{Object}
+– notification object
Adds a yellow warning notication to the notications collection +This should be used when an operations completes but something was not as expected
headline {String}
+Headline of the notification
message {String}
+longer text for the notication, trimmed after 200 characters, which can then be exanded
{Object}
+– notification object
Returns an array of current notifications to display
{string}
+– returns an array
serverValidationManager
+(service in module umbraco.services
+)
+Used to handle server side validation and wires up the UI with the messages. There are 2 types of validation messages, one +is for user defined properties (called Properties) and the other is for field properties which are attached to the native +model objects (not user defined). The methods below are named according to these rules: Properties vs Fields.
Adds an error message for a native content item field (not a user defined property, for Example, 'Name')
Adds an error message for the content property
Clears all errors
Gets all callbacks that has been registered using the subscribe method for the field.
Gets the error message for a content field
Gets all callbacks that has been registered using the subscribe method for the contentProperty + fieldName combo. +This will always return any callbacks registered for just the property (i.e. field name is empty) and for ones with an +explicit field name set.
Gets the error message for the content property
Checks if a content field has an error
Checks if the content property + field name combo has an error
Removes an error message for the content property
Clears all errors and notifies all callbacks that all server errros are now valid - used when submitting a form
This is primarily used for scenarios where the error collection needs to be persisted over a route change. Generally this +is when a content item (or any item) is created. The controller should call this method once the data is bound to the scope +so that any persisted validation errors are re-bound to their controls. Once they are re-binded this then clears the validation +colleciton so that if another route change occurs, the previously persisted validation errors are not re-bound to the new item.
Adds a callback method that is executed whenever validation changes for the field name + property specified. +This is generally used for server side validation in order to match up a server side validation error with +a particular field, otherwise we can only pinpoint that there is an error for a content property, not the +property's specific field. This is used with the val-server directive in which the directive specifies the +field alias to listen for. +If contentProperty is null, then this subscription is for a field property (not a user defined property).
treeService
+(service in module umbraco.services
+)
+The tree service factory, used internally by the umbTree and umbTreeItem directives
umbDataFormatter
+(service in module umbraco.services
+)
+A helper object used to format/transform JSON Umbraco data, mostly used for persisting data to the server
umbImageHelper
+(service in module umbraco.services
+)
+A helper object used for parsing image paths
umbPropertyEditorHelper
+(service in module umbraco.services
+)
+A helper object used for property editors
Returns the correct view path for a property editor, it will detect if it is a full virtual path but if not then default to the internal umbraco one
input {string}
+the view path currently stored for the property editor
umbRequestHelper
+(service in module umbraco.services
+)
+A helper object used for sending requests to the server
This will turn an array of key/value pairs into a query string
queryStrings {Array}
+An array of key/value pairs
This will return the webapi Url for the requested key based on the servervariables collection
apiName {string}
+The webapi name that is found in the servervariables["umbracoUrls"] dictionary
actionName {string}
+The webapi action name
queryStrings {object}
+Can be either a string or an array containing key/value pairs
This returns a promise with an underlying http call, it is a helper method to reduce +the amount of duplicate code needed to query http resources and automatically handle any +Http errors. See /docs/source/using-promises-resources.md
opts {object}
+A mixed object which can either be a string representing the error message to be +returned OR an object containing either: + { success: successCallback, errorMsg: errorMessage } + OR + { success: successCallback, error: errorCallback } +In both of the above, the successCallback must accept these parameters: data, status, headers, config +If using the errorCallback it must accept these parameters: data, status, headers, config +The success callback must return the data which will be resolved by the deferred object. +The error callback must return an object containing: {errorMsg: errorMessage, data: originalData }
umbracoMenuActions
+(service in module umbraco.services
+)
+Defines the methods that are called when menu items declare only an action to execute
q
+treeService
+
+
+This guide explains how to setup a simple property editor, how to hook it into Umbraco's datatypes +how to hook it into angulars modules and its injector, and finally how you can test your property editor.
+ +So all the steps we will go through:
+ +This is about how to use AngularJS with umbraco, so it does not cover AngularJS itself, as there are tons of resources on that already here:
+ + + +By the end of this guide, we will have a simple markdown editor running inside of Umbraco +registered as a data type in the backoffice, assigned to a document type, and the editor can +create and modify data.
+ +The first thing we must do is create a new folder inside /app_plugins folder. We will call it
+MarkDownEditor
Next We will create a simple manifest file to describe what this plugin does. This manifest will tell Umbraco about our new property editor and allows us to inject any needed files into the application, so we create the file /app_plugins/MarkDownEditor/package.manifest
Inside this package manifest we add a bit of json to describe the property editor, have a look at the inline comments in the json below for details on each bit:
+ +{
+ //you can define multiple editors
+ propertyEditors: [
+ {
+ //this must be a unique guid
+ id: "7e062c13-7c41-4ad9-b389-41d88aeef87c",
+ //the name
+ name: "Markdown editor",
+ //the html file we will load for the editor
+ editor: {
+ view: "~/App_Plugins/MarkDownEditor/markdowneditor.html"
+ }
+ }
+ ]
+ ,
+ //array of files we want to inject into the application on app_start
+ javascript: [
+ '~/App_Plugins/MarkDownEditor/markdowneditor.controller.js'
+ ]
+}
+
+
+Then we add 2 files to the /app_plugins/markdowneditor/ folder:
+- ´markdowneditor.html
+-markdowneditor.controller.js`
These will be our main files for the editor, with the .html file handling the view and the .js +part handling the functionality.
+ +In the .js file I will add a basic angularJS controller declaration
+ +angular.module("umbraco")
+ .controller("My.MarkdownEditorController",
+ function () {
+ alert("The controller has landed");
+ });
+
+
+And in the .html file I'll add:
+ +<div ng-controller="My.MarkdownEditorController">
+ <textarea ng-model="model.value"></textarea>
+</div>
+
+
+Now our basic parts of the editor is done namely:
+ +After the above edits are done, restart your application. Go to developer section, click the 3 dots next to the datatypes folder and create a new data type called "markdown". In the editor you can now select a property editor, where your newly added "markdown editor" will appear.
+ +Save the datatype, and add it to a document type of your choice, open a document of that type, and you will be greated with an alert message saying "The controller has landed", which means all is well, and you can now edit the assigned property's value with your editor.
+ +Lets go a bit further, and load in a markdown editor javascript library, I've chosen pagedown, but you can use whatever you want.
+ +First of, I'll add some external files to our package folder, in /app_plugins/markdowneditor/lib folder, these files comes from the pagedown editor project found here:
+ +Pagedown-bootstrap on github.com
+ +Then open the markdowneditor.controller.js file and edit it so it looks like this:
angular.module("umbraco")
+.controller("My.MarkdownEditorController",
+//inject umbracos assetsService
+function ($scope,assetsService) {
+
+ //tell the assetsService to load the markdown.editor libs from the markdown editors
+ //plugin folder
+ assetsService
+ .load([
+ "/app_plugins/markdowneditor/lib/markdown.converter.js",
+ "/app_plugins/markdowneditor/lib/markdown.sanitizer.js",
+ "/app_plugins/markdowneditor/lib/markdown.editor.js"
+ ])
+ .then(function () {
+ //this function will execute when all dependencies have loaded
+ alert("editor dependencies loaded");
+ });
+
+ //load the seperat css for the editor to avoid it blocking our js loading
+ assetsService.loadCss("/app_plugins/markdowneditor/lib/markdown.css");
+});
+
+
+This loads in our external dependency, but only when its needed by the editor.
+ +Now lets replace that alert() with some code that can instantiate the pagedown editor:
var converter2 = new Markdown.Converter();
+var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias);
+editor2.run();
+
+
+and add that id to the text area in the html, for more info on the html structure, see the pagedown demo here:
+ +<div ng-controller="My.MarkdownEditorController" class="wmd-panel">
+ <div id="wmd-button-bar-{{model.alias}}">
+
+
+ <textarea ng-model="model.value" class="wmd-input" id="wmd-input-{{model.alias}}">
+ your content
+ </textarea>
+
+ <div id="wmd-preview-{{model.alias}}" class="wmd-panel wmd-preview"></div>
+</div>
+
+
+Now, clear the cache, reload the document and see the pagedown editor running.
+ +When you save or publish the value of the editor is automaticly synced to the current content object and sent to the server, all through the power of angular and the ng-modelattribute.
The full source, including manifest and dependencies, can be found on the umbraco-cms project +here
+ +Simply copy the MarkdownEditor folder to /app_plugins and restart your website, and it will be up and running.
+
+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.
+ +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.
+ +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.
+ +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
+ +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.
So to add a service, which requires data from the server we should:
+ +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 /<umbracodir>/<apibaseDir>/contentyTypes/getAllowedChildren
+
+
+But for now, they are set in the servervariables file.
+
+This section contains longer written tutorials on how to perform certain tasks with the new angular-based UI.
+ +Our initial focus is getting up on running with their own property editors, and secondly we will focus on how to use the serverside support methods to hook into the clientside code from asp.net.
+ +It is always recommended to have a clear understanding of the underlying framework, before you start building on top of it.
+ +For our html, css and various widgets, we use twitter bootstrap 2
+ + + +For the underlying application framework we use AngularJS, there are plenty of really good ressources for learning but these are our preffered ones:
+ + + +We use various tools to built the client-side code. This is to ensure that our javascript is automatically tested, our .less files are compiled, and that files are minified and linted.
+ +For all these automated tasks, we use gruntjs which is a javascript based task-runner for node.js
+ +it is highly recommended to read and understand the basics of grunt, and have a closer look at our gruntfile.js in the source, since this ties the entire application together.
+
+This document is loosely connected notes and thoughts on naming conventions on all application components for the Umbraco 7 UI.
+ +Naming: last one wins +but to do this automaticly, we would need +to auto-register all modules needed on app-start. +This gives us a couple of complications for handling 3rd party applications. +As it would be a pain to scan every file for modules to load on app start.
+ +Proposed structure: +http://briantford.com/blog/huuuuuge-angular-apps.html
+ +Register all modules in app.js +Root module: Umbraco + - contains the core services: notifications,dialogs,etc + - contains the core resources: content,media etc + - contains the core directives
+ +1 module pr file princible
+ +1st level modules: + Umbraco (for misc system-level stuff, dialogs, notifications, etc) + Umbraco.PropertyEditors (for all editors, as they are shared between all modules) + - how would a 3rd party property editor access a 3rd party service in the Ucommerce module, when its loaded in the content editor? (which is inside the the content module?) + Umbraco.Content (registers that it needs umbraco module, for access to core services ) + Umbraco.Media + Umbraco.Settings + Ucommerce + TeaCommerce + Etc
+ +Should all (core + 3rd party) services, filters and directives just be dependencies in the global umbraco module?
+ +Each section namespace knows what modules to initially have loaded: +Umbraco.Content + - EditController + - SortController
+ +Inside the EditController module, it references the needed services: + - DialogService + - ContentFactory + - Etc
+ +So things are only loaded when needed, due to the modules, and our application, really only needs to know about the root modules, Content,Media,Settings, Ucommerce etc.
+ +If more directives have the same name, they will all be run
+ +Last one wins, so you can override core controllers +Can be namedspaced, but shouldnt, the module is the namespace? +Module: Umbraco.Section.Area.Page +Name: PageNameController
+ +Filename: /umbraco/section/area/pagename.controller.js +Ex: /umbraco/content/document/edit.controller.js +Ex: /umbraco/settings/documenttype/edit.controller.js
+ +There is a need to have an extra level in the content tree urls due to all +other sections have this as well, and we dont want to confuse the routing.
+ +The ctrl acronym is horrible and should not be used IMO.
+ +typeService ? - cant be namespaced +Module: Umbraco.Services.Type +Name: TypeService?
+ +Ex: Umbraco.Services.Notifications.NotificationsService +Filename: /umbraco/common/services/notifcations.service.js
+ +Ex: Umbraco.Resources.Content.ContentFactory? +filename: /umbraco/common/resources/content.resources.js ? or +/umbraco/common/resoures/content.factory.js ?
+
+Document to outline the way we work with data from the Umbraco rest API which is always returned as a promise, so we in an async way can resolve our data.
+ +All Umbraco resource methods utilize a helper method:
+ +umbRequestHelper.resourcePromise
+
+
+This method accepts 2 arguments:
+ +Here's an example of the usage in an Umbraco resource. This example is the method of the treeResource that fetches data to display the menu for a tree node:
+ +/** Loads in the data to display the nodes menu */
+loadMenu: function (node) {
+
+ return umbRequestHelper.resourcePromise(
+ $http.get(getTreeMenuUrl(node)),
+ "Failed to retreive data for a node's menu " + node.id);
+}
+
+
+HTTP error handling is performed automatically inside of the umbRequestHelper.resourcePromise and inside of Umbraco's response interceptors.
When consuming Umbraco resources, a normal angular promise will be returned based on the above umbRequestHelper.resourcePromise. The success callback will always receive the RAW json data from the server and the error callback will always receive an object containing these properties:
Error handling will be done automatically in the Umbraco resource. Http error handling should not be done during the consumption of an Umbraco resource.
+ +An simple example of consuming an Umbraco resource:
+ +treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ scope.menu = data;
+ });
+
+
+Sometimes the consumption of an Umbraco resource needs to return a promise itself. This is required in some circumstances such as:
+ +The data from a result of an http resource might need to be transformed into something usable in the UI so a Service may need to call a resource, transform the result and continue to return it's own promise (since everything happens async).
+ +This is actually very simple to do, the Service (or whatever is consuming the resource) just returns the result of their 'then' call. Example - this example is the getActions method of the treeService that consumes the treeResource, transforms the result and continues to return it's own promise:
getActions: function(treeItem, section) {
+
+ return treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ //need to convert the icons to new ones
+ for (var i = 0; i < data.length; i++) {
+ data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass);
+ }
+ return data;
+ });
+}
+
+
+Notice that this is just returning the call to 'then' which will return a promise that resolves the data from it's return statement.
+ +Ok, what about error handling ? This is really simple as well, we just add an additional method to the .then call. A simple example:
+ +treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ scope.menu = data;
+ }, function(err) {
+ //display the error
+ notificationsService.error(err.errorMsg);
+ });
+
+
+This is one of those things that is important to note! If you need to return a custom promise based on the result of an Umbraco resource (like the example above in Transforming result data) then you will need to 'throw' an error if you want to 'bubble' the error to the handler of your custom promise.
+ +The good news is, this is very simple to do, example:
+ +getActions: function(treeItem, section) {
+
+ return treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ //need to convert the icons to new ones
+ for (var i = 0; i < data.length; i++) {
+ data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass);
+ }
+ return data;
+ }, function(err) {
+ //display the error
+ notificationsService.error(err.errorMsg);
+
+ //since we want the handler of this promise to be notified of this error
+ // we just need to rethrow it:
+ throw err;
+ });
+}
+
+
+The next thing that is important to note is that you don't have to do anything if you don't want to do anything with the error but still want the error bubbled up to your promises handlers. So for example, if you are expecting the handler of this promise to handle the error and display something in the UI, just leave out the function(err) callback which would look exactly the same as the example for 'Transforming result data'
| t |