Compare commits

...
42 Commits
Author SHA1 Message Date
Bjarke Berg 4df0827035 Bump version 2022-09-05 10:21:30 +02:00
Bjarke Berg e97d3366c0 Limit what API does when not in used in Install level 2022-09-05 09:56:27 +02:00
Nikolaj c018fcb66b Bump version 2022-08-10 09:38:13 +02:00
Nikolaj 3f8d688ca6 Merge branch 'v9/dev' into v9/9.5 2022-08-10 09:25:27 +02:00
Bjarke BergandGitHub a1dea955c1 Only expose the filename when uploading content types (#12814) 2022-08-09 17:11:48 +02:00
MoleandGitHub 040801d3dd Replace usage of Random().Next with RNGCryptoServiceProvider (#12803) 2022-08-09 11:09:54 +02:00
Bjarke Berg c0bfd25bb2 Use user key as alias instead of username 2022-08-05 10:47:46 +02:00
Sebastiaan Janssen 180f205195 Force the allowed avatar image types 2022-08-02 11:35:53 +02:00
gilbertaoeandZeegaan 5eae921e43 Update MemberRepository.cs (#12641)
Ensuring that MemberManager.ConfirmEmailAsync persists, same changes in pull request #12640 but for v9
2022-07-04 09:04:45 +02:00
Bjarke BergandZeegaan 502d65bea7 #12007 - Bypass the redirect to login page if the current page is the login page. Same for access denied page. (#12620) 2022-06-27 08:26:54 +02:00
Bjarke BergandGitHub f6dd2d00c7 #12007 - Bypass the redirect to login page if the current page is the login page. Same for access denied page. (#12620) 2022-06-27 08:25:54 +02:00
MoleandGitHub 5030477fdd V9: Only send telemetry when runtime level is run (#12602)
* Cherry pick #12600

* Fix hosted service registration
2022-06-22 12:28:00 +02:00
Ronald Barendseandnikolajlauridsen 474b14d5ea Fix Block List settings exception and optimize PVCs (#12256)
* Don't use MapModelType to get model type

* Optimize block list item activation (cache constructors)

* Fix exceptions in NestedContentSingleValueConverter (zero content types or multiple stored items)
2022-06-21 13:39:59 +02:00
Ronald BarendseandGitHub 9d4ff5e94a Fix Block List settings exception and optimize PVCs (#12256)
* Don't use MapModelType to get model type

* Optimize block list item activation (cache constructors)

* Fix exceptions in NestedContentSingleValueConverter (zero content types or multiple stored items)
2022-06-21 13:39:26 +02:00
Nikolaj b4bbad8a79 Merge branch 'v9/9.5' into v9/dev 2022-06-13 11:17:00 +02:00
Nikolaj 2bcf052d9b Fix missing import statements in FileSystemMainDomLock 2022-06-13 10:02:05 +02:00
Nikolaj 39eec09209 Bump version 2022-06-13 08:35:03 +02:00
Paul JohnsonandBjarke Berg e536f9fd10 Fix attempting to write lock files to LocalTempPath before it exists (#12563) 2022-06-10 14:09:24 +02:00
Bjarke BergandGitHub a5b7794207 Merge pull request #12555 from umbraco/v9/hotfix/fix_relation_issue_when_moving_root_item_to_recycle_bin
V9: fix relation issue when moving root item to recycle bin
2022-06-09 10:49:12 +02:00
Bjarke Berg 1923a20da7 Fixes relation issue, when moving a root item to recycle bin, the "Relate Parent Media Folder On Delete"/"Relate Parent Document On Delete" cannot get the parent node type, because it is a fake root. 2022-06-09 09:46:42 +02:00
Paul Johnson 54077725c3 Improve FlagOutOfDateModels property behaviour. 2022-06-08 08:25:25 +01:00
Paul JohnsonandGitHub 6b0149803a Fix logic error WRT models builder flag out of date models. (#12548) 2022-06-08 09:16:26 +02:00
Jacob OvergaardandBjarke Berg 833c2ed13b update jquery to 3.6.0 and jquery-ui to 1.13.1 because of CVE-2021-41184 (#12533)
(cherry picked from commit 61068e6ef0)
2022-06-08 09:06:55 +02:00
Jacob OvergaardandGitHub 61068e6ef0 update jquery to 3.6.0 and jquery-ui to 1.13.1 because of CVE-2021-41184 (#12533) 2022-06-08 08:36:04 +02:00
Bjarke BergandGitHub 3cd17b7ede Fixed query after tests on sqlite (#12544)
(cherry picked from commit 0beaf19063)
2022-06-07 15:26:32 +02:00
Nikolaj a18039b97c Merge remote-tracking branch 'origin/v9/9.5' into v9/dev 2022-06-02 15:07:14 +02:00
MoleandGitHub 7710ca27fe Fix domain for invariant content nodes (#12404) 2022-05-18 13:52:29 +02:00
Nikolaj 2a3deb6c14 Merge branch 'v9/9.5' into v9/dev 2022-05-04 09:25:30 +02:00
nikolajlauridsen bb9fc9b4d8 Merge branch 'v9/9.5' into v9/dev 2022-05-02 10:00:13 +02:00
Bjarke BergandGitHub 41662c4cff Use static id for acceptance tests (#12318) 2022-04-29 09:26:03 +02:00
Andy ButlandandGitHub c9b8ebc6f8 Updated references to latest versions of Forms and Deploy in JSONSchema project. (#12305) 2022-04-26 11:15:11 +02:00
Sebastiaan Janssen d5371a15f5 Fix PR first response to work for outside contributors too 2022-04-26 10:28:00 +02:00
7483a706ac V9/feature/log telemetry level changing (#12299)
* Add logging to SetConsentLevel

* Obsolete old ctor and change logging to email

* Fixed a few issues with MetricsConsentService logging

* Fixed null check

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-04-26 08:50:50 +02:00
Bjarke Berg 7474eaa0d8 Merge remote-tracking branch 'origin/v9/9.5' into v9/dev 2022-04-22 14:59:56 +02:00
Bjarke Berg d1fcacb442 Merge remote-tracking branch 'origin/v9/9.5' into v9/dev 2022-04-22 14:28:07 +02:00
Nikolaj GeisleandGitHub fd8750f99a Try fixing flaky language test (#12281) 2022-04-22 09:26:12 +02:00
Sebastiaan Janssen 5f168b59d5 Merge branch 'v9/contrib' into v9/dev 2022-04-21 14:37:02 +02:00
Sebastiaan JanssenandGitHub 16a02e0827 Update pr-first-response.yml 2022-04-21 14:36:19 +02:00
MoleandGitHub 9b864ddbeb Fix member properties always being sensitive (#12282) 2022-04-21 13:51:24 +02:00
Bjarke Berg 7becf76a02 Added notification when requires user 2fa, so implementors can use this to send emails etc. 2022-04-20 10:56:15 +02:00
VWA Software internetandSebastiaan Janssen 7e6e9c7431 Include the PluginController Area when searching for matching surface… (#12218) 2022-04-19 17:58:59 +02:00
VWA Software internetandGitHub eb0ddbd729 Include the PluginController Area when searching for matching surface… (#12218) 2022-04-19 14:04:11 +02:00
27 changed files with 21470 additions and 175 deletions
+42 -15
View File
@@ -1,26 +1,53 @@
name: pr-first-response
on:
pull_request:
pull_request_target:
types: [opened]
jobs:
send-response:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Fetch random comment 🗣️
uses: JamesIves/fetch-api-data-action@v2.1.0
with:
ENDPOINT: https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment
CONFIGURATION: '{ "method": "POST", "headers": {"Authorization": "Bearer ${{ secrets.OUR_BOT_API_TOKEN }}", "Content-Type": "application/json" }, "body": { "repo": "${{ github.repository }}", "number": "${{ github.event.number }}", "actor": "${{ github.actor }}", "commentType": "opened-pr-first-comment"} }'
- name: Add PR comment
if: "${{ env.fetch-api-data != '' }}"
uses: actions/github-script@v5
- name: Install dependencies
run: |
npm install node-fetch@2
- name: Fetch random comment 🗣️ and add it to the PR
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `${{ env.fetch-api-data }}`
})
const fetch = require('node-fetch')
const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', {
method: 'post',
body: JSON.stringify({
repo: '${{ github.repository }}',
number: '${{ github.event.number }}',
actor: '${{ github.actor }}',
commentType: 'opened-pr-first-comment'
}),
headers: {
'Authorization': 'Bearer ${{ secrets.OUR_BOT_API_TOKEN }}',
'Content-Type': 'application/json'
}
});
try {
const data = await response.text();
if(response.status === 200 && data !== '') {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: data
});
} else {
console.log("Status code did not indicate success:", response.status);
console.log("Returned data:", data);
}
} catch(error) {
console.log(error);
}
+2 -2
View File
@@ -210,7 +210,7 @@ stages:
gulpFile: src\Umbraco.Web.UI.Client\gulpfile.js
targets: build
workingDirectory: src\Umbraco.Web.UI.Client
- powershell: Start-Process -FilePath "dotnet" -ArgumentList "run", "-p", "src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
- powershell: Start-Process -FilePath "dotnet" -ArgumentList "run", "-p", "src\Umbraco.Web.UI\Umbraco.Web.UI.csproj /Umbraco:CMS:Global:Id=0000000-0000-0000-0000-000000000042"
displayName: dotnet run
# - powershell: dotnet run --no-build -p .\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj
# displayName: dotnet run
@@ -313,7 +313,7 @@ stages:
displayName: dotnet run
inputs:
targetType: 'inline'
script: 'nohup dotnet run --no-build -p ./src/Umbraco.Web.UI/ > $(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt &'
script: 'nohup dotnet run --no-build -p ./src/Umbraco.Web.UI/ /Umbraco:CMS:Global:Id=0000000-0000-0000-0000-000000000042 > $(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt &'
- task: Bash@3
displayName: Generate Cypress.env.json
inputs:
+4 -4
View File
@@ -3,10 +3,10 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<Version>9.5.1</Version>
<AssemblyVersion>9.5.1</AssemblyVersion>
<InformationalVersion>9.5.1</InformationalVersion>
<FileVersion>9.5.1</FileVersion>
<Version>9.5.4</Version>
<AssemblyVersion>9.5.4</AssemblyVersion>
<InformationalVersion>9.5.4</InformationalVersion>
<FileVersion>9.5.4</FileVersion>
<LangVersion Condition="'$(LangVersion)' == ''">9.0</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<Company>Umbraco CMS</Company>
@@ -42,18 +42,17 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// </remarks>
public bool FlagOutOfDateModels
{
get => _flagOutOfDateModels;
set
get
{
if (!ModelsMode.IsAuto())
if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsAuto())
{
_flagOutOfDateModels = false;
return;
return false;
}
_flagOutOfDateModels = value;
return _flagOutOfDateModels;
}
set => _flagOutOfDateModels = value;
}
/// <summary>
@@ -0,0 +1,26 @@
using System;
using System.Security.Cryptography;
namespace Umbraco.Extensions
{
public static class CryptoServiceProviderExtensions
{
/// <summary>
/// Generates a random int withing a specified range.
/// </summary>
/// <param name="provider">Random bytes provider.</param>
/// <param name="minValue">The minimum value of the resulting int.</param>
/// <param name="maxValue">The maximum value of the resulting int.</param>
/// <returns>A random integer that falls withing the specified range.</returns>
public static int GetInt32(this RNGCryptoServiceProvider provider, int minValue, int maxValue)
{
var randomBytes = new byte[4];
provider.GetBytes(randomBytes);
var randomInt = Math.Abs(BitConverter.ToInt32(randomBytes, 0));
// We call do mod to ensure that the value is within the specified range.
return (randomInt % (maxValue - minValue + 1)) + minValue;
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.Models
public string NodeType { get; set; }
[DataMember(Name = "udi")]
public Udi NodeUdi => Udi.Create(NodeType, NodeKey);
public Udi NodeUdi => NodeType == Constants.UdiEntityType.Unknown ? null : Udi.Create(NodeType, NodeKey);
[DataMember(Name = "icon")]
public string ContentTypeIcon { get; set; }
+16 -9
View File
@@ -16,31 +16,38 @@ namespace Umbraco.Cms.Core.Security
{
private readonly IPasswordConfiguration _passwordConfiguration;
public PasswordGenerator(IPasswordConfiguration passwordConfiguration)
{
public PasswordGenerator(IPasswordConfiguration passwordConfiguration) =>
_passwordConfiguration = passwordConfiguration;
}
public string GeneratePassword()
{
var password = PasswordStore.GeneratePassword(
_passwordConfiguration.RequiredLength,
_passwordConfiguration.GetMinNonAlphaNumericChars());
var random = new Random();
var passwordChars = password.ToCharArray();
using var numberGenerator = new RNGCryptoServiceProvider();
if (_passwordConfiguration.RequireDigit && passwordChars.ContainsAny(Enumerable.Range(48, 58).Select(x => (char)x)))
password += Convert.ToChar(random.Next(48, 58)); // 0-9
{
password += Convert.ToChar(numberGenerator.GetInt32(48, 58)); // 0-9
}
if (_passwordConfiguration.RequireLowercase && passwordChars.ContainsAny(Enumerable.Range(97, 123).Select(x => (char)x)))
password += Convert.ToChar(random.Next(97, 123)); // a-z
{
password += Convert.ToChar(numberGenerator.GetInt32(97, 123)); // a-z
}
if (_passwordConfiguration.RequireUppercase && passwordChars.ContainsAny(Enumerable.Range(65, 91).Select(x => (char)x)))
password += Convert.ToChar(random.Next(65, 91)); // A-Z
{
password += Convert.ToChar(numberGenerator.GetInt32(65, 91)); // A-Z
}
if (_passwordConfiguration.RequireNonLetterOrDigit && passwordChars.ContainsAny(Enumerable.Range(33, 48).Select(x => (char)x)))
password += Convert.ToChar(random.Next(33, 48)); // symbols !"#$%&'()*+,-./
{
password += Convert.ToChar(numberGenerator.GetInt32(33, 48)); // symbols !"#$%&'()*+,-./
}
return password;
}
@@ -6,8 +6,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Core.Telemetry.Models;
using Umbraco.Cms.Web.Common.DependencyInjection;
@@ -16,18 +18,29 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
{
public class ReportSiteTask : RecurringHostedServiceBase
{
private static HttpClient _httpClient = new();
private readonly ILogger<ReportSiteTask> _logger;
private readonly ITelemetryService _telemetryService;
private static HttpClient s_httpClient;
private readonly IRuntimeState _runtimeState;
public ReportSiteTask(
ILogger<ReportSiteTask> logger,
ITelemetryService telemetryService)
: base(logger, TimeSpan.FromDays(1), TimeSpan.FromMinutes(1))
ITelemetryService telemetryService,
IRuntimeState runtimeState)
: base(logger, TimeSpan.FromDays(1), TimeSpan.FromMinutes(5))
{
_logger = logger;
_telemetryService = telemetryService;
s_httpClient = new HttpClient();
_runtimeState = runtimeState;
_httpClient = new HttpClient();
}
[Obsolete("Use the constructor that takes IRuntimeState, scheduled for removal in V12")]
public ReportSiteTask(
ILogger<ReportSiteTask> logger,
ITelemetryService telemetryService)
: this(logger, telemetryService, StaticServiceProvider.Instance.GetRequiredService<IRuntimeState>())
{
}
[Obsolete("Use the constructor that takes ITelemetryService instead, scheduled for removal in V11")]
@@ -45,6 +58,12 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
/// </summary>
public override async Task PerformExecuteAsync(object state)
{
if (_runtimeState.Level is not RuntimeLevel.Run)
{
// We probably haven't installed yet, so we can't get telemetry.
return;
}
if (_telemetryService.TryGetTelemetryReportData(out TelemetryReportData telemetryReportData) is false)
{
_logger.LogWarning("No telemetry marker found");
@@ -54,19 +73,19 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
try
{
if (s_httpClient.BaseAddress is null)
if (_httpClient.BaseAddress is null)
{
// Send data to LIVE telemetry
s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
#if DEBUG
// Send data to DEBUG telemetry service
s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
#endif
}
s_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
using (var request = new HttpRequestMessage(HttpMethod.Post, "installs/"))
{
@@ -75,7 +94,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
// Make a HTTP Post to telemetry service
// https://telemetry.umbraco.com/installs/
// Fire & Forget, do not need to know if its a 200, 500 etc
using (HttpResponseMessage response = await s_httpClient.SendAsync(request))
using (await _httpClient.SendAsync(request))
{
}
}
@@ -94,13 +94,13 @@ namespace Umbraco.Cms.Core.Models.Mapping
// Umbraco.Code.MapAll -Udi -Trashed
private static void Map(IUser source, EntityBasic target, MapperContext context)
{
target.Alias = source.Username;
target.Alias = source.Key.ToString();
target.Icon = Constants.Icons.User;
target.Id = source.Id;
target.Key = source.Key;
target.Name = source.Name;
target.ParentId = -1;
target.Path = "";
target.Path = string.Empty;
}
// Umbraco.Code.MapAll -Trashed
@@ -796,6 +796,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
memberDto.PasswordConfig = entity.PasswordConfiguration ?? DefaultPasswordConfigJson;
changedCols.Add("passwordConfig");
}
if (entity.IsPropertyDirty("EmailConfirmedDate"))
{
changedCols.Add("emailConfirmedDate");
}
// If userlogin or the email has changed then need to reset security stamp
if (changedCols.Contains("Email") || changedCols.Contains("LoginName"))
@@ -195,7 +195,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
aliasRight: "ct")
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId,
aliasLeft: "ct", aliasRight: "ctn")
.Where<UnionHelperDto>(x => x.OtherId != id, "x");
.Where<UnionHelperDto>(x => x.Id == id, "x");
if (filterMustBeIsDependency)
{
@@ -53,11 +53,14 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
{
var publishedContentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot().Content;
var publishedContentType = publishedContentCache.GetContentType(contentTypeKey);
if (publishedContentType != null)
if (publishedContentType != null && publishedContentType.IsElement)
{
var modelType = ModelType.For(publishedContentType.Alias);
return _publishedModelFactory.MapModelType(modelType);
// TODO Get the model type without having to construct a list
var listType = _publishedModelFactory.CreateModelList(publishedContentType.Alias).GetType();
if (listType.GenericTypeArguments.Length == 1)
{
return listType.GenericTypeArguments[0];
}
}
return typeof(IPublishedElement);
@@ -30,7 +30,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList);
/// <inheritdoc />
public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(BlockListModel);
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
=> typeof(BlockListModel);
/// <inheritdoc />
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
@@ -38,24 +39,27 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
/// <inheritdoc />
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
return source?.ToString();
}
=> source?.ToString();
/// <inheritdoc />
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
// NOTE: The intermediate object is just a json string, we don't actually convert from source -> intermediate since source is always just a json string
// NOTE: The intermediate object is just a JSON string, we don't actually convert from source -> intermediate since source is always just a JSON string
using (_proflog.DebugDuration<BlockListPropertyValueConverter>($"ConvertPropertyToBlockList ({propertyType.DataType.Id})"))
{
var value = (string)inter;
// Short-circuit on empty values
if (string.IsNullOrWhiteSpace(value)) return BlockListModel.Empty;
if (string.IsNullOrWhiteSpace(value))
{
return BlockListModel.Empty;
}
var converted = _blockListEditorDataConverter.Deserialize(value);
if (converted.BlockValue.ContentData.Count == 0) return BlockListModel.Empty;
if (converted.BlockValue.ContentData.Count == 0)
{
return BlockListModel.Empty;
}
var blockListLayout = converted.Layout.ToObject<IEnumerable<BlockListLayoutItem>>();
@@ -68,45 +72,69 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
var contentPublishedElements = new Dictionary<Guid, IPublishedElement>();
foreach (var data in converted.BlockValue.ContentData)
{
if (!blockConfigMap.ContainsKey(data.ContentTypeKey)) continue;
if (!blockConfigMap.ContainsKey(data.ContentTypeKey))
{
continue;
}
var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview);
if (element == null) continue;
if (element == null)
{
continue;
}
contentPublishedElements[element.Key] = element;
}
// If there are no content elements, it doesn't matter what is stored in layout
if (contentPublishedElements.Count == 0) return BlockListModel.Empty;
if (contentPublishedElements.Count == 0)
{
return BlockListModel.Empty;
}
// Convert the settings data
var settingsPublishedElements = new Dictionary<Guid, IPublishedElement>();
foreach (var data in converted.BlockValue.SettingsData)
{
if (!validSettingsElementTypes.Contains(data.ContentTypeKey)) continue;
if (!validSettingsElementTypes.Contains(data.ContentTypeKey))
{
continue;
}
var element = _blockConverter.ConvertToElement(data, referenceCacheLevel, preview);
if (element == null) continue;
if (element == null)
{
continue;
}
settingsPublishedElements[element.Key] = element;
}
var layout = new List<BlockListItem>();
// Cache constructors locally (it's tied to the current IPublishedSnapshot and IPublishedModelFactory)
var blockListItemActivator = new BlockListItemActivator(_blockConverter);
var list = new List<BlockListItem>();
foreach (var layoutItem in blockListLayout)
{
// Get the content reference
var contentGuidUdi = (GuidUdi)layoutItem.ContentUdi;
if (!contentPublishedElements.TryGetValue(contentGuidUdi.Guid, out var contentData))
{
continue;
}
if (!blockConfigMap.TryGetValue(contentData.ContentType.Key, out var blockConfig))
{
continue;
}
// Get the setting reference
IPublishedElement settingsData = null;
var settingGuidUdi = layoutItem.SettingsUdi != null ? (GuidUdi)layoutItem.SettingsUdi : null;
var settingGuidUdi = (GuidUdi)layoutItem.SettingsUdi;
if (settingGuidUdi != null)
{
settingsPublishedElements.TryGetValue(settingGuidUdi.Guid, out settingsData);
}
// This can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again
// We also ensure that the content types match, since maybe the settings type has been changed after this has been persisted
@@ -115,23 +143,49 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
settingsData = null;
}
// Get settings type from configuration
var settingsType = blockConfig.SettingsElementTypeKey.HasValue
? _blockConverter.GetModelType(blockConfig.SettingsElementTypeKey.Value)
: typeof(IPublishedElement);
// Create instance (use content/settings type from configuration)
var layoutRef = blockListItemActivator.CreateInstance(blockConfig.ContentElementTypeKey, blockConfig.SettingsElementTypeKey, contentGuidUdi, contentData, settingGuidUdi, settingsData);
// TODO: This should be optimized/cached, as calling Activator.CreateInstance is slow
var layoutType = typeof(BlockListItem<,>).MakeGenericType(contentData.GetType(), settingsType);
var layoutRef = (BlockListItem)Activator.CreateInstance(layoutType, contentGuidUdi, contentData, settingGuidUdi, settingsData);
layout.Add(layoutRef);
list.Add(layoutRef);
}
var model = new BlockListModel(layout);
return model;
return new BlockListModel(list);
}
}
private class BlockListItemActivator
{
private readonly BlockEditorConverter _blockConverter;
private readonly Dictionary<(Guid, Guid?), Func<Udi, IPublishedElement, Udi, IPublishedElement, BlockListItem>> _contructorCache = new();
public BlockListItemActivator(BlockEditorConverter blockConverter)
=> _blockConverter = blockConverter;
public BlockListItem CreateInstance(Guid contentTypeKey, Guid? settingsTypeKey, Udi contentUdi, IPublishedElement contentData, Udi settingsUdi, IPublishedElement settingsData)
{
if (!_contructorCache.TryGetValue((contentTypeKey, settingsTypeKey), out var constructor))
{
constructor = _contructorCache[(contentTypeKey, settingsTypeKey)] = EmitConstructor(contentTypeKey, settingsTypeKey);
}
return constructor(contentUdi, contentData, settingsUdi, settingsData);
}
private Func<Udi, IPublishedElement, Udi, IPublishedElement, BlockListItem> EmitConstructor(Guid contentTypeKey, Guid? settingsTypeKey)
{
var contentType = _blockConverter.GetModelType(contentTypeKey);
var settingsType = settingsTypeKey.HasValue ? _blockConverter.GetModelType(settingsTypeKey.Value) : typeof(IPublishedElement);
var type = typeof(BlockListItem<,>).MakeGenericType(contentType, settingsType);
var constructor = type.GetConstructor(new[] { typeof(Udi), contentType, typeof(Udi), settingsType });
if (constructor == null)
{
throw new InvalidOperationException($"Could not find the required public constructor on {type}.");
}
// We use unsafe here, because we know the contructor parameter count and types match
return ReflectionUtilities.EmitConstructorUnsafe<Func<Udi, IPublishedElement, Udi, IPublishedElement, BlockListItem>>(constructor);
}
}
}
}
@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -25,9 +25,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
/// </summary>
public NestedContentManyValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(publishedSnapshotAccessor, publishedModelFactory)
{
_proflog = proflog;
}
=> _proflog = proflog;
/// <inheritdoc />
public override bool IsConverter(IPublishedPropertyType propertyType)
@@ -37,6 +35,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
{
var contentTypes = propertyType.DataType.ConfigurationAs<NestedContentConfiguration>().ContentTypes;
return contentTypes.Length == 1
? typeof(IEnumerable<>).MakeGenericType(ModelType.For(contentTypes[0].Alias))
: typeof(IEnumerable<IPublishedElement>);
@@ -48,9 +47,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
/// <inheritdoc />
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
return source?.ToString();
}
=> source?.ToString();
/// <inheritdoc />
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
@@ -64,16 +61,24 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
: new List<IPublishedElement>();
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return elements;
if (string.IsNullOrWhiteSpace(value))
{
return elements;
}
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0) return elements;
if (objects.Count == 0)
{
return elements;
}
foreach (var sourceObject in objects)
{
var element = ConvertToElement(sourceObject, referenceCacheLevel, preview);
if (element != null)
{
elements.Add(element);
}
}
return elements;
@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -25,9 +25,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
/// </summary>
public NestedContentSingleValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog)
: base(publishedSnapshotAccessor, publishedModelFactory)
{
_proflog = proflog;
}
=> _proflog = proflog;
/// <inheritdoc />
public override bool IsConverter(IPublishedPropertyType propertyType)
@@ -37,9 +35,10 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
{
var contentTypes = propertyType.DataType.ConfigurationAs<NestedContentConfiguration>().ContentTypes;
return contentTypes.Length > 1
? typeof(IPublishedElement)
: ModelType.For(contentTypes[0].Alias);
return contentTypes.Length == 1
? ModelType.For(contentTypes[0].Alias)
: typeof(IPublishedElement);
}
/// <inheritdoc />
@@ -48,9 +47,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
/// <inheritdoc />
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
return source?.ToString();
}
=> source?.ToString();
/// <inheritdoc />
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
@@ -58,14 +55,18 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
using (_proflog.DebugDuration<NestedContentSingleValueConverter>($"ConvertPropertyToNestedContent ({propertyType.DataType.Id})"))
{
var value = (string)inter;
if (string.IsNullOrWhiteSpace(value)) return null;
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
var objects = JsonConvert.DeserializeObject<List<JObject>>(value);
if (objects.Count == 0)
{
return null;
if (objects.Count > 1)
throw new InvalidOperationException();
}
// Only return the first (existing data might contain more than is currently configured)
return ConvertToElement(objects[0], referenceCacheLevel, preview);
}
}
@@ -14,52 +14,56 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
{
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
protected IPublishedModelFactory PublishedModelFactory { get; }
protected NestedContentValueConverterBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory)
{
_publishedSnapshotAccessor = publishedSnapshotAccessor;
PublishedModelFactory = publishedModelFactory;
}
protected IPublishedModelFactory PublishedModelFactory { get; }
public static bool IsNested(IPublishedPropertyType publishedProperty)
{
return publishedProperty.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.NestedContent);
}
=> publishedProperty.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.NestedContent);
public static bool IsNestedSingle(IPublishedPropertyType publishedProperty)
private static bool IsSingle(IPublishedPropertyType publishedProperty)
{
if (!IsNested(publishedProperty))
return false;
var config = publishedProperty.DataType.ConfigurationAs<NestedContentConfiguration>();
return config.MinItems == 1 && config.MaxItems == 1;
}
public static bool IsNestedSingle(IPublishedPropertyType publishedProperty)
=> IsNested(publishedProperty) && IsSingle(publishedProperty);
public static bool IsNestedMany(IPublishedPropertyType publishedProperty)
{
return IsNested(publishedProperty) && !IsNestedSingle(publishedProperty);
}
=> IsNested(publishedProperty) && !IsSingle(publishedProperty);
protected IPublishedElement ConvertToElement(JObject sourceObject, PropertyCacheLevel referenceCacheLevel, bool preview)
{
var elementTypeAlias = sourceObject[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]?.ToObject<string>();
if (string.IsNullOrEmpty(elementTypeAlias))
{
return null;
}
var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot();
// only convert element types - content types will cause an exception when PublishedModelFactory creates the model
// Only convert element types - content types will cause an exception when PublishedModelFactory creates the model
var publishedContentType = publishedSnapshot.Content.GetContentType(elementTypeAlias);
if (publishedContentType == null || publishedContentType.IsElement == false)
{
return null;
}
var propertyValues = sourceObject.ToObject<Dictionary<string, object>>();
if (!propertyValues.TryGetValue("key", out var keyo)
|| !Guid.TryParse(keyo.ToString(), out var key))
if (!propertyValues.TryGetValue("key", out var keyo) || !Guid.TryParse(keyo.ToString(), out var key))
{
key = Guid.Empty;
}
IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
element = PublishedModelFactory.CreateModel(element);
return element;
}
}
@@ -14,6 +14,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
internal class FileSystemMainDomLock : IMainDomLock
{
private readonly ILogger<FileSystemMainDomLock> _logger;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly string _lockFilePath;
@@ -29,6 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
IOptionsMonitor<GlobalSettings> globalSettings)
{
_logger = logger;
_hostingEnvironment = hostingEnvironment;
_globalSettings = globalSettings;
var lockFileName = $"MainDom_{mainDomKeyGenerator.GenerateKey()}.lock";
@@ -45,6 +47,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
{
try
{
Directory.CreateDirectory(_hostingEnvironment.LocalTempPath);
_logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath);
_lockFileStream = File.Open(_lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
DeleteLockReleaseSignalFile();
@@ -552,7 +552,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public IActionResult Import(string file)
{
var filePath = Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data), file);
var filePath = Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempFileUploads), file);
if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(filePath))
{
return NotFound();
@@ -601,13 +601,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (ext.InvariantEquals("udt"))
{
model.TempFileName = Path.Combine(root, fileName);
model.TempFileName = fileName;
var xd = new XmlDocument
{
XmlResolver = null
};
xd.Load(model.TempFileName);
xd.Load(Path.Combine(root, fileName));
model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value;
model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value;
@@ -226,8 +226,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
var fileName = file.FileName.Trim(new[] { '\"' }).TrimEnd();
var safeFileName = fileName.ToSafeFileName(shortStringHelper);
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower();
const string allowedAvatarFileTypes = "jpeg,jpg,gif,bmp,png,tiff,tif,webp";
if (contentSettings.DisallowedUploadFiles.Contains(ext) == false)
if (allowedAvatarFileTypes.Contains(ext) == true && contentSettings.DisallowedUploadFiles.Contains(ext) == false)
{
//generate a path of known data, we don't want this path to be guessable
user.Avatar = "UserAvatars/" + (user.Id + safeFileName).GenerateHash<SHA1>() + "." + ext;
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Install;
using Umbraco.Cms.Core.Install.Models;
using Umbraco.Cms.Core.Logging;
@@ -93,11 +94,14 @@ namespace Umbraco.Cms.Web.BackOffice.Install
[HttpPost]
public async Task<ActionResult> CompleteInstall()
{
var levelBeforeRestart = _runtime.State.Level;
await _runtime.RestartAsync();
var identityUser = await _backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserIdAsString);
_backOfficeSignInManager.SignInAsync(identityUser, false);
if (levelBeforeRestart == RuntimeLevel.Install)
{
var identityUser = await _backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserIdAsString);
_backOfficeSignInManager.SignInAsync(identityUser, false);
}
return NoContent();
}
@@ -186,7 +186,8 @@ namespace Umbraco.Extensions
builder.Services.AddHostedService(provider =>
new ReportSiteTask(
provider.GetRequiredService<ILogger<ReportSiteTask>>(),
provider.GetRequiredService<ITelemetryService>()));
provider.GetRequiredService<ITelemetryService>(),
provider.GetRequiredService<IRuntimeState>()));
return builder;
}
+21167 -40
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -39,8 +39,8 @@
"diff": "3.5.0",
"flatpickr": "4.6.9",
"font-awesome": "4.7.0",
"jquery": "^3.5.1",
"jquery-ui-dist": "1.12.1",
"jquery": "3.6.0",
"jquery-ui-dist": "1.13.1",
"jquery-ui-touch-punch": "0.2.3",
"lazyload-js": "1.0.0",
"moment": "2.22.2",
@@ -84,12 +84,20 @@ namespace Umbraco.Cms.Web.Website.Routing
switch (publicAccessStatus)
{
case PublicAccessStatus.NotLoggedIn:
_logger.LogDebug("EnsurePublishedContentAccess: Not logged in, redirect to login page");
routeValues = await SetPublishedContentAsOtherPageAsync(httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result.LoginNodeId);
// redirect if this is not the login page
if (publicAccessAttempt.Result.LoginNodeId != publishedContent.Id)
{
_logger.LogDebug("EnsurePublishedContentAccess: Not logged in, redirect to login page");
routeValues = await SetPublishedContentAsOtherPageAsync(httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result.LoginNodeId);
}
break;
case PublicAccessStatus.AccessDenied:
_logger.LogDebug("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
routeValues = await SetPublishedContentAsOtherPageAsync(httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result.NoAccessNodeId);
// Redirect if this is not the access denied page
if (publicAccessAttempt.Result.NoAccessNodeId != publishedContent.Id)
{
_logger.LogDebug("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
routeValues = await SetPublishedContentAsOtherPageAsync(httpContext, routeValues.PublishedRequest, publicAccessAttempt.Result.NoAccessNodeId);
}
break;
case PublicAccessStatus.LockedOut:
_logger.LogDebug("Current member is locked out, redirect to error page");
@@ -46,7 +46,7 @@
"description": "The version of Umbraco.Cms to add as PackageReference.",
"type": "parameter",
"datatype": "string",
"defaultValue": "9.5.1",
"defaultValue": "9.5.4",
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
},
"namespaceReplacer": {
@@ -44,7 +44,7 @@
"description": "The version of Umbraco.Cms to add as PackageReference.",
"type": "parameter",
"datatype": "string",
"defaultValue": "9.5.1",
"defaultValue": "9.5.4",
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
},
"UseHttpsRedirect": {
@@ -13,7 +13,8 @@ context('Languages', () => {
cy.umbracoEnsureLanguageCultureNotExists(culture);
cy.umbracoSection('settings');
cy.get('.umb-tree-root-link').contains('Settings')
cy.get('.umb-box-content').should('be.visible');
cy.get('li .umb-tree-root:contains("Settings")').should("be.visible");
// Enter language tree and create new language
cy.umbracoTreeItem('settings', ['Languages']).click();
cy.umbracoButtonByLabelKey('languages_addLanguage').click();