This commit is contained in:
Per Ploug Krogslund
2013-06-10 10:56:44 -02:00
20 changed files with 321 additions and 54 deletions

View File

@@ -292,6 +292,7 @@
<Compile Include="App_Plugins\MyPackage\PropertyEditors\ServerInfoPropertyEditor.cs" />
<Compile Include="App_Plugins\MyPackage\System\MyStartupHandler.cs" />
<Compile Include="App_Plugins\MyPackage\Trees\LegacyTestTree.cs" />
<Compile Include="App_Plugins\TestPackage\PropertyEditors\TestEditor.cs" />
<Compile Include="Install\Default.aspx.cs">
<DependentUpon>default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
@@ -563,6 +564,8 @@
<Content Include="App_Plugins\MyPackage\PropertyEditors\Views\FileUploadEditor.html" />
<Content Include="App_Plugins\MyPackage\PropertyEditors\Views\PostcodeEditor.html" />
<Content Include="App_Plugins\MyPackage\PropertyEditors\Views\RegexEditor.html" />
<Content Include="App_Plugins\TestPackage\PropertyEditors\Js\Test.js" />
<Content Include="App_Plugins\TestPackage\PropertyEditors\Views\Test.html" />
<Content Include="Config\Splashes\booting.aspx" />
<Content Include="Config\Splashes\noNodes.aspx" />
<Content Include="Umbraco\lib\Umbraco\Extensions.js" />

View File

@@ -21,8 +21,6 @@
@*Currently this needs to be loaded before anything*@
<script src="@Url.Action("ServerVariables", "BackOffice")" type="text/javascript"></script>
<style type="text/css">
.validation-message
@@ -68,7 +66,7 @@
<umb-login></umb-login>
<umb-notifications></umb-notifications>
<script src="lib/require/require.min.js" type="text/javascript"></script>
<script src="lib/require/require.min.js" type="text/javascript"></script>
@*We cannot use the data-main attribute above because we need to load in the application
via a server side JavaScript request, so we just request it after requirejs*@

View File

@@ -0,0 +1,44 @@
<div class="umb-panel" ng-controller="Umbraco.Dialogs.MediaPickerController">
<div class="umb-panel-header">
<div class="umb-el-wrap umb-panel-buttons">
<div class="pull-right umb-btn-toolbar">
<button class="btn">Create <i class="icon-upload"></i></button>
<button type="button" ng-click="submit(dialogData)" class="btn btn-primary">Select (x)</button>
</div>
</div>
</div>
<div class="umb-panel-body umb-scrollable" auto-scale="1">
<div class="umb-panel">
<div class="umb-control-group">
<ul class="breadcrumb">
<li><strong>You are here:</strong></li>
<li><a href="#">Media</a></li>
</ul>
<div id="search-form" ng-animate="'slide'">
<form class="form-search" ng-controller="SearchController">
<i class="icon-search"></i>
<input type="text"
ng-model="ui.searchTerm"
class="umb-search-field search-query"
placeholder="{{localization.app.search.typeToSearch}}"
on-blur="deActivateSearch()"
on-keyup="performSearch(ui.searchTerm)">
</form>
</div>
</ul>
<ul class="thumbnails">
<li class="span2" ng-repeat="image in images">
<a href="#" class="thumbnail" ng-class="{selected: dialogData.selection.indexOf(image) > -1}" ng-click="select(image)" prevent-default>
<img ng-src="{{image.thumbnail}}" />
</a>
</li>
</ul>
</div>
</div>
</div>
</div>

View File

