Merge branch 'v8/dev' into temp8-4427-3
This commit is contained in:
@@ -203,6 +203,28 @@ namespace Umbraco.Core
|
||||
composition.RegisterUnique(_ => registrar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the database server messenger options.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="factory">A function creating the options.</param>
|
||||
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
|
||||
public static void SetDatabaseServerMessengerOptions(this Composition composition, Func<IFactory, DatabaseServerMessengerOptions> factory)
|
||||
{
|
||||
composition.RegisterUnique(factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the database server messenger options.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="options">Options.</param>
|
||||
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
|
||||
public static void SetDatabaseServerMessengerOptions(this Composition composition, DatabaseServerMessengerOptions options)
|
||||
{
|
||||
composition.RegisterUnique(_ => options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the short string helper.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Web;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NPoco.Expressions;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -52,8 +53,8 @@ namespace Umbraco.Core
|
||||
return ContentStatus.Unpublished;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -134,9 +135,14 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Sets the posted file value of a property.
|
||||
/// </summary>
|
||||
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
|
||||
/// you need to store the file by yourself using Store and then figure out
|
||||
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
|
||||
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, HttpPostedFileBase postedFile, string culture = null, string segment = null)
|
||||
{
|
||||
content.SetValue(contentTypeBaseServiceProvider, propertyTypeAlias, postedFile.FileName, postedFile.InputStream, culture, segment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the posted file value of a property.
|
||||
/// </summary>
|
||||
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
|
||||
{
|
||||
if (filename == null || filestream == null) return;
|
||||
|
||||
@@ -23,21 +23,21 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// There are some special routes we need to check to properly determine this:
|
||||
///
|
||||
///
|
||||
/// If any route has an extension in the path like .aspx = back office
|
||||
///
|
||||
///
|
||||
/// These are def back office:
|
||||
/// /Umbraco/BackOffice = back office
|
||||
/// /Umbraco/Preview = back office
|
||||
/// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end
|
||||
/// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice
|
||||
/// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute.
|
||||
///
|
||||
///
|
||||
/// These are def front-end:
|
||||
/// /Umbraco/Surface = front-end
|
||||
/// /Umbraco/Api = front-end
|
||||
/// But if we've got this far we'll just have to assume it's front-end anyways.
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
internal static bool IsBackOfficeRequest(this Uri url, string applicationPath, IGlobalSettings globalSettings)
|
||||
{
|
||||
@@ -152,9 +152,9 @@ namespace Umbraco.Core
|
||||
var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"};
|
||||
return toInclude.Any(ext.InvariantEquals) == false;
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
Current.Logger.Error(typeof(UriExtensions), ex, "Failed to determine if request was client side");
|
||||
Current.Logger.Debug(typeof(UriExtensions), "Failed to determine if request was client side (invalid chars in path \"{Path}\"?)", url.LocalPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -32,6 +33,8 @@ namespace Umbraco.Tests.PublishedContent
|
||||
protected override void Compose()
|
||||
{
|
||||
base.Compose();
|
||||
_publishedSnapshotAccessorMock = new Mock<IPublishedSnapshotAccessor>();
|
||||
Composition.RegisterUnique<IPublishedSnapshotAccessor>(_publishedSnapshotAccessorMock.Object);
|
||||
|
||||
Composition.RegisterUnique<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
|
||||
Composition.RegisterUnique<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
|
||||
@@ -87,6 +90,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
}
|
||||
|
||||
private readonly Guid _node1173Guid = Guid.NewGuid();
|
||||
private Mock<IPublishedSnapshotAccessor> _publishedSnapshotAccessorMock;
|
||||
|
||||
protected override string GetXmlContent(int templateId)
|
||||
{
|
||||
@@ -792,6 +796,91 @@ namespace Umbraco.Tests.PublishedContent
|
||||
Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SiblingsAndSelf()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Level1.1: 1173 (parent 1046)
|
||||
// --- Level1.1.1: 1174 (parent 1173)
|
||||
// --- Level1.1.2: 117 (parent 1173)
|
||||
// --- Level1.1.3: 1177 (parent 1173)
|
||||
// --- Level1.1.4: 1178 (parent 1173)
|
||||
// --- Level1.1.5: 1176 (parent 1173)
|
||||
// -- Level1.2: 1175 (parent 1046)
|
||||
// -- Level1.3: 4444 (parent 1046)
|
||||
var root = GetNode(1046);
|
||||
var level1_1 = GetNode(1173);
|
||||
var level1_1_1 = GetNode(1174);
|
||||
var level1_1_2 = GetNode(117);
|
||||
var level1_1_3 = GetNode(1177);
|
||||
var level1_1_4 = GetNode(1178);
|
||||
var level1_1_5 = GetNode(1176);
|
||||
var level1_2 = GetNode(1175);
|
||||
var level1_3 = GetNode(4444);
|
||||
|
||||
_publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root});
|
||||
|
||||
CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf());
|
||||
|
||||
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_1.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_2.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_3.SiblingsAndSelf());
|
||||
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_3.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_4.SiblingsAndSelf());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_5.SiblingsAndSelf());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Siblings()
|
||||
{
|
||||
// Structure:
|
||||
// - Root : 1046 (no parent)
|
||||
// -- Level1.1: 1173 (parent 1046)
|
||||
// --- Level1.1.1: 1174 (parent 1173)
|
||||
// --- Level1.1.2: 117 (parent 1173)
|
||||
// --- Level1.1.3: 1177 (parent 1173)
|
||||
// --- Level1.1.4: 1178 (parent 1173)
|
||||
// --- Level1.1.5: 1176 (parent 1173)
|
||||
// -- Level1.2: 1175 (parent 1046)
|
||||
// -- Level1.3: 4444 (parent 1046)
|
||||
var root = GetNode(1046);
|
||||
var level1_1 = GetNode(1173);
|
||||
var level1_1_1 = GetNode(1174);
|
||||
var level1_1_2 = GetNode(117);
|
||||
var level1_1_3 = GetNode(1177);
|
||||
var level1_1_4 = GetNode(1178);
|
||||
var level1_1_5 = GetNode(1176);
|
||||
var level1_2 = GetNode(1175);
|
||||
var level1_3 = GetNode(4444);
|
||||
|
||||
_publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root});
|
||||
|
||||
CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings());
|
||||
|
||||
CollectionAssertAreEqual( new []{level1_2, level1_3}, level1_1.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1, level1_3}, level1_2.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1, level1_2}, level1_3.Siblings());
|
||||
|
||||
CollectionAssertAreEqual( new []{ level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_4, level1_1_5}, level1_1_3.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_5}, level1_1_4.Siblings());
|
||||
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4}, level1_1_5.Siblings());
|
||||
|
||||
}
|
||||
|
||||
private void CollectionAssertAreEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual)
|
||||
where T: IPublishedContent
|
||||
{
|
||||
var e = expected.Select(x => x.Id);
|
||||
var a = actual.Select(x => x.Id);
|
||||
CollectionAssert.AreEquivalent(e, a, $"\nExpected:\n{string.Join(", ", e)}\n\nActual:\n{string.Join(", ", a)}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FragmentProperty()
|
||||
|
||||
@@ -62,8 +62,8 @@ function valPropertyMsg(serverValidationManager) {
|
||||
if (!watcher) {
|
||||
watcher = scope.$watch("currentProperty.value",
|
||||
function (newValue, oldValue) {
|
||||
|
||||
if (!newValue || angular.equals(newValue, oldValue)) {
|
||||
|
||||
if (angular.equals(newValue, oldValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,10 +78,12 @@ function valPropertyMsg(serverValidationManager) {
|
||||
// based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
|
||||
// is the only one, then we'll clear.
|
||||
|
||||
if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
|
||||
if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
|
||||
scope.errorMsg = "";
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
stopWatch();
|
||||
} else if (showValidation && scope.errorMsg === "") {
|
||||
formCtrl.$setValidity('valPropertyMsg', false);
|
||||
scope.errorMsg = getErrorMsg();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
@@ -152,6 +154,7 @@ function valPropertyMsg(serverValidationManager) {
|
||||
showValidation = true;
|
||||
if (hasError && scope.errorMsg === "") {
|
||||
scope.errorMsg = getErrorMsg();
|
||||
startWatch();
|
||||
}
|
||||
else if (!hasError) {
|
||||
scope.errorMsg = "";
|
||||
|
||||
@@ -85,17 +85,17 @@ angular.module('umbraco.services')
|
||||
nArray.push(item);
|
||||
|
||||
if(!item.sticky) {
|
||||
$timeout(function() {
|
||||
var found = _.find(nArray, function(i) {
|
||||
return i.id === item.id;
|
||||
});
|
||||
|
||||
if (found) {
|
||||
var index = nArray.indexOf(found);
|
||||
nArray.splice(index, 1);
|
||||
}
|
||||
|
||||
}, 7000);
|
||||
$timeout(
|
||||
function() {
|
||||
var found = _.find(nArray, function(i) {
|
||||
return i.id === item.id;
|
||||
});
|
||||
if (found) {
|
||||
var index = nArray.indexOf(found);
|
||||
nArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
, 10000);
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -137,6 +137,7 @@
|
||||
@import "components/umb-iconpicker.less";
|
||||
@import "components/umb-insert-code-box.less";
|
||||
@import "components/umb-packages.less";
|
||||
@import "components/umb-logviewer.less";
|
||||
@import "components/umb-package-local-install.less";
|
||||
@import "components/umb-panel-group.less";
|
||||
@import "components/umb-lightbox.less";
|
||||
|
||||
@@ -22,7 +22,14 @@
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
border-radius: 10px;
|
||||
margin: 10px;
|
||||
|
||||
.close {
|
||||
top: 0;
|
||||
right: -6px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-notifications__notification.-extra-padding {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* PACKAGE DETAILS */
|
||||
|
||||
.umb-logviewer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
@sidebarwidth: 350px; // Width of sidebar. Ugly hack because of old version of Less
|
||||
|
||||
.umb-logviewer__main-content {
|
||||
flex: 1 1 auto;
|
||||
margin-right: 20px;
|
||||
width: calc(~'100%' - ~'@{sidebarwidth}' - ~'20px'); // Make sure that the main content area doesn't gets affected by inline styling
|
||||
min-width: 500px;
|
||||
|
||||
.btn-link {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-logviewer__sidebar {
|
||||
flex: 0 0 @sidebarwidth;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
.umb-logviewer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.umb-logviewer__main-content {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.umb-logviewer__sidebar {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,17 @@
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
position: absolute;;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.--notInFront .umb-modalcolumn::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: rgba(0,0,0,.4);
|
||||
}
|
||||
|
||||
/* re-align loader */
|
||||
|
||||
@@ -16,6 +16,75 @@
|
||||
border-left: 1px solid @gray-10;
|
||||
}
|
||||
|
||||
.date-wrapper__date .flatpickr-input > a {
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
padding: 4px 15px;
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
|
||||
color: @ui-action-discreet-type;
|
||||
border: 1px dashed @ui-action-discreet-border;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration: none;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
border-color: @ui-action-discreet-border-hover;
|
||||
|
||||
localize {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----- VARIANTS SCHEDULED PUBLISH ------
|
||||
|
||||
.date-wrapper-mini {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.date-wrapper-mini__date {
|
||||
display: flex;
|
||||
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.date-wrapper-mini__date .flatpickr-input > a {
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
padding: 1px 15px;
|
||||
box-sizing: border-box;
|
||||
min-width: 180px;
|
||||
|
||||
color: @ui-action-discreet-type;
|
||||
border: 1px dashed @ui-action-discreet-border;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration: none;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
border-color: @ui-action-discreet-border-hover;
|
||||
|
||||
localize {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------- HISTORY ------------------
|
||||
|
||||
.history {
|
||||
@@ -71,4 +140,4 @@
|
||||
.history-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{{vm.variants[0].releaseDateFormatted}}
|
||||
</button>
|
||||
|
||||
<a ng-hide="vm.variants[0].releaseDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
|
||||
<a ng-hide="vm.variants[0].releaseDate" href="">
|
||||
<localize key="content_setDate">Set date</localize>
|
||||
</a>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
{{vm.variants[0].expireDateFormatted}}
|
||||
</button>
|
||||
|
||||
<a ng-hide="vm.variants[0].expireDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
|
||||
<a ng-hide="vm.variants[0].expireDate" href="">
|
||||
<localize key="content_setDate">Set date</localize>
|
||||
</a>
|
||||
</div>
|
||||
@@ -84,8 +84,8 @@
|
||||
|
||||
<div class="umb-list umb-list--condensed">
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter">
|
||||
<ng-form name="scheduleSelectorForm">
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants">
|
||||
<ng-form name="scheduleSelectorForm" style="width:100%;">
|
||||
<div class="flex">
|
||||
|
||||
<umb-checkbox
|
||||
@@ -106,9 +106,9 @@
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex items-center" style="margin-top: 10px; margin-bottom: 10px;">
|
||||
<div class="date-wrapper-mini">
|
||||
<div class="date-wrapper-mini__date" ng-if="vm.dirtyVariantFilter(variant) && (variant.releaseDate || variant.save)">
|
||||
|
||||
<div class="flex items-center" ng-if="variant.releaseDate || variant.save">
|
||||
<div style="font-size: 13px; margin-right: 5px;">Publish:<em ng-show="!variant.save"> {{variant.releaseDateFormatted}}</em></div>
|
||||
|
||||
<div class="btn-group flex" style="font-size: 14px; margin-right: 10px;" ng-if="variant.save">
|
||||
@@ -123,7 +123,7 @@
|
||||
{{variant.releaseDateFormatted}}
|
||||
</button>
|
||||
|
||||
<a ng-hide="variant.releaseDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
|
||||
<a ng-hide="variant.releaseDate" href="">
|
||||
<localize key="content_setDate">Set date</localize>
|
||||
</a>
|
||||
</div>
|
||||
@@ -134,7 +134,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center" ng-if="variant.expireDate || variant.save">
|
||||
<div class="date-wrapper-mini__date" ng-if="variant.expireDate || variant.save">
|
||||
<div style="font-size: 13px; margin-right: 5px;">Unpublish:<em ng-show="!variant.save"> {{variant.expireDateFormatted}}</em></div>
|
||||
|
||||
<div class="btn-group flex" style="font-size: 14px;" ng-if="variant.save">
|
||||
@@ -149,7 +149,7 @@
|
||||
{{variant.expireDateFormatted}}
|
||||
</button>
|
||||
|
||||
<a ng-hide="variant.expireDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
|
||||
<a ng-hide="variant.expireDate" href="">
|
||||
<localize key="content_setDate">Set date</localize>
|
||||
</a>
|
||||
</div>
|
||||
@@ -182,24 +182,6 @@
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="vm.hasPristineVariants">
|
||||
<div style="margin-bottom: 15px; font-weight: bold;">
|
||||
<p><localize key="content_publishedLanguages"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter track by variant.language.culture">
|
||||
<div>
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span>{{ variant.language.name }}</span>
|
||||
</div>
|
||||
<div class="umb-permission__description">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
type="button"
|
||||
size="s"
|
||||
action="vm.disableUrlTracker($event)"
|
||||
label-key="redirectUrls_disableUrlTracker">
|
||||
label-key="redirectUrls_disableUrlTracker"
|
||||
button-style="white">
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
@@ -22,7 +23,8 @@
|
||||
size="s"
|
||||
button-style="success"
|
||||
action="vm.enableUrlTracker()"
|
||||
label-key="redirectUrls_enableUrlTracker">
|
||||
label-key="redirectUrls_enableUrlTracker"
|
||||
button-style="success">
|
||||
</umb-button>
|
||||
|
||||
</umb-editor-sub-header-section>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div data-element="editor-logs" ng-controller="Umbraco.Editors.LogViewer.OverviewController as vm" class="clearfix">
|
||||
<div data-element="editor-logs" ng-controller="Umbraco.Editors.LogViewer.OverviewController as vm" class="clearfix" id="logview">
|
||||
|
||||
<umb-editor-view footer="false">
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
</umb-box>
|
||||
</div>
|
||||
|
||||
<div class="umb-package-details" ng-show="!vm.loading && vm.canLoadLogs">
|
||||
<div class="umb-package-details__main-content">
|
||||
<div class="umb-logviewer" ng-show="!vm.loading && vm.canLoadLogs">
|
||||
<div class="umb-logviewer__main-content">
|
||||
<!-- Saved Searches -->
|
||||
<umb-box>
|
||||
<umb-box-header title="Saved Searches"></umb-box-header>
|
||||
@@ -68,7 +68,7 @@
|
||||
</umb-box>
|
||||
</div>
|
||||
|
||||
<div class="umb-package-details__sidebar">
|
||||
<div class="umb-logviewer__sidebar">
|
||||
<!-- No of Errors -->
|
||||
<umb-box ng-click="vm.searchLogQuery('Has(@Exception)')" style="cursor:pointer;">
|
||||
<umb-box-header title="Number of Errors"></umb-box-header>
|
||||
@@ -95,4 +95,4 @@
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
</umb-editor-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="@ViewBag.UmbracoBaseFolder/" />
|
||||
<base href="@ViewData.GetUmbracoBaseFolder()/" />
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
@@ -66,8 +66,8 @@
|
||||
var Umbraco = {};
|
||||
Umbraco.Sys = {};
|
||||
Umbraco.Sys.ServerVariables = {
|
||||
"installApiBaseUrl": "@ViewBag.InstallApiBaseUrl",
|
||||
"umbracoBaseUrl": "@ViewBag.UmbracoBaseFolder"
|
||||
"installApiBaseUrl": "@ViewData.GetInstallApiBaseUrl()",
|
||||
"umbracoBaseUrl": "@ViewData.GetUmbracoBaseFolder()"
|
||||
};
|
||||
</script>
|
||||
<script src="lib/lazyload-js/lazyload.min.js"></script>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
@{
|
||||
var externalLoginUrl = Url.Action("ExternalLogin", "BackOffice", new
|
||||
{
|
||||
area = ViewBag.UmbracoPath,
|
||||
area = ViewData.GetUmbracoPath(),
|
||||
//Custom redirect URL since we don't want to just redirect to the back office since this is for authing upgrades
|
||||
redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice")
|
||||
});
|
||||
@@ -61,7 +61,7 @@
|
||||
<script type="text/javascript">
|
||||
document.angularReady = function (app) {
|
||||
|
||||
@Html.AngularValueExternalLoginInfoScript((IEnumerable<string>)ViewBag.ExternalSignInError)
|
||||
@Html.AngularValueExternalLoginInfoScript(ViewData.GetExternalSignInError())
|
||||
@Html.AngularValueResetPasswordCodeInfoScript(ViewData["PasswordResetCode"])
|
||||
|
||||
}
|
||||
|
||||
@@ -112,12 +112,12 @@
|
||||
on-login="hideLoginScreen()">
|
||||
</umb-login>
|
||||
|
||||
@Html.BareMinimumServerVariablesScript(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath }), Model.Features, Current.Configs.Global())
|
||||
@Html.BareMinimumServerVariablesScript(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewData.GetUmbracoPath() }), Model.Features, Current.Configs.Global())
|
||||
|
||||
<script>
|
||||
|
||||
document.angularReady = function(app) {
|
||||
@Html.AngularValueExternalLoginInfoScript((IEnumerable<string>)ViewBag.ExternalSignInError)
|
||||
@Html.AngularValueExternalLoginInfoScript(ViewData.GetExternalSignInError())
|
||||
@Html.AngularValueResetPasswordCodeInfoScript(ViewData["PasswordResetCode"])
|
||||
//required for the noscript trick
|
||||
document.getElementById("mainwrapper").style.display = "inherit";
|
||||
|
||||
@@ -27,9 +27,8 @@ namespace Umbraco.Web
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
|
||||
public BatchedDatabaseServerMessenger(
|
||||
IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings,
|
||||
bool enableDistCalls, DatabaseServerMessengerOptions options)
|
||||
: base(runtime, scopeProvider, sqlContext, proflog, globalSettings, enableDistCalls, options)
|
||||
IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings, DatabaseServerMessengerOptions options)
|
||||
: base(runtime, scopeProvider, sqlContext, proflog, globalSettings, true, options)
|
||||
{
|
||||
_databaseFactory = databaseFactory;
|
||||
}
|
||||
|
||||
@@ -38,54 +38,44 @@ namespace Umbraco.Web.Compose
|
||||
|
||||
public sealed class DatabaseServerRegistrarAndMessengerComposer : ComponentComposer<DatabaseServerRegistrarAndMessengerComponent>, ICoreComposer
|
||||
{
|
||||
public static DatabaseServerMessengerOptions GetDefaultOptions(IFactory factory)
|
||||
{
|
||||
var logger = factory.GetInstance<ILogger>();
|
||||
var indexRebuilder = factory.GetInstance<IndexRebuilder>();
|
||||
|
||||
return new DatabaseServerMessengerOptions
|
||||
{
|
||||
//These callbacks will be executed if the server has not been synced
|
||||
// (i.e. it is a new server or the lastsynced.txt file has been removed)
|
||||
InitializingCallbacks = new Action[]
|
||||
{
|
||||
//rebuild the xml cache file if the server is not synced
|
||||
() =>
|
||||
{
|
||||
// rebuild the published snapshot caches entirely, if the server is not synced
|
||||
// this is equivalent to DistributedCache RefreshAll... but local only
|
||||
// (we really should have a way to reuse RefreshAll... locally)
|
||||
// note: refresh all content & media caches does refresh content types too
|
||||
var svc = Current.PublishedSnapshotService;
|
||||
svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
|
||||
svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _, out _);
|
||||
svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _);
|
||||
},
|
||||
|
||||
//rebuild indexes if the server is not synced
|
||||
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
|
||||
// indexes then they can adjust this logic themselves.
|
||||
() => { ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000); }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void Compose(Composition composition)
|
||||
{
|
||||
base.Compose(composition);
|
||||
|
||||
composition.SetServerMessenger(factory =>
|
||||
{
|
||||
var runtime = factory.GetInstance<IRuntimeState>();
|
||||
var databaseFactory = factory.GetInstance<IUmbracoDatabaseFactory>();
|
||||
var globalSettings = factory.GetInstance<IGlobalSettings>();
|
||||
var proflog = factory.GetInstance<IProfilingLogger>();
|
||||
var scopeProvider = factory.GetInstance<IScopeProvider>();
|
||||
var sqlContext = factory.GetInstance<ISqlContext>();
|
||||
var logger = factory.GetInstance<ILogger>();
|
||||
var indexRebuilder = factory.GetInstance<IndexRebuilder>();
|
||||
|
||||
return new BatchedDatabaseServerMessenger(
|
||||
runtime, databaseFactory, scopeProvider, sqlContext, proflog, globalSettings,
|
||||
true,
|
||||
//Default options for web including the required callbacks to build caches
|
||||
new DatabaseServerMessengerOptions
|
||||
{
|
||||
//These callbacks will be executed if the server has not been synced
|
||||
// (i.e. it is a new server or the lastsynced.txt file has been removed)
|
||||
InitializingCallbacks = new Action[]
|
||||
{
|
||||
//rebuild the xml cache file if the server is not synced
|
||||
() =>
|
||||
{
|
||||
// rebuild the published snapshot caches entirely, if the server is not synced
|
||||
// this is equivalent to DistributedCache RefreshAll... but local only
|
||||
// (we really should have a way to reuse RefreshAll... locally)
|
||||
// note: refresh all content & media caches does refresh content types too
|
||||
var svc = Current.PublishedSnapshotService;
|
||||
svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
|
||||
svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _, out _);
|
||||
svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _);
|
||||
},
|
||||
|
||||
//rebuild indexes if the server is not synced
|
||||
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
|
||||
// indexes then they can adjust this logic themselves.
|
||||
() =>
|
||||
{
|
||||
ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
composition.SetDatabaseServerMessengerOptions(GetDefaultOptions);
|
||||
composition.SetServerMessenger<BatchedDatabaseServerMessenger>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +118,7 @@ namespace Umbraco.Web.Compose
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
{
|
||||
//We will start the whole process when a successful request is made
|
||||
if (_registrar != null || _messenger != null)
|
||||
UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce;
|
||||
|
||||
@@ -45,10 +45,6 @@ namespace Umbraco.Web.Editors
|
||||
private BackOfficeUserManager<BackOfficeIdentityUser> _userManager;
|
||||
private BackOfficeSignInManager _signInManager;
|
||||
|
||||
private const string TokenExternalSignInError = "ExternalSignInError";
|
||||
private const string TokenPasswordResetCode = "PasswordResetCode";
|
||||
private static readonly string[] TempDataTokenNames = { TokenExternalSignInError, TokenPasswordResetCode };
|
||||
|
||||
public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
|
||||
: base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger, umbracoHelper)
|
||||
{
|
||||
@@ -294,13 +290,13 @@ namespace Umbraco.Web.Editors
|
||||
if (result)
|
||||
{
|
||||
//Add a flag and redirect for it to be displayed
|
||||
TempData[TokenPasswordResetCode] = new ValidatePasswordResetCodeModel { UserId = userId, ResetCode = resetCode };
|
||||
TempData[ViewDataExtensions.TokenPasswordResetCode] = new ValidatePasswordResetCodeModel { UserId = userId, ResetCode = resetCode };
|
||||
return RedirectToLocal(Url.Action("Default", "BackOffice"));
|
||||
}
|
||||
}
|
||||
|
||||
//Add error and redirect for it to be displayed
|
||||
TempData[TokenPasswordResetCode] = new[] { Services.TextService.Localize("login/resetCodeExpired") };
|
||||
TempData[ViewDataExtensions.TokenPasswordResetCode] = new[] { Services.TextService.Localize("login/resetCodeExpired") };
|
||||
return RedirectToLocal(Url.Action("Default", "BackOffice"));
|
||||
}
|
||||
|
||||
@@ -314,7 +310,7 @@ namespace Umbraco.Web.Editors
|
||||
if (loginInfo == null)
|
||||
{
|
||||
//Add error and redirect for it to be displayed
|
||||
TempData[TokenExternalSignInError] = new[] { "An error occurred, could not get external login info" };
|
||||
TempData[ViewDataExtensions.TokenExternalSignInError] = new[] { "An error occurred, could not get external login info" };
|
||||
return RedirectToLocal(Url.Action("Default", "BackOffice"));
|
||||
}
|
||||
|
||||
@@ -325,7 +321,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//Add errors and redirect for it to be displayed
|
||||
TempData[TokenExternalSignInError] = result.Errors;
|
||||
TempData[ViewDataExtensions.TokenExternalSignInError] = result.Errors;
|
||||
return RedirectToLocal(Url.Action("Default", "BackOffice"));
|
||||
}
|
||||
|
||||
@@ -341,17 +337,12 @@ namespace Umbraco.Web.Editors
|
||||
if (defaultResponse == null) throw new ArgumentNullException("defaultResponse");
|
||||
if (externalSignInResponse == null) throw new ArgumentNullException("externalSignInResponse");
|
||||
|
||||
ViewBag.UmbracoPath = GlobalSettings.GetUmbracoMvcArea();
|
||||
ViewData.SetUmbracoPath(GlobalSettings.GetUmbracoMvcArea());
|
||||
|
||||
//check if there is the TempData with the any token name specified, if so, assign to view bag and render the view
|
||||
foreach (var tempDataTokenName in TempDataTokenNames)
|
||||
{
|
||||
if (TempData[tempDataTokenName] != null)
|
||||
{
|
||||
ViewData[tempDataTokenName] = TempData[tempDataTokenName];
|
||||
return defaultResponse();
|
||||
}
|
||||
}
|
||||
if (ViewData.FromTempData(TempData, ViewDataExtensions.TokenExternalSignInError) ||
|
||||
ViewData.FromTempData(TempData, ViewDataExtensions.TokenPasswordResetCode))
|
||||
return defaultResponse();
|
||||
|
||||
//First check if there's external login info, if there's not proceed as normal
|
||||
var loginInfo = await OwinContext.Authentication.GetExternalLoginInfoAsync(
|
||||
@@ -416,7 +407,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
if (await AutoLinkAndSignInExternalAccount(loginInfo, autoLinkOptions) == false)
|
||||
{
|
||||
ViewData[TokenExternalSignInError] = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to an account" };
|
||||
ViewData.SetExternalSignInError(new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to an account" });
|
||||
}
|
||||
|
||||
//Remove the cookie otherwise this message will keep appearing
|
||||
@@ -440,7 +431,7 @@ namespace Umbraco.Web.Editors
|
||||
//we are allowing auto-linking/creating of local accounts
|
||||
if (loginInfo.Email.IsNullOrWhiteSpace())
|
||||
{
|
||||
ViewData[TokenExternalSignInError] = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not provided an email address, the account cannot be linked." };
|
||||
ViewData.SetExternalSignInError(new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not provided an email address, the account cannot be linked." });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -448,7 +439,7 @@ namespace Umbraco.Web.Editors
|
||||
var foundByEmail = Services.UserService.GetByEmail(loginInfo.Email);
|
||||
if (foundByEmail != null)
|
||||
{
|
||||
ViewData[TokenExternalSignInError] = new[] { "A user with this email address already exists locally. You will need to login locally to Umbraco and link this external provider: " + loginInfo.Login.LoginProvider };
|
||||
ViewData.SetExternalSignInError(new[] { "A user with this email address already exists locally. You will need to login locally to Umbraco and link this external provider: " + loginInfo.Login.LoginProvider });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -477,21 +468,21 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
if (userCreationResult.Succeeded == false)
|
||||
{
|
||||
ViewData[TokenExternalSignInError] = userCreationResult.Errors;
|
||||
ViewData.SetExternalSignInError(userCreationResult.Errors);
|
||||
}
|
||||
else
|
||||
{
|
||||
var linkResult = await UserManager.AddLoginAsync(autoLinkUser.Id, loginInfo.Login);
|
||||
if (linkResult.Succeeded == false)
|
||||
{
|
||||
ViewData[TokenExternalSignInError] = linkResult.Errors;
|
||||
ViewData.SetExternalSignInError(linkResult.Errors);
|
||||
|
||||
//If this fails, we should really delete the user since it will be in an inconsistent state!
|
||||
var deleteResult = await UserManager.DeleteAsync(autoLinkUser);
|
||||
if (deleteResult.Succeeded == false)
|
||||
{
|
||||
//DOH! ... this isn't good, combine all errors to be shown
|
||||
ViewData[TokenExternalSignInError] = linkResult.Errors.Concat(deleteResult.Errors);
|
||||
ViewData.SetExternalSignInError(linkResult.Errors.Concat(deleteResult.Errors));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -60,10 +60,10 @@ namespace Umbraco.Web.Install.Controllers
|
||||
}
|
||||
|
||||
// gen the install base url
|
||||
ViewBag.InstallApiBaseUrl = Url.GetUmbracoApiService("GetSetup", "InstallApi", "UmbracoInstall").TrimEnd("GetSetup");
|
||||
ViewData.SetInstallApiBaseUrl(Url.GetUmbracoApiService("GetSetup", "InstallApi", "UmbracoInstall").TrimEnd("GetSetup"));
|
||||
|
||||
// get the base umbraco folder
|
||||
ViewBag.UmbracoBaseFolder = IOHelper.ResolveUrl(SystemDirectories.Umbraco);
|
||||
ViewData.SetUmbracoBaseFolder(IOHelper.ResolveUrl(SystemDirectories.Umbraco));
|
||||
|
||||
_installHelper.InstallStatus(false, "");
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Examine;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
@@ -21,6 +22,7 @@ namespace Umbraco.Web
|
||||
// see notes in PublishedElementExtensions
|
||||
//
|
||||
private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback;
|
||||
private static IPublishedSnapshot PublishedSnapshot => Current.PublishedSnapshot;
|
||||
|
||||
#region Urls
|
||||
|
||||
@@ -235,7 +237,8 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
/// <param name="contents">The content items.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null).</param>
|
||||
internal static IEnumerable<IPublishedContent> WhereIsInvariantOrHasCulture(this IEnumerable<IPublishedContent> contents, string culture = null)
|
||||
internal static IEnumerable<T> WhereIsInvariantOrHasCulture<T>(this IEnumerable<T> contents, string culture = null)
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
if (contents == null) throw new ArgumentNullException(nameof(contents));
|
||||
|
||||
@@ -1117,6 +1120,97 @@ namespace Umbraco.Web
|
||||
|
||||
#endregion
|
||||
|
||||
#region Axes: Siblings
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <returns>The siblings of the content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent> Siblings(this IPublishedContent content, string culture = null)
|
||||
{
|
||||
return SiblingsAndSelf(content, culture).Where(x => x.Id != content.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <returns>The siblings of the content, of the given content type.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent> SiblingsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null)
|
||||
{
|
||||
return SiblingsAndSelfOfType(content, contentTypeAlias, culture).Where(x => x.Id != content.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The content type.</typeparam>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <returns>The siblings of the content, of the given content type.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent> Siblings<T>(this IPublishedContent content, string culture = null)
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
return SiblingsAndSelf<T>(content, culture).Where(x => x.Id != content.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content including the node itself to indicate the position.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <returns>The siblings of the content including the node itself.</returns>
|
||||
public static IEnumerable<IPublishedContent> SiblingsAndSelf(this IPublishedContent content, string culture = null)
|
||||
{
|
||||
return content.Parent != null
|
||||
? content.Parent.Children(culture)
|
||||
: PublishedSnapshot.Content.GetAtRoot().WhereIsInvariantOrHasCulture(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content including the node itself to indicate the position, of a given content type.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <returns>The siblings of the content including the node itself, of the given content type.</returns>
|
||||
public static IEnumerable<IPublishedContent> SiblingsAndSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null)
|
||||
{
|
||||
return content.Parent != null
|
||||
? content.Parent.ChildrenOfType(contentTypeAlias, culture)
|
||||
: PublishedSnapshot.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content including the node itself to indicate the position, of a given content type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The content type.</typeparam>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="culture">The specific culture to filter for. If null is used the current culture is used. (Default is null)</param>
|
||||
/// <returns>The siblings of the content including the node itself, of the given content type.</returns>
|
||||
public static IEnumerable<T> SiblingsAndSelf<T>(this IPublishedContent content, string culture = null)
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
return content.Parent != null
|
||||
? content.Parent.Children<T>(culture)
|
||||
: PublishedSnapshot.Content.GetAtRoot().OfType<T>().WhereIsInvariantOrHasCulture(culture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Axes: custom
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -289,11 +289,31 @@ namespace Umbraco.Web.Security
|
||||
return MemberCache.GetByProviderKey(key);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByProviderKeys(IEnumerable<object> keys)
|
||||
{
|
||||
return keys?.Select(GetByProviderKey).WhereNotNull() ?? Enumerable.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetById(int memberId)
|
||||
{
|
||||
return MemberCache.GetById(memberId);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByIds(IEnumerable<int> memberIds)
|
||||
{
|
||||
return memberIds?.Select(GetById).WhereNotNull() ?? Enumerable.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetById(Guid memberId)
|
||||
{
|
||||
return GetByProviderKey(memberId);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByIds(IEnumerable<Guid> memberIds)
|
||||
{
|
||||
return GetByProviderKeys(memberIds.OfType<object>());
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetByUsername(string username)
|
||||
{
|
||||
return MemberCache.GetByUsername(username);
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
<Compile Include="Models\TemplateQuery\OperatorFactory.cs" />
|
||||
<Compile Include="UmbracoContextFactory.cs" />
|
||||
<Compile Include="UmbracoContextReference.cs" />
|
||||
<Compile Include="ViewDataExtensions.cs" />
|
||||
<Compile Include="WebApi\Filters\AdminUsersAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\OnlyLocalRequestsAttribute.cs" />
|
||||
<Compile Include="PropertyEditors\MultiUrlPickerConfiguration.cs" />
|
||||
|
||||
@@ -264,7 +264,7 @@ namespace Umbraco.Web
|
||||
|
||||
public IPublishedContent Member(Guid id)
|
||||
{
|
||||
return MembershipHelper.GetByProviderKey(id);
|
||||
return MembershipHelper.GetById(id);
|
||||
}
|
||||
|
||||
public IPublishedContent Member(object id)
|
||||
@@ -289,6 +289,56 @@ namespace Umbraco.Web
|
||||
return asInt ? MembershipHelper.GetById(asInt.Result) : MembershipHelper.GetByProviderKey(id);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<int> ids)
|
||||
{
|
||||
return MembershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<string> ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<Guid> ids)
|
||||
{
|
||||
return MembershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<Udi> ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<object> ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params int[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params string[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params Guid[] ids)
|
||||
{
|
||||
return MembershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params Udi[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params object[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
71
src/Umbraco.Web/ViewDataExtensions.cs
Normal file
71
src/Umbraco.Web/ViewDataExtensions.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
public static class ViewDataExtensions
|
||||
{
|
||||
public const string TokenUmbracoPath = "UmbracoPath";
|
||||
public const string TokenInstallApiBaseUrl = "InstallApiBaseUrl";
|
||||
public const string TokenUmbracoBaseFolder = "UmbracoBaseFolder";
|
||||
public const string TokenExternalSignInError = "ExternalSignInError";
|
||||
public const string TokenPasswordResetCode = "PasswordResetCode";
|
||||
|
||||
public static bool FromTempData(this ViewDataDictionary viewData, TempDataDictionary tempData, string token)
|
||||
{
|
||||
if (tempData[token] == null) return false;
|
||||
viewData[token] = tempData[token];
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetUmbracoPath(this ViewDataDictionary viewData)
|
||||
{
|
||||
return (string)viewData[TokenUmbracoPath];
|
||||
}
|
||||
|
||||
public static void SetUmbracoPath(this ViewDataDictionary viewData, string value)
|
||||
{
|
||||
viewData[TokenUmbracoPath] = value;
|
||||
}
|
||||
|
||||
public static string GetInstallApiBaseUrl(this ViewDataDictionary viewData)
|
||||
{
|
||||
return (string)viewData[TokenInstallApiBaseUrl];
|
||||
}
|
||||
|
||||
public static void SetInstallApiBaseUrl(this ViewDataDictionary viewData, string value)
|
||||
{
|
||||
viewData[TokenInstallApiBaseUrl] = value;
|
||||
}
|
||||
|
||||
public static string GetUmbracoBaseFolder(this ViewDataDictionary viewData)
|
||||
{
|
||||
return (string)viewData[TokenUmbracoBaseFolder];
|
||||
}
|
||||
|
||||
public static void SetUmbracoBaseFolder(this ViewDataDictionary viewData, string value)
|
||||
{
|
||||
viewData[TokenUmbracoBaseFolder] = value;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetExternalSignInError(this ViewDataDictionary viewData)
|
||||
{
|
||||
return (IEnumerable<string>)viewData[TokenExternalSignInError];
|
||||
}
|
||||
|
||||
public static void SetExternalSignInError(this ViewDataDictionary viewData, IEnumerable<string> value)
|
||||
{
|
||||
viewData[TokenExternalSignInError] = value;
|
||||
}
|
||||
|
||||
public static string GetPasswordResetCode(this ViewDataDictionary viewData)
|
||||
{
|
||||
return (string)viewData[TokenPasswordResetCode];
|
||||
}
|
||||
|
||||
public static void SetPasswordResetCode(this ViewDataDictionary viewData, string value)
|
||||
{
|
||||
viewData[TokenPasswordResetCode] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user