Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9310

# Conflicts:
#	src/Umbraco.Core/Models/UmbracoObjectTypes.cs
This commit is contained in:
Shannon
2017-02-01 15:23:29 +11:00
25 changed files with 312 additions and 64 deletions

View File

@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
alpha055
alpha056

View File

@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha055")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha056")]

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "alpha055"; } }
public static string CurrentComment { get { return "alpha056"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx

View File

@@ -162,6 +162,36 @@ namespace Umbraco.Core
/// Guid for a relation type.
/// </summary>
public static readonly Guid RelationTypeGuid = new Guid(RelationType);
/// <summary>
/// Guid for a Forms Form.
/// </summary>
public const string FormsForm = "F5A9F787-6593-46F0-B8FF-BFD9BCA9F6BB";
/// <summary>
/// Guid for a Forms Form.
/// </summary>
public static readonly Guid FormsFormGuid = new Guid(FormsForm);
/// <summary>
/// Guid for a Forms Workflow.
/// </summary>
public const string FormsWorkflow = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
/// <summary>
/// Guid for a Forms Workflow.
/// </summary>
public static readonly Guid FormsWorkflowGuid = new Guid(FormsWorkflow);
/// <summary>
/// Guid for a Forms Record.
/// </summary>
public const string FormsRecord = "CFED6CE4-9359-443E-9977-9956FEB1D867";
/// <summary>
/// Guid for a Forms Record.
/// </summary>
public static readonly Guid FormsRecordGuid = new Guid(FormsRecord);
}
}
}

View File

@@ -157,6 +157,27 @@ namespace Umbraco.Core.Models
[UmbracoObjectType(Constants.ObjectTypes.RelationType)]
[FriendlyName("Relation Type")]
[UmbracoUdiType(Constants.UdiEntityType.RelationType)]
RelationType
RelationType,
/// <summary>
/// Forms Form
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.FormsForm)]
[FriendlyName("Form")]
FormsForm,
/// <summary>
/// Forms Workflow
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.FormsWorkflow)]
[FriendlyName("Workflow")]
FormsWorkflow,
/// <summary>
/// Forms Record
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.FormsRecord)]
[FriendlyName("Record")]
FormsRecord
}
}

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
@@ -55,6 +55,11 @@ namespace Umbraco.Core
[UdiType(UdiType.GuidUdi)]
public const string RelationType = "relation-type";
// forms
public const string FormsForm = "forms-form";
public const string FormsWorkflow = "forms-workflow";
public const string FormsRecord = "forms-record";
// string entity types
@@ -108,6 +113,12 @@ namespace Umbraco.Core
return Stylesheet;
case UmbracoObjectTypes.RelationType:
return RelationType;
case UmbracoObjectTypes.FormsForm:
return FormsForm;
case UmbracoObjectTypes.FormsWorkflow:
return FormsWorkflow;
case UmbracoObjectTypes.FormsRecord:
return FormsRecord;
}
throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType));
}
@@ -144,6 +155,12 @@ namespace Umbraco.Core
return UmbracoObjectTypes.Stylesheet;
case RelationType:
return UmbracoObjectTypes.RelationType;
case FormsForm:
return UmbracoObjectTypes.FormsForm;
case FormsWorkflow:
return UmbracoObjectTypes.FormsWorkflow;
case FormsRecord:
return UmbracoObjectTypes.FormsRecord;
}
throw new NotSupportedException(
string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType));

View File

@@ -127,13 +127,13 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
* </pre>
*
* <pre>
* codefileResource.deleteByPath('partialView', 'Grid%2fEditors%2fBase.cshtml')
* codefileResource.deleteByPath('partialViews', 'Grid%2fEditors%2fBase.cshtml')
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {type} the type of script (partialView, partialViewMacro, script)
* @param {type} the type of script (partialViews, partialViewMacros, scripts)
* @param {virtualpath} the virtual path of the script
* @returns {Promise} resourcePromise object.
*
@@ -145,7 +145,7 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
"codeFileApiBaseUrl",
"Delete",
[{ type: type }, { virtualPath: virtualpath}])),
"Failed to delete item " + id);
"Failed to delete item: " + virtualpath);
},
/**

View File

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

View File

@@ -204,7 +204,7 @@ angular.module('umbraco.services')
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId };
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
@@ -232,7 +232,7 @@ angular.module('umbraco.services')
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId };
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
//TODO: This is a mega backwards compatibility hack... These variables SHOULD NOT exist in the server variables
// since they are not supposed to be dynamic but I accidentally added them there in 7.1.5 IIRC so some people might

View File

@@ -8,7 +8,7 @@
* The main application controller
*
*/
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) {
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService) {
//the null is important because we do an explicit bool check on this in the view
//the avatar is by default the umbraco logo
@@ -81,6 +81,14 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$location.path("/").search("");
historyService.removeAll();
treeService.clearCache();
//if the user changed, clearout local storage too - could contain sensitive data
localStorageService.clearAll();
}
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
if (data.loginType === "credentials") {
localStorageService.clearAll();
}
//Load locale file