@@ -129,7 +129,7 @@ angular.module('umbraco').controller("MainController",
});
//used for the media picker dialog
angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController", function ($scope, mediaFactory) {
angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController", function ($scope, mediaResource) {
$scope.$on("treeNodeSelect", function(event, args){
$(args.event.target.parentElement).find("i").attr("class", "icon umb-tree-icon sprTree icon-check blue");
@@ -147,8 +147,12 @@ angular.module("umbraco").controller("Umbraco.Dialogs.MacroPickerController", fu
};
});
//used for the media picker dialog
angular.module("umbraco").controller("Umbraco.Dialogs.MediaPickerController", function ($scope, mediaFactory) {
$scope.images = mediaFactory.rootMedia();
angular.module("umbraco").controller("Umbraco.Dialogs.MediaPickerController", function ($scope, mediaResource) {
mediaResource.rootMedia()
.then(function (data) {
$scope.images = data;
});
});
angular.module("umbraco").controller("Umbraco.Common.LegacyController",
function($scope, $routeParams){

View File

@@ -6,6 +6,44 @@
'use strict';
define(['app', 'angular'], function (app, angular) {
/**
* @ngdoc factory
* @name umbraco.resources.treeResource
* @description Loads in data for trees
**/
function mediaResource($q, $http) {
/** internal method to get the api url */
function getRootMediaUrl(section) {
return Umbraco.Sys.ServerVariables.mediaApiBaseUrl + "GetRootMedia";
}
return {
rootMedia: function () {
var deferred = $q.defer();
//go and get the tree data
$http.get(getRootMediaUrl()).
success(function (data, status, headers, config) {
deferred.resolve(data);
}).
error(function (data, status, headers, config) {
deferred.reject('Failed to retreive data for application tree ' + section);
});
return deferred.promise;
return [
{ id: 1234, src: "/Media/boston.jpg", thumbnail: "/Media/boston.jpg" },
{ src: "/Media/bird.jpg", thumbnail: "/Media/bird.jpg" },
{ src: "/Media/frog.jpg", thumbnail: "/Media/frog.jpg" }
];
}
};
}
angular.module('umbraco.resources.media', []).factory('mediaResource', mediaResource);
/**
* @ngdoc factory
@@ -125,7 +163,6 @@ define(['app', 'angular'], function (app, angular) {
}
angular.module('umbraco.resources.contentType', []).factory('contentTypeResource', contentTypeResource);
/**
* @ngdoc factory
* @name umbraco.resources.contentResource
@@ -135,15 +172,15 @@ define(['app', 'angular'], function (app, angular) {
/** internal method to get the api url */
function getContentUrl(contentId) {
return Umbraco.Sys.ServerVariables.contentEditorApiBaseUrl + "GetContent?id=" + contentId;
return Umbraco.Sys.ServerVariables.contentApiBaseUrl + "GetContent?id=" + contentId;
}
/** internal method to get the api url */
function getEmptyContentUrl(contentTypeAlias, parentId) {
return Umbraco.Sys.ServerVariables.contentEditorApiBaseUrl + "GetEmptyContent?contentTypeAlias=" + contentTypeAlias + "&parentId=" + parentId;
return Umbraco.Sys.ServerVariables.contentApiBaseUrl + "GetEmptyContent?contentTypeAlias=" + contentTypeAlias + "&parentId=" + parentId;
}
/** internal method to get the api url for publishing */
function getSaveUrl() {
return Umbraco.Sys.ServerVariables.contentEditorApiBaseUrl + "PostSaveContent";
return Umbraco.Sys.ServerVariables.contentApiBaseUrl + "PostSave";
}
/** internal method process the saving of data and post processing the result */
function saveContentItem(content, action) {
@@ -603,19 +640,21 @@ angular.module('umbraco.resources.macro', [])
}
};
});
angular.module('umbraco.resources.media', [])
.factory('mediaFactory', function () {
var mediaArray = [];
return {
rootMedia: function(){
return [
{id: 1234, src: "/Media/boston.jpg", thumbnail: "/Media/boston.jpg" },
{src: "/Media/bird.jpg", thumbnail: "/Media/bird.jpg" },
{src: "/Media/frog.jpg", thumbnail: "/Media/frog.jpg" }
];
}
};
});
//angular.module('umbraco.resources.media', [])
//.factory('mediaFactory', function () {
// var mediaArray = [];
// return {
// rootMedia: function(){
// return [
// {id: 1234, src: "/Media/boston.jpg", thumbnail: "/Media/boston.jpg" },
// {src: "/Media/bird.jpg", thumbnail: "/Media/bird.jpg" },
// {src: "/Media/frog.jpg", thumbnail: "/Media/frog.jpg" }
// ];
// }
// };
//});
angular.module('umbraco.resources.tags', [])
.factory('tagsFactory', function () {
return {

View File

@@ -47,7 +47,8 @@ namespace Umbraco.Web.Editors
var d = new Dictionary<string, object>
{
{"umbracoPath", GlobalSettings.Path},
{"contentEditorApiBaseUrl", Url.GetUmbracoApiService<ContentEditorApiController>("PostSaveContent").TrimEnd("PostSaveContent")},
{"contentApiBaseUrl", Url.GetUmbracoApiService<ContentController>("PostSave").TrimEnd("PostSave")},
{"mediaApiBaseUrl", Url.GetUmbracoApiService<MediaController>("GetRootMedia").TrimEnd("GetRootMedia")},
{"treeApplicationApiBaseUrl", Url.GetUmbracoApiService<ApplicationTreeApiController>("GetTreeData").TrimEnd("GetTreeData")},
{"contentTypeApiBaseUrl", Url.GetUmbracoApiService<ContentTypeApiController>("GetAllowedChildrenForContent").TrimEnd("GetAllowedChildrenForContent")}
};

View File

@@ -18,16 +18,16 @@ namespace Umbraco.Web.Editors
/// <summary>
/// The API controller used for editing content
/// </summary>
[PluginController("UmbracoEditors")]
[PluginController("UmbracoApi")]
[ValidationFilter]
public class ContentEditorApiController : UmbracoAuthorizedApiController
public class ContentController : UmbracoAuthorizedApiController
{
private readonly ContentModelMapper _contentModelMapper;
/// <summary>
/// Constructor
/// </summary>
public ContentEditorApiController()
public ContentController()
: this(UmbracoContext.Current, new ContentModelMapper(UmbracoContext.Current.Application, new ProfileModelMapper()))
{
}
@@ -37,7 +37,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="contentModelMapper"></param>
internal ContentEditorApiController(UmbracoContext umbracoContext, ContentModelMapper contentModelMapper)
internal ContentController(UmbracoContext umbracoContext, ContentModelMapper contentModelMapper)
: base(umbracoContext)
{
_contentModelMapper = contentModelMapper;
@@ -85,6 +85,7 @@ namespace Umbraco.Web.Editors
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var emptyContent = new Content("Empty", parentId, contentType);
return _contentModelMapper.ToContentItemDisplay(emptyContent);
}
@@ -95,9 +96,9 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
[ContentItemValidationFilter]
[FileUploadCleanupFilter]
public ContentItemDisplay PostSaveContent(
public ContentItemDisplay PostSave(
[ModelBinder(typeof(ContentItemBinder))]
ContentItemSave contentItem)
ContentItemSave contentItem)
{
//If we've reached here it means:
// * Our model has been bound
@@ -135,4 +136,4 @@ namespace Umbraco.Web.Editors
}
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Linq;
namespace Umbraco.Web.Editors
{
//internal interface IUmbracoApiService<T>
//{
// T Get(int id);
// IEnumerable<T> GetChildren(int id);
// HttpResponseMessage Delete(int id);
// //copy
// //move
// //update
// //create
//}
[PluginController("UmbracoApi")]
public class MediaController : UmbracoAuthorizedApiController
{
private readonly MediaModelMapper _mediaModelMapper;
/// <summary>
/// Constructor
/// </summary>
public MediaController()
: this(UmbracoContext.Current, new MediaModelMapper(UmbracoContext.Current.Application, new ProfileModelMapper()))
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="mediaModelMapper"></param>
internal MediaController(UmbracoContext umbracoContext, MediaModelMapper mediaModelMapper)
: base(umbracoContext)
{
_mediaModelMapper = mediaModelMapper;
}
/// <summary>
/// Returns the root media objects
/// </summary>
public IEnumerable<ContentItemBasic<ContentPropertyBasic>> GetRootMedia()
{
return Services.MediaService.GetRootMedia()
.Select(x => _mediaModelMapper.ToMediaItemSimple(x));
}
}
}

View File

@@ -11,10 +11,10 @@ namespace Umbraco.Web.Models.ContentEditing
/// A model representing a basic content item
/// </summary>
[DataContract(Name = "content", Namespace = "")]
public abstract class ContentItemBasic<T>
where T: ContentPropertyBase
public class ContentItemBasic<T>
where T: ContentPropertyBasic
{
protected ContentItemBasic()
public ContentItemBasic()
{
//ensure its not null
_properties = new List<T>();

View File

@@ -7,6 +7,8 @@ using System.Linq;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// A model representing a content item to be displayed in the back office
/// </summary>

View File

@@ -8,7 +8,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// <summary>
/// A model representing a content item to be saved
/// </summary>
public class ContentItemSave : ContentItemBasic<ContentPropertyBase>
public class ContentItemSave : ContentItemBasic<ContentPropertyBasic>
{
public ContentItemSave()
{

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// Represents a content property to be saved
/// </summary>
[DataContract(Name = "property", Namespace = "")]
public class ContentPropertyBase
public class ContentPropertyBasic
{
[DataMember(Name = "id", IsRequired = true)]
[Required]
@@ -16,7 +16,7 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "value")]
public string Value { get; set; }
protected bool Equals(ContentPropertyBase other)
protected bool Equals(ContentPropertyBasic other)
{
return Id == other.Id;
}
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Models.ContentEditing
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
var other = obj as ContentPropertyBase;
var other = obj as ContentPropertyBasic;
return other != null && Equals(other);
}

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// <summary>
/// Represents a content property that is displayed in the UI
/// </summary>
public class ContentPropertyDisplay : ContentPropertyBase
public class ContentPropertyDisplay : ContentPropertyBasic
{
[DataMember(Name = "label", IsRequired = true)]
[Required]

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// <summary>
/// Represents a content property from the database
/// </summary>
internal class ContentPropertyDto : ContentPropertyBase
internal class ContentPropertyDto : ContentPropertyBasic
{
public IDataTypeDefinition DataType { get; set; }
public string Label { get; set; }

View File

@@ -106,6 +106,11 @@ namespace Umbraco.Web.Models.Mapping
});
}
internal ContentItemBasic<ContentPropertyBasic> ToContentItemSimple(IContent content)
{
return CreateContent<ContentItemBasic<ContentPropertyBasic>, ContentPropertyBasic>(content, null, null);
}
/// <summary>
/// Creates a new content item
/// </summary>
@@ -121,7 +126,7 @@ namespace Umbraco.Web.Models.Mapping
Action<TContentProperty, Property, PropertyEditor> propertyCreatedCallback = null,
bool createProperties = true)
where TContent : ContentItemBasic<TContentProperty>, new()
where TContentProperty : ContentPropertyBase, new()
where TContentProperty : ContentPropertyBasic, new()
{
var result = new TContent
{
@@ -150,12 +155,25 @@ namespace Umbraco.Web.Models.Mapping
private static TContentProperty CreateProperty<TContentProperty>(
Property property,
Action<TContentProperty, Property, PropertyEditor> callback = null)
where TContentProperty : ContentPropertyBase, new()
where TContentProperty : ContentPropertyBasic, new()
{
var editor = PropertyEditorResolver.Current.GetById(property.PropertyType.DataTypeId);
if (editor == null)
{
throw new NullReferenceException("The property editor with id " + property.PropertyType.DataTypeId + " does not exist");
//TODO: Remove this check as we shouldn't support this at all!
var legacyEditor = DataTypesResolver.Current.GetById(property.PropertyType.DataTypeId);
if (legacyEditor == null)
{
throw new NullReferenceException("The property editor with id " + property.PropertyType.DataTypeId + " does not exist");
}
var legacyResult = new TContentProperty
{
Id = property.Id,
Value = property.Value.ToString()
};
if (callback != null) callback(legacyResult, property, null);
return legacyResult;
}
var result = new TContentProperty
{

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models.Mapping
{
internal class MediaModelMapper
{
private readonly ApplicationContext _applicationContext;
private readonly ProfileModelMapper _profileMapper;
public MediaModelMapper(ApplicationContext applicationContext, ProfileModelMapper profileMapper)
{
_applicationContext = applicationContext;
_profileMapper = profileMapper;
}
internal ContentItemBasic<ContentPropertyBasic> ToMediaItemSimple(IMedia media)
{
return CreateMedia<ContentItemBasic<ContentPropertyBasic>, ContentPropertyBasic>(media, null, null);
}
/// <summary>
/// Creates a new content item
/// </summary>
/// <typeparam name="TContent"></typeparam>
/// <typeparam name="TContentProperty"></typeparam>
/// <param name="media"></param>
/// <param name="contentCreatedCallback"></param>
/// <param name="propertyCreatedCallback"></param>
/// <param name="createProperties"></param>
/// <returns></returns>
private TContent CreateMedia<TContent, TContentProperty>(IMedia media,
Action<TContent, IMedia> contentCreatedCallback = null,
Action<TContentProperty, Property, PropertyEditor> propertyCreatedCallback = null,
bool createProperties = true)
where TContent : ContentItemBasic<TContentProperty>, new()
where TContentProperty : ContentPropertyBasic, new()
{
var result = new TContent
{
Id = media.Id,
Owner = _profileMapper.ToBasicUser(media.GetCreatorProfile()),
Updator = null,
ParentId = media.ParentId,
UpdateDate = media.UpdateDate,
CreateDate = media.CreateDate,
ContentTypeAlias = media.ContentType.Alias
};
if (createProperties)
result.Properties = media.Properties.Select(p => CreateProperty(p, propertyCreatedCallback)).ToArray();
if (contentCreatedCallback != null)
contentCreatedCallback(result, media);
return result;
}
/// <summary>
/// Creates the property with the basic property values mapped
/// </summary>
/// <typeparam name="TContentProperty"></typeparam>
/// <param name="property"></param>
/// <param name="callback"></param>
/// <returns></returns>
private static TContentProperty CreateProperty<TContentProperty>(
Property property,
Action<TContentProperty, Property, PropertyEditor> callback = null)
where TContentProperty : ContentPropertyBasic, new()
{
var editor = PropertyEditorResolver.Current.GetById(property.PropertyType.DataTypeId);
if (editor == null)
{
//TODO: Remove this check as we shouldn't support this at all!
var legacyEditor = DataTypesResolver.Current.GetById(property.PropertyType.DataTypeId);
if (legacyEditor == null)
{
throw new NullReferenceException("The property editor with id " + property.PropertyType.DataTypeId + " does not exist");
}
var legacyResult = new TContentProperty
{
Id = property.Id,
Value = property.Value.ToString()
};
if (callback != null) callback(legacyResult, property, null);
return legacyResult;
}
var result = new TContentProperty
{
Id = property.Id,
Value = editor.ValueEditor.SerializeValue(property.Value)
};
if (callback != null) callback(result, property, editor);
return result;
}
}
}

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Web.Mvc
bool isMvc = true)
{
Mandate.ParameterNotNullOrEmpty(controllerName, "controllerName");
Mandate.ParameterNotNullOrEmpty(controllerSuffixName, "controllerSuffixName");
Mandate.ParameterNotNull(controllerSuffixName, "controllerSuffixName");
Mandate.ParameterNotNull(controllerType, "controllerType");
Mandate.ParameterNotNull(routes, "routes");

View File

@@ -70,7 +70,7 @@ namespace Umbraco.Web.Mvc
{
foreach (var s in surfaceControllers)
{
var route = this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Surface", "Index", UrlParameter.Optional, "surface");
var route = this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "", "Index", UrlParameter.Optional, "surface");
//set the route handler to our SurfaceRouteHandler
route.RouteHandler = new SurfaceRouteHandler();
}
@@ -85,7 +85,7 @@ namespace Umbraco.Web.Mvc
{
foreach (var s in apiControllers)
{
this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Api", "", UrlParameter.Optional, "api", isMvc: false);
this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "", "", UrlParameter.Optional, "api", isMvc: false);
}
}
}

View File

@@ -292,6 +292,7 @@
<Compile Include="Cache\UserCacheRefresher.cs" />
<Compile Include="Cache\UserTypeCacheRefresher.cs" />
<Compile Include="Configuration\WebRouting.cs" />
<Compile Include="Editors\ContentController.cs" />
<Compile Include="Editors\ContentTypeApiController.cs" />
<Compile Include="HttpCookieExtensions.cs" />
<Compile Include="Models\ContentEditing\ContentSaveAction.cs" />
@@ -301,6 +302,7 @@
<Compile Include="Models\Mapping\ContentModelMapper.cs" />
<Compile Include="FormDataCollectionExtensions.cs" />
<Compile Include="Models\Mapping\ContentTypeModelMapper.cs" />
<Compile Include="Models\Mapping\MediaModelMapper.cs" />
<Compile Include="Models\Mapping\ProfileModelMapper.cs" />
<Compile Include="Mvc\BackOfficeArea.cs" />
<Compile Include="Trees\LegacyTreeApiController.cs" />
@@ -329,7 +331,7 @@
<Compile Include="Models\ContentEditing\ContentItemDto.cs" />
<Compile Include="Models\ContentEditing\ContentItemFile.cs" />
<Compile Include="Models\ContentEditing\ContentItemSave.cs" />
<Compile Include="Models\ContentEditing\ContentPropertyBase.cs" />
<Compile Include="Models\ContentEditing\ContentPropertyBasic.cs" />
<Compile Include="Models\ContentEditing\ContentPropertyDisplay.cs" />
<Compile Include="Models\ContentEditing\ContentPropertyDto.cs" />
<Compile Include="Standalone\ServiceContextManager.cs" />
@@ -428,7 +430,7 @@
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="UrlHelperExtensions.cs" />
<Compile Include="Editors\ContentEditorApiController.cs" />
<Compile Include="Editors\MediaController.cs" />
<Compile Include="WebApi\Binders\ContentItemBinder.cs" />
<Compile Include="WebApi\Filters\ContentItemValidationFilterAttribute.cs" />
<Compile Include="WebApi\Filters\FileUploadCleanupFilterAttribute.cs" />

View File

@@ -178,7 +178,7 @@ namespace Umbraco.Web
SurfaceControllerResolver.Current.RegisteredSurfaceControllers.Concat(
UmbracoApiControllerResolver.Current.RegisteredUmbracoApiControllers).ToArray();
//local controllers do not contain the attribute
//local controllers do not contain the attribute
var localControllers = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace());
foreach (var s in localControllers)
{
@@ -211,8 +211,7 @@ namespace Umbraco.Web
var route = RouteTable.Routes.MapHttpRoute(
string.Format("umbraco-{0}-{1}", "api", meta.ControllerName),
umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}",//url to match
new { controller = meta.ControllerName, id = UrlParameter.Optional },
new { controller = @"(\w+)Api" }); //Must be suffixed with "Api" (i.e. MyApiController)
new { controller = meta.ControllerName, id = UrlParameter.Optional });
//web api routes don't set the data tokens object
if (route.DataTokens == null)
{
@@ -229,9 +228,7 @@ namespace Umbraco.Web
var route = RouteTable.Routes.MapRoute(
string.Format("umbraco-{0}-{1}", "surface", meta.ControllerName),
umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}",//url to match
new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
//NOTE: There SHOULD be a constraint here to only match controllers with a "SurfaceController" suffix but we forgot to include it in the
// 4.10 release so we can't include it now :(
new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
new[] { meta.ControllerNamespace }); //look in this namespace to create the controller
route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set
route.DataTokens.Add("UseNamespaceFallback", false); //Don't look anywhere else except this namespace!