Merge branch 'temp8' into temp8-macros-dont-render-and-other-macro-bugs
# Conflicts: # src/Umbraco.Tests/Testing/TestingTests/MockTests.cs # src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs # src/Umbraco.Web/Templates/TemplateRenderer.cs
This commit is contained in:
@@ -150,7 +150,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
urlHelper.Setup(provider => provider.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlProviderMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns(UrlInfo.Url("/hello/world/1234"));
|
||||
|
||||
var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), null, Mock.Of<AppCaches>(), Mock.Of<ILogger>());
|
||||
var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), Mock.Of<AppCaches>(), Mock.Of<ILogger>());
|
||||
|
||||
var umbHelper = new UmbracoHelper(umbCtx,
|
||||
Mock.Of<ITagQuery>(),
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Umbraco.Tests.Testing.TestingTests
|
||||
Mock.Of<ICultureDictionaryFactory>(),
|
||||
Mock.Of<IUmbracoComponentRenderer>(),
|
||||
Mock.Of<IPublishedContentQuery>(),
|
||||
new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), null, Mock.Of<AppCaches>(), Mock.Of<ILogger>()));
|
||||
new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), Mock.Of<AppCaches>(), Mock.Of<ILogger>()));
|
||||
Assert.Pass();
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Umbraco.Tests.Testing
|
||||
|
||||
// web
|
||||
Composition.RegisterUnique(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor);
|
||||
Composition.RegisterUnique<PublishedRouter>();
|
||||
Composition.RegisterUnique<IPublishedRouter, PublishedRouter>();
|
||||
Composition.WithCollectionBuilder<ContentFinderCollectionBuilder>();
|
||||
Composition.RegisterUnique<IContentLastChanceFinder, TestLastChanceFinder>();
|
||||
Composition.RegisterUnique<IVariationContextAccessor, TestVariationContextAccessor>();
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Tests.Web.Mvc
|
||||
Mock.Of<ICultureDictionaryFactory>(),
|
||||
Mock.Of<IUmbracoComponentRenderer>(),
|
||||
Mock.Of<IPublishedContentQuery>(),
|
||||
new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), null, Mock.Of<AppCaches>(), Mock.Of<ILogger>()));
|
||||
new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), Mock.Of<AppCaches>(), Mock.Of<ILogger>()));
|
||||
|
||||
var ctrl = new TestSurfaceController(umbracoContext, helper);
|
||||
var result = ctrl.GetContent(2) as PublishedContentResult;
|
||||
|
||||
@@ -26,20 +26,43 @@
|
||||
$scope.allowOpen = true;
|
||||
$scope.app = null;
|
||||
|
||||
function init(content) {
|
||||
if (!$scope.app) {
|
||||
// set first app to active
|
||||
function init() {
|
||||
|
||||
var content = $scope.content;
|
||||
|
||||
// we need to check wether an app is present in the current data, if not we will present the default app.
|
||||
var isAppPresent = false;
|
||||
|
||||
// on first init, we dont have any apps. but if we are re-initializing, we do, but ...
|
||||
if ($scope.app) {
|
||||
|
||||
// lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.)
|
||||
_.forEach(content.apps, function(app) {
|
||||
if (app === $scope.app) {
|
||||
isAppPresent = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if we did reload our DocType, but still have the same app we will try to find it by the alias.
|
||||
if (isAppPresent === false) {
|
||||
_.forEach(content.apps, function(app) {
|
||||
if (app.alias === $scope.app.alias) {
|
||||
isAppPresent = true;
|
||||
app.active = true;
|
||||
$scope.appChanged(app);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we still dont have a app, lets show the first one:
|
||||
if (isAppPresent === false) {
|
||||
content.apps[0].active = true;
|
||||
$scope.app = content.apps[0];
|
||||
$scope.appChanged(content.apps[0]);
|
||||
}
|
||||
|
||||
if (infiniteMode) {
|
||||
createInfiniteModeButtons(content);
|
||||
} else {
|
||||
createButtons(content);
|
||||
}
|
||||
|
||||
editorState.set($scope.content);
|
||||
|
||||
editorState.set(content);
|
||||
|
||||
//We fetch all ancestors of the node to generate the footer breadcrumb navigation
|
||||
if (!$scope.page.isNew) {
|
||||
@@ -129,7 +152,7 @@
|
||||
"/content/content/edit/" + data.parentId;
|
||||
}
|
||||
|
||||
init($scope.content);
|
||||
init();
|
||||
|
||||
syncTreeNode($scope.content, $scope.content.path, true);
|
||||
|
||||
@@ -340,7 +363,7 @@
|
||||
showNotifications: args.showNotifications
|
||||
}).then(function (data) {
|
||||
//success
|
||||
init($scope.content);
|
||||
init();
|
||||
syncTreeNode($scope.content, data.path);
|
||||
|
||||
eventsService.emit("content.saved", { content: $scope.content, action: args.action });
|
||||
@@ -414,7 +437,7 @@
|
||||
|
||||
$scope.content = data;
|
||||
|
||||
init($scope.content);
|
||||
init();
|
||||
|
||||
resetLastListPageNumber($scope.content);
|
||||
|
||||
@@ -454,7 +477,7 @@
|
||||
.then(function (data) {
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
contentEditingHelper.reBindChangedProperties($scope.content, data);
|
||||
init($scope.content);
|
||||
init();
|
||||
syncTreeNode($scope.content, data.path);
|
||||
$scope.page.buttonGroupState = "success";
|
||||
eventsService.emit("content.unpublished", { content: $scope.content });
|
||||
@@ -845,8 +868,14 @@
|
||||
* @param {any} app
|
||||
*/
|
||||
$scope.appChanged = function (app) {
|
||||
|
||||
$scope.app = app;
|
||||
createButtons($scope.content);
|
||||
|
||||
if (infiniteMode) {
|
||||
createInfiniteModeButtons($scope.content);
|
||||
} else {
|
||||
createButtons($scope.content);
|
||||
}
|
||||
};
|
||||
|
||||
// methods for infinite editing
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
}
|
||||
|
||||
//when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
|
||||
appState.setMenuState("currentNode", scope.currentNode);
|
||||
|
||||
// Niels: No i think we are wrong, we should not set the currentNode, cause it represents the currentNode of interaction.
|
||||
//appState.setMenuState("currentNode", scope.currentNode);
|
||||
|
||||
if (!scope.actions) {
|
||||
treeService.getMenu({ treeNode: scope.currentNode })
|
||||
.then(function (data) {
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
|
||||
vm.isLoading = false;
|
||||
|
||||
configureViewModel();
|
||||
//ensure that the models are formatted correctly
|
||||
configureViewModel(true);
|
||||
|
||||
// Set the visible prompt to -1 to ensure it will not be visible
|
||||
vm.promptIsVisible = "-1";
|
||||
@@ -123,9 +124,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for value changes
|
||||
* @param {any} changes
|
||||
*/
|
||||
function onChanges(changes) {
|
||||
|
||||
// watch for value changes externally
|
||||
//when the model 'value' changes, sync the viewModel object
|
||||
if (changes.value) {
|
||||
if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) {
|
||||
|
||||
@@ -145,13 +150,15 @@
|
||||
$element.find('.tags-' + vm.htmlId).typeahead('destroy');
|
||||
}
|
||||
|
||||
function configureViewModel() {
|
||||
function configureViewModel(isInitLoad) {
|
||||
if (vm.value) {
|
||||
if (angular.isString(vm.value) && vm.value.length > 0) {
|
||||
if (vm.config.storageType === "Json") {
|
||||
//json storage
|
||||
vm.viewModel = JSON.parse(vm.value);
|
||||
updateModelValue(vm.viewModel);
|
||||
if (!isInitLoad) {
|
||||
updateModelValue(vm.viewModel);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//csv storage
|
||||
@@ -165,7 +172,10 @@
|
||||
return self.indexOf(v) === i;
|
||||
});
|
||||
|
||||
updateModelValue(vm.viewModel);
|
||||
if (!isInitLoad) {
|
||||
updateModelValue(vm.viewModel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (angular.isArray(vm.value)) {
|
||||
@@ -175,13 +185,18 @@
|
||||
}
|
||||
|
||||
function updateModelValue(val) {
|
||||
if (val) {
|
||||
vm.onValueChanged({ value: val });
|
||||
|
||||
//need to format the underlying model value for persistence based on the storage type
|
||||
if (vm.config.storageType === "Json") {
|
||||
val = val ? val : [];
|
||||
}
|
||||
else {
|
||||
vm.onValueChanged({ value: [] });
|
||||
//then it is csv and we need to format it like that
|
||||
val = val ? val.join() : "";
|
||||
}
|
||||
|
||||
vm.onValueChanged({ value: val });
|
||||
|
||||
reValidate();
|
||||
}
|
||||
|
||||
@@ -273,8 +288,10 @@
|
||||
}
|
||||
|
||||
function reValidate() {
|
||||
//this is required to re-validate
|
||||
vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
|
||||
//this is required to re-validate for the mandatory validation
|
||||
if (vm.tagEditorForm && vm.tagEditorForm.tagCount) {
|
||||
vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,10 +60,13 @@ angular.module("umbraco.directives")
|
||||
if (!node) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
// TODO: This is called constantly because as a method in a template it's re-evaluated pretty much all the time
|
||||
// it would be better if we could cache the processing. The problem is that some of these things are dynamic.
|
||||
|
||||
//is this the current action node (this is not the same as the current selected node!)
|
||||
var actionNode = appState.getMenuState("currentNode");
|
||||
|
||||
var css = [];
|
||||
if (node.cssClasses) {
|
||||
_.each(node.cssClasses, function(c) {
|
||||
@@ -73,17 +76,27 @@ angular.module("umbraco.directives")
|
||||
if (node.selected) {
|
||||
css.push("umb-tree-node-checked");
|
||||
}
|
||||
if (node == scope.currentNode) {
|
||||
css.push("current");
|
||||
if (actionNode && actionNode.id !== node.id) {
|
||||
css.push("current-not-active");// when its the current node, but its not the active(current node for the given action)
|
||||
}
|
||||
}
|
||||
if (node.hasChildren) {
|
||||
css.push("has-children");
|
||||
}
|
||||
if (node.deleteAnimations) {
|
||||
css.push("umb-tree-item--deleted");
|
||||
}
|
||||
|
||||
//is this the current action node (this is not the same as the current selected node!)
|
||||
var actionNode = appState.getMenuState("currentNode");
|
||||
if (actionNode) {
|
||||
if (actionNode.id === node.id && String(actionNode.id) !== "-1") {
|
||||
if (actionNode.id === node.id && String(node.id) !== "-1") {
|
||||
css.push("active");
|
||||
}
|
||||
|
||||
|
||||
// special handling of root nodes with id -1
|
||||
// as there can be many nodes with id -1 in a tree we need to check the treeAlias instead
|
||||
if (String(actionNode.id) === "-1" && actionNode.metaData.treeAlias === node.metaData.treeAlias) {
|
||||
if (String(node.id) === "-1" && actionNode.metaData.treeAlias === node.metaData.treeAlias) {
|
||||
css.push("active");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,11 +140,11 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (scope.items.length > 0) {
|
||||
setFlexValues(scope.items);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function setItemData(item) {
|
||||
@@ -235,7 +235,7 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function setFlexValues(mediaItems) {
|
||||
|
||||
var flexSortArray = mediaItems;
|
||||
@@ -269,12 +269,12 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
"min-height": itemMinHeight + "px"
|
||||
};
|
||||
|
||||
mediaItem.flexStyle = flexStyle;
|
||||
mediaItem.flexStyle = flexStyle;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
scope.clickItem = function(item, $event, $index) {
|
||||
if (scope.onClick) {
|
||||
scope.onClick(item, $event, $index);
|
||||
|
||||
@@ -34,7 +34,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is activated, this is the same as clicking it and we need to set the
|
||||
//current menu item to be this node as well.
|
||||
appState.setMenuState("currentNode", args.node);
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
}
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
|
||||
args.event.preventDefault();
|
||||
|
||||
//Set the current action node (this is not the same as the current selected node!)
|
||||
appState.setMenuState("currentNode", args.node);
|
||||
//appState.setMenuState("currentNode", args.node);// Niels: No, we are setting it from the dialog.
|
||||
|
||||
if (args.event && args.event.altKey) {
|
||||
args.skipDefault = true;
|
||||
@@ -96,7 +96,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
|
||||
//put this node into the tree state
|
||||
appState.setTreeState("selectedNode", args.node);
|
||||
//when a node is clicked we also need to set the active menu node to this node
|
||||
appState.setMenuState("currentNode", args.node);
|
||||
//appState.setMenuState("currentNode", args.node);
|
||||
|
||||
//not legacy, lets just set the route value and clear the query string if there is one.
|
||||
$location.path(n.routePath);
|
||||
|
||||
@@ -197,7 +197,12 @@ input[type="button"] {
|
||||
}
|
||||
// Made for Umbraco, 2019
|
||||
.btn-action {
|
||||
.buttonBackground(@pinkLight, @blueDark, @blueExtraDark, @u-white);
|
||||
.buttonBackground(@blueExtraDark, @blueDark, @pinkLight, @u-white);
|
||||
}
|
||||
// Made for Umbraco, 2019
|
||||
.btn-selection {
|
||||
@btnSelectionBackgroundHover: darken(@pinkLight, 10%);
|
||||
.buttonBackground(@pinkLight, @btnSelectionBackgroundHover, @blueExtraDark, @blueDark);
|
||||
}
|
||||
// Made for Umbraco, 2019, used for buttons that has to stand back.
|
||||
.btn-white {
|
||||
|
||||
@@ -45,7 +45,6 @@ h4 {
|
||||
|
||||
h5 {
|
||||
margin: 0 0 6px 0;
|
||||
color: #535353;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
display: block;
|
||||
min-width: 100%;
|
||||
width: auto;
|
||||
margin-top:1px;
|
||||
|
||||
.umb-tree-item__label {
|
||||
user-select: none;
|
||||
@@ -44,6 +45,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
// active is equivilant to selected, its the item that is begin affected by the actions performed in the right-click-dialog.
|
||||
.umb-tree-item.active {
|
||||
|
||||
}
|
||||
.umb-tree-item.active > .umb-tree-item__inner {
|
||||
//background: @ui-selected;
|
||||
color: @ui-selected-type;
|
||||
a {
|
||||
color: @ui-selected-type;
|
||||
}
|
||||
|
||||
border-color: @ui-selected-border;
|
||||
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 2px solid fade(white, 80%);
|
||||
}
|
||||
&:hover {
|
||||
//background: @ui-selected-hover;
|
||||
//border-color: @ui-selected-border-hover;// dont hover border, cause its cant be deselected in current code.
|
||||
color: @ui-selected-type-hover;
|
||||
a {
|
||||
color: @ui-selected-type-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-tree-item.current > .umb-tree-item__inner {
|
||||
|
||||
background: @ui-active;
|
||||
@@ -75,3 +108,10 @@
|
||||
//border-color: @ui-active;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-tree-item.current-not-active > .umb-tree-item__inner {
|
||||
|
||||
background: @ui-active-blur;
|
||||
color:@ui-active-type;
|
||||
|
||||
}
|
||||
|
||||
@@ -87,23 +87,6 @@ body.touch .umb-tree {
|
||||
}
|
||||
}
|
||||
|
||||
// active is equivilant to selected, its the item that is begin affected by the actions performed in the right-click-dialog.
|
||||
.umb-tree-item > .umb-tree-item__inner.active {
|
||||
//background: @ui-selected;
|
||||
border-color: @ui-selected-border;
|
||||
color: @ui-selected-type;
|
||||
a {
|
||||
color: @ui-selected-type;
|
||||
}
|
||||
&:hover {
|
||||
//background: @ui-selected-hover;
|
||||
border-color: @ui-selected-border-hover;
|
||||
color: @ui-selected-type-hover;
|
||||
a {
|
||||
color: @ui-selected-type-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
.umb-tree-root, .umb-tree-item__inner {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
@@ -112,7 +95,7 @@ body.touch .umb-tree {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
|
||||
border:2px dashed transparent;
|
||||
border:2px solid transparent;
|
||||
|
||||
color: @ui-option-type;
|
||||
a {
|
||||
|
||||
@@ -14,10 +14,32 @@
|
||||
user-select: none;
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
|
||||
border-radius: 3px;
|
||||
|
||||
color: @ui-option-type;
|
||||
&:hover {
|
||||
color:@ui-option-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-content-grid__item.-selected {
|
||||
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.35);
|
||||
//box-shadow: 0 2px 8px 0 rgba(0,0,0,0.35);
|
||||
|
||||
//color:@ui-selected-type;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index:2;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
border: 2px solid @ui-selected-border;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-content-grid__icon-container {
|
||||
@@ -31,12 +53,12 @@
|
||||
.umb-content-grid__icon[class^="icon-"],
|
||||
.umb-content-grid__icon[class*=" icon-"] {
|
||||
font-size: 20px;
|
||||
color: @gray-8;
|
||||
//color: @gray-8;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.umb-content-grid__icon.-light {
|
||||
color: @gray-5;
|
||||
//color: @gray-5;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +70,7 @@
|
||||
.umb-content-grid__item-name {
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: @black;
|
||||
//color: @black;
|
||||
line-height: 1.4em;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
flex: 1 1 200px;
|
||||
border: 2px dashed transparent;
|
||||
//border: 2px solid transparent;
|
||||
transition: border 0.2s;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
@@ -27,15 +27,29 @@
|
||||
}
|
||||
|
||||
.umb-folder-grid__folder.-selected {
|
||||
box-shadow: 0 2px 8px 0 darken(@ui-selected-border, 20);
|
||||
//box-shadow: 0 2px 8px 0 darken(@ui-selected-border, 20);
|
||||
|
||||
//background: @ui-selected;
|
||||
color:@ui-selected-type;
|
||||
border-color:@ui-selected-border;
|
||||
//border-color:@ui-selected-border;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index:2;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
border: 2px solid @ui-selected-border;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color:@ui-selected-type-hover;
|
||||
border-color:@ui-selected-border-hover;
|
||||
//border-color:@ui-selected-border-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +73,12 @@
|
||||
.umb-folder-grid__folder-icon[class*=" icon-"] {
|
||||
font-size: 20px;
|
||||
margin-right: 15px;
|
||||
color: @black;
|
||||
//color: @black;
|
||||
}
|
||||
|
||||
.umb-folder-grid__folder-name {
|
||||
font-size: 13px;
|
||||
color: @black;
|
||||
//color: @black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
.umb-layout-selector__active-layout {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border: 1px solid @inputBorder;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
.umb-layout-selector__active-layout:hover {
|
||||
border-color: @gray-8;
|
||||
border-color: @inputBorderFocus;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown {
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
|
||||
margin: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
//overflow: hidden;
|
||||
|
||||
user-select: none;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
border: 2px dashed transparent;
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,.2);
|
||||
//border: 2px solid transparent;
|
||||
|
||||
transition: box-shadow 150ms ease-in-out;
|
||||
}
|
||||
@@ -35,16 +38,31 @@
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-selected {
|
||||
box-shadow: 0 2px 8px 0 darken(@ui-selected-border, 20);
|
||||
|
||||
//background: @ui-selected;
|
||||
color:@ui-selected-type;
|
||||
border-color:@ui-selected-border;
|
||||
//border-color: @ui-selected-border;
|
||||
//box-shadow: 0 2px 8px 0 darken(@ui-selected-border, 20);
|
||||
|
||||
&:hover {
|
||||
color:@ui-selected-type-hover;
|
||||
border-color:@ui-selected-border-hover;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index:2;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
border: 2px solid @ui-selected-border;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.umb-media-grid__item-overlay {
|
||||
color: @ui-selected-type;
|
||||
//background: @ui-selected-border;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-media-grid__item-file-icon > span {
|
||||
@@ -64,16 +82,22 @@
|
||||
}
|
||||
|
||||
.umb-media-grid__item-image {
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
//max-width: 100% !important;
|
||||
//height: auto;
|
||||
position: relative;
|
||||
|
||||
object-fit: contain;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.umb-media-grid__item-image-placeholder {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
//max-width: 100%;
|
||||
//height: auto;
|
||||
position: relative;
|
||||
|
||||
object-fit: contain;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.umb-media-grid__image-background {
|
||||
@@ -95,17 +119,22 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
z-index: 1;
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
color: @black;
|
||||
white-space: nowrap;
|
||||
border-top:1px solid @gray-9;
|
||||
background: @white;
|
||||
border-top:1px solid fade(black, 4%);
|
||||
background: fade(@white, 92%);
|
||||
transition: opacity 150ms;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.umb-media-grid__item.-file .umb-media-grid__item-overlay {
|
||||
opacity: 1;
|
||||
@@ -180,6 +209,10 @@
|
||||
align-items: center;
|
||||
color: @black;
|
||||
transition: opacity 150ms;
|
||||
|
||||
&:hover {
|
||||
color: @ui-action-disgrete-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-media-grid__item:hover .umb-media-grid__edit {
|
||||
|
||||
@@ -90,13 +90,13 @@ input.umb-table__input {
|
||||
|
||||
.umb-table-body .umb-table-row {
|
||||
color: @gray-5;
|
||||
border-top: 1px solid @gray-8;
|
||||
border-top: 1px solid @gray-9;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
min-height: 52px;
|
||||
&:hover {
|
||||
background-color: @gray-10;
|
||||
background-color: @ui-option-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +151,26 @@ input.umb-table__input {
|
||||
|
||||
// Show checkmark when checked, hide file icon
|
||||
.umb-table-row--selected {
|
||||
/*
|
||||
.umb-table-body__fileicon {
|
||||
display: none;
|
||||
}
|
||||
.umb-table-body__checkicon {
|
||||
display: inline-block;
|
||||
}
|
||||
*/
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index:1;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
border: 2px solid @ui-selected-border;
|
||||
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Table Row Styles
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
@pinkRedLight: #ff8a89;// added 2019
|
||||
@brown: #9d8057;// added 2019
|
||||
@brownLight: #e4e0dd;// added 2019
|
||||
@brownGrayLight: #f3f2f1;// added 2019
|
||||
@brownGrayLight: #f6f4f4;// added 2019
|
||||
@orange: #ff9412;// added 2019
|
||||
|
||||
//@u-greyLight: #f2ebe6;// added 2019
|
||||
@@ -134,6 +134,7 @@
|
||||
|
||||
//@ui-active: #346ab3;
|
||||
@ui-active: @pinkLight;
|
||||
@ui-active-blur: @brownLight;
|
||||
@ui-active-type: @blueExtraDark;
|
||||
@ui-active-type-hover: @blueMid;
|
||||
|
||||
@@ -142,7 +143,7 @@
|
||||
@ui-selected-type: @blueExtraDark;
|
||||
@ui-selected-type-hover: @blueMid;
|
||||
@ui-selected-border: @pinkLight;
|
||||
@ui-selected-border-hover: @pinkLight;
|
||||
@ui-selected-border-hover: darken(@pinkLight, 10);
|
||||
|
||||
@ui-light-border: @pinkLight;
|
||||
@ui-light-type: @gray-4;
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
<li class="umb-tree-item" data-element="tree-item-{{::node.dataElement}}" ng-class="{'current': (node == currentNode), 'has-children': node.hasChildren, 'umb-tree-item--deleted': node.deleteAnimations}" on-right-click="altSelect(node, $event)">
|
||||
<div class="umb-tree-item__inner" ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" ng-dblclick="load(node)" >
|
||||
<ins data-element="tree-item-expand"
|
||||
ng-class="{'icon-navigation-right': !node.expanded || node.metaData.isContainer, 'icon-navigation-down': node.expanded && !node.metaData.isContainer}"
|
||||
<li class="umb-tree-item" data-element="tree-item-{{::node.dataElement}}" ng-class="getNodeCssClass(node)" on-right-click="altSelect(node, $event)">
|
||||
<div class="umb-tree-item__inner" ng-swipe-right="options(node, $event)" ng-dblclick="load(node)" >
|
||||
<ins data-element="tree-item-expand"
|
||||
ng-class="{'icon-navigation-right': !node.expanded || node.metaData.isContainer, 'icon-navigation-down': node.expanded && !node.metaData.isContainer}"
|
||||
ng-style="{'visibility': (scope.enablelistviewexpand === 'true' || node.hasChildren && (!node.metaData.isContainer || isDialog)) ? 'visible' : 'hidden'}"
|
||||
ng-click="load(node)"> </ins>
|
||||
|
||||
<i class="icon umb-tree-icon sprTree" ng-class="::node.cssClass" title="{{::node.routePath}}" ng-click="select(node, $event)" ng-style="::node.style"></i>
|
||||
<a class="umb-tree-item__label" ng-href="#/{{::node.routePath}}" ng-click="select(node, $event)">{{node.name}}</a>
|
||||
|
||||
|
||||
<!-- NOTE: These are the 'option' elipses -->
|
||||
<a data-element="tree-item-options" class="umb-options" ng-click="options(node, $event)" ng-if="::node.menuUrl"><i></i><i></i><i></i></a>
|
||||
<div ng-show="node.loading" class="l"><div></div></div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul ng-class="{collapsed: !node.expanded}">
|
||||
<umb-tree-item class="umb-animated" ng-repeat="child in node.children track by child.id" enablelistviewexpand="{{enablelistviewexpand}}" tree="tree" current-node="currentNode" node="child" is-dialog="isDialog" section="{{section}}"></umb-tree-item>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
ng-repeat="item in content"
|
||||
ng-class="{'-selected': item.selected}"
|
||||
ng-click="clickItem(item, $event, $index)">
|
||||
|
||||
<i ng-if="item.selected" class="icon-check umb-content-grid__checkmark"></i>
|
||||
|
||||
<!--<i ng-if="item.selected" class="icon-check umb-content-grid__checkmark"></i>-->
|
||||
|
||||
<div class="umb-content-grid__content">
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
hide-description="true"
|
||||
hide-alias="true"
|
||||
navigation="content.apps"
|
||||
on-select-navigation-item="appChanged(app)">
|
||||
on-select-navigation-item="appChanged(item)">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* @description
|
||||
* The controller for the media editor
|
||||
*/
|
||||
function mediaEditController($scope, $routeParams, $q, appState, mediaResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, editorState, umbRequestHelper, $http, eventsService) {
|
||||
function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
|
||||
entityResource, navigationService, notificationsService, angularHelper,
|
||||
serverValidationManager, contentEditingHelper, fileManager, formHelper,
|
||||
editorState, umbRequestHelper, $http, eventsService) {
|
||||
|
||||
var evts = [];
|
||||
var nodeId = null;
|
||||
@@ -41,7 +44,91 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
|
||||
$scope.page.listViewPath = null;
|
||||
$scope.page.saveButtonState = "init";
|
||||
$scope.page.submitButtonLabelKey = "buttons_save";
|
||||
$scope.app = null;
|
||||
|
||||
if (create) {
|
||||
|
||||
$scope.page.loading = true;
|
||||
|
||||
mediaResource.getScaffold(nodeId, $routeParams.doctype)
|
||||
.then(function (data) {
|
||||
$scope.content = data;
|
||||
|
||||
init();
|
||||
|
||||
$scope.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.page.loading = true;
|
||||
loadMedia()
|
||||
.then(function(){
|
||||
$scope.page.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
var content = $scope.content;
|
||||
|
||||
// we need to check wether an app is present in the current data, if not we will present the default app.
|
||||
var isAppPresent = false;
|
||||
|
||||
// on first init, we dont have any apps. but if we are re-initializing, we do, but ...
|
||||
if ($scope.app) {
|
||||
|
||||
// lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.)
|
||||
_.forEach(content.apps, function(app) {
|
||||
if (app === $scope.app) {
|
||||
isAppPresent = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if we did reload our DocType, but still have the same app we will try to find it by the alias.
|
||||
if (isAppPresent === false) {
|
||||
_.forEach(content.apps, function(app) {
|
||||
if (app.alias === $scope.app.alias) {
|
||||
isAppPresent = true;
|
||||
app.active = true;
|
||||
$scope.appChanged(app);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we still dont have a app, lets show the first one:
|
||||
if (isAppPresent === false) {
|
||||
content.apps[0].active = true;
|
||||
$scope.appChanged(content.apps[0]);
|
||||
}
|
||||
|
||||
|
||||
editorState.set($scope.content);
|
||||
|
||||
bindEvents();
|
||||
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
//bindEvents can be called more than once and we don't want to have multiple bound events
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
|
||||
evts.push(eventsService.on("editors.mediaType.saved", function(name, args) {
|
||||
// if this media item uses the updated media type we need to reload the media item
|
||||
if(args && args.mediaType && args.mediaType.key === $scope.content.contentType.key) {
|
||||
$scope.page.loading = true;
|
||||
loadMedia().then(function() {
|
||||
$scope.page.loading = false;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
$scope.page.submitButtonLabelKey = "buttons_save";
|
||||
|
||||
/** Syncs the content item to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(content, path, initialLoad) {
|
||||
|
||||
@@ -68,45 +155,6 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (create) {
|
||||
|
||||
$scope.page.loading = true;
|
||||
|
||||
mediaResource.getScaffold(nodeId, $routeParams.doctype)
|
||||
.then(function (data) {
|
||||
$scope.content = data;
|
||||
|
||||
editorState.set($scope.content);
|
||||
|
||||
init();
|
||||
|
||||
$scope.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.page.loading = true;
|
||||
loadMedia()
|
||||
.then(function(){
|
||||
$scope.page.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
if (!$scope.app) {
|
||||
// set first app to active
|
||||
$scope.content.apps[0].active = true;
|
||||
$scope.app = $scope.content.apps[0];
|
||||
}
|
||||
|
||||
// setup infinite mode
|
||||
if(infiniteMode) {
|
||||
$scope.page.submitButtonLabelKey = "buttons_saveAndClose";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$scope.save = function () {
|
||||
|
||||
@@ -212,14 +260,12 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource,
|
||||
|
||||
$scope.appChanged = function (app) {
|
||||
$scope.app = app;
|
||||
}
|
||||
|
||||
evts.push(eventsService.on("editors.mediaType.saved", function(name, args) {
|
||||
// if this media item uses the updated media type we need to reload the media item
|
||||
if(args && args.mediaType && args.mediaType.key === $scope.content.contentType.key) {
|
||||
loadMedia();
|
||||
|
||||
// setup infinite mode
|
||||
if(infiniteMode) {
|
||||
$scope.page.submitButtonLabelKey = "buttons_saveAndClose";
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper, eventsService) {
|
||||
function MediaTypesEditController($scope, $routeParams, mediaTypeResource,
|
||||
dataTypeResource, editorState, contentEditingHelper, formHelper,
|
||||
navigationService, iconHelper, contentTypeHelper, notificationsService,
|
||||
$filter, $q, localizationService, overlayHelper, eventsService) {
|
||||
|
||||
var vm = this;
|
||||
var evts = [];
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
<p><span>{{item.alias}}</span> <small>({{item.width}}px × {{item.height}}px)</small></p>
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<a href="#" prevent-default class="umb-node-preview__action umb-node-preview__action--red" ng-click="edit(item, $event)" class="umb-prevalues-multivalues__action">Edit</a>
|
||||
<a class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></a>
|
||||
<a href="#" prevent-default class="umb-node-preview__action" ng-click="edit(item, $event)"><localize key="general_edit">Edit</localize></a>
|
||||
<a href="#" prevent-default class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
<umb-editor-sub-header-section ng-if="(listViewAllowedTypes && listViewAllowedTypes.length > 0 && (selection.length == 0)) && (currentNodePermissions == null || currentNodePermissions.canCreate)">
|
||||
|
||||
<div class="btn-group" ng-show="createAllowedButtonSingle">
|
||||
<a class="btn btn-success" ng-click="createBlank(entityType,listViewAllowedTypes[0].alias)">
|
||||
<a class="btn btn-white" ng-click="createBlank(entityType,listViewAllowedTypes[0].alias)">
|
||||
<localize key="actions_create">Create</localize> {{listViewAllowedTypes[0].name}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" ng-show="createAllowedButtonSingleWithBlueprints">
|
||||
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown" ng-href="">
|
||||
<a class="btn btn-white dropdown-toggle" data-toggle="dropdown" ng-href="">
|
||||
<span ng-click="createBlank(entityType,listViewAllowedTypes[0].alias)">
|
||||
<localize key="actions_create">Create</localize> {{listViewAllowedTypes[0].name}}
|
||||
</span>
|
||||
@@ -85,6 +85,7 @@
|
||||
type="button"
|
||||
label="Clear selection"
|
||||
label-key="buttons_clearSelection"
|
||||
button-style="selection"
|
||||
action="clearSelection()"
|
||||
disabled="actionInProgress">
|
||||
</umb-button>
|
||||
@@ -139,7 +140,7 @@
|
||||
ng-if="options.allowBulkPublish && (buttonPermissions == null || buttonPermissions.canPublish)"
|
||||
style="margin-right: 5px;"
|
||||
type="button"
|
||||
button-style="outline"
|
||||
button-style="selection"
|
||||
label-key="actions_publish"
|
||||
icon="icon-globe"
|
||||
action="publish()"
|
||||
@@ -152,7 +153,7 @@
|
||||
ng-if="options.allowBulkUnpublish && (buttonPermissions == null || buttonPermissions.canUnpublish)"
|
||||
style="margin-right: 5px;"
|
||||
type="button"
|
||||
button-style="outline"
|
||||
button-style="selection"
|
||||
label-key="actions_unpublish"
|
||||
icon="icon-block"
|
||||
action="unpublish()"
|
||||
@@ -165,7 +166,7 @@
|
||||
ng-if="options.allowBulkCopy && (buttonPermissions == null || buttonPermissions.canCopy)"
|
||||
style="margin-right: 5px;"
|
||||
type="button"
|
||||
button-style="outline"
|
||||
button-style="selection"
|
||||
label-key="actions_copy"
|
||||
icon="icon-documents"
|
||||
action="copy()"
|
||||
@@ -178,7 +179,7 @@
|
||||
ng-if="options.allowBulkMove && (buttonPermissions == null || buttonPermissions.canMove)"
|
||||
style="margin-right: 5px;"
|
||||
type="button"
|
||||
button-style="outline"
|
||||
button-style="selection"
|
||||
label-key="actions_move"
|
||||
icon="icon-enter"
|
||||
action="move()"
|
||||
@@ -190,7 +191,7 @@
|
||||
<umb-button
|
||||
ng-if="options.allowBulkDelete && (buttonPermissions == null || buttonPermissions.canDelete)"
|
||||
type="button"
|
||||
button-style="outline"
|
||||
button-style="selection"
|
||||
label-key="actions_delete"
|
||||
icon="icon-trash"
|
||||
action="delete()"
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.TagsController",
|
||||
function ($scope, angularHelper) {
|
||||
function ($scope) {
|
||||
|
||||
$scope.valueChanged = function(value) {
|
||||
$scope.model.value = value;
|
||||
// the model value seems to be a reference to the same array, so we need
|
||||
// to set the form as dirty explicitly when the content of the array changes
|
||||
angularHelper.getCurrentForm($scope).$setDirty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal class ContentUrlResolver : IValueResolver<IContent, ContentItemDisplay, UrlInfo[]>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IContentService _contentService;
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public ContentUrlResolver(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
PublishedRouter publishedRouter,
|
||||
IPublishedRouter publishedRouter,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <param name="contentId"></param>
|
||||
public EnsurePublishedContentRequestAttribute(UmbracoContext umbracoContext, int contentId)
|
||||
{
|
||||
if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_umbracoContext = umbracoContext;
|
||||
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_contentId = contentId;
|
||||
}
|
||||
|
||||
@@ -63,8 +62,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <param name="dataTokenName"></param>
|
||||
public EnsurePublishedContentRequestAttribute(UmbracoContext umbracoContext, string dataTokenName)
|
||||
{
|
||||
if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_umbracoContext = umbracoContext;
|
||||
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_dataTokenName = dataTokenName;
|
||||
}
|
||||
|
||||
@@ -74,7 +72,7 @@ namespace Umbraco.Web.Mvc
|
||||
protected UmbracoContext UmbracoContext => _umbracoContext ?? (_umbracoContext = UmbracoContext.Current);
|
||||
|
||||
// TODO: try lazy property injection?
|
||||
private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance<PublishedRouter>();
|
||||
private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance<IPublishedRouter>();
|
||||
|
||||
/// <summary>
|
||||
/// Exposes an UmbracoHelper
|
||||
|
||||
@@ -377,7 +377,7 @@ namespace Umbraco.Web.Mvc
|
||||
if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false)
|
||||
&& routeDef.HasHijackedRoute == false)
|
||||
{
|
||||
request.UpdateOnMissingTemplate(); // request will go 404
|
||||
request.UpdateToNotFound(); // request will go 404
|
||||
|
||||
// HandleHttpResponseStatus returns a value indicating that the request should
|
||||
// not be processed any further, eg because it has been redirect. then, exit.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Mvc
|
||||
public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler
|
||||
{
|
||||
// TODO: try lazy property injection?
|
||||
private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance<PublishedRouter>();
|
||||
private IPublishedRouter PublishedRouter => Current.Factory.GetInstance<IPublishedRouter>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UmbracoContext for this route handler
|
||||
|
||||
54
src/Umbraco.Web/Routing/IPublishedRouter.cs
Normal file
54
src/Umbraco.Web/Routing/IPublishedRouter.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Routes requests.
|
||||
/// </summary>
|
||||
public interface IPublishedRouter
|
||||
{
|
||||
// TODO: consider this and RenderRouteHandler - move some code around?
|
||||
|
||||
/// <summary>
|
||||
/// Creates a published request.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The current Umbraco context.</param>
|
||||
/// <param name="uri">The (optional) request Uri.</param>
|
||||
/// <returns>A published request.</returns>
|
||||
PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null);
|
||||
|
||||
/// <summary>
|
||||
/// Prepares a request for rendering.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>A value indicating whether the request was successfully prepared and can be rendered.</returns>
|
||||
bool PrepareRequest(PublishedRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to route a request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>A value indicating whether the request can be routed to a document.</returns>
|
||||
bool TryRouteRequest(PublishedRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a template.
|
||||
/// </summary>
|
||||
/// <param name="alias">The template alias</param>
|
||||
/// <returns>The template.</returns>
|
||||
ITemplate GetTemplate(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the request to "not found".
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <remarks>
|
||||
/// <para>This method is invoked when the pipeline decides it cannot render
|
||||
/// the request, for whatever reason, and wants to force it to be re-routed
|
||||
/// and rendered as if no document were found (404).</para>
|
||||
/// </remarks>
|
||||
void UpdateRequestToNotFound(PublishedRequest request);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
public class PublishedRequest
|
||||
{
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
|
||||
private bool _readonly; // after prepared
|
||||
private bool _readonlyUri; // after preparing
|
||||
@@ -35,7 +35,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="publishedRouter">The published router.</param>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="uri">The request <c>Uri</c>.</param>
|
||||
internal PublishedRequest(PublishedRouter publishedRouter, UmbracoContext umbracoContext, Uri uri = null)
|
||||
internal PublishedRequest(IPublishedRouter publishedRouter, UmbracoContext umbracoContext, Uri uri = null)
|
||||
{
|
||||
UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
|
||||
_publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter));
|
||||
@@ -290,11 +290,11 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
public bool HasTemplate => TemplateModel != null;
|
||||
|
||||
internal void UpdateOnMissingTemplate()
|
||||
internal void UpdateToNotFound()
|
||||
{
|
||||
var __readonly = _readonly;
|
||||
_readonly = false;
|
||||
_publishedRouter.UpdateRequestOnMissingTemplate(this);
|
||||
_publishedRouter.UpdateRequestToNotFound(this);
|
||||
_readonly = __readonly;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -18,8 +19,10 @@ using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
// TODO: making sense to have an interface?
|
||||
public class PublishedRouter
|
||||
/// <summary>
|
||||
/// Provides the default <see cref="IPublishedRouter"/> implementation.
|
||||
/// </summary>
|
||||
public class PublishedRouter : IPublishedRouter
|
||||
{
|
||||
private readonly IWebRoutingSection _webRoutingSection;
|
||||
private readonly ContentFinderCollection _contentFinders;
|
||||
@@ -47,15 +50,9 @@ namespace Umbraco.Web.Routing
|
||||
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
|
||||
_variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
|
||||
_logger = proflog;
|
||||
|
||||
GetRolesForLogin = s => Roles.Provider.GetRolesForUser(s);
|
||||
}
|
||||
|
||||
// TODO: in 7.7 this is cached in the PublishedContentRequest, which ... makes little sense
|
||||
// killing it entirely, if we need cache, just implement it properly !!
|
||||
// this is all so weird
|
||||
public Func<string, IEnumerable<string>> GetRolesForLogin { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null)
|
||||
{
|
||||
return new PublishedRequest(this, umbracoContext, uri ?? umbracoContext.CleanedUmbracoUrl);
|
||||
@@ -63,10 +60,8 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
#region Request
|
||||
|
||||
/// <summary>
|
||||
/// Tries to route the request.
|
||||
/// </summary>
|
||||
internal bool TryRouteRequest(PublishedRequest request)
|
||||
/// <inheritdoc />
|
||||
public bool TryRouteRequest(PublishedRequest request)
|
||||
{
|
||||
// disabled - is it going to change the routing?
|
||||
//_pcr.OnPreparing();
|
||||
@@ -96,12 +91,7 @@ namespace Umbraco.Web.Routing
|
||||
_variationContextAccessor.VariationContext = new VariationContext(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns false if the request was not successfully prepared
|
||||
/// </returns>
|
||||
/// <inheritdoc />
|
||||
public bool PrepareRequest(PublishedRequest request)
|
||||
{
|
||||
// note - at that point the original legacy module did something do handle IIS custom 404 errors
|
||||
@@ -210,11 +200,8 @@ namespace Umbraco.Web.Routing
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the request when there is no template to render the content.
|
||||
/// </summary>
|
||||
/// <remarks>This is called from Mvc when there's a document to render but no template.</remarks>
|
||||
public void UpdateRequestOnMissingTemplate(PublishedRequest request)
|
||||
/// <inheritdoc />
|
||||
public void UpdateRequestToNotFound(PublishedRequest request)
|
||||
{
|
||||
// clear content
|
||||
var content = request.PublishedContent;
|
||||
@@ -381,11 +368,7 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
#region Document and template
|
||||
|
||||
/// <summary>
|
||||
/// Gets a template.
|
||||
/// </summary>
|
||||
/// <param name="alias">The template alias</param>
|
||||
/// <returns>The template.</returns>
|
||||
/// <inheritdoc />
|
||||
public ITemplate GetTemplate(string alias)
|
||||
{
|
||||
return _services.FileService.GetTemplate(alias);
|
||||
@@ -602,7 +585,7 @@ namespace Umbraco.Web.Routing
|
||||
if (loginPageId != request.PublishedContent.Id)
|
||||
request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId);
|
||||
}
|
||||
else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, GetRolesForLogin(membershipHelper.CurrentUserName)) == false)
|
||||
else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, membershipHelper.GetCurrentUserRoles()) == false)
|
||||
{
|
||||
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
|
||||
var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Contains all the Urls that we can figure out (based upon domains, etc).</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<UrlInfo> GetContentUrls(this IContent content,
|
||||
PublishedRouter publishedRouter,
|
||||
IPublishedRouter publishedRouter,
|
||||
UmbracoContext umbracoContext,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService textService,
|
||||
@@ -92,7 +92,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<UrlInfo> GetContentUrlsByCulture(IContent content,
|
||||
IEnumerable<string> cultures,
|
||||
PublishedRouter publishedRouter,
|
||||
IPublishedRouter publishedRouter,
|
||||
UmbracoContext umbracoContext,
|
||||
IContentService contentService,
|
||||
ILocalizedTextService textService,
|
||||
@@ -161,7 +161,7 @@ namespace Umbraco.Web.Routing
|
||||
return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture);
|
||||
}
|
||||
|
||||
private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo)
|
||||
private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo)
|
||||
{
|
||||
// test for collisions on the 'main' url
|
||||
var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute);
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace Umbraco.Web.Runtime
|
||||
composition.RegisterAuto(typeof(UmbracoViewPage<>));
|
||||
|
||||
// register published router
|
||||
composition.RegisterUnique<PublishedRouter>();
|
||||
composition.RegisterUnique<IPublishedRouter, PublishedRouter>();
|
||||
composition.Register(_ => Current.Configs.Settings().WebRouting);
|
||||
|
||||
// register preview SignalR hub
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Umbraco.Web.Security
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
@@ -46,7 +45,6 @@ namespace Umbraco.Web.Security
|
||||
IMemberTypeService memberTypeService,
|
||||
IUserService userService,
|
||||
IPublicAccessService publicAccessService,
|
||||
PublishedRouter publishedRouter,
|
||||
AppCaches appCaches,
|
||||
ILogger logger
|
||||
)
|
||||
@@ -57,7 +55,6 @@ namespace Umbraco.Web.Security
|
||||
_memberTypeService = memberTypeService;
|
||||
_userService = userService;
|
||||
_publicAccessService = publicAccessService;
|
||||
_publishedRouter = publishedRouter;
|
||||
_appCaches = appCaches;
|
||||
_logger = logger;
|
||||
|
||||
@@ -116,7 +113,7 @@ namespace Umbraco.Web.Security
|
||||
{
|
||||
return UmbracoContext.PublishedRequest == null
|
||||
? _publicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser)
|
||||
: _publicAccessService.HasAccess(path, CurrentUserName, _publishedRouter.GetRolesForLogin);
|
||||
: _publicAccessService.HasAccess(path, CurrentUserName, GetUserRoles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -514,6 +511,24 @@ namespace Umbraco.Web.Security
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current user's roles.
|
||||
/// </summary>
|
||||
/// <remarks>Roles are cached per user name, at request level.</remarks>
|
||||
public IEnumerable<string> GetCurrentUserRoles()
|
||||
=> GetUserRoles(CurrentUserName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user's roles.
|
||||
/// </summary>
|
||||
/// <remarks>Roles are cached per user name, at request level.</remarks>
|
||||
public IEnumerable<string> GetUserRoles(string userName)
|
||||
{
|
||||
// optimize by caching per-request (v7 cached per PublishedRequest, in PublishedRouter)
|
||||
var key = "Umbraco.Web.Security.MembershipHelper__Roles__" + userName;
|
||||
return _appCaches.RequestCache.GetCacheItem(key, () => Roles.Provider.GetRolesForUser(userName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the login status model of the currently logged in member, if no member is logged in it returns null;
|
||||
/// </summary>
|
||||
@@ -618,7 +633,7 @@ namespace Umbraco.Web.Security
|
||||
var provider = _membershipProvider;
|
||||
|
||||
string username;
|
||||
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider())
|
||||
{
|
||||
var member = GetCurrentPersistedMember();
|
||||
@@ -626,7 +641,7 @@ namespace Umbraco.Web.Security
|
||||
if (member == null)
|
||||
return false;
|
||||
username = member.Username;
|
||||
|
||||
|
||||
// If types defined, check member is of one of those types
|
||||
var allowTypesList = allowTypes as IList<string> ?? allowTypes.ToList();
|
||||
if (allowTypesList.Any(allowType => allowType != string.Empty))
|
||||
|
||||
@@ -184,6 +184,7 @@
|
||||
<Compile Include="Models\ContentEditing\MacroDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MacroParameterDisplay.cs" />
|
||||
<Compile Include="Models\TemplateQuery\QueryConditionExtensions.cs" />
|
||||
<Compile Include="Routing\IPublishedRouter.cs" />
|
||||
<Compile Include="Services\DashboardService.cs" />
|
||||
<Compile Include="Services\IDashboardService.cs" />
|
||||
<Compile Include="Models\Link.cs" />
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Web
|
||||
private readonly UrlProviderCollection _urlProviders;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
public UmbracoInjectedModule(
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Web
|
||||
UrlProviderCollection urlProviders,
|
||||
IRuntimeState runtime,
|
||||
ILogger logger,
|
||||
PublishedRouter publishedRouter,
|
||||
IPublishedRouter publishedRouter,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
{
|
||||
_combinedRouteCollection = new Lazy<RouteCollection>(CreateRouteCollection);
|
||||
|
||||
Reference in New Issue
Block a user