View File

@@ -13,11 +13,7 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService',
/** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */
eventsService.on("app.authenticated", function(evt, data) {
//Removes all stored LocalStorage browser items - that may contain sensitive data
//So if a machine or computer is shared and a new user logs in, we clear out the previous persons localStorage items
localStorageService.clearAll();
assetsService._loadInitAssets().then(function() {
appState.setGlobalState("isReady", true);

View File

@@ -1,10 +1,10 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.DocumentType.DeleteController
* @name Umbraco.Editors.MemberTypes.DeleteController
* @function
*
* @description
* The controller for deleting content
* The controller for deleting member types
*/
function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) {

View File

@@ -0,0 +1,34 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.PartialViewMacros.DeleteController
* @function
*
* @description
* The controller for deleting partial view macros
*/
function PartialViewMacrosDeleteController($scope, codefileResource, treeService, navigationService) {
$scope.performDelete = function() {
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViewMacros', virtualPath)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
var rootNode = treeService.getTreeRoot($scope.currentNode);
//TODO: Need to sync tree, etc...
treeService.removeNode($scope.currentNode);
navigationService.hideMenu();
});
};
$scope.cancel = function() {
navigationService.hideDialog();
};
}
angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.DeleteController", PartialViewMacrosDeleteController);

View File

@@ -0,0 +1,12 @@
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.PartialViewMacros.DeleteController">
<div class="umb-dialog-body">
<p class="umb-abstract">
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong> ?
</p>
<umb-confirm on-confirm="performDelete" on-cancel="cancel">
</umb-confirm>
</div>
</div>

View File

@@ -0,0 +1,34 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.PartialViews.DeleteController
* @function
*
* @description
* The controller for deleting partial views
*/
function PartialViewsDeleteController($scope, codefileResource, treeService, navigationService) {
$scope.performDelete = function() {
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViews', virtualPath)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
var rootNode = treeService.getTreeRoot($scope.currentNode);
//TODO: Need to sync tree, etc...
treeService.removeNode($scope.currentNode);
navigationService.hideMenu();
});
};
$scope.cancel = function() {
navigationService.hideDialog();
};
}
angular.module("umbraco").controller("Umbraco.Editors.PartialViews.DeleteController", PartialViewsDeleteController);

View File

@@ -0,0 +1,12 @@
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.PartialViews.DeleteController">
<div class="umb-dialog-body">
<p class="umb-abstract">
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong> ?
</p>
<umb-confirm on-confirm="performDelete" on-cancel="cancel">
</umb-confirm>
</div>
</div>

View File

@@ -0,0 +1,34 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Scripts.DeleteController
* @function
*
* @description
* The controller for deleting scripts
*/
function ScriptsDeleteController($scope, codefileResource, treeService, navigationService) {
$scope.performDelete = function() {
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('scripts', virtualPath)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
var rootNode = treeService.getTreeRoot($scope.currentNode);
//TODO: Need to sync tree, etc...
treeService.removeNode($scope.currentNode);
navigationService.hideMenu();
});
};
$scope.cancel = function() {
navigationService.hideDialog();
};
}
angular.module("umbraco").controller("Umbraco.Editors.Scripts.DeleteController", ScriptsDeleteController);

View File

@@ -0,0 +1,12 @@
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.Scripts.DeleteController">
<div class="umb-dialog-body">
<p class="umb-abstract">
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong> ?
</p>
<umb-confirm on-confirm="performDelete" on-cancel="cancel">
</umb-confirm>
</div>
</div>

View File

@@ -4,11 +4,9 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Text;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.ModelBinding.Binders;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Logging;
@@ -17,24 +15,15 @@ using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Binders;
using Umbraco.Web.WebApi.Filters;
using umbraco;
using Umbraco.Core.Models;
using Umbraco.Core.Dynamics;
using umbraco.BusinessLogic.Actions;
using umbraco.cms.businesslogic.web;
using umbraco.presentation.preview;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.UI;
using Constants = Umbraco.Core.Constants;
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
namespace Umbraco.Web.Editors
{

View File

@@ -4,21 +4,26 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using System.Web.SessionState;
using AutoMapper;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using umbraco;
using Umbraco.Core;
namespace Umbraco.Web.Editors
{
/// <summary>
/// API controller to deal with Macro data
/// </summary>
/// <remarks>
/// Note that this implements IRequiresSessionState which will enable HttpContext.Session - generally speaking we don't normally
/// enable this for webapi controllers, however since this controller is used to render macro content and macros can access
/// Session, we don't want it to throw null reference exceptions.
/// </remarks>
[PluginController("UmbracoApi")]
public class MacroController : UmbracoAuthorizedJsonController
public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState
{
/// <summary>
/// Gets the macro parameters to be filled in for a particular macro
/// </summary>
@@ -124,6 +129,6 @@ namespace Umbraco.Web.Editors
"text/html");
return result;
}
}
}

