Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92453c4652 | |||
| 5ddba3e64e | |||
| c72a5c07fe | |||
| 409ae99bf7 | |||
| 969a9c1193 | |||
| 3643e170c5 | |||
| afc872de5a | |||
| 56aef39014 | |||
| ae07b2b299 | |||
| b99cf238e0 | |||
| 68457b7eaf | |||
| 24c74676f6 | |||
| ac1ee2fe0c | |||
| 4f30dd32f1 | |||
| 8f17d6fcd2 | |||
| 1300389ce6 | |||
| ea8d8ba771 | |||
| 1eb6ae10c9 | |||
| 5d3e1fb95f | |||
| 62463ecc21 | |||
| 3a469cdc77 | |||
| 6665695986 | |||
| 4dd51f645a | |||
| cca1393f2b | |||
| 69548aed20 | |||
| 7656de693c | |||
| 4806920179 | |||
| 5637c44f69 | |||
| 99f0e6c05a | |||
| 2c40e46d56 | |||
| 2c3a2b29b2 | |||
| 94a4e34e0c | |||
| 7d42583177 | |||
| 77a8697ca7 | |||
| 7462686778 | |||
| 44d5ce4a2e | |||
| 34888f31e6 | |||
| 0994aac656 | |||
| 0e0d38be30 | |||
| 91735e7e3f | |||
| 0d4b3e4173 | |||
| d174499e50 | |||
| df20feb703 | |||
| a4c5b7118d | |||
| 52a56c8852 | |||
| 7a2f09643a | |||
| c9819b2d89 | |||
| 7acafe67a4 | |||
| 9eb2b28232 | |||
| 832ec68140 | |||
| d8f0af1a94 | |||
| a8d5bd6d1f | |||
| e252a0849b | |||
| ba4b84d7f4 | |||
| e1c9b1818e | |||
| 76bece07ef | |||
| 7729dc946e | |||
| 5b285f71fa | |||
| e60417925b | |||
| 5a78c000a0 | |||
| 54faeee837 | |||
| cefa2c9040 | |||
| 77cf071e04 | |||
| 0d0179cb47 | |||
| a0f15bba44 | |||
| 007cdeb9ab | |||
| 5716353518 | |||
| 87340d6c2c | |||
| fc742bcf18 | |||
| 4eb4cd8962 |
@@ -1,4 +1,4 @@
|
||||
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
|
||||
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/dev-v8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
|
||||
# Contributing to Umbraco CMS
|
||||
|
||||
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[](https://ci.appveyor.com/project/Umbraco/umbraco-cms-b2cri/branch/dev-v7)
|
||||
[](https://pullreminders.com?ref=badge)
|
||||
|
||||
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/tree/temp8) to go to the v8 branch_
|
||||
|
||||
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.13.2")]
|
||||
[assembly: AssemblyInformationalVersion("7.13.2")]
|
||||
[assembly: AssemblyFileVersion("7.14.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.14.0")]
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.13.2");
|
||||
private static readonly Version Version = new Version("7.14.0");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -447,6 +447,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string NestedContentAlias = "Umbraco.NestedContent";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the multi url picker editor.
|
||||
/// </summary>
|
||||
public const string MultiUrlPickerAlias = "Umbraco.MultiUrlPicker";
|
||||
|
||||
public static class PreValueKeys
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
Id = contentType.Id;
|
||||
Alias = contentType.Alias;
|
||||
Name = contentType.Name;
|
||||
Description = contentType.Description;
|
||||
_compositionAliases = new HashSet<string>(contentType.CompositionAliases(), StringComparer.InvariantCultureIgnoreCase);
|
||||
_propertyTypes = contentType.CompositionPropertyTypes
|
||||
.Select(x => new PublishedPropertyType(this, x))
|
||||
@@ -33,10 +35,12 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
|
||||
// internal so it can be used for unit tests
|
||||
internal PublishedContentType(int id, string alias, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
internal PublishedContentType(int id, string alias, string name, string description, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
{
|
||||
Id = id;
|
||||
Alias = alias;
|
||||
Name = name;
|
||||
Description = description;
|
||||
_compositionAliases = new HashSet<string>(compositionAliases, StringComparer.InvariantCultureIgnoreCase);
|
||||
_propertyTypes = propertyTypes.ToArray();
|
||||
foreach (var propertyType in _propertyTypes)
|
||||
@@ -46,7 +50,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
// create detached content type - ie does not match anything in the DB
|
||||
internal PublishedContentType(string alias, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: this(0, alias, compositionAliases, propertyTypes)
|
||||
: this(0, alias, string.Empty, string.Empty, compositionAliases, propertyTypes)
|
||||
{ }
|
||||
|
||||
private void InitializeIndexes()
|
||||
@@ -63,6 +67,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
public int Id { get; private set; }
|
||||
public string Alias { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public HashSet<string> CompositionAliases { get { return _compositionAliases; } }
|
||||
|
||||
#endregion
|
||||
|
||||
+3
-3
@@ -16,17 +16,17 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourtee
|
||||
public override void Up()
|
||||
{
|
||||
// move the data for all member group properties from the NVarchar to the NText column and clear the NVarchar column
|
||||
Execute.Sql(@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL
|
||||
Execute.Sql($@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL
|
||||
WHERE dataNtext IS NULL AND id IN (
|
||||
SELECT id FROM cmsPropertyData WHERE propertyTypeId in (
|
||||
SELECT id from cmsPropertyType where dataTypeID IN (
|
||||
SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker'
|
||||
SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = '{Constants.PropertyEditors.MemberGroupPickerAlias}'
|
||||
)
|
||||
)
|
||||
)");
|
||||
|
||||
// ensure that all exising member group properties are defined as NText
|
||||
Execute.Sql("UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker'");
|
||||
Execute.Sql($"UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = '{Constants.PropertyEditors.MemberGroupPickerAlias}'");
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
|
||||
@@ -53,6 +53,12 @@ namespace Umbraco.Core.PropertyEditors
|
||||
nestedContentEditorFromPackage.Name = "(Obsolete) " + nestedContentEditorFromPackage.Name;
|
||||
nestedContentEditorFromPackage.IsDeprecated = true;
|
||||
}
|
||||
var multiUrlPickerEditorFromPackage = editors.FirstOrDefault(x => x.Alias == "RJP.MultiUrlPicker");
|
||||
if (multiUrlPickerEditorFromPackage != null)
|
||||
{
|
||||
multiUrlPickerEditorFromPackage.Name = "(Obsolete) " + multiUrlPickerEditorFromPackage.Name;
|
||||
multiUrlPickerEditorFromPackage.IsDeprecated = true;
|
||||
}
|
||||
return editors;
|
||||
|
||||
}
|
||||
|
||||
@@ -107,6 +107,11 @@ namespace Umbraco.Core.Security
|
||||
|
||||
await EnsureValidSessionId(context);
|
||||
|
||||
if (context?.Identity == null)
|
||||
{
|
||||
context?.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
|
||||
return;
|
||||
}
|
||||
await base.ValidateIdentity(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -69,6 +70,11 @@ namespace Umbraco.Core.Services
|
||||
|
||||
public void Save(IMemberGroup memberGroup, bool raiseEvents = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(memberGroup.Name))
|
||||
{
|
||||
throw new InvalidOperationException("The name of a MemberGroup can not be empty");
|
||||
}
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMemberGroup>(memberGroup);
|
||||
@@ -133,4 +139,4 @@ namespace Umbraco.Core.Services
|
||||
/// </remarks>
|
||||
public static event TypedEventHandler<IMemberGroupService, SaveEventArgs<IMemberGroup>> Saved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,6 +731,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="stringToConvert">Referrs to itself</param>
|
||||
/// <returns>The MD5 hashed string</returns>
|
||||
[Obsolete("Please use the GenerateHash method instead. This may be removed in future versions")]
|
||||
public static string ToMd5(this string stringToConvert)
|
||||
{
|
||||
return stringToConvert.GenerateHash("MD5");
|
||||
@@ -741,6 +742,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="stringToConvert">referrs to itself</param>
|
||||
/// <returns>The SHA1 hashed string</returns>
|
||||
[Obsolete("Please use the GenerateHash method instead. This may be removed in future versions")]
|
||||
public static string ToSHA1(this string stringToConvert)
|
||||
{
|
||||
return stringToConvert.GenerateHash("SHA1");
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Umbraco.Tests.CodeFirst
|
||||
new PublishedPropertyType("articleDate", 0, "?"),
|
||||
new PublishedPropertyType("pageTitle", 0, "?"),
|
||||
};
|
||||
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
Debug.Print("INIT STRONG {0}",
|
||||
PublishedContentType.Get(PublishedItemType.Content, "anything")
|
||||
@@ -148,4 +148,4 @@ namespace Umbraco.Tests.CodeFirst
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Tests
|
||||
// AutoPublishedContentType will auto-generate other properties
|
||||
new PublishedPropertyType("content", 0, "?"),
|
||||
};
|
||||
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
Debug.Print("INIT LIB {0}",
|
||||
PublishedContentType.Get(PublishedItemType.Content, "anything")
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Tests.Membership
|
||||
new PublishedPropertyType("bodyText", 0, "?"),
|
||||
new PublishedPropertyType("author", 0, "?")
|
||||
};
|
||||
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
new PublishedPropertyType("creatorName", 0, "?"),
|
||||
new PublishedPropertyType("blah", 0, "?"), // ugly error when that one is missing...
|
||||
};
|
||||
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
|
||||
}
|
||||
@@ -729,4 +729,4 @@ namespace Umbraco.Tests.PublishedContent
|
||||
return s.Contains(val.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
// need to specify a custom callback for unit tests
|
||||
// AutoPublishedContentTypes generates properties automatically
|
||||
var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] {});
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", new PublishedPropertyType[] {});
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
|
||||
// need to specify a different callback for testing
|
||||
@@ -251,4 +251,4 @@ namespace Umbraco.Tests.PublishedContent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,9 +234,9 @@ namespace Umbraco.Tests.PublishedContent
|
||||
new PublishedPropertyType("prop1", 1, "?"),
|
||||
};
|
||||
|
||||
var contentType1 = new PublishedContentType(1, "ContentType1", Enumerable.Empty<string>(), props);
|
||||
var contentType2 = new PublishedContentType(2, "ContentType2", Enumerable.Empty<string>(), props);
|
||||
var contentType2s = new PublishedContentType(3, "ContentType2Sub", Enumerable.Empty<string>(), props);
|
||||
var contentType1 = new PublishedContentType(1, "ContentType1", "ContentType1", "ContentType1", Enumerable.Empty<string>(), props);
|
||||
var contentType2 = new PublishedContentType(2, "ContentType2", "ContentType2", "ContentType2", Enumerable.Empty<string>(), props);
|
||||
var contentType2s = new PublishedContentType(3, "ContentType2Sub", "ContentType2Sub", "ContentType2Sub", Enumerable.Empty<string>(), props);
|
||||
|
||||
cache.Add(new SolidPublishedContent(contentType1)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
// AutoPublishedContentType will auto-generate other properties
|
||||
new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias),
|
||||
};
|
||||
var type = new AutoPublishedContentType(0, "anything", propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
|
||||
var rCtx = GetRoutingContext("/test", 1234);
|
||||
@@ -65,4 +65,4 @@ namespace Umbraco.Tests.PublishedContent
|
||||
UmbracoContext.Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,12 +354,12 @@ namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, "?");
|
||||
|
||||
public AutoPublishedContentType(int id, string alias, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: base(id, alias, Enumerable.Empty<string>(), propertyTypes)
|
||||
public AutoPublishedContentType(int id, string alias, string name, string description, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: base(id, alias, name, description, Enumerable.Empty<string>(), propertyTypes)
|
||||
{ }
|
||||
|
||||
public AutoPublishedContentType(int id, string alias, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: base(id, alias, compositionAliases, propertyTypes)
|
||||
public AutoPublishedContentType(int id, string alias, string name, string description, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
|
||||
: base(id, alias, name, description, compositionAliases, propertyTypes)
|
||||
{ }
|
||||
|
||||
public override PublishedPropertyType GetPropertyType(string alias)
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
new PublishedPropertyType("testRecursive", 0, "?"),
|
||||
};
|
||||
var compositionAliases = new[] { "MyCompositionAlias" };
|
||||
var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes);
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", compositionAliases, propertyTypes);
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
|
||||
[TestFixture, RequiresSTA]
|
||||
public class MemberGroupServiceTests : BaseServiceTest
|
||||
{
|
||||
[SetUp]
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException("System.InvalidOperationException")]
|
||||
public void New_MemberGroup_Is_Not_Allowed_With_Empty_Name()
|
||||
{
|
||||
var service = ServiceContext.MemberGroupService;
|
||||
|
||||
service.Save(new MemberGroup {Name = ""});
|
||||
|
||||
Assert.Fail("An exception should have been thrown");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
// need to specify a custom callback for unit tests
|
||||
// AutoPublishedContentTypes generates properties automatically
|
||||
var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] {});
|
||||
var type = new AutoPublishedContentType(0, "anything", "anything", "anything", new PublishedPropertyType[] {});
|
||||
PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\Home.cs" />
|
||||
<Compile Include="Services\AuditServiceTests.cs" />
|
||||
<Compile Include="Services\ConsentServiceTests.cs" />
|
||||
<Compile Include="Services\MemberGroupServiceTests.cs" />
|
||||
<Compile Include="TestHelpers\ControllerTesting\AuthenticateEverythingExtensions.cs" />
|
||||
<Compile Include="TestHelpers\ControllerTesting\AuthenticateEverythingMiddleware.cs" />
|
||||
<Compile Include="TestHelpers\ControllerTesting\SpecificAssemblyResolver.cs" />
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
@ngdoc directive
|
||||
@name umbraco.directives.directive:umbCheckbox
|
||||
@restrict E
|
||||
@scope
|
||||
|
||||
@description
|
||||
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco checkbox.
|
||||
|
||||
<h3>Markup example</h3>
|
||||
<pre>
|
||||
<div ng-controller="My.Controller as vm">
|
||||
|
||||
<umb-checkbox
|
||||
name="checkboxlist"
|
||||
value="{{key}}"
|
||||
model="true"
|
||||
text="{{text}}">
|
||||
</umb-checkbox>
|
||||
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the checkbox to checked or unchecked.
|
||||
@param {string} value Set the value of the checkbox.
|
||||
@param {string} name Set the name of the checkbox.
|
||||
@param {string} text Set the text for the checkbox label.
|
||||
|
||||
|
||||
**/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function CheckboxDirective() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/forms/umb-checkbox.html',
|
||||
scope: {
|
||||
model: "=",
|
||||
value: "@",
|
||||
name: "@",
|
||||
text: "@",
|
||||
required: "="
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbCheckbox', CheckboxDirective);
|
||||
|
||||
})();
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
@ngdoc directive
|
||||
@name umbraco.directives.directive:umbRadiobutton
|
||||
@restrict E
|
||||
@scope
|
||||
|
||||
@description
|
||||
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco radio button.
|
||||
|
||||
<h3>Markup example</h3>
|
||||
<pre>
|
||||
<div ng-controller="My.Controller as vm">
|
||||
|
||||
<umb-radiobutton
|
||||
name="checkboxlist"
|
||||
value="{{key}}"
|
||||
model="true"
|
||||
text="{{text}}">
|
||||
</umb-radiobutton>
|
||||
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the radiobutton to checked or unchecked.
|
||||
@param {string} value Set the value of the radiobutton.
|
||||
@param {string} name Set the name of the radiobutton.
|
||||
@param {string} text Set the text for the radiobutton label.
|
||||
|
||||
|
||||
**/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function RadiobuttonDirective() {
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/forms/umb-radiobutton.html',
|
||||
scope: {
|
||||
model: "=",
|
||||
value: "@",
|
||||
name: "@",
|
||||
text: "@"
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbRadiobutton', RadiobuttonDirective);
|
||||
|
||||
})();
|
||||
+14
-35
@@ -103,34 +103,6 @@ angular.module("umbraco.directives")
|
||||
scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
|
||||
};
|
||||
|
||||
|
||||
//when loading an image without any crop info, we center and fit it
|
||||
var resizeImageToEditor = function(){
|
||||
//returns size fitting the cropper
|
||||
var size = cropperHelper.calculateAspectRatioFit(
|
||||
scope.dimensions.image.width,
|
||||
scope.dimensions.image.height,
|
||||
scope.dimensions.cropper.width,
|
||||
scope.dimensions.cropper.height,
|
||||
true);
|
||||
|
||||
//sets the image size and updates the scope
|
||||
scope.dimensions.image.width = size.width;
|
||||
scope.dimensions.image.height = size.height;
|
||||
|
||||
//calculate the best suited ratios
|
||||
scope.dimensions.scale.min = size.ratio;
|
||||
scope.dimensions.scale.max = 2;
|
||||
scope.dimensions.scale.current = size.ratio;
|
||||
|
||||
//center the image
|
||||
var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper);
|
||||
scope.dimensions.top = position.top;
|
||||
scope.dimensions.left = position.left;
|
||||
|
||||
setConstraints();
|
||||
};
|
||||
|
||||
//resize to a given ratio
|
||||
var resizeImageToScale = function(ratio){
|
||||
//do stuff
|
||||
@@ -227,12 +199,19 @@ angular.module("umbraco.directives")
|
||||
//set dimensions on image, viewport, cropper etc
|
||||
setDimensions(image);
|
||||
|
||||
//if we have a crop already position the image
|
||||
if(scope.crop){
|
||||
resizeImageToCrop();
|
||||
}else{
|
||||
resizeImageToEditor();
|
||||
}
|
||||
//create a default crop if we haven't got one already
|
||||
var createDefaultCrop = !scope.crop;
|
||||
if (createDefaultCrop) {
|
||||
calculateCropBox();
|
||||
}
|
||||
|
||||
resizeImageToCrop();
|
||||
|
||||
//if we're creating a new crop, make sure to zoom out fully
|
||||
if (createDefaultCrop) {
|
||||
scope.dimensions.scale.current = scope.dimensions.scale.min;
|
||||
resizeImageToScale(scope.dimensions.scale.min);
|
||||
}
|
||||
|
||||
//sets constaints for the cropper
|
||||
setConstraints();
|
||||
@@ -253,7 +232,7 @@ angular.module("umbraco.directives")
|
||||
var throttledResizing = _.throttle(function(){
|
||||
resizeImageToScale(scope.dimensions.scale.current);
|
||||
calculateCropBox();
|
||||
}, 100);
|
||||
}, 16);
|
||||
|
||||
|
||||
//happens when we change the scale
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ angular.module("umbraco.directives")
|
||||
var $overlay = element.find(".overlay");
|
||||
|
||||
scope.style = function () {
|
||||
if (scope.dimensions.width <= 0) {
|
||||
if (scope.dimensions.width <= 0 || scope.dimensions.height <= 0) {
|
||||
setDimensions();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function relationResource($q, $http, umbRequestHelper) {
|
||||
umbRequestHelper.getApiUrl(
|
||||
"relationApiBaseUrl",
|
||||
"GetByChildId",
|
||||
[{ childId: id, relationTypeAlias: alias }])),
|
||||
{ childId: id, relationTypeAlias: alias })),
|
||||
"Failed to get relation by child ID " + id + " and type of " + alias);
|
||||
},
|
||||
|
||||
@@ -62,4 +62,4 @@ function relationResource($q, $http, umbRequestHelper) {
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('umbraco.resources').factory('relationResource', relationResource);
|
||||
angular.module('umbraco.resources').factory('relationResource', relationResource);
|
||||
|
||||
@@ -123,13 +123,6 @@ function cropperHelper(umbRequestHelper, $http) {
|
||||
return crop;
|
||||
},
|
||||
|
||||
centerInsideViewPort : function(img, viewport){
|
||||
var left = viewport.width/ 2 - img.width / 2,
|
||||
top = viewport.height / 2 - img.height / 2;
|
||||
|
||||
return {left: left, top: top};
|
||||
},
|
||||
|
||||
alignToCoordinates : function(image, center, viewport){
|
||||
|
||||
var min_left = (image.width) - (viewport.width);
|
||||
|
||||
@@ -16,17 +16,17 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
|
||||
};
|
||||
|
||||
//add to umbraco installer facts here
|
||||
var facts = ['Umbraco helped millions of people watch a man jump from the edge of space',
|
||||
'Over 440 000 websites are currently powered by Umbraco',
|
||||
var facts = ["Umbraco helped millions of people watch a man jump from the edge of space",
|
||||
"Over 500 000 websites are currently powered by Umbraco",
|
||||
"At least 2 people have named their cat 'Umbraco'",
|
||||
'On an average day, more than 1000 people download Umbraco',
|
||||
'<a target="_blank" href="https://umbraco.tv">umbraco.tv</a> is the premier source of Umbraco video tutorials to get you started',
|
||||
'You can find the world\'s friendliest CMS community at <a target="_blank" href="https://our.umbraco.com">our.umbraco.com</a>',
|
||||
'You can become a certified Umbraco developer by attending one of the official courses',
|
||||
'Umbraco works really well on tablets',
|
||||
'You have 100% control over your markup and design when crafting a website in Umbraco',
|
||||
'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company',
|
||||
"There's a pretty big chance, you've visited a website powered by Umbraco today",
|
||||
"On an average day more than 1000 people download Umbraco",
|
||||
"<a target='_blank' href='https://umbraco.tv/'>umbraco.tv</a> is the premier source of Umbraco video tutorials to get you started",
|
||||
"You can find the world's friendliest CMS community at <a target='_blank' href='https://our.umbraco.com/'>our.umbraco.com</a>",
|
||||
"You can become a certified Umbraco developer by attending one of the official courses",
|
||||
"Umbraco works really well on tablets",
|
||||
"You have 100% control over your markup and design when crafting a website in Umbraco",
|
||||
"Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company",
|
||||
"There's a pretty big chance you've visited a website powered by Umbraco today",
|
||||
"'Umbraco-spotting' is the game of spotting big brands running Umbraco",
|
||||
"At least 4 people have the Umbraco logo tattooed on them",
|
||||
"'Umbraco' is the Danish name for an allen key",
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
@import "components/umb-confirm-action.less";
|
||||
@import "components/umb-keyboard-shortcuts-overview.less";
|
||||
@import "components/umb-checkbox-list.less";
|
||||
@import "components/umb-form-check.less";
|
||||
@import "components/umb-locked-field.less";
|
||||
@import "components/umb-tabs.less";
|
||||
@import "components/umb-load-indicator.less";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@checkboxWidth: 15px;
|
||||
@checkboxHeight: 15px;
|
||||
|
||||
.umb-checkbox-list {
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
@checkboxWidth: 15px;
|
||||
@checkboxHeight: 15px;
|
||||
|
||||
.umb-form-check {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
&__text{
|
||||
margin: 0 0 0 26px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
&__input{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
|
||||
&:checked ~ .umb-form-check__state .umb-form-check__check{
|
||||
border-color: @green;
|
||||
}
|
||||
|
||||
&:focus:checked ~ .umb-form-check .umb-form-check__check,
|
||||
&:focus ~ .umb-form-check__state .umb-form-check__check{
|
||||
border-color: @gray-5;
|
||||
}
|
||||
|
||||
&:checked ~ .umb-form-check__state{
|
||||
.umb-form-check__check{
|
||||
// This only happens if the state has a radiobutton modifier
|
||||
.umb-form-check--radiobutton &{
|
||||
&:before{
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
// This only happens if state has the checkbox modifier
|
||||
.umb-form-check--checkbox &{
|
||||
&:before{
|
||||
width: @checkboxWidth;
|
||||
height: @checkboxHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This only happens if state has the checkbox modifier
|
||||
.umb-form-check--checkbox &{
|
||||
.umb-form-check__icon{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__state{
|
||||
height: 17px;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&__check{
|
||||
position: relative;
|
||||
border: 1px solid @gray-7;
|
||||
width: @checkboxWidth;
|
||||
height: @checkboxHeight;
|
||||
|
||||
&:before{
|
||||
content: "";
|
||||
background: @green;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
// This only happens if state has the radiobutton modifier
|
||||
.umb-form-check--radiobutton &{
|
||||
border-radius: 100%;
|
||||
|
||||
&:before{
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 100%;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: .15s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
// This only happens if state has the checkbox modifier
|
||||
.umb-form-check--checkbox &{
|
||||
&:before{
|
||||
width: 0;
|
||||
height: 0;
|
||||
transition: .05s ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__icon{
|
||||
color: @white;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
opacity: 0;
|
||||
transition: .2s ease-out;
|
||||
|
||||
&:before{
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,16 @@
|
||||
|
||||
&-push {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
&--list{
|
||||
float: left;
|
||||
}
|
||||
|
||||
&__item{
|
||||
line-height: 1;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-editor-tiny {
|
||||
@@ -246,7 +255,7 @@ div.umb-codeeditor .umb-btn-toolbar {
|
||||
margin: 24px 0 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
&-wrap{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div ng-controller="Umbraco.Overlays.UserController">
|
||||
|
||||
<div class="umb-control-group" ng-if="!showPasswordFields">
|
||||
|
||||
<h5><localize key="user_yourProfile" /></h5>
|
||||
@@ -78,7 +77,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="umb-control-group" ng-if="!showPasswordFields">
|
||||
<div class="umb-control-group" ng-if="!showPasswordFields && history.length">
|
||||
<h5><localize key="user_yourHistory" /></h5>
|
||||
<ul class="umb-tree">
|
||||
<li ng-repeat="item in history | orderBy:'time':true">
|
||||
@@ -126,7 +125,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group">
|
||||
<div class="umb-control-group" ng-if="tab.length">
|
||||
<div ng-repeat="tab in dashboard">
|
||||
<div ng-repeat="property in tab.properties">
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<label class="checkbox umb-form-check umb-form-check--checkbox">
|
||||
<input type="checkbox" name="{{name}}"
|
||||
value="{{value}}"
|
||||
ng-model="model"
|
||||
class="umb-form-check__input"
|
||||
ng-required="required" />
|
||||
|
||||
<div class="umb-form-check__state umb-form-check__state" aria-hidden="true">
|
||||
<div class="umb-form-check__check">
|
||||
<i class="umb-form-check__icon icon-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span class="umb-form-check__text">{{text}}</span>
|
||||
</label>
|
||||
@@ -0,0 +1,11 @@
|
||||
<label class="radio umb-form-check umb-form-check--radiobutton">
|
||||
<input type="radio" name="radiobuttons-{{name}}"
|
||||
value="{{value}}"
|
||||
ng-model="model"
|
||||
class="umb-form-check__input" />
|
||||
|
||||
<div class="umb-form-check__state umb-form-check__state" aria-hidden="true">
|
||||
<div class="umb-form-check__check"></div>
|
||||
</div>
|
||||
<span class="umb-form-check__text">{{text}}</span>
|
||||
</label>
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="crop-slider">
|
||||
<div class="crop-slider" ng-if="loaded">
|
||||
<i class="icon-picture"></i>
|
||||
<input
|
||||
type="range"
|
||||
@@ -16,4 +16,4 @@
|
||||
ng-model="dimensions.scale.current" />
|
||||
<i class="icon-picture" style="font-size: 22px"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController",
|
||||
function ($scope, relationResource, contentResource, navigationService, appState, treeService, userService) {
|
||||
function ($scope, relationResource, contentResource, navigationService, appState, treeService, userService, localizationService) {
|
||||
var dialogOptions = $scope.dialogOptions;
|
||||
|
||||
$scope.source = _.clone(dialogOptions.currentNode);
|
||||
@@ -21,6 +21,10 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
|
||||
});
|
||||
$scope.labels = {};
|
||||
localizationService.localizeMany(["treeHeaders_content"]).then(function (data) {
|
||||
$scope.labels.treeRoot = data[0];
|
||||
});
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
|
||||
@@ -96,7 +100,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
|
||||
$scope.relation = data[0];
|
||||
|
||||
if ($scope.relation.parentId == -1) {
|
||||
$scope.target = { id: -1, name: "Root" };
|
||||
$scope.target = { id: -1, name: $scope.labels.treeRoot };
|
||||
|
||||
} else {
|
||||
$scope.loading = true;
|
||||
|
||||
@@ -1,63 +1,138 @@
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController",
|
||||
function ($scope, relationResource, mediaResource, navigationService, appState, treeService, localizationService) {
|
||||
function ($scope, relationResource, mediaResource, navigationService, appState, treeService, userService, localizationService) {
|
||||
var dialogOptions = $scope.dialogOptions;
|
||||
|
||||
var node = dialogOptions.currentNode;
|
||||
$scope.source = _.clone(dialogOptions.currentNode);
|
||||
|
||||
$scope.error = null;
|
||||
$scope.success = false;
|
||||
$scope.error = null;
|
||||
$scope.loading = true;
|
||||
$scope.moving = false;
|
||||
$scope.success = false;
|
||||
|
||||
relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) {
|
||||
$scope.dialogTreeEventHandler = $({});
|
||||
$scope.searchInfo = {
|
||||
showSearch: false,
|
||||
results: [],
|
||||
selectedSearchResults: []
|
||||
}
|
||||
$scope.treeModel = {
|
||||
hideHeader: false
|
||||
}
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
|
||||
});
|
||||
$scope.labels = {};
|
||||
localizationService.localizeMany(["treeHeaders_media"]).then(function (data) {
|
||||
$scope.labels.treeRoot = data[0];
|
||||
});
|
||||
|
||||
if (data.length == 0) {
|
||||
$scope.success = false;
|
||||
$scope.error = {
|
||||
errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'),
|
||||
data: {
|
||||
Message: localizationService.localize('recycleBin_noRestoreRelation')
|
||||
}
|
||||
}
|
||||
return;
|
||||
function nodeSelectHandler(ev, args) {
|
||||
|
||||
if (args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.hideSearch = function () {
|
||||
$scope.searchInfo.showSearch = false;
|
||||
$scope.searchInfo.results = [];
|
||||
}
|
||||
|
||||
// method to select a search result
|
||||
$scope.selectResult = function (evt, result) {
|
||||
result.selected = result.selected === true ? false : true;
|
||||
nodeSelectHandler(evt, { event: evt, node: result });
|
||||
};
|
||||
|
||||
//callback when there are search results
|
||||
$scope.onSearchResults = function (results) {
|
||||
$scope.searchInfo.results = results;
|
||||
$scope.searchInfo.showSearch = true;
|
||||
};
|
||||
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
relationResource.getByChildId($scope.source.id, "relateParentMediaFolderOnDelete").then(function (data) {
|
||||
$scope.loading = false;
|
||||
|
||||
if (!data.length) {
|
||||
$scope.moving = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.relation = data[0];
|
||||
|
||||
if ($scope.relation.parentId == -1) {
|
||||
$scope.target = { id: -1, name: "Root" };
|
||||
$scope.target = { id: -1, name: $scope.labels.treeRoot };
|
||||
|
||||
} else {
|
||||
$scope.loading = true;
|
||||
|
||||
mediaResource.getById($scope.relation.parentId).then(function (data) {
|
||||
$scope.loading = false;
|
||||
$scope.target = data;
|
||||
|
||||
// make sure the target item isn't in the recycle bin
|
||||
if ($scope.target.path.indexOf("-20") !== -1) {
|
||||
$scope.error = {
|
||||
errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'),
|
||||
data: {
|
||||
Message: localizationService.localize('recycleBin_restoreUnderRecycled').then(function (value) {
|
||||
value.replace('%0%', $scope.target.name);
|
||||
})
|
||||
}
|
||||
};
|
||||
$scope.success = false;
|
||||
// make sure the target item isn't in the recycle bin
|
||||
if ($scope.target.path.indexOf("-21") !== -1) {
|
||||
$scope.moving = true;
|
||||
$scope.target = null;
|
||||
}
|
||||
|
||||
}, function (err) {
|
||||
$scope.success = false;
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
});
|
||||
}
|
||||
|
||||
}, function (err) {
|
||||
$scope.success = false;
|
||||
$scope.error = err;
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
$scope.restore = function () {
|
||||
$scope.restore = function () {
|
||||
$scope.loading = true;
|
||||
|
||||
// this code was copied from `content.move.controller.js`
|
||||
mediaResource.move({ parentId: $scope.target.id, id: node.id })
|
||||
mediaResource.move({ parentId: $scope.target.id, id: $scope.source.id })
|
||||
.then(function (path) {
|
||||
|
||||
$scope.loading = false;
|
||||
$scope.success = true;
|
||||
|
||||
//first we need to remove the node that launched the dialog
|
||||
@@ -78,7 +153,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController",
|
||||
});
|
||||
|
||||
}, function (err) {
|
||||
$scope.success = false;
|
||||
$scope.loading = false;
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,25 +1,96 @@
|
||||
<div ng-controller="Umbraco.Editors.Media.RestoreController">
|
||||
<div class="umb-dialog-body">
|
||||
<div class="umb-dialog-body" ng-cloak>
|
||||
<umb-pane>
|
||||
<umb-load-indicator
|
||||
ng-if="loading">
|
||||
</umb-load-indicator>
|
||||
|
||||
<p class="abstract" ng-hide="error != null || success == true">
|
||||
<localize key="actions_restore">Restore</localize> <strong>{{currentNode.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>?
|
||||
</p>
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.Message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-error" ng-show="error != null">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.Message}}</div>
|
||||
</div>
|
||||
<div ng-show="success">
|
||||
<div class="alert alert-success">
|
||||
<strong>{{source.name}}</strong>
|
||||
<span ng-hide="moving"><localize key="recycleBin_wasRestored">was restored under</localize></span>
|
||||
<span ng-show="moving"><localize key="editdatatype_wasMoved">was moved underneath</localize></span>
|
||||
<strong>{{target.name}}</strong>
|
||||
</div>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" ng-show="success == true">
|
||||
<p><strong>{{currentNode.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize> <strong>{{target.name}}</strong></p>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()"><localize key="general_ok">OK</localize></button>
|
||||
</div>
|
||||
<div ng-hide="moving || loading || success">
|
||||
|
||||
<p class="abstract" ng-hide="error || success">
|
||||
<localize key="actions_restore">Restore</localize> <strong>{{source.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>?
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-hide="!moving || loading || success">
|
||||
<div>
|
||||
<div class="alert alert-info">
|
||||
<div><strong><localize key="recycleBin_itemCannotBeRestored">Cannot automatically restore this item</localize></strong></div>
|
||||
<div><localize key="recycleBin_itemCannotBeRestoredHelpText">There is no location where this item can be automatically restored. You can move the item manually using the tree below.</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="media">
|
||||
</umb-tree-search-box>
|
||||
|
||||
<br />
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="media"
|
||||
hideheader="{{treeModel.hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-pane>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success == true">
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || moving || success">
|
||||
<a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a>
|
||||
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || !moving || success">
|
||||
<a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a>
|
||||
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null" ng-disabled="!target"><localize key="actions_move">Move</localize></button>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success == true">
|
||||
<a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a>
|
||||
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function booleanEditorController($scope) {
|
||||
function booleanEditorController($scope, angularHelper) {
|
||||
|
||||
function setupViewModel() {
|
||||
$scope.renderModel = {
|
||||
@@ -28,7 +28,8 @@ function booleanEditorController($scope) {
|
||||
};
|
||||
|
||||
// Update the value when the toggle is clicked
|
||||
$scope.toggle = function(){
|
||||
$scope.toggle = function () {
|
||||
angularHelper.getCurrentForm($scope).$setDirty();
|
||||
if($scope.renderModel.value){
|
||||
$scope.model.value = "0";
|
||||
setupViewModel();
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
<div class="umb-editor umb-checkboxlist" ng-controller="Umbraco.PropertyEditors.CheckboxListController">
|
||||
<div class="umb-editor umb-editor--list" ng-controller="Umbraco.PropertyEditors.CheckboxListController">
|
||||
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="item in selectedItems">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="checkboxlist"
|
||||
value="{{item.key}}"
|
||||
ng-model="item.checked"
|
||||
ng-required="model.validation.mandatory && !model.value.length" />
|
||||
{{item.val}}
|
||||
</label>
|
||||
<li ng-repeat="item in selectedItems" class="umb-editor__item">
|
||||
<umb-checkbox name="checkboxlist" value="{{item.key}}" model="item.checked" text="{{item.val}}" required="model.validation.mandatory && !model.value.length"></umb-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ function ColorPickerController($scope, angularHelper) {
|
||||
|
||||
$scope.onSelect = function (color) {
|
||||
// did the value change?
|
||||
if ($scope.model.value.value === color) {
|
||||
if ($scope.model.value != null && $scope.model.value.value === color) {
|
||||
// User clicked the currently selected color
|
||||
// to remove the selection, they don't want
|
||||
// to select any color after all.
|
||||
|
||||
+2
-2
@@ -145,9 +145,9 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
|
||||
|
||||
var element = $element.find("div:first");
|
||||
|
||||
// Open the datepicker and add a changeDate eventlistener
|
||||
// Open the datepicker and add a changeDate eventlistener
|
||||
element
|
||||
.datetimepicker(angular.extend({ useCurrent: true }, $scope.model.config))
|
||||
.datetimepicker(angular.extend({ useCurrent: $scope.model.config.defaultEmpty !== "1" }, $scope.model.config))
|
||||
.on("dp.change", applyDate)
|
||||
.on("dp.error", function(a, b, c) {
|
||||
$scope.hasDatetimePickerValue = false;
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
function multiUrlPickerController($scope, angularHelper, localizationService, entityResource, iconHelper) {
|
||||
|
||||
$scope.renderModel = [];
|
||||
|
||||
if ($scope.preview) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray($scope.model.value)) {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
|
||||
var currentForm = angularHelper.getCurrentForm($scope);
|
||||
|
||||
$scope.sortableOptions = {
|
||||
distance: 10,
|
||||
tolerance: "pointer",
|
||||
scroll: true,
|
||||
zIndex: 6000,
|
||||
update: function () {
|
||||
currentForm.$setDirty();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.model.value.forEach(function (link) {
|
||||
link.icon = iconHelper.convertFromLegacyIcon(link.icon);
|
||||
$scope.renderModel.push(link);
|
||||
});
|
||||
|
||||
$scope.$on("formSubmitting", function () {
|
||||
$scope.model.value = $scope.renderModel;
|
||||
});
|
||||
|
||||
$scope.$watch(
|
||||
function () {
|
||||
return $scope.renderModel.length;
|
||||
},
|
||||
function () {
|
||||
if ($scope.model.config && $scope.model.config.minNumber) {
|
||||
$scope.multiUrlPickerForm.minCount.$setValidity(
|
||||
"minCount",
|
||||
+$scope.model.config.minNumber <= $scope.renderModel.length
|
||||
);
|
||||
}
|
||||
if ($scope.model.config && $scope.model.config.maxNumber) {
|
||||
$scope.multiUrlPickerForm.maxCount.$setValidity(
|
||||
"maxCount",
|
||||
+$scope.model.config.maxNumber >= $scope.renderModel.length
|
||||
);
|
||||
}
|
||||
$scope.sortableOptions.disabled = $scope.renderModel.length === 1;
|
||||
}
|
||||
);
|
||||
|
||||
$scope.remove = function ($index) {
|
||||
$scope.renderModel.splice($index, 1);
|
||||
|
||||
currentForm.$setDirty();
|
||||
};
|
||||
|
||||
$scope.openLinkPicker = function (link, $index) {
|
||||
var target = link ? {
|
||||
name: link.name,
|
||||
anchor: link.queryString,
|
||||
// the linkPicker breaks if it get an udi for media
|
||||
udi: link.isMedia ? null : link.udi,
|
||||
url: link.url,
|
||||
target: link.target
|
||||
} : null;
|
||||
|
||||
$scope.linkPickerOverlay = {
|
||||
view: "linkpicker",
|
||||
currentTarget: target,
|
||||
show: true,
|
||||
submit: function (model) {
|
||||
if (model.target.url || model.target.anchor) {
|
||||
// if an anchor exists, check that it is appropriately prefixed
|
||||
if (model.target.anchor && model.target.anchor[0] !== '?' && model.target.anchor[0] !== '#') {
|
||||
model.target.anchor = (model.target.anchor.indexOf('=') === -1 ? '#' : '?') + model.target.anchor;
|
||||
}
|
||||
if (link) {
|
||||
if (link.isMedia && link.url === model.target.url) {
|
||||
// we can assume the existing media item is changed and no new file has been selected
|
||||
// so we don't need to update the udi and isMedia fields
|
||||
} else {
|
||||
link.udi = model.target.udi;
|
||||
link.isMedia = model.target.isMedia;
|
||||
}
|
||||
|
||||
link.name = model.target.name || model.target.url || model.target.anchor;
|
||||
link.queryString = model.target.anchor;
|
||||
link.target = model.target.target;
|
||||
link.url = model.target.url;
|
||||
} else {
|
||||
link = {
|
||||
isMedia: model.target.isMedia,
|
||||
name: model.target.name || model.target.url || model.target.anchor,
|
||||
queryString: model.target.anchor,
|
||||
target: model.target.target,
|
||||
udi: model.target.udi,
|
||||
url: model.target.url
|
||||
};
|
||||
$scope.renderModel.push(link);
|
||||
}
|
||||
|
||||
if (link.udi) {
|
||||
var entityType = link.isMedia ? "media" : "document";
|
||||
|
||||
entityResource.getById(link.udi, entityType).then(function (data) {
|
||||
link.icon = iconHelper.convertFromLegacyIcon(data.icon);
|
||||
link.published = (data.metaData && data.metaData.IsPublished === false && entityType === "Document") ? false : true;
|
||||
link.trashed = data.trashed;
|
||||
if (link.trashed) {
|
||||
item.url = localizationService.dictionary.general_recycleBin;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
link.icon = "icon-link";
|
||||
link.published = true;
|
||||
}
|
||||
|
||||
currentForm.$setDirty();
|
||||
}
|
||||
|
||||
$scope.linkPickerOverlay.show = false;
|
||||
$scope.linkPickerOverlay = null;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.MultiUrlPickerController", multiUrlPickerController);
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.MultiUrlPickerController" class="umb-editor umb-contentpicker">
|
||||
<p ng-if="(renderModel|filter:{trashed:true}).length == 1"><localize key="contentPicker_pickedTrashedItem"></localize></p>
|
||||
<p ng-if="(renderModel|filter:{trashed:true}).length > 1"><localize key="contentPicker_pickedTrashedItems"></localize></p>
|
||||
|
||||
<ng-form name="multiUrlPickerForm">
|
||||
<div ui-sortable="sortableOptions" ng-model="renderModel">
|
||||
<umb-node-preview
|
||||
ng-repeat="link in renderModel"
|
||||
icon="link.icon"
|
||||
name="link.name"
|
||||
published="link.published"
|
||||
description="link.url + (link.queryString ? link.queryString : '')"
|
||||
sortable="!sortableOptions.disabled"
|
||||
allow-remove="true"
|
||||
allow-edit="true"
|
||||
on-remove="remove($index)"
|
||||
on-edit="openLinkPicker(link, $index)">
|
||||
</umb-node-preview>
|
||||
</div>
|
||||
|
||||
<a ng-show="!model.config.maxNumber || renderModel.length < model.config.maxNumber"
|
||||
class="umb-node-preview-add"
|
||||
href
|
||||
ng-click="openLinkPicker()"
|
||||
prevent-default>
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
|
||||
<div class="umb-contentpicker__min-max-help">
|
||||
|
||||
<!-- Both min and max items -->
|
||||
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber !== model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Equal min and max -->
|
||||
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber === model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add {{model.config.minNumber - renderModel.length}} item(s)</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Only max -->
|
||||
<span ng-if="!model.config.minNumber && model.config.maxNumber">
|
||||
<span ng-if="renderModel.length < model.config.maxNumber">Add up to {{model.config.maxNumber}} items</span>
|
||||
<span ng-if="renderModel.length > model.config.maxNumber">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Only min -->
|
||||
<span ng-if="model.config.minNumber && !model.config.maxNumber && renderModel.length < model.config.minNumber">
|
||||
Add at least {{model.config.minNumber}} item(s)
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<!--These are here because we need ng-form fields to validate against-->
|
||||
<input type="hidden" name="minCount" ng-model="renderModel" />
|
||||
<input type="hidden" name="maxCount" ng-model="renderModel" />
|
||||
|
||||
<div class="help-inline" val-msg-for="minCount" val-toggle-msg="minCount">
|
||||
<localize key="validation_minCount">You need to add at least</localize> {{model.config.minNumber}} <localize key="validation_items">items</localize>
|
||||
</div>
|
||||
|
||||
<div class="help-inline" val-msg-for="maxCount" val-toggle-msg="maxCount">
|
||||
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
|
||||
</div>
|
||||
</ng-form>
|
||||
<umb-overlay ng-if="linkPickerOverlay.show"
|
||||
model="linkPickerOverlay"
|
||||
view="linkPickerOverlay.view"
|
||||
position="right">
|
||||
</umb-overlay>
|
||||
</div>
|
||||
@@ -1,12 +1,8 @@
|
||||
<div class="umb-editor umb-radiobuttons" ng-controller="Umbraco.PropertyEditors.RadioButtonsController">
|
||||
<div class="umb-editor umb-edtitor--list" ng-controller="Umbraco.PropertyEditors.RadioButtonsController">
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="item in model.config.items">
|
||||
<label class="radio">
|
||||
<input type="radio" name="radiobuttons-{{model.alias}}"
|
||||
value="{{item.id}}"
|
||||
ng-model="model.value" />
|
||||
{{item.value}}
|
||||
</label>
|
||||
|
||||
<li ng-repeat="item in model.config.items" class="umb-editor__item">
|
||||
<umb-radiobutton name="{{model.alias}}" value="{{item.id}}" model="model.value" text="{{item.value}}"></umb-radiobutton>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -278,7 +278,7 @@ angular.module("umbraco")
|
||||
$scope.linkPickerOverlay = {
|
||||
view: "linkpicker",
|
||||
currentTarget: currentTarget,
|
||||
anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)),
|
||||
anchors: editorState.current ? tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)) : [],
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
|
||||
|
||||
@@ -1038,9 +1038,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>7132</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>7140</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7132</IISUrl>
|
||||
<IISUrl>http://localhost:7140</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -1350,4 +1350,4 @@
|
||||
<key alias="enabledConfirm">轉址追蹤器已開啟。</key>
|
||||
<key alias="enableError">啟動轉址追蹤器錯誤,更多資訊請參閱您的紀錄檔。</key>
|
||||
</area>
|
||||
</language>
|
||||
</language>
|
||||
|
||||
@@ -8,19 +8,19 @@ For details on the format of this configuration file see:
|
||||
https://our.umbraco.com/documentation/reference/config/healthchecks
|
||||
-->
|
||||
<HealthChecks>
|
||||
<disabledChecks>
|
||||
<!--<check id="1B5D221B-CE99-4193-97CB-5F3261EC73DF" disabledOn="" disabledBy="0" />-->
|
||||
</disabledChecks>
|
||||
<notificationSettings enabled="true" firstRunTime="" periodInHours="24">
|
||||
<notificationMethods>
|
||||
<notificationMethod alias="email" enabled="true" verbosity="Summary">
|
||||
<settings>
|
||||
<add key="recipientEmail" value="" />
|
||||
</settings>
|
||||
</notificationMethod>
|
||||
</notificationMethods>
|
||||
<disabledChecks>
|
||||
<!--<check id="EB66BB3B-1BCD-4314-9531-9DA2C1D6D9A7" disabledOn="" disabledBy="0" />-->
|
||||
</disabledChecks>
|
||||
</notificationSettings>
|
||||
</HealthChecks>
|
||||
<!--<check id="1B5D221B-CE99-4193-97CB-5F3261EC73DF" disabledOn="" disabledBy="0" />-->
|
||||
</disabledChecks>
|
||||
<notificationSettings enabled="true" firstRunTime="" periodInHours="24">
|
||||
<notificationMethods>
|
||||
<notificationMethod alias="email" enabled="true" verbosity="Summary">
|
||||
<settings>
|
||||
<add key="recipientEmail" value="" />
|
||||
</settings>
|
||||
</notificationMethod>
|
||||
</notificationMethods>
|
||||
<disabledChecks>
|
||||
<!--<check id="EB66BB3B-1BCD-4314-9531-9DA2C1D6D9A7" disabledOn="" disabledBy="0" />-->
|
||||
</disabledChecks>
|
||||
</notificationSettings>
|
||||
</HealthChecks>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
https://our.umbraco.com/documentation/using-umbraco/config-files/umbracoSettings/
|
||||
Many of the optional settings are not explicitly listed here
|
||||
but can be found in the online documentation.
|
||||
-->
|
||||
|
||||
-->
|
||||
|
||||
<backOffice>
|
||||
<tours enable="true"></tours>
|
||||
</backOffice>
|
||||
</backOffice>
|
||||
|
||||
<content>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<notifications>
|
||||
<!-- the email that should be used as from mail when umbraco sends a notification -->
|
||||
<!-- you can add a display name to the email like thist: <email>Your display name here <your@email.here></email> -->
|
||||
<!-- you can add a display name to the email like this: <email>Your display name here <your@email.here></email> -->
|
||||
<email>your@email.here</email>
|
||||
</notifications>
|
||||
|
||||
@@ -61,14 +61,14 @@
|
||||
<EnablePropertyValueConverters>true</EnablePropertyValueConverters>
|
||||
|
||||
<!-- You can specify your own background image for the login screen here. The image will automatically get an overlay to match back office colors - this path is relative to the ~/umbraco path. The default location is: /umbraco/assets/img/installer.jpg -->
|
||||
<loginBackgroundImage>assets/img/installer.jpg</loginBackgroundImage>
|
||||
<loginBackgroundImage>assets/img/installer.jpg</loginBackgroundImage>
|
||||
|
||||
</content>
|
||||
|
||||
<security>
|
||||
<!-- set to true to auto update login interval (and there by disabling the lock screen -->
|
||||
<keepUserLoggedIn>false</keepUserLoggedIn>
|
||||
<!-- by default this is true and if not specified in config will be true. set to false to always show a separate username field in the back office user editor -->
|
||||
<!-- by default this is true and if not specified in config will be true. set to false to always show a separate username field in the back office user editor -->
|
||||
<usernameIsEmail>true</usernameIsEmail>
|
||||
<!-- change in 4.8: Disabled users are now showed dimmed and last in the tree. If you prefer not to display them set this to true -->
|
||||
<hideDisabledUsersInBackoffice>false</hideDisabledUsersInBackoffice>
|
||||
|
||||
@@ -1458,9 +1458,9 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="macros">Makroer</key>
|
||||
<key alias="mediaTypes">Medietyper</key>
|
||||
<key alias="member">Medlemmer</key>
|
||||
<key alias="memberGroups">Medlemsgruppe</key>
|
||||
<key alias="memberGroups">Medlemsgrupper</key>
|
||||
<key alias="memberRoles">Roller</key>
|
||||
<key alias="memberTypes">Medlemstype</key>
|
||||
<key alias="memberTypes">Medlemstyper</key>
|
||||
<key alias="documentTypes">Dokumenttyper</key>
|
||||
<key alias="relationTypes">Relationstyper</key>
|
||||
<key alias="packager">Pakker</key>
|
||||
|
||||
@@ -2206,6 +2206,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notification email is still set to the default value of <strong>%0%</strong>.]]></key>
|
||||
<key alias="scheduledHealthCheckEmailBody"><![CDATA[<html><body><p>Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:</p>%2%</body></html>]]></key>
|
||||
<key alias="scheduledHealthCheckEmailSubject">Umbraco Health Check Status: %0%</key>
|
||||
|
||||
<key alias="tls12HealthCheckSuccess">Your site can use the TLS 1.2 security protocol when making outbound connections to HTTPS endpoints.</key>
|
||||
<key alias="tls12HealthCheckWarn">Your site isn't configured to allow the TLS 1.2 security protocol when making outbound connections: some HTTPS endpoints might not be reachable using a less secure protocol.</key>
|
||||
</area>
|
||||
<area alias="redirectUrls">
|
||||
<key alias="disableUrlTracker">Disable URL tracker</key>
|
||||
|
||||
@@ -2199,6 +2199,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notification email is still set to the default value of <strong>%0%</strong>.]]></key>
|
||||
<key alias="scheduledHealthCheckEmailBody"><![CDATA[<html><body><p>Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:</p>%2%</body></html>]]></key>
|
||||
<key alias="scheduledHealthCheckEmailSubject">Umbraco Health Check Status: %0%</key>
|
||||
|
||||
<key alias="tls12HealthCheckSuccess">Your site can use the TLS 1.2 security protocol when making outbound connections to HTTPS endpoints.</key>
|
||||
<key alias="tls12HealthCheckWarn">Your site isn't configured to allow the TLS 1.2 security protocol when making outbound connections: some HTTPS endpoints might not be reachable using a less secure protocol.</key>
|
||||
</area>
|
||||
<area alias="redirectUrls">
|
||||
<key alias="disableUrlTracker">Disable URL tracker</key>
|
||||
|
||||
@@ -1856,6 +1856,9 @@
|
||||
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[El email de notificación está todavía configurado en tuvalor por defecto: <strong>%0%</strong>.]]></key>
|
||||
<key alias="scheduledHealthCheckEmailBody"><![CDATA[<html><body><p>Los resultados de los Chequeos de Salud de Umbraco programados para ejecutarse el %0% a las %1% son:</p>%2%</body></html>]]></key>
|
||||
<key alias="scheduledHealthCheckEmailSubject">Status de los Chequeos de Salud de Umbraco: %0%</key>
|
||||
|
||||
<key alias="tlsHealthCheckSuccess">Su sitio web está configurado para usar TLS 1.2 o superior para las conexiones salientes.</key>
|
||||
<key alias="tlsHealthCheckWarn">Las conexiones salientes de su sitio web están siendo servidas a través de un protocolo antiguo. Deberías considerar actualizarlo para usar TLS 1.2 o superior.</key>
|
||||
</area>
|
||||
<area alias="redirectUrls">
|
||||
<key alias="disableUrlTracker">Desactivar URL tracker</key>
|
||||
|
||||
@@ -1623,6 +1623,9 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
|
||||
|
||||
<key alias="notificationEmailsCheckSuccessMessage"><![CDATA[Notificatie email is verzonden naar <strong>%0%</strong>.]]></key>
|
||||
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notificatie email staat nog steeds op de default waarde van <strong>%0%</strong>.]]></key>
|
||||
|
||||
<key alias="tls12HealthCheckSuccess">Uw website kan het TLS 1.2 beveiligingsprotocol gebruiken bij het maken van uitgaande verbindingen naar HTTPS-eindpunten.</key>
|
||||
<key alias="tls12HealthCheckWarn">Uw website is niet geconfigureerd om het TLS 1.2 beveiligingsprotocol te kunnen gebruiken bij het maken van uitgaande verbindingen: sommige HTTPS-eindpunten zijn mogelijk niet bereikbaar via een minder veilig protocol.</key>
|
||||
</area>
|
||||
<area alias="redirectUrls">
|
||||
<key alias="disableUrlTracker">URL tracker uitzetten</key>
|
||||
|
||||
@@ -117,6 +117,7 @@ namespace Umbraco.Web.Cache
|
||||
LegacyMediaPickerPropertyConverter.ClearCaches();
|
||||
SliderValueConverter.ClearCaches();
|
||||
MediaPickerPropertyConverter.ClearCaches();
|
||||
MultiUrlPickerPropertyConverter.ClearCaches();
|
||||
|
||||
|
||||
base.Refresh(jsonPayload);
|
||||
|
||||
@@ -847,7 +847,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </remarks>
|
||||
[HttpDelete]
|
||||
[HttpPost]
|
||||
[EnsureUserPermissionForContent(Constants.System.RecycleBinContent)]
|
||||
[EnsureUserPermissionForContent(Constants.System.RecycleBinContent, 'D')]
|
||||
public HttpResponseMessage EmptyRecycleBin()
|
||||
{
|
||||
Services.ContentService.EmptyRecycleBin();
|
||||
@@ -1179,7 +1179,7 @@ namespace Umbraco.Web.Editors
|
||||
return allowed;
|
||||
}
|
||||
|
||||
[EnsureUserPermissionForContent("contentId", 'R')]
|
||||
[EnsureUserPermissionForContent("contentId", 'F')]
|
||||
public IEnumerable<NotifySetting> GetNotificationOptions(int contentId)
|
||||
{
|
||||
var notifications = new List<NotifySetting>();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Services
|
||||
{
|
||||
[HealthCheck("6D7D4E11-758E-4697-BA72-7E02A530CBD4",
|
||||
"HTTPS endpoints (TLS 1.2)",
|
||||
Description = "Checks whether the TLS 1.2 security protocol can be used when making outbound connections to HTTPS endpoints.",
|
||||
Group = "Services")]
|
||||
public class Tls12Check : HealthCheck
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
public Tls12Check(HealthCheckContext healthCheckContext) : base(healthCheckContext)
|
||||
{
|
||||
_textService = healthCheckContext.ApplicationContext.Services.TextService;
|
||||
}
|
||||
|
||||
public override IEnumerable<HealthCheckStatus> GetStatus()
|
||||
{
|
||||
return new[] { CheckTls() };
|
||||
}
|
||||
|
||||
public override HealthCheckStatus ExecuteAction(HealthCheckAction action)
|
||||
{
|
||||
throw new InvalidOperationException("This check has no executable actions.");
|
||||
}
|
||||
|
||||
public HealthCheckStatus CheckTls()
|
||||
{
|
||||
// When set to 0 (SystemDefault), allows the operating system to choose the best protocol to use, and to block protocols that are not secure (this allows TLS 1.2 by default).
|
||||
var success = ServicePointManager.SecurityProtocol == 0 ||
|
||||
ServicePointManager.SecurityProtocol.HasFlag(SecurityProtocolType.Tls12);
|
||||
|
||||
var message = success
|
||||
? _textService.Localize("healthcheck/tls12HealthCheckSuccess")
|
||||
: _textService.Localize("healthcheck/tls12HealthCheckWarn");
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
return new HealthCheckStatus(message)
|
||||
{
|
||||
ResultType = success
|
||||
? StatusResultType.Success
|
||||
: StatusResultType.Warning,
|
||||
Actions = actions
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace Umbraco.Web.HealthCheck
|
||||
public object GetStatus(Guid id)
|
||||
{
|
||||
var check = GetCheckById(id);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
//Core.Logging.LogHelper.Debug<HealthCheckController>("Running health check: " + check.Name);
|
||||
@@ -113,4 +113,4 @@ namespace Umbraco.Web.HealthCheck
|
||||
return check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "link", Namespace = "")]
|
||||
internal class LinkDisplay
|
||||
{
|
||||
[DataMember(Name = "icon")]
|
||||
public string Icon { get; set; }
|
||||
|
||||
[DataMember(Name = "isMedia")]
|
||||
public bool IsMedia { get; set; }
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
[DataMember(Name = "queryString")]
|
||||
public string QueryString { get; set; }
|
||||
|
||||
[DataMember(Name = "target")]
|
||||
public string Target { get; set; }
|
||||
|
||||
[DataMember(Name = "trashed")]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
[DataMember(Name = "udi")]
|
||||
public GuidUdi Udi { get; set; }
|
||||
|
||||
[DataMember(Name = "url")]
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
public class Link
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Target { get; set; }
|
||||
public LinkType Type { get; set; }
|
||||
public Udi Udi { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
public enum LinkType
|
||||
{
|
||||
Content,
|
||||
Media,
|
||||
External
|
||||
}
|
||||
}
|
||||
@@ -323,7 +323,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5()))
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id))
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
|
||||
@@ -6,5 +6,8 @@ namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[PreValueField("format", "Date format", "textstring", Description = "If left empty then the format is YYYY-MM-DD. (see momentjs.com for supported formats)")]
|
||||
public string DefaultValue { get; set; }
|
||||
|
||||
[PreValueField("defaultEmpty", "Default empty", "boolean", Description = "When enabled the date picker will remain empty when opened. Otherwise it will default to \"today\".")]
|
||||
public string DefaultEmpty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[PropertyEditor(Constants.PropertyEditors.MultiUrlPickerAlias, "Multi Url Picker", PropertyEditorValueTypes.Json, "multiurlpicker", Group = "pickers", Icon = "icon-link")]
|
||||
public class MultiUrlPickerPropertyEditor : PropertyEditor
|
||||
{
|
||||
protected override PreValueEditor CreatePreValueEditor()
|
||||
{
|
||||
return new MultiUrlPickerPreValueEditor();
|
||||
}
|
||||
|
||||
protected override PropertyValueEditor CreateValueEditor()
|
||||
{
|
||||
return new MultiUrlPickerPropertyValueEditor(base.CreateValueEditor());
|
||||
}
|
||||
|
||||
private class MultiUrlPickerPreValueEditor : PreValueEditor
|
||||
{
|
||||
public MultiUrlPickerPreValueEditor()
|
||||
{
|
||||
Fields.Add(new PreValueField
|
||||
{
|
||||
Key = "minNumber",
|
||||
View = "number",
|
||||
Name = "Minimum number of items"
|
||||
});
|
||||
Fields.Add(new PreValueField
|
||||
{
|
||||
Key = "maxNumber",
|
||||
View = "number",
|
||||
Name = "Maximum number of items"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiUrlPickerPropertyValueEditor : PropertyValueEditorWrapper
|
||||
{
|
||||
public MultiUrlPickerPropertyValueEditor(PropertyValueEditor wrapped) : base(wrapped)
|
||||
{
|
||||
}
|
||||
|
||||
public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService)
|
||||
{
|
||||
if (property.Value == null)
|
||||
return Enumerable.Empty<object>();
|
||||
|
||||
var value = property.Value.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return Enumerable.Empty<object>();
|
||||
|
||||
try
|
||||
{
|
||||
var umbHelper = new UmbracoHelper(UmbracoContext.Current);
|
||||
var services = ApplicationContext.Current.Services;
|
||||
var entityService = services.EntityService;
|
||||
var contentTypeService = services.ContentTypeService;
|
||||
string deletedLocalization = null;
|
||||
string recycleBinLocalization = null;
|
||||
|
||||
var dtos = JsonConvert.DeserializeObject<List<LinkDto>>(value);
|
||||
|
||||
var documentLinks = dtos.FindAll(link =>
|
||||
link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Document
|
||||
);
|
||||
|
||||
var mediaLinks = dtos.FindAll(link =>
|
||||
link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Media
|
||||
);
|
||||
|
||||
var entities = new List<IUmbracoEntity>();
|
||||
if (documentLinks.Count > 0)
|
||||
{
|
||||
entities.AddRange(
|
||||
entityService.GetAll(UmbracoObjectTypes.Document,
|
||||
documentLinks.Select(link => link.Udi.Guid).ToArray())
|
||||
);
|
||||
}
|
||||
|
||||
if (mediaLinks.Count > 0)
|
||||
{
|
||||
entities.AddRange(
|
||||
entityService.GetAll(UmbracoObjectTypes.Media,
|
||||
mediaLinks.Select(link => link.Udi.Guid).ToArray())
|
||||
);
|
||||
}
|
||||
|
||||
var links = new List<LinkDisplay>();
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
var link = new LinkDisplay
|
||||
{
|
||||
Icon = "icon-link",
|
||||
IsMedia = false,
|
||||
Name = dto.Name,
|
||||
Published = true,
|
||||
QueryString = dto.QueryString,
|
||||
Target = dto.Target,
|
||||
Trashed = false,
|
||||
Udi = dto.Udi,
|
||||
Url = dto.Url ?? "",
|
||||
};
|
||||
|
||||
links.Add(link);
|
||||
|
||||
if (dto.Udi == null)
|
||||
continue;
|
||||
|
||||
var entity = entities.Find(e => e.Key == dto.Udi.Guid);
|
||||
if (entity == null)
|
||||
{
|
||||
if (deletedLocalization == null)
|
||||
deletedLocalization = services.TextService.Localize("general/deleted");
|
||||
|
||||
link.Published = false;
|
||||
link.Trashed = true;
|
||||
link.Url = deletedLocalization;
|
||||
}
|
||||
else
|
||||
{
|
||||
var entityType =
|
||||
Equals(entity.AdditionalData["NodeObjectTypeId"], Constants.ObjectTypes.MediaGuid)
|
||||
? Constants.UdiEntityType.Media
|
||||
: Constants.UdiEntityType.Document;
|
||||
|
||||
var udi = new GuidUdi(entityType, entity.Key);
|
||||
|
||||
var contentTypeAlias = (string)entity.AdditionalData["ContentTypeAlias"];
|
||||
if (entity.Trashed)
|
||||
{
|
||||
if (recycleBinLocalization == null)
|
||||
recycleBinLocalization = services.TextService.Localize("general/recycleBin");
|
||||
|
||||
link.Trashed = true;
|
||||
link.Url = recycleBinLocalization;
|
||||
}
|
||||
|
||||
if (udi.EntityType == Constants.UdiEntityType.Document)
|
||||
{
|
||||
var contentType = contentTypeService.GetContentType(contentTypeAlias);
|
||||
|
||||
if (contentType == null)
|
||||
continue;
|
||||
|
||||
link.Icon = contentType.Icon;
|
||||
link.Published = Equals(entity.AdditionalData["IsPublished"], true);
|
||||
|
||||
if (link.Trashed == false)
|
||||
link.Url = umbHelper.Url(entity.Id, UrlProviderMode.Relative);
|
||||
}
|
||||
else
|
||||
{
|
||||
link.IsMedia = true;
|
||||
|
||||
var mediaType = contentTypeService.GetMediaType(contentTypeAlias);
|
||||
|
||||
if (mediaType == null)
|
||||
continue;
|
||||
|
||||
link.Icon = mediaType.Icon;
|
||||
|
||||
if (link.Trashed)
|
||||
continue;
|
||||
|
||||
var media = umbHelper.TypedMedia(entity.Id);
|
||||
if (media != null)
|
||||
link.Url = media.Url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ApplicationContext.Current.ProfilingLogger.Logger.Error<MultiUrlPickerPropertyValueEditor>($"Error getting links.\r\n{property.Value}", ex);
|
||||
}
|
||||
|
||||
return base.ConvertDbToEditor(property, propertyType, dataTypeService);
|
||||
}
|
||||
|
||||
public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
|
||||
{
|
||||
if (editorValue.Value == null)
|
||||
return null;
|
||||
|
||||
var value = editorValue.Value.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.SerializeObject(
|
||||
from link in JsonConvert.DeserializeObject<List<LinkDisplay>>(value)
|
||||
select new LinkDto
|
||||
{
|
||||
Name = link.Name,
|
||||
QueryString = link.QueryString,
|
||||
Target = link.Target,
|
||||
Udi = link.Udi,
|
||||
Url = link.Udi == null ? link.Url : null, // only save the url for external links
|
||||
},
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ApplicationContext.Current.ProfilingLogger.Logger.Error<MultiUrlPickerPropertyValueEditor>($"Error saving links.\r\n{editorValue.Value}", ex);
|
||||
}
|
||||
return base.ConvertEditorToDb(editorValue, currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
internal class LinkDto
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "queryString")]
|
||||
public string QueryString { get; set; }
|
||||
|
||||
[DataMember(Name = "target")]
|
||||
public string Target { get; set; }
|
||||
|
||||
[DataMember(Name = "udi")]
|
||||
public GuidUdi Udi { get; set; }
|
||||
|
||||
[DataMember(Name = "url")]
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[PropertyEditor(Constants.PropertyEditors.RelatedLinks2Alias, "Related links", "relatedlinks", ValueType = PropertyEditorValueTypes.Json, Icon = "icon-thumbnail-list", Group = "pickers")]
|
||||
// TODO: Remove in V8
|
||||
[Obsolete("This editor is obsolete, use MultiUrlPickerPropertyEditor instead")]
|
||||
[PropertyEditor(Constants.PropertyEditors.RelatedLinks2Alias, "Related links", "relatedlinks", ValueType = PropertyEditorValueTypes.Json, Icon = "icon-thumbnail-list", Group = "pickers", IsDeprecated = true)]
|
||||
public class RelatedLinks2PropertyEditor : PropertyEditor
|
||||
{
|
||||
public RelatedLinks2PropertyEditor()
|
||||
@@ -33,4 +36,4 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public int Maximum { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors.ValueConverters
|
||||
{
|
||||
[DefaultPropertyValueConverter(typeof(JsonValueConverter))]
|
||||
public class MultiUrlPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public MultiUrlPickerPropertyConverter(IDataTypeService dataTypeService)
|
||||
{
|
||||
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
//TODO: Remove this ctor in v8 since the other one will use IoC
|
||||
public MultiUrlPickerPropertyConverter() : this(ApplicationContext.Current.Services.DataTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiUrlPickerAlias);
|
||||
}
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
if (source.ToString().Trim().StartsWith("[") == false)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return JArray.Parse(source.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<MultiUrlPickerPropertyConverter>("Error parsing JSON", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
var isMultiple = IsMultipleDataType(propertyType.DataTypeId, out var maxNumber);
|
||||
if (source == null)
|
||||
return isMultiple
|
||||
? Enumerable.Empty<Link>()
|
||||
: null;
|
||||
|
||||
//TODO: Inject an UmbracoHelper and create a GetUmbracoHelper method based on either injected or singleton
|
||||
if (UmbracoContext.Current == null)
|
||||
return source;
|
||||
|
||||
var umbHelper = new UmbracoHelper(UmbracoContext.Current);
|
||||
|
||||
var links = new List<Link>();
|
||||
var dtos = ((JArray) source).ToObject<IEnumerable<MultiUrlPickerPropertyEditor.LinkDto>>();
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
var type = LinkType.External;
|
||||
var url = dto.Url;
|
||||
if (dto.Udi != null)
|
||||
{
|
||||
type = dto.Udi.EntityType == Constants.UdiEntityType.Media
|
||||
? LinkType.Media
|
||||
: LinkType.Content;
|
||||
|
||||
if (type == LinkType.Media)
|
||||
{
|
||||
var media = umbHelper.TypedMedia(dto.Udi);
|
||||
if (media == null)
|
||||
continue;
|
||||
url = media.Url;
|
||||
}
|
||||
else
|
||||
{
|
||||
var content = umbHelper.TypedContent(dto.Udi);
|
||||
if (content == null)
|
||||
continue;
|
||||
url = content.Url;
|
||||
}
|
||||
}
|
||||
|
||||
var link = new Link
|
||||
{
|
||||
Name = dto.Name,
|
||||
Target = dto.Target,
|
||||
Type = type,
|
||||
Udi = dto.Udi,
|
||||
Url = url + dto.QueryString,
|
||||
};
|
||||
|
||||
links.Add(link);
|
||||
}
|
||||
|
||||
if (isMultiple == false)
|
||||
return links.FirstOrDefault();
|
||||
if (maxNumber > 0)
|
||||
return links.Take(maxNumber);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
public Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||
{
|
||||
return IsMultipleDataType(propertyType.DataTypeId, out var maxNumber)
|
||||
? typeof(IEnumerable<Link>)
|
||||
: typeof(Link);
|
||||
}
|
||||
|
||||
public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
|
||||
{
|
||||
switch (cacheValue)
|
||||
{
|
||||
case PropertyCacheValue.Source:
|
||||
return PropertyCacheLevel.Content;
|
||||
case PropertyCacheValue.Object:
|
||||
case PropertyCacheValue.XPath:
|
||||
return PropertyCacheLevel.ContentCache;
|
||||
}
|
||||
|
||||
return PropertyCacheLevel.None;
|
||||
}
|
||||
|
||||
private bool IsMultipleDataType(int dataTypeId, out int maxNumber)
|
||||
{
|
||||
// GetPreValuesCollectionByDataTypeId is cached at repository level;
|
||||
// still, the collection is deep-cloned so this is kinda expensive,
|
||||
// better to cache here + trigger refresh in DataTypeCacheRefresher
|
||||
|
||||
maxNumber = Storages.GetOrAdd(dataTypeId, id =>
|
||||
{
|
||||
var preValues = _dataTypeService.GetPreValuesCollectionByDataTypeId(id).PreValuesAsDictionary;
|
||||
|
||||
return preValues.TryGetValue("maxNumber", out var maxNumberPreValue)
|
||||
? maxNumberPreValue.Value.TryConvertTo<int>().Result
|
||||
: 0;
|
||||
});
|
||||
|
||||
return maxNumber != 1;
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<int, int> Storages = new ConcurrentDictionary<int, int>();
|
||||
|
||||
internal static void ClearCaches()
|
||||
{
|
||||
Storages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -613,13 +613,13 @@ namespace Umbraco.Web.Routing
|
||||
// Note: we used to set a default value here but that would then be the default
|
||||
// for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example
|
||||
// see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752
|
||||
internal HttpCacheability Cacheability { get; set; }
|
||||
public HttpCacheability Cacheability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of Extensions to append to the Response.Cache object
|
||||
/// </summary>
|
||||
private List<string> _cacheExtensions = new List<string>();
|
||||
internal List<string> CacheExtensions
|
||||
public List<string> CacheExtensions
|
||||
{
|
||||
get { return _cacheExtensions; }
|
||||
set { _cacheExtensions = value; }
|
||||
@@ -629,7 +629,7 @@ namespace Umbraco.Web.Routing
|
||||
/// Gets or sets a dictionary of Headers to append to the Response object
|
||||
/// </summary>
|
||||
private Dictionary<string, string> _headers = new Dictionary<string, string>();
|
||||
internal Dictionary<string, string> Headers
|
||||
public Dictionary<string, string> Headers
|
||||
{
|
||||
get { return _headers; }
|
||||
set { _headers = value; }
|
||||
@@ -640,4 +640,4 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
public bool IgnorePublishedContentCollisions { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,29 +622,36 @@ namespace Umbraco.Web.Routing
|
||||
}
|
||||
else
|
||||
{
|
||||
// grab the current member
|
||||
var member = membershipHelper.GetCurrentMember();
|
||||
// if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access
|
||||
var memberIsActive = true;
|
||||
if (member != null)
|
||||
if (membershipHelper.IsUmbracoMembershipProviderActive())
|
||||
{
|
||||
if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false)
|
||||
memberIsActive = member.GetPropertyValue<bool>(Constants.Conventions.Member.IsApproved);
|
||||
// grab the current member
|
||||
var member = membershipHelper.GetCurrentMember();
|
||||
// if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access
|
||||
var memberIsActive = true;
|
||||
if (member != null)
|
||||
{
|
||||
if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false)
|
||||
memberIsActive = member.GetPropertyValue<bool>(Constants.Conventions.Member.IsApproved);
|
||||
|
||||
if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false)
|
||||
memberIsActive = member.GetPropertyValue<bool>(Constants.Conventions.Member.IsLockedOut) == false;
|
||||
}
|
||||
if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false)
|
||||
memberIsActive = member.GetPropertyValue<bool>(Constants.Conventions.Member.IsLockedOut) == false;
|
||||
}
|
||||
|
||||
if (memberIsActive == false)
|
||||
{
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Current member is either unapproved or locked out, redirect to error page", () => tracePrefix);
|
||||
var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
|
||||
if (errorPageId != _pcr.PublishedContent.Id)
|
||||
_pcr.PublishedContent = _routingContext.UmbracoContext.ContentCache.GetById(errorPageId);
|
||||
if (memberIsActive == false)
|
||||
{
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Current member is either unapproved or locked out, redirect to error page", () => tracePrefix);
|
||||
var errorPageId = publicAccessAttempt.Result.NoAccessNodeId;
|
||||
if (errorPageId != _pcr.PublishedContent.Id)
|
||||
_pcr.PublishedContent = _routingContext.UmbracoContext.ContentCache.GetById(errorPageId);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Current member has access", () => tracePrefix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Current member has access", () => tracePrefix);
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Current custom MembershipProvider member has access", () => tracePrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Web.Scheduling
|
||||
private readonly ApplicationContext _appContext;
|
||||
private readonly IHealthCheckResolver _healthCheckResolver;
|
||||
|
||||
public HealthCheckNotifier(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
public HealthCheckNotifier(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
ApplicationContext appContext)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
@@ -31,8 +32,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="onlyInitialized">An optional bool (defaults to true), if set to false it will also load uninitialized trees</param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public async Task<SectionRootNode> GetApplicationTrees(string application, string tree, FormDataCollection queryStrings, bool onlyInitialized = true)
|
||||
public async Task<SectionRootNode> GetApplicationTrees(string application, string tree, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings, bool onlyInitialized = true)
|
||||
{
|
||||
application = application.CleanForXss();
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using Umbraco.Web.WebApi.Filters;
|
||||
using umbraco;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using System.Globalization;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
@@ -30,8 +31,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNode GetTreeNode(string id, FormDataCollection queryStrings)
|
||||
public TreeNode GetTreeNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
|
||||
{
|
||||
int asInt;
|
||||
Guid asGuid = Guid.Empty;
|
||||
@@ -346,8 +346,15 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
if (RecycleBinId.ToInvariantString() == id)
|
||||
{
|
||||
// get the default assigned permissions for this user
|
||||
var actions = ActionsResolver.Current.FromActionSymbols(Security.CurrentUser.GetPermissions(Constants.System.RecycleBinContentString, Services.UserService)).ToList();
|
||||
|
||||
var menu = new MenuItemCollection();
|
||||
menu.Items.Add<ActionEmptyTranscan>(ui.Text("actions", "emptyTrashcan"));
|
||||
// only add empty recycle bin if the current user is allowed to delete by default
|
||||
if (actions.Contains(ActionDelete.Instance))
|
||||
{
|
||||
menu.Items.Add<ActionEmptyTranscan>(ui.Text("actions", "emptyTrashcan"));
|
||||
}
|
||||
menu.Items.Add<ActionRefresh>(ui.Text("actions", ActionRefresh.Instance.Alias), true);
|
||||
return menu;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -53,8 +54,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNode GetTreeNode(string id, FormDataCollection queryStrings)
|
||||
public TreeNode GetTreeNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
|
||||
{
|
||||
var node = GetSingleTreeNode(id, queryStrings);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
@@ -43,7 +44,7 @@ namespace Umbraco.Web.Trees
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
protected abstract TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings);
|
||||
protected abstract TreeNodeCollection GetTreeNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the menu structure for the node
|
||||
@@ -51,7 +52,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings);
|
||||
protected abstract MenuItemCollection GetMenuForNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// The name to display on the root node
|
||||
@@ -68,8 +69,7 @@ namespace Umbraco.Web.Trees
|
||||
/// </summary>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNode GetRootNode(FormDataCollection queryStrings)
|
||||
public TreeNode GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var node = CreateRootNode(queryStrings);
|
||||
@@ -108,8 +108,7 @@ namespace Umbraco.Web.Trees
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings)
|
||||
public TreeNodeCollection GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var nodes = GetTreeNodes(id, queryStrings);
|
||||
@@ -140,8 +139,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public MenuItemCollection GetMenu(string id, FormDataCollection queryStrings)
|
||||
public MenuItemCollection GetMenu(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var menu = GetMenuForNode(id, queryStrings);
|
||||
@@ -390,4 +388,4 @@ namespace Umbraco.Web.Trees
|
||||
if (handler != null) handler(instance, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,6 +338,7 @@
|
||||
<Compile Include="Editors\CodeFileController.cs" />
|
||||
<Compile Include="Editors\TourController.cs" />
|
||||
<Compile Include="Features\EnabledFeatures.cs" />
|
||||
<Compile Include="HealthCheck\Checks\Services\Tls12Check.cs" />
|
||||
<Compile Include="HealthCheck\Checks\Security\XssProtectionCheck.cs" />
|
||||
<Compile Include="HealthCheck\Checks\Security\HstsCheck.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFilter.cs" />
|
||||
@@ -419,6 +420,7 @@
|
||||
<Compile Include="Models\ContentEditing\ContentTypeSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\DocumentTypeSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\InstalledPackageModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\LinkDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MediaTypeDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MediaTypeSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\MemberPropertyTypeBasic.cs" />
|
||||
@@ -444,6 +446,8 @@
|
||||
<Compile Include="Models\DetachedPublishedProperty.cs" />
|
||||
<Compile Include="Models\ContentEditing\UserInvite.cs" />
|
||||
<Compile Include="Models\ContentEditing\UserSave.cs" />
|
||||
<Compile Include="Models\Link.cs" />
|
||||
<Compile Include="Models\LinkType.cs" />
|
||||
<Compile Include="Models\LocalPackageInstallModel.cs" />
|
||||
<Compile Include="Models\Mapping\CodeFileDisplayMapper.cs" />
|
||||
<Compile Include="Models\Mapping\ContentTypeModelMapperExtensions.cs" />
|
||||
@@ -485,6 +489,7 @@
|
||||
<Compile Include="PropertyEditors\MediaPicker2PropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\MemberPicker2PropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\MultiNodeTreePicker2PropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\MultiUrlPickerPropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\NestedContentController.cs" />
|
||||
<Compile Include="PropertyEditors\NestedContentHelper.cs" />
|
||||
<Compile Include="PropertyEditors\NestedContentPropertyEditor.cs" />
|
||||
@@ -499,6 +504,7 @@
|
||||
<Compile Include="PropertyEditors\ValueConverters\MemberPickerPropertyConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\MultiNodeTreePickerPropertyConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\LegacyMediaPickerPropertyConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\MultiUrlPickerPropertyConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentSingleValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentManyValueConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentPublishedPropertyTypeExtensions.cs" />
|
||||
@@ -834,6 +840,7 @@
|
||||
<Compile Include="WebApi\Filters\EnableOverrideAuthorizationAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\FeatureAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\FilterGrouping.cs" />
|
||||
<Compile Include="WebApi\Filters\HttpQueryStringModelBinder.cs" />
|
||||
<Compile Include="WebApi\Filters\LegacyTreeAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\OutgoingEditorModelEventAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\OutgoingNoHyphenGuidFormatAttribute.cs" />
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
_nodeId = nodeId;
|
||||
}
|
||||
|
||||
public EnsureUserPermissionForContentAttribute(int nodeId, char permissionToCheck)
|
||||
: this(nodeId)
|
||||
{
|
||||
_permissionToCheck = permissionToCheck;
|
||||
}
|
||||
|
||||
public EnsureUserPermissionForContentAttribute(string paramName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(paramName)) throw new ArgumentException("Value cannot be null or whitespace.", "paramName");
|
||||
|
||||
@@ -6,13 +6,7 @@ using System.Web.Http.Filters;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows an Action to execute with an arbitrary number of QueryStrings
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number
|
||||
/// but this will allow you to do it
|
||||
/// </remarks>
|
||||
[Obsolete("Use HttpQueryStringModelBinder to model bind FormDataCollection in a GET request")]
|
||||
public sealed class HttpQueryStringFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public string ParameterName { get; private set; }
|
||||
@@ -40,4 +34,4 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
base.OnActionExecuting(actionContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.ModelBinding;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows an Action to execute with an arbitrary number of QueryStrings
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number
|
||||
/// but this will allow you to do it
|
||||
/// </remarks>
|
||||
public sealed class HttpQueryStringModelBinder : IModelBinder
|
||||
{
|
||||
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
|
||||
{
|
||||
//get the query strings from the request properties
|
||||
if (actionContext.Request.Properties.ContainsKey("MS_QueryNameValuePairs"))
|
||||
{
|
||||
if (actionContext.Request.Properties["MS_QueryNameValuePairs"] is IEnumerable<KeyValuePair<string, string>> queryStrings)
|
||||
{
|
||||
var formData = new FormDataCollection(queryStrings);
|
||||
bindingContext.Model = formData;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.Http;
|
||||
@@ -130,6 +131,10 @@ namespace Umbraco.Web
|
||||
InstallHelper insHelper = new InstallHelper(UmbracoContext.Current);
|
||||
insHelper.DeleteLegacyInstaller();
|
||||
|
||||
// Tell .NET 4.5 that we also want to try connecting over TLS 1.2, not just TLS 1.1 (but only if specific protocols are defined)
|
||||
if (ServicePointManager.SecurityProtocol != 0)
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -539,6 +544,19 @@ namespace Umbraco.Web
|
||||
() => PluginManager.ResolveTypes<HealthCheck.HealthCheck>());
|
||||
HealthCheckNotificationMethodResolver.Current = new HealthCheckNotificationMethodResolver(LoggerResolver.Current.Logger,
|
||||
() => PluginManager.ResolveTypes<HealthCheck.NotificationMethods.IHealthCheckNotificatationMethod>());
|
||||
|
||||
// Disable duplicate community health checks which appear in Our.Umbraco.HealtchChecks and Umbraco Core.
|
||||
// See this issue to understand why https://github.com/umbraco/Umbraco-CMS/issues/4174
|
||||
var disabledHealthCheckTypes = new[]
|
||||
{
|
||||
"Our.Umbraco.HealthChecks.Checks.Security.HstsCheck",
|
||||
"Our.Umbraco.HealthChecks.Checks.Security.TlsCheck"
|
||||
}.Select(TypeFinder.GetTypeByName).WhereNotNull();
|
||||
|
||||
foreach (var type in disabledHealthCheckTypes)
|
||||
{
|
||||
HealthCheckResolver.Current.RemoveType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,12 +4,13 @@ using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Logging;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.cms.businesslogic.language;
|
||||
using umbraco.cms.businesslogic.property;
|
||||
using umbraco.cms.businesslogic.task;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Language = umbraco.cms.businesslogic.language.Language;
|
||||
using Property = umbraco.cms.businesslogic.property.Property;
|
||||
using Task = umbraco.cms.businesslogic.task.Task;
|
||||
|
||||
namespace umbraco.cms.businesslogic.translation
|
||||
{
|
||||
@@ -32,8 +33,10 @@ namespace umbraco.cms.businesslogic.translation
|
||||
task.Save();
|
||||
|
||||
// Add log entry
|
||||
// Note: not updated to LogHelper as LogTypes.SendToTranslate might be something special, not sure
|
||||
Log.Add(LogTypes.SendToTranslate, user, node.Id, "Translator: " + translator.Name + ", Language: " + language.FriendlyName);
|
||||
ApplicationContext.Current.Services.AuditService.Add(AuditType.SendToTranslate,
|
||||
"Translator: " + translator.Name + ", Language: " + language.FriendlyName,
|
||||
user.Id,
|
||||
node.Id);
|
||||
|
||||
// send it
|
||||
if (sendEmail)
|
||||
@@ -128,4 +131,4 @@ namespace umbraco.cms.businesslogic.translation
|
||||
return collection.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user