diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 45088aa1bf..923caf995a 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -2,6 +2,8 @@ using System; using System.IO; using System.Linq; using System.Text; +using System.Web; +using Umbraco.Core.Configuration; namespace Umbraco.Core { @@ -10,6 +12,23 @@ namespace Umbraco.Core /// public static class UriExtensions { + /// + /// Checks if the uri is a request for the default back office page + /// + /// + /// + internal static bool IsDefaultBackOfficeRequest(this Uri url) + { + if (url.AbsolutePath.InvariantEquals(GlobalSettings.Path.TrimEnd("/")) + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/')) + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default") + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default/")) + { + return true; + } + return false; + } + /// /// This is a performance tweak to check if this is a .css, .js or .ico, .jpg, .jpeg, .png, .gif file request since /// .Net will pass these requests through to the module when in integrated mode. diff --git a/src/Umbraco.Web.UI.Client/gruntFile.js b/src/Umbraco.Web.UI.Client/gruntFile.js index a118085506..418fe4c529 100644 --- a/src/Umbraco.Web.UI.Client/gruntFile.js +++ b/src/Umbraco.Web.UI.Client/gruntFile.js @@ -99,7 +99,14 @@ module.exports = function (grunt) { files: [{ dest: '<%= distdir %>/js', src : '*.js', expand: true, cwd: 'src/common/mocks/' }] }, vs: { - files: [{ dest: '<%= vsdir %>/', src : '**', expand: true, cwd: '<%= distdir %>/' }] + files: [ + //everything except the index.html root file! + //then we need to figure out how to not copy all the test stuff either!? + { dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' }, + { dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' }, + { dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' }, + { dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' } + ] }, packages: { files: [{ dest: '<%= vsdir %>/../App_Plugins', src : '**', expand: true, cwd: 'src/packages/' }] diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index b91bb3906b..bb5ee40cf8 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -1,7 +1,7 @@ app.config(function ($routeProvider) { $routeProvider - .when('/:section', { - templateUrl: function (rp) { + .when('/:section', { + templateUrl: function (rp) { if (rp.section === "default") { rp.section = "content"; diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6e440d873b..e8236c813f 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2175,7 +2175,6 @@ - diff --git a/src/Umbraco.Web.UI/umbraco/Default.aspx b/src/Umbraco.Web.UI/umbraco/Default.aspx deleted file mode 100644 index 963ced80c2..0000000000 --- a/src/Umbraco.Web.UI/umbraco/Default.aspx +++ /dev/null @@ -1,55 +0,0 @@ -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - -<%@ Page Language="c#" CodeBehind="Default.aspx.cs" AutoEventWireup="True" Inherits="umbraco._Default" %> - - - - - Start Umbraco - - - - - - -
- umbraco
-
-

umbraco <%=umbraco.ui.Text("dashboard", "openinnew")%>

- - -
-
- - <%=umbraco.ui.Text("dashboard", "restart")%> - umbraco   - <%=umbraco.ui.Text("dashboard", "browser")%>
- -
-
- - (<%=umbraco.ui.Text("dashboard", "nothinghappens")%>) -
-
- - <%=umbraco.ui.Text("dashboard", "visit")%> - umbraco.org -
- - diff --git a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml index 4650a0eb27..5ffa797bd8 100644 --- a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml @@ -11,6 +11,7 @@ + @@ -33,15 +34,15 @@ - - + + @*Currently this needs to be loaded before anything*@ - + @*And finally we can load in our angular app*@ - + diff --git a/src/Umbraco.Web.UI/umbraco/index.html b/src/Umbraco.Web.UI/umbraco/index.html deleted file mode 100644 index 4dc83db4f3..0000000000 --- a/src/Umbraco.Web.UI/umbraco/index.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - Umbraco - - - - -
- - - -
-
-
-
-
- -
- - - - - - - - - diff --git a/src/Umbraco.Web.UI/umbraco/js/umbraco.app.js b/src/Umbraco.Web.UI/umbraco/js/umbraco.app.js deleted file mode 100644 index 33a752cd0f..0000000000 --- a/src/Umbraco.Web.UI/umbraco/js/umbraco.app.js +++ /dev/null @@ -1,807 +0,0 @@ -'use strict'; - -define(['angular', 'namespaceMgr'], function (angular) { - - // declare and return the app module - var app = angular.module('myApp', ['uModules.Content.Helpers']); - - Umbraco.Sys.registerNamespace("Umbraco.Content"); - - var contentHelpers = angular.module('uModules.Content.Helpers', []); - - //This directive is used to associate a field with a server-side validation response - // so that the validators in angular are updated based on server-side feedback. - app.directive('valServer', function () { - return { - require: 'ngModel', - restrict: "A", - link: function (scope, element, attr, ctrl) { - if (!scope.model || !scope.model.alias) - throw "valServer can only be used in the scope of a content property object"; - var parentErrors = scope.$parent.serverErrors; - if (!parentErrors) return; - - var fieldName = scope.$eval(attr.valServer); - - //subscribe to the changed event of the element. This is required because when we - // have a server error we actually invalidate the form which means it cannot be - // 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. - element.keydown(function () { - if (ctrl.$invalid) { - ctrl.$setValidity('valServer', true); - } - }); - element.change(function() { - if (ctrl.$invalid) { - ctrl.$setValidity('valServer', true); - } - }); - //TODO: DO we need to watch for other changes on the element ? - - //subscribe to the server validation changes - parentErrors.subscribe(scope.model, fieldName, function (isValid, propertyErrors, allErrors) { - if (!isValid) { - ctrl.$setValidity('valServer', false); - //assign an error msg property to the current validator - ctrl.errorMsg = propertyErrors[0].errorMsg; - } - else { - ctrl.$setValidity('valServer', true); - //reset the error message - ctrl.errorMsg = ""; - } - }, true); - } - }; - }); - - app.directive('valRegex', function () { - - /// - /// 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 - /// - - return { - require: 'ngModel', - restrict: "A", - link: function (scope, elm, attrs, ctrl) { - - var regex = new RegExp(scope.$eval(attrs.valRegex)); - - var patternValidator = function (viewValue) { - //NOTE: we don't validate on empty values, use required validator for that - if (!viewValue || regex.test(viewValue)) { - // it is valid - ctrl.$setValidity('valRegex', true); - //assign a message to the validator - ctrl.errorMsg = ""; - return viewValue; - } - else { - // it is invalid, return undefined (no model update) - ctrl.$setValidity('valRegex', false); - //assign a message to the validator - ctrl.errorMsg = "Value is invalid, it does not match the correct pattern"; - return undefined; - } - }; - - ctrl.$formatters.push(patternValidator); - ctrl.$parsers.push(patternValidator); - } - }; - }); - - app.directive('umbContentProperty', function () { - return { - replace: true, //replace the element with the template - restrict: "E", //restrict to element - template: "
", - link: function (scope, element, attr, ctrl) { - - scope.editorView = ""; - - //let's make a requireJs call to try and retrieve the associated js - // for this view - if (scope.model.view && scope.model.view != "") { - //get the js file which exists at ../Js/EditorName.js - var lastSlash = scope.model.view.lastIndexOf("/"); - var fullViewName = scope.model.view.substring(lastSlash + 1, scope.model.view.length); - var viewName = fullViewName.indexOf(".") > 0 - ? fullViewName.substring(0, fullViewName.indexOf(".")) - : fullViewName; - var jsPath = scope.model.view.substring(0, lastSlash + 1) + "../Js/" + viewName + ".js"; - require([jsPath], - function () { - //the script loaded so load the view - //NOTE: The use of $apply because we're operating outside of the angular scope with this callback. - scope.$apply(function () { - scope.editorView = scope.model.view; - }); - }, function (err) { - //an error occurred... most likely there is no JS file to load for this editor - //NOTE: The use of $apply because we're operating outside of the angular scope with this callback. - scope.$apply(function () { - scope.editorView = scope.model.view; - }); - }); - } - else { - scope.editorView = editor; - } - } - }; - }); - - ////This is a highly specialized directive used to load in a property editor. - //// It is similar to ngInclude, however ngInclude does not emit an event before it compiles the - //// output whereas before we compile the view after we've retreived it from http, we want to check - //// for an ngController attribute and if we find one, then we'll make a request to load in the JS. - //app.directive('umbContentProperty', ['$http', '$templateCache', '$anchorScroll', '$compile', - // function($http, $templateCache, $anchorScroll, $compile) { - // return { - // replace: true, //replace the element with the template - // restrict: "E", //restrict to element - // terminal: true, - // compile: function(element, attr) { - // var srcExp = attr.ngInclude || attr.src; - - // return function(scope, element) { - // var changeCounter = 0, - // childScope; - - // var clearContent = function() { - // if (childScope) { - // childScope.$destroy(); - // childScope = null; - // } - - // element.html(''); - // }; - - // scope.$watch(srcExp, function ngIncludeWatchAction(src) { - // var thisChangeId = ++changeCounter; - - // if (src) { - // $http.get(src, { cache: $templateCache }).success(function(response) { - // if (thisChangeId !== changeCounter) return; - - // if (childScope) childScope.$destroy(); - // childScope = scope.$new(); - - // element.html(response); - // $compile(element.contents())(childScope); - - // if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - // $anchorScroll(); - // } - - // childScope.$emit('$includeContentLoaded'); - // scope.$eval(onloadExp); - // }).error(function() { - // if (thisChangeId === changeCounter) clearContent(); - // }); - // } - // else clearContent(); - // }); - // }; - // } - // }; - // }]); - - - //This directive is used to control the display of the property level validation message. - // We will listen for server side validation changes based on the parent scope's error collection - // and when an error is detected for this property we'll show the error message and then we need - // to emit the valBubble event so that any parent listening can update it's UI (like the validation summary) - app.directive('valPropertyMessage', function() { - return { - scope: true, // create a new scope for this directive - replace: true, //replace the element with the template - restrict: "E", //restrict to element - template: "
{{errorMsg}}
", - link: function(scope, element, attr, ctrl) { - - if (!scope.propertyForm) - throw "valPropertyMessage must exist within a form called propertyForm"; - - //flags for use in the below closures - var showValidation = false; - var hasError = false; - - //create properties on our custom scope so we can use it in our template - scope.errorMsg = ""; - - //listen for form validation - scope.$watch("$parent.propertyForm.$valid", function(isValid, oldValue) { - if (!isValid) { - //check if it's one of the properties that is invalid in the current content property - if (element.closest(".content-property").find(".ng-invalid").length > 0) { - hasError = true; - if (showValidation) { - //update the validation message - scope.errorMsg = scope.$parent.$parent.serverErrors.getError(scope.$parent.model, ''); - } - } - else { - hasError = false; - scope.errorMsg = ""; - } - } - else { - hasError = false; - scope.errorMsg = ""; - } - }); - - //add a watch to update our waitingOnValidation flag for use in the above closure - scope.$watch("$parent.$parent.ui.waitingOnValidation", function(isWaiting, oldValue) { - showValidation = isWaiting; - if (hasError && showValidation) { - //update the validation message - scope.errorMsg = scope.$parent.$parent.serverErrors.getError(scope.$parent.model, ''); - } - else { - scope.errorMsg = ""; - } - }); - - var parentErrors = scope.$parent.$parent.serverErrors; - if (!parentErrors) return; - //NOTE: we pass in "" in order to listen for all validation changes to the content property, not for - // validation changes to fields in the property this is because some server side validators may not - // return the field name for which the error belongs too, just the property for which it belongs. - parentErrors.subscribe(scope.model, "", function(isValid, propertyErrors, allErrors) { - hasError = !isValid; - if (hasError) { - //set the error message to the server message - scope.errorMsg = propertyErrors[0].errorMsg; - //now that we've used the server validation message, we need to remove it from the - //error collection... it is a 'one-time' usage so that when the field is invalidated - //again, the message we display is the client side message. - //NOTE: 'this' in the subscribe callback context is the validation manager object. - this.removeError(scope.model, ""); - //emit an event upwards - scope.$emit("valBubble", { - isValid: false, // it is INVALID - element: element, // the element that the validation applies to - scope: scope.$parent, // the parent scope since we've creaed a new one for this directive - ctrl: ctrl // the current controller - }); - } - else { - scope.errorMsg = ""; - //emit an event upwards - scope.$emit("valBubble", { - isValid: true, // it is VALID - element: element, // the element that the validation applies to - scope: scope.$parent, // the parent scope since we've creaed a new one for this directive - ctrl: ctrl // the current controller - }); - } - }, true); - - } - }; - }); - - //This directive is used for validation messages to associate them with a field that the message is for. - // It is referenced from valToggleMsg. - app.directive('valMsgFor', function () { - return { - restrict: "A", - link: function (scope, element, attr, ctrl) { - //This directive doesn't actually do anything though, it's referenced from valToggleMsg - } - }; - }); - - //This directive will show/hide an error based on: - // * is the value + the given validator invalid - // * AND, has the form been submitted ? - app.directive('valToggleMsg', function (u$AngularHelper) { - return { - restrict: "A", - link: function (scope, element, attr, ctrl) { - - if (!attr.valToggleMsg) - throw "valToggleMsg requires that a reference to a validator is specified"; - if (!attr.valMsgFor) - throw "valToggleMsg requires that the attribute valMsgFor exists on the element"; - - //create a flag for us to be able to reference in the below closures for watching. - var showValidation = false; - var hasError = false; - - var currentForm = u$AngularHelper.getCurrentForm(scope); - if (!currentForm || !currentForm.$name) - throw "valToggleMsg requires that a name is assigned to the ng-form containing the validated input"; - - //add a watch to the validator for the value (i.e. $parent.myForm.value.$error.required ) - scope.$watch(currentForm.$name + "." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function (isInvalid, oldValue) { - hasError = isInvalid; - if (hasError && showValidation) { - element.show(); - } - else { - element.hide(); - } - }); - - //add a watch to update our waitingOnValidation flag for use in the above closure - scope.$watch("$parent.ui.waitingOnValidation", function (isWaiting, oldValue) { - showValidation = isWaiting; - if (hasError && showValidation) { - element.show(); - } - else { - element.hide(); - } - }); - } - }; - }); - - //This directive will bubble up a notification via an emit event (upwards) - // describing the state of the validation element. This is useful for - // parent elements to know about child element validation state. - app.directive('valBubble', function (u$AngularHelper) { - return { - require: 'ngModel', - restrict: "A", - link: function (scope, element, attr, ctrl) { - - if (!attr.name) { - throw "valBubble must be set on an input element that has a 'name' attribute"; - } - - var currentForm = u$AngularHelper.getCurrentForm(scope); - if (!currentForm || !currentForm.$name) - throw "valBubble requires that a name is assigned to the ng-form containing the validated input"; - - //watch the current form's validation for the current field name - scope.$watch(currentForm.$name + "." + ctrl.$name + ".$valid", function (isValid, lastValue) { - if (isValid != undefined) { - //emit an event upwards - scope.$emit("valBubble", { - isValid: isValid, // if the field is valid - element: element, // the element that the validation applies to - expression: this.exp, // the expression that was watched to check validity - scope: scope, // the current scope - ctrl: ctrl // the current controller - }); - } - }); - } - }; - }); - - //This directive will display a validation summary for the current form based on the - //content properties of the current content item. - app.directive('valSummary', function () { - return { - scope: true, // create a new scope for this directive - replace: true, // replace the html element with the template - restrict: "E", // restrict to an element - template: '
  • {{model}}
', - link: function (scope, element, attr, ctrl) { - - //create properties on our custom scope so we can use it in our template - scope.validationSummary = []; - - //create a flag for us to be able to reference in the below closures for watching. - var showValidation = false; - - //add a watch to update our waitingOnValidation flag for use in the below closures - scope.$watch("$parent.ui.waitingOnValidation", function (isWaiting, oldValue) { - showValidation = isWaiting; - if (scope.validationSummary.length > 0 && showValidation) { - element.show(); - } - else { - element.hide(); - } - }); - - //if we are to show field property based errors. - //this requires listening for bubbled events from valBubble directive. - - scope.$parent.$on("valBubble", function (evt, args) { - var msg = "The value assigned for the property " + args.scope.model.label + " is invalid"; - var exists = _.contains(scope.validationSummary, msg); - - //we need to check if the entire property is valid, even though the args says this field is valid there - // may be multiple values attached to a content property. The easiest way to do this is check the DOM - // just like we are doing for the property level validation message. - var propertyHasErrors = args.element.closest(".content-property").find(".ng-invalid").length > 0; - - if (args.isValid && exists && !propertyHasErrors) { - //it is valid but we have a val msg for it so we'll need to remove the message - scope.validationSummary = _.reject(scope.validationSummary, function (item) { - return item == msg; - }); - } - else if (!args.isValid && !exists) { - //it is invalid and we don't have a msg for it already - scope.validationSummary.push(msg); - } - - //show the summary if there are errors and the form has been submitted - if (showValidation && scope.validationSummary.length > 0) { - element.show(); - } - }); - //listen for form invalidation so we know when to hide it - scope.$watch("contentForm.$error", function (errors) { - //check if there is an error and hide the summary if not - var hasError = _.find(errors, function (err) { - return (err.length && err.length > 0); - }); - if (!hasError) { - element.hide(); - } - }, true); - } - }; - }); - - //A directive applied to a file input box so that outer scopes can listen for when a file is selected - app.directive('umbFileUpload', function () { - return { - scope: true, //create a new scope - link: function (scope, el, attrs) { - el.bind('change', function (event) { - var files = event.target.files; - //emit event upward - scope.$emit("filesSelected", { files: files }); - }); - } - }; - }); - - //A helper class for dealing with content - contentHelpers.factory('u$ContentHelper', function () { - return { - formatPostData: function (displayModel) { - /// formats the display model used to display the content to the model used to save the content - - //NOTE: the display model inherits from the save model so we can in theory just post up the display model but - // we don't want to post all of the data as it is unecessary. - - var saveModel = { - id: displayModel.id, - properties: [] - }; - for (var p in displayModel.properties) { - saveModel.properties.push({ - id: displayModel.properties[p].id, - value: displayModel.properties[p].value - }); - } - return saveModel; - } - }; - }); - - //This service is some helper methods for extending angular - contentHelpers.factory('u$AngularHelper', function() { - return { - getCurrentForm: function(scope) { - /// Returns the current form object applied to the scope or null if one is not found - //NOTE: There isn't a way in angular to get a reference to the current form object since the form object - // is just defined as a property of the scope when it is named but you'll always need to know the name which - // isn't very convenient. If we want to watch for validation changes we need to get a form reference. - // The way that we detect the form object is a bit hackerific in that we detect all of the required properties - // that exist on a form object. - - var form = null; - var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$invalid", "$addControl", "$removeControl", "$setValidity", "$setDirty"]; - for (var p in scope) { - if (_.isObject(scope[p]) && !_.isFunction(scope[p]) && !_.isArray(scope[p]) && p.substr(0, 1) != "$") { - var props = _.keys(scope[p]); - if (props.length < requiredFormProps.length) continue; - if (_.every(requiredFormProps, function(item) { - return _.contains(props, item); - })) { - form = scope[p]; - break; - } - } - } - return form; - } - }; - }); - - //This service is used to wire up all server-side valiation messages - // back into the UI in a consistent format. - contentHelpers.factory('u$ServerValidation', function () { - - return { - _callbacks: [], - subscribe: function (contentProperty, fieldName, callback) { - /// - /// 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. - /// - - this._callbacks.push({ propertyAlias: contentProperty.alias, fieldName: fieldName, callback: callback }); - }, - getCallbacks: function (contentProperty, fieldName) { - /// Gets all callbacks that has been registered using the subscribe method for the contentProperty + fieldName combo - var found = _.filter(this._callbacks, function (item) { - return (item.propertyAlias == contentProperty.alias && item.fieldName == fieldName); - }); - return found; - }, - addError: function (contentProperty, fieldName, errorMsg) { - /// Adds an error message for the content property - - if (!contentProperty) return; - //only add the item if it doesn't exist - if (!this.hasError(contentProperty)) { - this.items.push({ - propertyAlias: contentProperty.alias, - fieldName: fieldName, - errorMsg: errorMsg - }); - } - - - //find all errors for this item - var errorsForCallback = _.filter(this.items, function (item) { - return (item.propertyAlias == contentProperty.alias && item.fieldName == fieldName); - }); - //we should now call all of the call backs registered for this error - var callbacks = this.getCallbacks(contentProperty, fieldName); - //call each callback for this error - for (var cb in callbacks) { - callbacks[cb].callback.apply(this, [ - false, //pass in a value indicating it is invalid - errorsForCallback, //pass in the errors for this item - this.items]); //pass in all errors in total - } - }, - removeError: function (contentProperty, fieldName) { - /// Removes an error message for the content property - - if (!contentProperty) return; - //remove the item - this.items = _.reject(this.items, function(item) { - return (item.propertyAlias == contentProperty.alias && item.fieldName == fieldName); - }); - }, - reset: function() { - /// Clears all errors and notifies all callbacks that all server errros are now valid - used when submitting a form - this.items = []; - for (var cb in this._callbacks) { - this._callbacks[cb].callback.apply(this, [ - true, //pass in a value indicating it is VALID - [], //pass in empty collection - []]); //pass in empty collection - } - }, - getError: function (contentProperty, fieldName) { - /// - /// Gets the error message for the content property - /// - var err = _.find(this.items, function(item) { - return (item.propertyAlias == contentProperty.alias && item.fieldName == fieldName); - }); - //return generic property error message if the error doesn't exist - return err ? err : "Property has errors"; - }, - hasError: function (contentProperty, fieldName) { - var err = _.find(this.items, function (item) { - return (item.propertyAlias == contentProperty.alias && item.fieldName == fieldName); - }); - return err ? true : false; - }, - items: [] - }; - }); - - Umbraco.Content.ContentController = function ($scope, $element, $http, u$ContentHelper, u$ServerValidation) { - - //initialize the data model and methods - $scope.model = {}; - $scope.files = []; - $scope.addFiles = function(propertyId, files) { - //this will clear the files for the current property and then add the new ones for the current property - $scope.files = _.reject($scope.files, function(item) { - return item.id == propertyId; - }); - for (var i = 0; i < files.length; i++) { - //save the file object to the scope's files collection - $scope.files.push({ id: propertyId, file: files[i] }); - } - }; - //model for updating the UI - $scope.ui = { - working: false, - formFailed: false, - canSubmit: function () { - //NOTE: we're getting the form element for the current element so we're not hard coding - // the reference to the form name here. - //return $scope[$element.closest("form").attr("name")].$valid || !$scope.ui.working; - return !$scope.ui.working; - } - }; - //wire up validation manager - $scope.serverErrors = u$ServerValidation; - - //the url to get the content from - var getContentUrl = Umbraco.Sys.ServerVariables.contentEditorApiBaseUrl + "GetContent?id=" + 1166; - var saveContentUrl = Umbraco.Sys.ServerVariables.contentEditorApiBaseUrl + "PostSaveContent"; - - //go get the content from the server - $scope.ui.working = true; - $http.get(getContentUrl, $scope.valueToPost). - success(function (data, status, headers, config) { - //set the model to the value returned by the server - $scope.model = data; - $scope.ui.working = false; - }). - error(function (data, status, headers, config) { - alert("failed!"); - $scope.ui.working = false; - }); - - $scope.save = function () { - - //flag that is set informing the validation controls to be displayed if any are in error - $scope.ui.waitingOnValidation = true; - - //don't continue if the form is invalid - if ($scope.contentForm.$invalid) return; - - //reset all errors and listeners - $scope.serverErrors.reset(); - - $scope.ui.working = true; - - //broadcast an event downward - $scope.$broadcast("contentSaving", null); - - $http({ - method: 'POST', - url: saveContentUrl, - //IMPORTANT!!! You might think this should be set to 'multipart/form-data' but this is not true because when we are sending up files - // the request needs to include a 'boundary' parameter which identifies the boundary name between parts in this multi-part request - // and setting the Content-type manually will not set this boundary parameter. For whatever reason, setting the Content-type to 'false' - // will force the request to automatically populate the headers properly including the boundary parameter. - headers: { 'Content-Type': false }, - transformRequest: function (data) { - var formData = new FormData(); - //need to convert our json object to a string version of json - formData.append("contentItem", angular.toJson(u$ContentHelper.formatPostData(data.model))); - //now add all of the assigned files - for (var f in data.files) { - //each item has a property id and the file object, we'll ensure that the id is suffixed to the key - // so we know which property it belongs to on the server side - formData.append("file_" + data.files[f].id, data.files[f].file); - } - return formData; - }, - data: { model: $scope.model, files: $scope.files } - }). - success(function(data, status, headers, config) { - alert("success!"); - - //clear the files - $scope.files = []; - - //broadcast an event downward - $scope.$broadcast("contentSaved", null); - - //set the model to the value returned by the server - $scope.model = data; - $scope.ui.working = false; - $scope.ui.waitingOnValidation = false; - $scope.serverErrors.reset(); - }). - error(function(data, status, headers, config) { - //When the status is a 403 status, we have validation errors. - //Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something). - //Or, some strange server error - if (status == 403) { - //now we need to look through all the validation errors - if (data && data.ModelState) { - for (var e in data.ModelState) { - - //the alias in model state can be in dot notation which indicates - // * the first part is the content property alias - // * the second part is the field to which the valiation msg is associated with - var parts = e.split("."); - var propertyAlias = parts[0]; - - //find the content property for the current error - var contentProperty = _.find($scope.model.properties, function(item) { - return (item.alias == propertyAlias); - }); - if (contentProperty) { - //if it contains a '.' then we will wire it up to a property's field - if (parts.length > 1) { - //add an error with a reference to the field for which the validation belongs too - $scope.serverErrors.addError(contentProperty, parts[1], data.ModelState[e][0]); - } - else { - //add a generic error for the property, no reference to a specific field - $scope.serverErrors.addError(contentProperty, "", data.ModelState[e][0]); - } - } - } - - } - } - else { - alert("failed!"); - } - - $scope.ui.working = false; - $scope.ui.waitingOnValidation = true; - }); - - //$http.post(saveContentUrl, u$ContentHelper.formatPostData($scope.model)). - // success(function (data, status, headers, config) { - // alert("success!"); - // $scope.ui.working = false; - // $scope.ui.waitingOnValidation = false; - // $scope.serverErrors.reset(); - // }). - // error(function (data, status, headers, config) { - // //When the status is a 403 status, we have validation errors. - // //Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something). - // //Or, some strange server error - // if (status == 403) { - // //now we need to look through all the validation errors - // if (data && data.ModelState) { - // for (var e in data.ModelState) { - - // //the alias in model state can be in dot notation which indicates - // // * the first part is the content property alias - // // * the second part is the field to which the valiation msg is associated with - // var parts = e.split("."); - // var propertyAlias = parts[0]; - - // //find the content property for the current error - // var contentProperty = _.find($scope.model.properties, function(item) { - // return (item.alias == propertyAlias); - // }); - // if (contentProperty) { - // //if it contains a '.' then we will wire it up to a property's field - // if (parts.length > 1) { - // //add an error with a reference to the field for which the validation belongs too - // $scope.serverErrors.addError(contentProperty, parts[1], data.ModelState[e][0]); - // } - // else { - // //add a generic error for the property, no reference to a specific field - // $scope.serverErrors.addError(contentProperty, "", data.ModelState[e][0]); - // } - // } - // } - - // } - // } - // else { - // alert("failed!"); - // } - - // $scope.ui.working = false; - // $scope.ui.waitingOnValidation = true; - // }); - }; - - }; - - - //return the module - return app; - -}); \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/BackOfficeArea.cs b/src/Umbraco.Web/Mvc/BackOfficeArea.cs index b91cbba9b2..33d0629161 100644 --- a/src/Umbraco.Web/Mvc/BackOfficeArea.cs +++ b/src/Umbraco.Web/Mvc/BackOfficeArea.cs @@ -21,7 +21,6 @@ namespace Umbraco.Web.Mvc /// public override void RegisterArea(AreaRegistrationContext context) { - //Default back office route context.MapRoute( "Umbraco_back_office", GlobalSettings.UmbracoMvcArea + "/{action}/{id}", diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index c93b99d180..6b4668854a 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -63,6 +63,14 @@ namespace Umbraco.Web /// Processses the Umbraco Request ///
/// + /// + /// + /// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default ) + /// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because + /// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up. + /// So we handle it here and explicitly execute the MVC controller. + /// + /// void ProcessRequest(HttpContextBase httpContext) { // do not process if client-side request @@ -76,6 +84,13 @@ namespace Umbraco.Web var umbracoContext = UmbracoContext.Current; + //re-write for the default back office path + if (httpContext.Request.Url.IsDefaultBackOfficeRequest()) + { + RewriteToBackOfficeHandler(httpContext); + return; + } + // do not process but remap to handler if it is a base rest request if (BaseRest.BaseRestHandler.IsBaseRestRequest(umbracoContext.OriginalRequestUrl)) { @@ -345,6 +360,30 @@ namespace Umbraco.Web #endregion + /// + /// Rewrites to the default back office page. + /// + /// + private void RewriteToBackOfficeHandler(HttpContextBase context) + { + // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) + var rewritePath = GlobalSettings.Path.TrimEnd(new[] { '/' }) + "/Default"; + // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc) + context.RewritePath(rewritePath, "", "", false); + + //if it is MVC we need to do something special, we are not using TransferRequest as this will + //require us to rewrite the path with query strings and then reparse the query strings, this would + //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create + //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: + // * Looks up the route based on the new rewritten URL + // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route + //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal + //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink + //a bunch of things! + var urlRouting = new UrlRoutingModule(); + urlRouting.PostResolveRequestCache(context); + } + /// /// Rewrites to the correct Umbraco handler, either WebForms or Mvc /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs index e3f11e6d80..78d6b82778 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; +using System.Web.Mvc; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; @@ -18,8 +19,10 @@ namespace umbraco { protected void Page_Load(object sender, System.EventArgs e) { - - Server.Transfer("umbraco.aspx"); + //var mvcHandler = new MvcHandler() + //Server.TransferRequest(); + //Server.Transfer("~/Umbraco/Default"); + //Server.Transfer("umbraco.aspx"); // Put user code to initialize the page here }