View File

@@ -181,16 +181,10 @@ namespace Umbraco.Web.Models.Mapping
{
{"items", templateItemConfig}
}
},
new ContentPropertyDisplay
{
Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedText.Localize("content/urls"),
Value = string.Join(",", display.Urls),
View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor
}
};
TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText, properties.ToArray(),
genericProperties =>
{
@@ -222,6 +216,15 @@ namespace Umbraco.Web.Models.Mapping
//TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor
docTypeProperty.View = "urllist";
}
// inject 'Link to document' as the first generic property
genericProperties.Insert(0, new ContentPropertyDisplay
{
Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedText.Localize("content/urls"),
Value = string.Join(",", display.Urls),
View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor
});
});
}

View File

@@ -69,8 +69,8 @@ namespace Umbraco.Web.Models.Mapping
private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger)
{
// Adapted from ContentModelMapper
//map the IsChildOfListView (this is actually if it is a descendant of a list view!)
// Adapted from ContentModelMapper
//map the IsChildOfListView (this is actually if it is a descendant of a list view!)
//TODO: Fix this shorthand .Ancestors() lookup, at least have an overload to use the current
if (media.HasIdentity)
{
@@ -95,7 +95,7 @@ namespace Umbraco.Web.Models.Mapping
display.IsChildOfListView = ancesctorListView != null;
}
}
//map the tree node url
if (HttpContext.Current != null)
{
@@ -103,12 +103,12 @@ namespace Umbraco.Web.Models.Mapping
var url = urlHelper.GetUmbracoApiService<MediaTreeController>(controller => controller.GetTreeNode(display.Id.ToString(), null));
display.TreeNodeUrl = url;
}
if (media.ContentType.IsContainer)
{
TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, localizedText);
}
var genericProperties = new List<ContentPropertyDisplay>
{
new ContentPropertyDisplay
@@ -120,20 +120,6 @@ namespace Umbraco.Web.Models.Mapping
}
};
var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger);
if (links.Any())
{
var link = new ContentPropertyDisplay
{
Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedText.Localize("media/urls"),
Value = string.Join(",", links),
View = "urllist"
};
genericProperties.Add(link);
}
TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties, properties =>
{
if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null
@@ -155,6 +141,20 @@ namespace Umbraco.Web.Models.Mapping
};
docTypeProperty.View = "urllist";
}
// inject 'Link to media' as the first generic property
var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger);
if (links.Any())
{
var link = new ContentPropertyDisplay
{
Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedText.Localize("media/urls"),
Value = string.Join(",", links),
View = "urllist"
};
properties.Insert(0, link);
}
});
}
}

View File

@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
@@ -93,6 +95,13 @@ namespace Umbraco.Web.Mvc
}
//look in this namespace to create the controller
controllerPluginRoute.DataTokens.Add("Namespaces", new[] {controllerType.Namespace});
//Special case! Check if the controller type implements IRequiresSessionState and if so use our
//custom webapi session handler
if (typeof(IRequiresSessionState).IsAssignableFrom(controllerType))
{
controllerPluginRoute.RouteHandler = new SessionHttpControllerRouteHandler();
}
}
//Don't look anywhere else except this namespace!
@@ -100,9 +109,9 @@ namespace Umbraco.Web.Mvc
//constraints: only match controllers ending with 'controllerSuffixName' and only match this controller's ID for this route
if (controllerSuffixName.IsNullOrWhiteSpace() == false)
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", @"(\w+)" + controllerSuffixName}
});

View File

@@ -972,6 +972,7 @@
<Compile Include="WebApi\NamespaceHttpControllerSelector.cs" />
<Compile Include="WebApi\PrefixlessBodyModelValidator.cs" />
<Compile Include="WebApi\PrefixlessBodyModelValidatorAttribute.cs" />
<Compile Include="WebApi\SessionHttpControllerRouteHandler.cs" />
<Compile Include="WebApi\UmbracoApiController.cs" />
<Compile Include="WebApi\UmbracoApiControllerBase.cs" />
<Compile Include="WebApi\UmbracoApiControllerResolver.cs" />

View File

@@ -0,0 +1,31 @@
using System.Web;
using System.Web.Http.WebHost;
using System.Web.Routing;
using System.Web.SessionState;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// A custom WebApi route handler that enables session on the HttpContext - use with caution!
/// </summary>
/// <remarks>
/// WebApi controllers (and REST in general) shouldn't have session state enabled since it's stateless,
/// enabling session state puts additional locks on requests so only use this when absolutley needed
/// </remarks>
internal class SessionHttpControllerRouteHandler : HttpControllerRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new SessionHttpControllerHandler(requestContext.RouteData);
}
/// <summary>
/// A custom WebApi handler that enables session on the HttpContext
/// </summary>
private class SessionHttpControllerHandler : HttpControllerHandler, IRequiresSessionState
{
public SessionHttpControllerHandler(RouteData routeData) : base(routeData)
{ }
}
}
}