Compare commits

...

70 Commits

Author SHA1 Message Date
Niels Lyngsoe 92453c4652 fix front-end requirement of umb-checkbox 2019-03-06 11:18:07 +01:00
Shannon Deminick 5ddba3e64e Merge pull request #3851 from umbraco/temp-fix-formdatacollection-3796
Fixes binding of FormDataCollection for GET requests which fixes the …
2019-03-05 13:39:37 +10:00
Niels Lyngsø c72a5c07fe Merge pull request #3464 from BatJan/feature/temp-U4-3463
3463 - Suggestion: Make checkboxes look prettier in checkbox lists
2019-03-04 11:39:08 +01:00
Niels Lyngsoe 409ae99bf7 renaming of umb-form-control to umb-form-check 2019-03-04 10:29:58 +01:00
Jan Skovgaard 969a9c1193 Update radiobuttons view to make use of the umb-radiobutton directive 2019-02-27 09:56:22 +01:00
Jan Skovgaard 3643e170c5 Add radiobutton directive 2019-02-27 09:55:52 +01:00
Jan Skovgaard afc872de5a Refactor checkboxlist to make use of the umbcheckbox directive 2019-02-27 09:40:48 +01:00
Jan Skovgaard 56aef39014 Add checkbox directive 2019-02-27 09:40:25 +01:00
Jan Skovgaard ae07b2b299 Refactor modifiers so they're done on the top level of the block instead 2019-02-26 21:42:24 +01:00
Jan Skovgaard b99cf238e0 Fix order of pseudo selectors to :focus is actually also set on the focused radio button 2019-02-26 21:26:14 +01:00
Jan Skovgaard 68457b7eaf Refactor styling of checkbox and radiobuttons so they're now both "umb-form-control" with modifiers, which makes it easier to reuse especially the checkbox throughout the UI. The umb-editor has been extented to have a item element and a list modfier too. Overall less code too 2019-02-26 21:21:55 +01:00
Jan Skovgaard 24c74676f6 Merge branch 'dev-v7' into feature/temp-U4-3463 2019-02-26 19:45:51 +01:00
Per Søndergaard Jensen ac1ee2fe0c #4428 - Replaced Log.Add calls with AuditService.Add in translations 2019-02-26 11:17:08 +01:00
Skid Vis 4f30dd32f1 Updates link to dev-v8
Was linked to temp8 which no longer exists and results in 404
2019-02-25 17:20:35 +01:00
Sebastiaan Janssen 8f17d6fcd2 Merge branch 'patch-7.13.2' into dev-v7
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js
2019-02-19 11:22:59 +01:00
SimonHilo 1300389ce6 Skip the check for approved/locked out members if not using Umbracos built-in MembershipProvider (#4607) 2019-02-18 20:49:27 +01:00
Kenn Jacobsen ea8d8ba771 Make the image crop slider move at 66 hertz 2019-02-18 20:39:16 +01:00
Kenn Jacobsen 1eb6ae10c9 Fix the initial zoom offset for new crops in the image cropper 2019-02-18 20:38:11 +01:00
Jan Skovgaard 5d3e1fb95f v7: User history showing even if it's empty (#4524) 2019-02-18 20:36:36 +01:00
Mathieu Deslauriers 62463ecc21 Issue 4634 : Fixed colorpicker unselection condition
When the user remove the selection the model is set to null.
When the user tries to select another value after that, the condition is trying to check the "value" property for a null object.
2019-02-18 14:52:13 +01:00
Rasmus John Pedersen 3a469cdc77 Multi Url Picker fixes (#4527)
*  Fix not being able to save link with only an anchor

* Don't allow Multi Url Picker as a parameter editor
2019-02-14 18:11:44 +01:00
matthew-hair 6665695986 Added null check for context.Identity
Signed-off-by: matthew-hair <matthew.hair@sedgwick.gov>
2019-02-14 12:55:56 +01:00
Kenn Jacobsen 4dd51f645a Make sure the color picker label is stored on first select (#4325) 2019-01-30 16:12:50 +01:00
Vigan Shemsiu cca1393f2b Validate both width and height
Make sure to validate both width and height before running "setDimensions();". 

The focus point will not work if we have "img { width: 100%; }" in the stylesheet. 

This pull request will fix the bug.
2019-01-29 22:26:26 +01:00
Kenn Jacobsen 69548aed20 Fix the offset in the crop editor 2019-01-29 22:22:34 +01:00
Damiaan Peeters 7656de693c Add unittest 2019-01-29 22:18:53 +01:00
Damiaan Peeters 4806920179 Do not allow a empty name with for the MemberGroup 2019-01-29 22:18:53 +01:00
Kenn Jacobsen 5637c44f69 Explicitly set the form dirty when toggling a boolean 2019-01-29 17:41:28 +01:00
Kenn Jacobsen 99f0e6c05a Fix callback 2019-01-29 14:11:04 +01:00
Kenn Jacobsen 2c40e46d56 Set form dirty on color picker value change + update selected item label + clean up unused code 2019-01-29 14:11:04 +01:00
Benjamin Carleski 2c3a2b29b2 Obsolete direct hash generation methods, and switch to generic GenerateHash
As noted in https://github.com/umbraco/Umbraco-CMS/issues/4292, using direct hash methods can break compliance when on restricted systems that require FIPS compliance.  This has been addressed in a few commits.  This commit goes further to obsolete methods not previously obsoleted relating to direct hashing calls, and switches a direct call to one of these methods to use the correct, generic GenerateHash.
2019-01-29 12:58:51 +01:00
Jan Skovgaard 94a4e34e0c Make sure to hide the whole state element for screen readers rather than just the icon 2019-01-27 15:32:52 +01:00
Jan Skovgaard 7d42583177 Fix animation 2019-01-27 15:30:02 +01:00
Jan Skovgaard 77a8697ca7 Make sure the border is green when the checkbox is checked and that it's darker gray when it is focused (also while checked) 2019-01-27 15:24:49 +01:00
Jan Skovgaard 7462686778 Add styles and minor refactoring so we no longer use the "wing" icon when a radio button is checked but rather make it look like a readio button 2019-01-27 15:22:37 +01:00
Jan Skovgaard 44d5ce4a2e Include the radiobuttonlist component 2019-01-27 15:21:54 +01:00
Jan Skovgaard 34888f31e6 Place the ul styling in the proper context 2019-01-27 14:50:15 +01:00
Jan Skovgaard 0994aac656 Restructure markup to ensure the label text is properly wrapped and make sure it all aligns nicely on small breakpoints too 2019-01-27 14:38:27 +01:00
Jan Skovgaard 0e0d38be30 Refactor styling of the checkbox so the boxes are now a tad smaller and better aligned with one another and the UI in general 2019-01-27 14:18:41 +01:00
Jan Skovgaard 91735e7e3f Fix bad merge 2019-01-27 14:17:59 +01:00
Jan Skovgaard 0d4b3e4173 Merge branch 'dev-v7' into feature/temp-U4-3463
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/components/umb-radiobuttons-list.less
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html
2019-01-27 13:31:33 +01:00
Sebastiaan Janssen d174499e50 Fixes #4260 incorrect permissions required for Notifications sign up 2019-01-27 13:30:44 +01:00
Kenn Jacobsen df20feb703 Make cache properties on PublishedContentRequest public 2019-01-27 10:39:34 +01:00
Nathan Woulfe a4c5b7118d check for editorstate.current before attempting to access properties object 2019-01-26 17:33:45 +01:00
Ronald Barendse 52a56c8852 Only add TLS 1.2 if explicit protocols are defined 2019-01-26 17:09:47 +01:00
Ronald Barendse 7a2f09643a Changed health check to indicate HTTPS endpoint connectivity using TLS 1.2 2019-01-26 15:30:47 +01:00
Paul Seal c9819b2d89 - disabled the duplicated community health checks (#4175) 2019-01-26 15:01:03 +01:00
Christian Alkærsig 7acafe67a4 Delete template2.cshtml
Auto-generated test file
2019-01-23 00:28:21 +01:00
Alkaersig 9eb2b28232 Added (Doctype) Name and Description to PublishedContentType 2019-01-23 00:28:21 +01:00
Paul Seal 832ec68140 - tls check to check if the site is set up to use at least TLS 1.2 for outgoing connections.
- added translations for all languages which have a healthcheck section in the language file.
2019-01-23 00:11:35 +01:00
Bjarne Fyrstenborg d8f0af1a94 Fix plural localization 2019-01-22 22:59:52 +01:00
Kenn Jacobsen a8d5bd6d1f Don't let the users empty the recycle bin without delete permissions (#3699) 2019-01-22 22:05:12 +01:00
Kenn Jacobsen e252a0849b Add "default empty" prevalue to date and datetime 2019-01-22 21:58:17 +01:00
Sebastiaan Janssen ba4b84d7f4 Fixes #4166 - Outgoing connections sometimes fail because they require TLS 1.2 2019-01-21 11:10:01 +01:00
Rasmus John Pedersen e1c9b1818e Add Multi Url Picker to core (#2323) 2019-01-20 21:12:00 +01:00
Abi Noda 76bece07ef add pull reminders badge to readme 2019-01-20 21:10:02 +01:00
Dave Woestenborghs 7729dc946e Fix 4058 : restore from recycle bin when item has multiple relations (#4073) 2019-01-20 20:47:13 +01:00
Kenn Jacobsen 5b285f71fa Remove hardcoded "Root" when restoring to tree root 2019-01-18 14:39:54 +01:00
Callum Whyte e60417925b Consistency is king! Fix up " vs ' in installer service facts array 2019-01-17 15:41:44 +01:00
Callum Whyte 5a78c000a0 Correcting punctuation in installer screen facts 2019-01-17 15:41:44 +01:00
Callum Whyte 54faeee837 Updating number of active Umbraco sites in installer screen facts 2019-01-17 15:41:44 +01:00
Nicholas-Westby cefa2c9040 Typo ("thist"). 2019-01-17 15:34:52 +01:00
Kenn Jacobsen 77cf071e04 Add "Move" option for deleted items in the media tree (#3914) 2019-01-17 12:20:13 +01:00
Sebastiaan Janssen 0d0179cb47 Use constants in upgrade migration 2019-01-17 12:14:17 +01:00
Shannon a0f15bba44 Fixes binding of FormDataCollection for GET requests which fixes the compatibility with the latest WebApi 5.2.7 version 2018-12-11 11:58:39 +11:00
Jan Skovgaard 007cdeb9ab Fix long label issue on RADIO button list 2018-10-31 21:48:47 +01:00
Jan Skovgaard 5716353518 Offset the state 1 px to align it with the radio button list if they ever occur as properties beneath each other 2018-10-31 21:46:00 +01:00
Jan Skovgaard 87340d6c2c Fix width issue on the checkbox label 2018-10-31 21:24:35 +01:00
Jan Skovgaard fc742bcf18 Merge branch 'dev-v7' into feature/temp-U4-3463 2018-10-31 20:03:05 +01:00
Jan Skovgaard 4eb4cd8962 Refactor checkboxlist to show a prettier checkbox than the default browser one 2018-10-28 16:35:37 +01:00
83 changed files with 1532 additions and 250 deletions
+1 -1
View File
@@ -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
View File
@@ -1,4 +1,5 @@
[![Build status](https://ci.appveyor.com/api/projects/status/6by6harxtxt0ocdx/branch/dev-v7?svg=true)](https://ci.appveyor.com/project/Umbraco/umbraco-cms-b2cri/branch/dev-v7)
[![pullreminders](https://pullreminders.com/badge.svg)](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
View File
@@ -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
@@ -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;
}
}
}
+2
View File
@@ -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
}
}
}
+1 -1
View File
@@ -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");
}
}
}
+1 -1
View File
@@ -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;
}
+1
View File
@@ -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" />
@@ -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);
})();
@@ -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);
})();
@@ -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
@@ -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>
@@ -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.
@@ -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;
@@ -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);
@@ -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);
+2 -2
View File
@@ -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>
+15 -15
View File
@@ -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 &lt;your@email.here&gt;</email> -->
<!-- you can add a display name to the email like this: <email>Your display name here &lt;your@email.here&gt;</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);
+2 -2
View File
@@ -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; }
}
}
+13
View File
@@ -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; }
}
}
+9
View File
@@ -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);
+7 -9
View File
@@ -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);
}
}
}
}
+7
View File
@@ -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;
}
}
}
+18
View File
@@ -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;
}
}
}
}