diff --git a/Directory.Build.props b/Directory.Build.props
index 1d8f755b342c..87042fbd86ce 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -35,7 +35,7 @@
true
-
+
$(MSBuildThisFileDirectory)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 75ca1ce51b44..513d9dd2a446 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -45,8 +45,8 @@
-
-
+
+
diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml
index aa0ddf1db68a..cc71b984230f 100644
--- a/build/azure-pipelines.yml
+++ b/build/azure-pipelines.yml
@@ -338,7 +338,9 @@ stages:
# Integration Tests (SQL Server)
- job:
timeoutInMinutes: 120
- condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
+ # We are currently encountering issues when running SQL Server Linux tests Microsoft.Data.SqlClient.SqlException (0x80131904)
+ # condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
+ condition: eq(${{parameters.sqlServerIntegrationTests}}, True)
displayName: Integration Tests (SQL Server)
strategy:
matrix:
@@ -529,8 +531,8 @@ stages:
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- - pwsh: npx playwright install --with-deps
- displayName: Install Playwright
+ - pwsh: npx playwright install chromium
+ displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
@@ -560,13 +562,24 @@ stages:
displayName: Copy Playwright results
condition: succeededOrFailed()
- # Publish
+ # Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
+
+ # Publish test results
+ - task: PublishTestResults@2
+ displayName: "Publish test results"
+ condition: succeededOrFailed()
+ inputs:
+ testResultsFormat: 'JUnit'
+ testResultsFiles: '*.xml'
+ searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
+ testRunTitle: "$(Agent.JobName)"
+
- job:
displayName: E2E Tests (SQL Server)
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerAcceptanceTests}})
@@ -685,14 +698,13 @@ stages:
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- - pwsh: npx playwright install --with-deps
- displayName: Install Playwright
+ - pwsh: npx playwright install chromium
+ displayName: Install Playwright only with Chromium browser
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: $(testCommand)
displayName: Run Playwright tests
- continueOnError: true
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
env:
CI: true
@@ -725,7 +737,7 @@ stages:
displayName: Copy Playwright results
condition: succeededOrFailed()
- # Publish
+ # Publish test artifacts
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
@@ -733,6 +745,16 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)"
+ # Publish test results
+ - task: PublishTestResults@2
+ displayName: "Publish test results"
+ condition: succeededOrFailed()
+ inputs:
+ testResultsFormat: 'JUnit'
+ testResultsFiles: '*.xml'
+ searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results"
+ testRunTitle: "$(Agent.JobName)"
+
###############################################
## Release
###############################################
@@ -745,6 +767,8 @@ stages:
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}}))
jobs:
- job:
+ pool:
+ vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
displayName: Push to pre-release feed
steps:
- checkout: none
@@ -771,6 +795,8 @@ stages:
condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}}))
jobs:
- job:
+ pool:
+ vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available.
displayName: Push to NuGet
steps:
- checkout: none
diff --git a/src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicy.cs b/src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicy.cs
index da1580554cbb..c1c823f8a9ab 100644
--- a/src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicy.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicy.cs
@@ -1,5 +1,6 @@
-using Microsoft.AspNetCore.OutputCaching;
+using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Primitives;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Caching;
@@ -7,9 +8,13 @@ namespace Umbraco.Cms.Api.Delivery.Caching;
internal sealed class DeliveryApiOutputCachePolicy : IOutputCachePolicy
{
private readonly TimeSpan _duration;
+ private readonly StringValues _varyByHeaderNames;
- public DeliveryApiOutputCachePolicy(TimeSpan duration)
- => _duration = duration;
+ public DeliveryApiOutputCachePolicy(TimeSpan duration, StringValues varyByHeaderNames)
+ {
+ _duration = duration;
+ _varyByHeaderNames = varyByHeaderNames;
+ }
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
@@ -18,8 +23,14 @@ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, Cance
.RequestServices
.GetRequiredService();
- context.EnableOutputCaching = requestPreviewService.IsPreview() is false;
+ IApiAccessService apiAccessService = context
+ .HttpContext
+ .RequestServices
+ .GetRequiredService();
+
+ context.EnableOutputCaching = requestPreviewService.IsPreview() is false && apiAccessService.HasPublicAccess();
context.ResponseExpirationTimeSpan = _duration;
+ context.CacheVaryByRules.HeaderNames = _varyByHeaderNames;
return ValueTask.CompletedTask;
}
diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs
index 4fed35a28196..7120553fb17e 100644
--- a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs
@@ -19,7 +19,7 @@ public class ByRouteContentApiController : ContentApiItemControllerBase
private readonly IRequestRedirectService _requestRedirectService;
private readonly IRequestPreviewService _requestPreviewService;
private readonly IRequestMemberAccessService _requestMemberAccessService;
- private const string PreviewContentRequestPathPrefix = $"/{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}";
+ private const string PreviewContentRequestPathPrefix = $"/{Umbraco.Cms.Core.Constants.DeliveryApi.Routing.PreviewContentPathPrefix}";
[Obsolete($"Please use the constructor that does not accept {nameof(IPublicAccessService)}. Will be removed in V14.")]
public ByRouteContentApiController(
@@ -145,6 +145,11 @@ private async Task HandleRequest(string path)
path = DecodePath(path);
path = path.Length == 0 ? "/" : path;
+ if (_apiContentPathResolver.IsResolvablePath(path) is false)
+ {
+ return NotFound();
+ }
+
IPublishedContent? contentItem = GetContent(path);
if (contentItem is not null)
{
diff --git a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs
index 20141f210853..48399a62e3f1 100644
--- a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -5,6 +5,7 @@
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Primitives;
using Umbraco.Cms.Api.Common.DependencyInjection;
using Umbraco.Cms.Api.Delivery.Accessors;
using Umbraco.Cms.Api.Delivery.Caching;
@@ -108,12 +109,20 @@ private static IUmbracoBuilder AddOutputCache(this IUmbracoBuilder builder)
if (outputCacheSettings.ContentDuration.TotalSeconds > 0)
{
- options.AddPolicy(Constants.DeliveryApi.OutputCache.ContentCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.ContentDuration));
+ options.AddPolicy(
+ Constants.DeliveryApi.OutputCache.ContentCachePolicy,
+ new DeliveryApiOutputCachePolicy(
+ outputCacheSettings.ContentDuration,
+ new StringValues([Constants.DeliveryApi.HeaderNames.AcceptLanguage, Constants.DeliveryApi.HeaderNames.StartItem])));
}
if (outputCacheSettings.MediaDuration.TotalSeconds > 0)
{
- options.AddPolicy(Constants.DeliveryApi.OutputCache.MediaCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.MediaDuration));
+ options.AddPolicy(
+ Constants.DeliveryApi.OutputCache.MediaCachePolicy,
+ new DeliveryApiOutputCachePolicy(
+ outputCacheSettings.MediaDuration,
+ Constants.DeliveryApi.HeaderNames.StartItem));
}
});
diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs
index 0d2d35fb2399..748033e4a25f 100644
--- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs
@@ -1,4 +1,4 @@
-using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Delivery.Configuration;
@@ -21,7 +21,7 @@ protected override void ApplyOperation(OpenApiOperation operation, OperationFilt
operation.Parameters.Add(new OpenApiParameter
{
- Name = "Accept-Language",
+ Name = Core.Constants.DeliveryApi.HeaderNames.AcceptLanguage,
In = ParameterLocation.Header,
Required = false,
Description = "Defines the language to return. Use this when querying language variant content items.",
@@ -37,7 +37,7 @@ protected override void ApplyOperation(OpenApiOperation operation, OperationFilt
operation.Parameters.Add(new OpenApiParameter
{
- Name = "Preview",
+ Name = Core.Constants.DeliveryApi.HeaderNames.Preview,
In = ParameterLocation.Header,
Required = false,
Description = "Whether to request draft content.",
@@ -46,7 +46,7 @@ protected override void ApplyOperation(OpenApiOperation operation, OperationFilt
operation.Parameters.Add(new OpenApiParameter
{
- Name = "Start-Item",
+ Name = Core.Constants.DeliveryApi.HeaderNames.StartItem,
In = ParameterLocation.Header,
Required = false,
Description = "URL segment or GUID of a root content item.",
diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs
index 52acddaca960..8a450a2f9b97 100644
--- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -63,7 +63,7 @@ protected void AddFields(OpenApiOperation operation, OperationFilterContext cont
protected void AddApiKey(OpenApiOperation operation) =>
operation.Parameters.Add(new OpenApiParameter
{
- Name = "Api-Key",
+ Name = Core.Constants.DeliveryApi.HeaderNames.ApiKey,
In = ParameterLocation.Header,
Required = false,
Description = "API key specified through configuration to authorize access to the API.",
diff --git a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs
index 06b6472506a0..32df9adeef18 100644
--- a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs
@@ -6,6 +6,7 @@
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.Security;
namespace Umbraco.Cms.Api.Delivery.Handlers;
@@ -16,16 +17,21 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica
private readonly ILogger _logger;
private readonly DeliveryApiSettings _deliveryApiSettings;
private readonly IServiceScopeFactory _serviceScopeFactory;
+ private readonly IServerRoleAccessor _serverRoleAccessor;
+ private static readonly SemaphoreSlim _locker = new(1);
+ private static bool _isInitialized = false;
public InitializeMemberApplicationNotificationHandler(
IRuntimeState runtimeState,
IOptions deliveryApiSettings,
ILogger logger,
- IServiceScopeFactory serviceScopeFactory)
+ IServiceScopeFactory serviceScopeFactory,
+ IServerRoleAccessor serverRoleAccessor)
{
_runtimeState = runtimeState;
_logger = logger;
_serviceScopeFactory = serviceScopeFactory;
+ _serverRoleAccessor = serverRoleAccessor;
_deliveryApiSettings = deliveryApiSettings.Value;
}
@@ -36,34 +42,55 @@ public async Task HandleAsync(UmbracoApplicationStartingNotification notificatio
return;
}
- // we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
- // install that is not allowed (no connection string means no DbContext)
- using IServiceScope scope = _serviceScopeFactory.CreateScope();
- IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService();
-
- if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
+ if (_serverRoleAccessor.CurrentServerRole is ServerRole.Subscriber)
{
- await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
+ // subscriber instances should not alter the member application
return;
}
- if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
+ try
{
- await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
- return;
- }
+ await _locker.WaitAsync(cancellationToken);
+ if (_isInitialized)
+ {
+ return;
+ }
+
+ _isInitialized = true;
+
+ // we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
+ // install that is not allowed (no connection string means no DbContext)
+ using IServiceScope scope = _serviceScopeFactory.CreateScope();
+ IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService();
+
+ if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
+ {
+ await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
+ return;
+ }
- if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
- && ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
+ if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
+ {
+ await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
+ return;
+ }
+
+ if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
+ && ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
+ {
+ await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
+ return;
+ }
+
+ await memberApplicationManager.EnsureMemberApplicationAsync(
+ _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
+ _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
+ cancellationToken);
+ }
+ finally
{
- await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
- return;
+ _locker.Release();
}
-
- await memberApplicationManager.EnsureMemberApplicationAsync(
- _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
- _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
- cancellationToken);
}
private bool ValidateRedirectUrls(Uri[] redirectUrls)
diff --git a/src/Umbraco.Cms.Api.Delivery/Services/ApiAccessService.cs b/src/Umbraco.Cms.Api.Delivery/Services/ApiAccessService.cs
index 0ba7df9b4932..0a581af9f59f 100644
--- a/src/Umbraco.Cms.Api.Delivery/Services/ApiAccessService.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Services/ApiAccessService.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DeliveryApi;
@@ -29,7 +29,7 @@ public ApiAccessService(IHttpContextAccessor httpContextAccessor, IOptionsMonito
private bool IfEnabled(Func condition) => _deliveryApiSettings.Enabled && condition();
private bool HasValidApiKey() => _deliveryApiSettings.ApiKey.IsNullOrWhiteSpace() == false
- && _deliveryApiSettings.ApiKey.Equals(GetHeaderValue("Api-Key"));
+ && _deliveryApiSettings.ApiKey.Equals(GetHeaderValue(Core.Constants.DeliveryApi.HeaderNames.ApiKey));
private bool IfMediaEnabled(Func condition) => _deliveryApiSettings is { Enabled: true, Media.Enabled: true } && condition();
}
diff --git a/src/Umbraco.Cms.Api.Delivery/Services/RequestPreviewService.cs b/src/Umbraco.Cms.Api.Delivery/Services/RequestPreviewService.cs
index 874e2af7bb1e..25689125f538 100644
--- a/src/Umbraco.Cms.Api.Delivery/Services/RequestPreviewService.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Services/RequestPreviewService.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Services;
@@ -11,5 +11,5 @@ public RequestPreviewService(IHttpContextAccessor httpContextAccessor)
}
///
- public bool IsPreview() => GetHeaderValue("Preview") == "true";
+ public bool IsPreview() => string.Equals(GetHeaderValue(Core.Constants.DeliveryApi.HeaderNames.Preview), "true", StringComparison.OrdinalIgnoreCase);
}
diff --git a/src/Umbraco.Cms.Api.Delivery/Services/RequestStartItemProvider.cs b/src/Umbraco.Cms.Api.Delivery/Services/RequestStartItemProvider.cs
index dd72d930bdbe..f3169e1a75b4 100644
--- a/src/Umbraco.Cms.Api.Delivery/Services/RequestStartItemProvider.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Services/RequestStartItemProvider.cs
@@ -58,5 +58,5 @@ public RequestStartItemProvider(
}
///
- public string? RequestedStartItem() => GetHeaderValue("Start-Item");
+ public string? RequestedStartItem() => GetHeaderValue(Constants.DeliveryApi.HeaderNames.StartItem);
}
diff --git a/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs b/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
index 32a8affd6158..c12b701e6da5 100644
--- a/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
@@ -36,7 +36,7 @@ protected Uri GetDefaultRequestUri(string requestedPath)
}
protected static string GetContentRoute(DomainAndUri domainAndUri, Uri contentRoute)
- => $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.AbsolutePath)}";
+ => $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.LocalPath)}"; // Use LocalPath over AbsolutePath to keep the path decoded.
protected DomainAndUri? GetDomainAndUriForRoute(Uri contentUrl)
{
diff --git a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
index 8948a49a1a7b..7c31936545d1 100644
--- a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
+++ b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
@@ -13,6 +13,9 @@
<_Parameter1>Umbraco.Tests.UnitTests
+
+ <_Parameter1>Umbraco.Tests.Integration
+
<_Parameter1>DynamicProxyGenAssembly2
diff --git a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScope.cs b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScope.cs
index 461b09334c4a..382bcf5593e0 100644
--- a/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScope.cs
+++ b/src/Umbraco.Cms.Persistence.EFCore/Scoping/EFCoreScope.cs
@@ -127,10 +127,16 @@ public override void Dispose()
Locks.ClearLocks(InstanceId);
- if (ParentScope is null)
+ // Since we can nest EFCoreScopes in other scopes derived from CoreScope, we should check whether our ParentScope OR the base ParentScope exists.
+ // Only if neither do do we take responsibility for ensuring the locks are cleared.
+ // Eventually the highest parent will clear the locks.
+ // Further, these locks are a reference to the locks of the highest parent anyway (see the constructor of CoreScope).
+#pragma warning disable SA1100 // Do not prefix calls with base unless local implementation exists (justification: provides additional clarify here that this is defined on the base class).
+ if (ParentScope is null && base.HasParentScope is false)
{
Locks.EnsureLocksCleared(InstanceId);
}
+#pragma warning restore SA1100 // Do not prefix calls with base unless local implementation exists
_efCoreScopeProvider.PopAmbientScope();
diff --git a/src/Umbraco.Core/Cache/AppCacheExtensions.cs b/src/Umbraco.Core/Cache/AppCacheExtensions.cs
index 0f1f242ed01f..56130c4ea589 100644
--- a/src/Umbraco.Core/Cache/AppCacheExtensions.cs
+++ b/src/Umbraco.Core/Cache/AppCacheExtensions.cs
@@ -43,7 +43,7 @@ public static void InsertCacheItem(
public static T? GetCacheItem(this IAppCache provider, string cacheKey)
{
var result = provider.Get(cacheKey);
- if (result == null)
+ if (IsRetrievedItemNull(result))
{
return default;
}
@@ -54,11 +54,13 @@ public static void InsertCacheItem(
public static T? GetCacheItem(this IAppCache provider, string cacheKey, Func getCacheItem)
{
var result = provider.Get(cacheKey, () => getCacheItem());
- if (result == null)
+ if (IsRetrievedItemNull(result))
{
return default;
}
return result.TryConvertTo().Result;
}
+
+ private static bool IsRetrievedItemNull(object? result) => result is null or (object)Cms.Core.Constants.Cache.NullRepresentationInCache;
}
diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs
index 04ae44a64743..db507491ac4d 100644
--- a/src/Umbraco.Core/Cache/CacheKeys.cs
+++ b/src/Umbraco.Core/Cache/CacheKeys.cs
@@ -22,4 +22,6 @@ public static class CacheKeys
public const string ContentRecycleBinCacheKey = "recycleBin_content";
public const string MediaRecycleBinCacheKey = "recycleBin_media";
+
+ public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
}
diff --git a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs
index f1a925da7574..9baee336aede 100644
--- a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs
+++ b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs
@@ -159,15 +159,30 @@ public static void RefreshMemberCache(this DistributedCache dc, params IMember[]
=> dc.RefreshMemberCache(members.AsEnumerable());
public static void RefreshMemberCache(this DistributedCache dc, IEnumerable members)
- => dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.DistinctBy(x => (x.Id, x.Username)).Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, false)));
-
+ => dc.RefreshByPayload(
+ MemberCacheRefresher.UniqueId,
+ GetPayloads(members, false));
[Obsolete("Use the overload accepting IEnumerable instead to avoid allocating arrays. This overload will be removed in Umbraco 13.")]
public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members)
=> dc.RemoveMemberCache(members.AsEnumerable());
public static void RemoveMemberCache(this DistributedCache dc, IEnumerable members)
- => dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.DistinctBy(x => (x.Id, x.Username)).Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, true)));
+ => dc.RefreshByPayload(
+ MemberCacheRefresher.UniqueId,
+ GetPayloads(members, true));
+
+ // Internal for unit test.
+ internal static IEnumerable GetPayloads(IEnumerable members, bool removed)
+ => members
+ .DistinctBy(x => (x.Id, x.Username))
+ .Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, removed)
+ {
+ PreviousUsername = x.HasAdditionalData &&
+ x.AdditionalData!.TryGetValue(Cms.Core.Constants.Entities.AdditionalDataKeys.MemberPreviousUserName, out var previousUsername)
+ ? previousUsername?.ToString()
+ : null,
+ });
#endregion
diff --git a/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/IMemberPartialViewCacheInvalidator.cs b/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/IMemberPartialViewCacheInvalidator.cs
new file mode 100644
index 000000000000..289dad528844
--- /dev/null
+++ b/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/IMemberPartialViewCacheInvalidator.cs
@@ -0,0 +1,16 @@
+namespace Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
+
+///
+/// Defines behaviours for clearing of cached partials views that are configured to be cached individually by member.
+///
+public interface IMemberPartialViewCacheInvalidator
+{
+ ///
+ /// Clears the partial view cache items for the specified member ids.
+ ///
+ /// The member Ids to clear the cache for.
+ ///
+ /// Called from the when a member is saved or deleted.
+ ///
+ void ClearPartialViewCacheItems(IEnumerable memberIds);
+}
diff --git a/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/NoopMemberPartialViewCacheInvalidator.cs b/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/NoopMemberPartialViewCacheInvalidator.cs
new file mode 100644
index 000000000000..3fd366f2c7f0
--- /dev/null
+++ b/src/Umbraco.Core/Cache/PartialViewCacheInvalidators/NoopMemberPartialViewCacheInvalidator.cs
@@ -0,0 +1,9 @@
+namespace Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
+
+internal class NoopMemberPartialViewCacheInvalidator : IMemberPartialViewCacheInvalidator
+{
+ public void ClearPartialViewCacheItems(IEnumerable memberIds)
+ {
+ // No operation performed, this is a no-op implementation.
+ }
+}
diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/MemberCacheRefresher.cs
index ac9dac5a09d1..99d6004ec6d9 100644
--- a/src/Umbraco.Core/Cache/Refreshers/Implement/MemberCacheRefresher.cs
+++ b/src/Umbraco.Core/Cache/Refreshers/Implement/MemberCacheRefresher.cs
@@ -1,5 +1,8 @@
// using Newtonsoft.Json;
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
+using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -15,10 +18,37 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase())
+ {
+ }
- public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
- : base(appCaches, serializer, eventAggregator, factory) =>
+ public MemberCacheRefresher(
+ AppCaches appCaches,
+ IJsonSerializer serializer,
+ IIdKeyMap idKeyMap,
+ IEventAggregator eventAggregator,
+ ICacheRefresherNotificationFactory factory,
+ IMemberPartialViewCacheInvalidator memberPartialViewCacheInvalidator)
+ : base(appCaches, serializer, eventAggregator, factory)
+ {
_idKeyMap = idKeyMap;
+ _memberPartialViewCacheInvalidator = memberPartialViewCacheInvalidator;
+ }
#region Indirect
@@ -40,6 +70,8 @@ public JsonPayload(int id, string? username, bool removed)
public string? Username { get; }
+ public string? PreviousUsername { get; set; }
+
public bool Removed { get; }
}
@@ -67,16 +99,36 @@ public override void Remove(int id)
private void ClearCache(params JsonPayload[] payloads)
{
- AppCaches.ClearPartialViewCache();
+ // Clear the partial views cache for all partials that are cached by member, for the updates members.
+ _memberPartialViewCacheInvalidator.ClearPartialViewCacheItems(payloads.Select(p => p.Id));
+
Attempt memberCache = AppCaches.IsolatedCaches.Get();
foreach (JsonPayload p in payloads)
{
_idKeyMap.ClearCache(p.Id);
- if (memberCache.Success)
+ if (memberCache.Success is false)
+ {
+ continue;
+ }
+
+ memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(p.Id));
+ memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(p.Username));
+
+ // This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
+ // This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
+ // Ideally, we want to use a shared way of generating the key between this and the repository.
+ // Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
+ // See:
+ // https://github.com/umbraco/Umbraco-CMS/pull/17350
+ // https://github.com/umbraco/Umbraco-CMS/pull/17815
+ memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(CacheKeys.MemberUserNameCachePrefix + p.Username));
+
+ // If provided, clear the cache by the previous user name too.
+ if (string.IsNullOrEmpty(p.PreviousUsername) is false)
{
- memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(p.Id));
- memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(p.Username));
+ memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(p.PreviousUsername));
+ memberCache.Result?.Clear(RepositoryCacheKeys.GetKey(CacheKeys.MemberUserNameCachePrefix + p.PreviousUsername));
}
}
}
diff --git a/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs b/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs
index ff3bebd9894a..b6dc8f1ad589 100644
--- a/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/IndexingSettings.cs
@@ -15,4 +15,8 @@ public class IndexingSettings
///
[DefaultValue(StaticExplicitlyIndexEachNestedProperty)]
public bool ExplicitlyIndexEachNestedProperty { get; set; } = StaticExplicitlyIndexEachNestedProperty;
+ ///
+ /// Defines how many items to index in a single page
+ ///
+ public int IndexingPageSize { get; set; } = 10000;
}
diff --git a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
index e68162e6efe0..46fc9cf07339 100644
--- a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
@@ -2,6 +2,7 @@
// See LICENSE for more details.
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Core.Configuration.Models;
@@ -27,6 +28,8 @@ public class SecuritySettings
internal const int StaticMemberDefaultLockoutTimeInMinutes = 30 * 24 * 60;
internal const int StaticUserDefaultLockoutTimeInMinutes = 30 * 24 * 60;
+ internal const long StaticUserDefaultFailedLoginDurationInMilliseconds = 1000;
+ internal const long StaticUserMinimumFailedLoginDurationInMilliseconds = 250;
///
/// Gets or sets a value indicating whether to keep the user logged in.
@@ -125,4 +128,28 @@ public class SecuritySettings
///
[DefaultValue(StaticAllowConcurrentLogins)]
public bool AllowConcurrentLogins { get; set; } = StaticAllowConcurrentLogins;
+
+ ///
+ /// Gets or sets the default duration (in milliseconds) of failed login attempts.
+ ///
+ ///
+ /// The default duration (in milliseconds) of failed login attempts.
+ ///
+ ///
+ /// The user login endpoint ensures that failed login attempts take at least as long as the average successful login.
+ /// However, if no successful logins have occurred, this value is used as the default duration.
+ ///
+ [Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
+ [DefaultValue(StaticUserDefaultFailedLoginDurationInMilliseconds)]
+ public long UserDefaultFailedLoginDurationInMilliseconds { get; set; } = StaticUserDefaultFailedLoginDurationInMilliseconds;
+
+ ///
+ /// Gets or sets the minimum duration (in milliseconds) of failed login attempts.
+ ///
+ ///
+ /// The minimum duration (in milliseconds) of failed login attempts.
+ ///
+ [Range(0, int.MaxValue)] // TODO (V17): Change property type to short and update maximum range to short.MaxValue
+ [DefaultValue(StaticUserMinimumFailedLoginDurationInMilliseconds)]
+ public long UserMinimumFailedLoginDurationInMilliseconds { get; set; } = StaticUserMinimumFailedLoginDurationInMilliseconds;
}
diff --git a/src/Umbraco.Core/Constants-Cache.cs b/src/Umbraco.Core/Constants-Cache.cs
new file mode 100644
index 000000000000..dd52b2c4fe1a
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Cache.cs
@@ -0,0 +1,15 @@
+namespace Umbraco.Cms.Core;
+
+public static partial class Constants
+{
+ public static class Cache
+ {
+ ///
+ /// Defines the string used to represent a null value in the cache.
+ ///
+ ///
+ /// Used in conjunction with the option to cache null values on the repository caches, so we
+ /// can distinguish a true null "not found" value and a cached null value.
+ public const string NullRepresentationInCache = "*NULL*";
+ }
+}
diff --git a/src/Umbraco.Core/Constants-DeliveryApi.cs b/src/Umbraco.Core/Constants-DeliveryApi.cs
index 85677a23bc55..35d8513fa7cc 100644
--- a/src/Umbraco.Core/Constants-DeliveryApi.cs
+++ b/src/Umbraco.Core/Constants-DeliveryApi.cs
@@ -1,4 +1,4 @@
-namespace Umbraco.Cms.Core;
+namespace Umbraco.Cms.Core;
public static partial class Constants
{
@@ -24,14 +24,42 @@ public static class Routing
public static class OutputCache
{
///
- /// Output cache policy name for content
+ /// Output cache policy name for content.
///
public const string ContentCachePolicy = "DeliveryApiContent";
///
- /// Output cache policy name for media
+ /// Output cache policy name for media.
///
public const string MediaCachePolicy = "DeliveryApiMedia";
}
+
+ ///
+ /// Constants for Delivery API header names.
+ ///
+ public static class HeaderNames
+ {
+ ///
+ /// Header name for accept language.
+ ///
+ public const string AcceptLanguage = "Accept-Language";
+
+ ///
+ /// Header name for API key.
+ ///
+ public const string ApiKey = "Api-Key";
+
+ ///
+ /// Header name for preview.
+ ///
+ public const string Preview = "Preview";
+
+ ///
+ /// Header name for start item.
+ ///
+ public const string StartItem = "Start-Item";
+
+
+ }
}
}
diff --git a/src/Umbraco.Core/Constants-Entities.cs b/src/Umbraco.Core/Constants-Entities.cs
new file mode 100644
index 000000000000..d03f3be60d2e
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Entities.cs
@@ -0,0 +1,14 @@
+namespace Umbraco.Cms.Core;
+
+public static partial class Constants
+{
+ public static class Entities
+ {
+ public static class AdditionalDataKeys
+ {
+ public const string MemberPreviousUserName = "previousUsername";
+
+ public const string MemberGroupPreviousName = "previousName";
+ }
+ }
+}
diff --git a/src/Umbraco.Core/DeliveryApi/ApiContentPathResolver.cs b/src/Umbraco.Core/DeliveryApi/ApiContentPathResolver.cs
index 4ca6be3932aa..c3967c0bf02d 100644
--- a/src/Umbraco.Core/DeliveryApi/ApiContentPathResolver.cs
+++ b/src/Umbraco.Core/DeliveryApi/ApiContentPathResolver.cs
@@ -15,6 +15,27 @@ public ApiContentPathResolver(IRequestRoutingService requestRoutingService, IApi
_apiPublishedContentCache = apiPublishedContentCache;
}
+ [Obsolete("No longer used in V15. Scheduled for removal in V15.")]
+ public virtual bool IsResolvablePath(string path)
+ {
+ // File requests will blow up with an downstream exception in GetRequiredPublishedSnapshot, which fails due to an UmbracoContext
+ // not being available for what's considered a static file request.
+ // See: https://github.com/umbraco/Umbraco-CMS/issues/19051
+ // Given a URL segment and hence route can't contain a period, we can safely assume that if the last segment of the path contains
+ // a period, it's a file request and should return null here.
+ if (IsFileRequest(path))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static bool IsFileRequest(string path) => path
+ .Split('/', StringSplitOptions.RemoveEmptyEntries)
+ .LastOrDefault()?
+ .Contains('.') is true;
+
public virtual IPublishedContent? ResolveContentPath(string path)
{
path = path.EnsureStartsWith("/");
diff --git a/src/Umbraco.Core/DeliveryApi/IApiContentPathResolver.cs b/src/Umbraco.Core/DeliveryApi/IApiContentPathResolver.cs
index 391f18998e58..d8301666fbc5 100644
--- a/src/Umbraco.Core/DeliveryApi/IApiContentPathResolver.cs
+++ b/src/Umbraco.Core/DeliveryApi/IApiContentPathResolver.cs
@@ -4,5 +4,8 @@ namespace Umbraco.Cms.Core.DeliveryApi;
public interface IApiContentPathResolver
{
+ [Obsolete("No longer used in V15. Scheduled for removal in V15.")]
+ bool IsResolvablePath(string path) => true;
+
IPublishedContent? ResolveContentPath(string path);
}
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
index 96d200bf7bf1..1da27b02c6ad 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
@@ -8,12 +8,14 @@
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Grid;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Diagnostics;
using Umbraco.Cms.Core.Dictionary;
+using Umbraco.Cms.Core.DynamicRoot;
using Umbraco.Cms.Core.Editors;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Features;
@@ -35,7 +37,6 @@
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
-using Umbraco.Cms.Core.DynamicRoot;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Core.Templates;
@@ -341,6 +342,11 @@ private void AddCoreServices()
// Data type configuration cache
Services.AddUnique();
Services.AddNotificationHandler();
+
+ // Partial view cache invalidators (no-op, shipped implementation is added in Umbraco.Web.Website, but we
+ // need this to ensure we have a service registered for this interface even in headless setups).
+ // See: https://github.com/umbraco/Umbraco-CMS/issues/19661
+ Services.AddUnique();
}
}
}
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/ar.xml b/src/Umbraco.Core/EmbeddedResources/Lang/ar.xml
new file mode 100644
index 000000000000..4f69f147ad40
--- /dev/null
+++ b/src/Umbraco.Core/EmbeddedResources/Lang/ar.xml
@@ -0,0 +1,3880 @@
+
+
+
+ مجتمع أومبراكو
+ https://docs.umbraco.com/umbraco-cms/extending/language-files
+
+
+ الثقافة وأسماء المضيفين
+ سجل المراجعة
+ تصفح العقدة
+ تغيير نوع المستند
+ نسخ
+ إنشاء
+ تصدير
+ إنشاء حزمة
+ إنشاء مجموعة
+ حذف
+ تعطيل
+ تحرير الإعدادات
+ إفراغ سلة المحذوفات
+ تمكين
+ تصدير نوع المستند
+ استيراد نوع المستند
+ استيراد حزمة
+ تحرير في اللوحة
+ خروج
+ نقل
+ الإشعارات
+ تقييد الوصول العام
+ نشر
+ إلغاء النشر
+ إعادة تحميل
+ إعادة نشر الموقع بالكامل
+ إزالة
+ إعادة تسمية
+ استعادة
+ اختر مكان النسخ
+ اختر مكان النقل
+ اختر مكان الاستيراد
+ إلى في هيكل الشجرة أدناه
+ اختر مكان نسخ العنصر/العناصر المحددة
+ اختر مكان نقل العنصر/العناصر المحددة
+ تم نقله إلى
+ تم نسخه إلى
+ تم حذفه
+ الصلاحيات
+ التراجع
+ إرسال للنشر
+ إرسال للترجمة
+ تعيين مجموعة
+ ترتيب
+ ترجمة
+ تحديث
+ تعيين الصلاحيات
+ إلغاء القفل
+ إنشاء قالب المحتوى
+ إعادة إرسال الدعوة
+ إخفاء الخيارات غير المتاحة
+ تغيير نوع البيانات
+ تحرير المحتوى
+
+
+ عنوان الرابط
+ الرابط
+ مرساة / سلسلة الاستعلام
+ الاسم
+ إغلاق هذه النافذة
+ هل أنت متأكد من أنك تريد الحذف
+ %0% من %1% عنصر]]>
+ هل أنت متأكد من أنك تريد التعطيل
+ هل أنت متأكد من أنك تريد الإزالة
+ %0%]]>
+ هل أنت متأكد؟
+ هل أنت متأكد؟
+ قص
+ تحرير عنصر القاموس
+ تحرير اللغة
+ تحرير الوسائط المحددة
+ تحرير الويب هوك
+ إدراج رابط محلي
+ إدراج حرف
+ إدراج عنوان رسومي
+ إدراج صورة
+ إدراج رابط
+ انقر لإضافة ماكرو
+ إدراج جدول
+ هذا سيحذف اللغة وجميع المحتوى المتعلق باللغة
+ تغيير الثقافة للغة قد يكون عملية مكلفة وسيؤدي إلى إعادة بناء تخزين المحتوى المؤقت والفهارس
+
+ آخر تحرير
+ رابط
+ رابط داخلي:
+ عند استخدام الروابط المحلية، أدرج "#" أمام الرابط
+ فتح في نافذة جديدة؟
+ هذا الماكرو لا يحتوي على أي خصائص يمكنك تحريرها
+ لصق
+ تحرير الصلاحيات لـ
+ تعيين الصلاحيات لـ
+ تعيين الصلاحيات لـ %0% لمجموعة المستخدم %1%
+ حدد مجموعات المستخدمين التي تريد تعيين صلاحيات لها
+ العناصر في سلة المحذوفات يتم حذفها الآن. يرجى عدم إغلاق هذه النافذة بينما تحدث هذه العملية
+
+ سلة المحذوفات فارغة الآن
+ عندما يتم حذف العناصر من سلة المحذوفات، ستختفي للأبد
+
+ regexlib.com تواجه حالياً بعض المشاكل، والتي لا نملك السيطرة عليها. نعتذر بشدة عن هذا الإزعاج.]]>
+ ابحث عن تعبير نمطي لإضافة التحقق لحقل نموذج. مثال: 'email'، 'zip-code'، 'URL'.
+
+ إزالة الماكرو
+ حقل مطلوب
+ تم إعادة فهرسة الموقع
+ تم تحديث تخزين الموقع المؤقت. جميع المحتوى المنشور محدث الآن. بينما جميع المحتوى غير المنشور لا يزال غير منشور
+
+ سيتم تحديث تخزين الموقع المؤقت. سيتم تحديث جميع المحتوى المنشور، بينما سيبقى المحتوى غير المنشور غير منشور.
+
+ عدد الأعمدة
+ عدد الصفوف
+ انقر على الصورة لرؤية الحجم الكامل
+ اختيار العنصر
+ عرض عنصر التخزين المؤقت
+ ربط بالأصل
+ تضمين التوابع
+ أودّ المجتمع
+ رابط للصفحة
+ يفتح المستند المرتبط في نافذة أو علامة تبويب جديدة
+ رابط للوسائط
+ تحديد عقدة بداية المحتوى
+ تحديد حدث
+ تحديد وسائط
+ تحديد نوع الوسائط
+ تحديد أيقونة
+ تحديد عنصر
+ تحديد رابط
+ تحديد ماكرو
+ تحديد محتوى
+ تحديد نوع المحتوى
+ تحديد عقدة بداية الوسائط
+ تحديد عضو
+ تحديد مجموعة الأعضاء
+ تحديد نوع العضو
+ تحديد عقدة
+ تحديد اللغات
+ تحديد الأقسام
+ تحديد مستخدم
+ تحديد مستخدمين
+ لم يتم العثور على أيقونات
+ لا توجد معاملات لهذا الماكرو
+ لا توجد ماكروهات متاحة للإدراج
+ موفروا تسجيل الدخول الخارجي
+ تفاصيل الاستثناء
+ تتبع المكدس
+ الاستثناء الداخلي
+ ربط الـ
+ إلغاء ربط الـ
+ حساب
+ تحديد محرر
+ تحديد مقطع
+ هذا سيحذف العقدة وجميع لغاتها. إذا كنت تريد حذف لغة واحدة فقط، يجب إلغاء نشر العقدة بتلك اللغة بدلاً من ذلك.
+
+ %0%.]]>
+ %0% من مجموعة %1%]]>
+ نعم، إزالة
+ أنت تحذف التخطيط
+ تعديل التخطيط سيؤدي إلى فقدان البيانات لأي محتوى موجود يعتمد على هذا التكوين.
+ تحديد التكوين
+
+
+
+ لاستيراد عنصر قاموس، ابحث عن ملف ".udt" على جهاز الكمبيوت
+
+
+ المحتوى
+ الإدارة
+ الهيكل
+ أخرى
+
+
+ السماح بالوصول لتعيين الثقافة وأسماء المضيفين
+ السماح بالوصول لعرض سجل تاريخ العقدة
+ السماح بالوصول لعرض العقدة
+ السماح بالوصول لتغيير نوع المستند للعقدة
+ السماح بالوصول لنسخ العقدة
+ السماح بالوصول لإنشاء العقد
+ السماح بالوصول لحذف العقد
+ السماح بالوصول لنقل العقدة
+ السماح بالوصول لتعيين وتغيير قيود الوصول للعقدة
+ السماح بالوصول لنشر العقدة
+ السماح بالوصول لإلغاء نشر العقدة
+ السماح بالوصول لتغيير صلاحيات العقدة
+ السماح بالوصول للتراجع بالعقدة إلى حالة سابقة
+ السماح بالوصول لإرسال العقدة للموافقة قبل النشر
+ السماح بالوصول لإرسال العقدة للترجمة
+ السماح بالوصول لتغيير ترتيب العقد
+ السماح بالوصول لترجمة العقدة
+ السماح بالوصول لحفظ العقدة
+ السماح بالوصول لإنشاء قالب المحتوى
+ السماح بالوصول لإعداد الإشعارات لعقد المحتوى
+
+
+ المحتوى
+ معلومات
+
+
+ تم رفض الإذن.
+ إضافة نطاق جديد
+ إضافة النطاق الحالي
+ إزالة
+ عقدة غير صالحة.
+ نطاق واحد أو أكثر له تنسيق غير صالح.
+ تم تعيين النطاق بالفعل.
+ اللغة
+ النطاق
+ تم إنشاء النطاق الجديد '%0%'
+ تم حذف النطاق '%0%'
+ تم تعيين النطاق '%0%' بالفعل
+ تم تحديث النطاق '%0%'
+ تحرير النطاقات الحالية
+
+
+ وراثة
+ الثقافة
+
+ أو وراثة الثقافة من العقد الأصل. سيتم التطبيق أيضاً
+ على العقدة الحالية، ما لم ينطبق نطاق أدناه أيضاً.]]>
+
+ النطاقات
+
+
+ مسح التحديد
+ تحديد
+ افعل شيئاً آخر
+ عريض
+ إلغاء مسافة الفقرة البادئة
+ إدراج حقل النموذج
+ إدراج عنوان رسومي
+ تحرير Html
+ مسافة بادئة للفقرة
+ مائل
+ توسيط
+ محاذاة لليسار
+ محاذاة لليمين
+ إدراج رابط
+ إدراج رابط محلي (مرساة)
+ قائمة نقطية
+ قائمة رقمية
+ إدراج ماكرو
+ إدراج صورة
+ نشر وإغلاق
+ نشر مع التوابع
+ تحرير العلاقات
+ العودة للقائمة
+ حفظ
+ حفظ وإغلاق
+ حفظ ونشر
+ حفظ وإرسال للموافقة
+ حفظ عرض القائمة
+ جدولة
+ حفظ ومعاينة
+ المعاينة معطلة لأنه لا يوجد قالب معين
+ اختيار النمط
+ إظهار الأنماط
+ إدراج جدول
+ حفظ وإنشاء النماذج
+ تراجع
+ إعادة
+ حذف العلامة
+ إلغاء
+ تأكيد
+ المزيد من خيارات النشر
+ إرسال
+ إنشاء النماذج وإغلاق
+
+
+ تم حذف الوسائط
+ تم نقل الوسائط
+ تم نسخ الوسائط
+ تم حفظ الوسائط
+ تم إنشاء الوسائط
+
+
+ عرض لـ
+ تم حذف المحتوى
+ تم إلغاء نشر المحتوى
+ تم حفظ المحتوى ونشره
+ تم حفظ المحتوى ونشره للغات: %0%
+ تم حفظ المحتوى
+ تم حفظ المحتوى للغات: %0%
+ تم نقل المحتوى
+ تم نسخ المحتوى
+ تم التراجع بالمحتوى
+ تم إرسال المحتوى للنشر
+ تم إرسال المحتوى للنشر للغات: %0%
+ تم تنفيذ ترتيب العناصر الفرعية بواسطة المستخدم
+ %0%
+ تم تعطيل التنظيف للإصدار: %0%
+ تم تمكين التنظيف للإصدار: %0%
+ نسخ
+ نشر
+ نشر
+ نقل
+ حفظ
+ جديد
+ حفظ
+ حذف
+ إلغاء نشر
+ تراجع
+ إرسال للنشر
+ إرسال للنشر
+ ترتيب
+ مخصص
+ حفظ
+ حفظ
+ التاريخ (جميع المتغيرات)
+ تم إلغاء نشر المحتوى للغات: %0%
+ إلغاء نشر
+
+
+ لا يمكن أن يحتوي اسم المجلد على أحرف غير قانونية.
+ فشل في حذف العنصر: %0%
+
+
+ منشور
+ حول هذه الصفحة
+ الاسم المستعار
+ (كيف تصف الصورة عبر الهاتف)
+ الروابط البديلة
+ انقر لتحرير هذا العنصر
+ أنشئ بواسطة
+ المؤلف الأصلي
+ حُدث بواسطة
+ تاريخ الإنشاء
+ تاريخ/وقت إنشاء هذا المستند
+ نوع المستند
+ تحرير
+ إزالة في
+ تم تغيير هذا العنصر بعد النشر
+ هذا العنصر غير منشور
+ آخر نشر
+ لا توجد عناصر للعرض
+ لا توجد عناصر للعرض في القائمة.
+ لم تتم إضافة محتوى
+ لم تتم إضافة أعضاء
+ نوع الوسائط
+ رابط إلى عنصر/عناصر الوسائط
+ مجموعة الأعضاء
+ الدور
+ نوع العضو
+ لم يتم إجراء تغييرات
+ لم يتم اختيار تاريخ
+ عنوان الصفحة
+ عنصر الوسائط هذا ليس له رابط
+ الخصائص
+ هذا المستند منشور ولكنه غير مرئي لأن الأصل '%0%' غير منشور
+
+ هذه الثقافة منشورة ولكنها غير مرئية لأنها غير منشورة على الأصل '%0%'
+
+ هذا المستند منشور ولكنه ليس في التخزين المؤقت
+ لا يمكن الحصول على الرابط
+ هذا المستند منشور ولكن رابطه سيتصادم مع المحتوى %0%
+ هذا المستند منشور ولكن لا يمكن توجيه رابطه
+ نشر
+ منشور
+ منشور (تغييرات معلقة)
+ حالة النشر
+
+ %0% وجميع عناصر المحتوى أدناه وبالتالي جعل محتواها متاحاً للجمهور.]]>
+
+
+ نشر في
+ إلغاء النشر في
+ مسح التاريخ
+ تعيين التاريخ
+ تم تحديث ترتيب الفرز
+ لترتيب العقد، ما عليك سوى سحب العقد أو النقر على أحد عناوين الأعمدة. يمكنك تحديد عقد متعددة عن طريق الضغط على مفتاح "shift" أو "control" أثناء التحديد
+
+ إحصائيات
+ العنوان (اختياري)
+ النص البديل (اختياري)
+ التسمية التوضيحية (اختياري)
+ النوع
+ إلغاء النشر
+ غير منشور
+ غير مُنشأ
+ آخر تحرير
+ تاريخ/وقت تحرير هذا المستند
+ إزالة الملف/الملفات
+ انقر هنا لإزالة الصورة من عنصر الوسائط
+ انقر هنا لإزالة الملف من عنصر الوسائط
+ رابط إلى المستند
+ عضو في مجموعة/مجموعات
+ ليس عضواً في مجموعة/مجموعات
+ العناصر الفرعية
+ الهدف
+ هذا يترجم إلى الوقت التالي على الخادم:
+
+ ماذا يعني هذا؟]]>
+ هل أنت متأكد من أنك تريد حذف هذا العنصر؟
+ الخاصية %0% تستخدم المحرر %1% غير المدعوم بواسطة المحتوى المتداخل.
+
+ هل أنت متأكد من أنك تريد حذف جميع العناصر؟
+ هل أنت متأكد من أنك تريد حذف هذا العنصر؟
+ لا يوجد أنواع محتوى مكونة لهذه الخاصية.
+ إضافة نوع عنصر
+ تحديد نوع العنصر
+ حدد المجموعة التي يجب عرض خصائصها. إذا تُركت فارغة، سيتم استخدام المجموعة الأولى على نوع العنصر.
+
+ أدخل تعبير angular لتقييمه مقابل كل عنصر لاسمه. استخدم
+
+ لعرض فهرس العنصر
+ نوع العنصر المحدد لا يحتوي على أي مجموعات مدعومة (علامات التبويب غير مدعومة بواسطة هذا المحرر، إما قم بتغييرها إلى مجموعات أو استخدم محرر قائمة الكتل).
+ إضافة مربع نص آخر
+ إزالة مربع النص هذا
+ جذر المحتوى
+ تضمين عناصر المحتوى غير المنشورة.
+ هذه القيمة مخفية. إذا كنت بحاجة للوصول لعرض هذه القيمة، يرجى الاتصال بمدير الموقع.
+
+ هذه القيمة مخفية.
+ ما اللغات التي تريد نشرها؟
+ ما اللغات التي تريد إرسالها للموافقة؟
+ ما اللغات التي تريد جدولتها؟
+ حدد اللغات لإلغاء نشرها. إلغاء نشر لغة إلزامية سيلغي نشر جميع اللغات.
+
+ سيتم حفظ جميع المتغيرات الجديدة.
+ أي متغيرات تريد نشرها؟
+ اختر أي متغيرات سيتم حفظها.
+ المتغيرات التالية مطلوبة لحدوث النشر:
+ لسنا مستعدين للنشر
+ مستعد للنشر؟
+ مستعد للحفظ؟
+ إعادة تعيين نقطة التركيز
+ إرسال للموافقة
+ حدد التاريخ والوقت لنشر و/أو إلغاء نشر عنصر المحتوى.
+ إنشاء جديد
+ لصق من الحافظة
+ إزالة العنصر
+ هذا العنصر في سلة المحذوفات
+ لا يمكن إضافة محتوى لهذا العنصر
+ الحفظ غير مسموح
+ النشر غير مسموح
+ الإرسال للموافقة غير مسموح
+ الجدولة غير مسموحة
+ إلغاء النشر غير مسموح
+ تحديد جميع المتغيرات
+
+
+ %0%]]>
+ فارغ
+ اختيار قالب محتوى
+ تم إنشاء قالب المحتوى
+ تم إنشاء قالب محتوى من '%0%'
+ يوجد بالفعل قالب محتوى آخر بنفس الاسم
+ قالب المحتوى هو محتوى محدد مسبقاً يمكن للمحرر تحديده لاستخدامه كأساس لإنشاء محتوى جديد
+
+
+
+ انقر للرفع
+ أو انقر هنا لاختيار الملفات
+ لا يمكن رفع هذا الملف، ليس له نوع ملف معتمد
+ لا يمكن رفع هذا الملف، نوع الوسائط بالاسم المستعار '%0%' غير مسموح هنا
+ لا يمكن رفع هذا الملف، ليس له اسم ملف صالح
+ الحد الأقصى لحجم الملف هو
+ جذر الوسائط
+ فشل في إنشاء مجلد تحت المعرف الأصل %0%
+ فشل في إعادة تسمية المجلد بالمعرف %0%
+ اسحب وأفلت ملفك/ملفاتك في المنطقة
+ فشل في واحد أو أكثر من تحققات أمان الملفات
+ لا يمكن أن تكون مجلدات الأصل والوجهة هي نفسها
+ الرفع غير مسموح في هذا الموقع.
+
+
+ إنشاء عضو جديد
+ جميع الأعضاء
+ مجموعات الأعضاء ليس لها خصائص إضافية للتحرير.
+ المصادقة الثنائية
+ يوجد بالفعل عضو بهذا اسم تسجيل الدخول
+ العضو موجود بالفعل في المجموعة '%0%'
+ العضو لديه كلمة مرور محددة بالفعل
+ القفل غير مُمكن لهذا العضو
+ العضو ليس في المجموعة '%0%'
+
+
+ فشل في نسخ نوع المحتوى
+ فشل في نقل نوع المحتوى
+
+
+ فشل في نسخ نوع الوسائط
+ فشل في نقل نوع الوسائط
+ اختيار تلقائي
+
+
+ فشل في نسخ نوع العضو
+
+
+ أين تريد إنشاء %0% الجديد
+ إنشاء عنصر تحت
+ حدد نوع المستند الذي تريد إنشاء قالب محتوى له
+ أدخل اسم المجلد
+ اختر نوعاً وعنواناً
+
+ أنواع المستندات داخل قسم الإعدادات، بتحرير أنواع العقد الفرعية المسموحة تحت الصلاحيات.]]>
+
+ أنواع المستندات داخل قسم الإعدادات.]]>
+ الصفحة المحددة في شجرة المحتوى لا تسمح بإنشاء أي صفحات تحتها.
+
+ تحرير الصلاحيات لنوع المستند هذا
+ إنشاء نوع مستند جديد
+
+ أنواع المستندات داخل قسم الإعدادات، بتغيير خيار السماح كجذر تحت الصلاحيات.]]>
+
+ أنواع الوسائط داخل قسم الإعدادات، بتحرير أنواع العقد الفرعية المسموحة تحت الصلاحيات.]]>
+ الوسائط المحددة في الشجرة لا تسمح بإنشاء أي وسائط أخرى تحتها.
+
+ تحرير الصلاحيات لأنواع الوسائط هذه
+ نوع مستند بدون قالب
+ نوع مستند بقالب
+ تعريف البيانات لصفحة محتوى يمكن إنشاؤها بواسطة المحررين في شجرة المحتوى ويمكن الوصول إليها مباشرة عبر رابط.
+
+ نوع المستند
+ تعريف البيانات لمكون محتوى يمكن إنشاؤه بواسطة المحررين في شجرة المحتوى واختياره على صفحات أخرى ولكن ليس له رابط مباشر.
+
+ نوع العنصر
+ يحدد المخطط لمجموعة متكررة من الخصائص، على سبيل المثال، في محرر خاصية 'قائمة الكتل' أو 'شبكة الكتل'.
+
+ التركيب
+ يحدد مجموعة قابلة لإعادة الاستخدام من الخصائص التي يمكن تضمينها في تعريف أنواع مستندات أخرى متعددة. على سبيل المثال، مجموعة من 'إعدادات الصفحة المشتركة'.
+
+ مجلد
+ يُستخدم لتنظيم أنواع المستندات والتراكيب وأنواع العناصر المُنشأة في شجرة نوع المستند هذه.
+
+ مجلد جديد
+ نوع بيانات جديد
+ ملف JavaScript جديد
+ عرض جزئي فارغ جديد
+ ماكرو عرض جزئي جديد
+ عرض جزئي جديد من مقطع
+ ماكرو عرض جزئي جديد من مقطع
+ ماكرو عرض جزئي جديد (بدون ماكرو)
+ ملف ورقة أنماط جديد
+ ملف ورقة أنماط محرر النص المنسق الجديد
+
+
+ تصفح موقعك
+ - إخفاء
+ إذا لم يفتح أومبراكو، قد تحتاج للسماح بالنوافذ المنبثقة من هذا الموقع
+ تم فتحه في نافذة جديدة
+ إعادة التشغيل
+ زيارة
+ أهلاً وسهلاً
+
+
+ البقاء
+ تجاهل التغييرات
+ لديك تغييرات غير محفوظة
+ هل أنت متأكد من أنك تريد الانتقال بعيداً عن هذه الصفحة؟ - لديك تغييرات غير محفوظة
+
+ النشر سيجعل العناصر المحددة مرئية على الموقع.
+ إلغاء النشر سيزيل العناصر المحددة وجميع توابعها من الموقع.
+
+ إلغاء النشر سيزيل هذه الصفحة وجميع توابعها من الموقع.
+ لديك تغييرات غير محفوظة. إجراء تغييرات على نوع المستند سيتجاهل التغييرات.
+
+
+
+ تم
+ تم حذف %0% عنصر
+ تم حذف %0% عنصر
+ تم حذف %0% من %1% عنصر
+ تم حذف %0% من %1% عنصر
+ تم نشر %0% عنصر
+ تم نشر %0% عنصر
+ تم نشر %0% من %1% عنصر
+ تم نشر %0% من %1% عنصر
+ تم إلغاء نشر %0% عنصر
+ تم إلغاء نشر %0% عنصر
+ تم إلغاء نشر %0% من %1% عنصر
+ تم إلغاء نشر %0% من %1% عنصر
+ تم نقل %0% عنصر
+ تم نقل %0% عنصر
+ تم نقل %0% من %1% عنصر
+ تم نقل %0% من %1% عنصر
+ تم نسخ %0% عنصر
+ تم نسخ %0% عنصر
+ تم نسخ %0% من %1% عنصر
+ تم نسخ %0% من %1% عنصر
+
+
+ عنوان الرابط
+ رابط
+ مرساة / استعلام
+ اسم
+ إغلاق هذه النافذة
+ هل أنت متأكد أنك تريد حذف
+ %0% من %1% عناصر]]>
+ هل أنت متأكد أنك تريد تعطيل
+ هل أنت متأكد أنك تريد إزالة
+ %0%]]>
+ هل أنت متأكد؟
+ هل أنت متأكد؟
+ قص
+ تحرير عنصر القاموس
+ تحرير اللغة
+ تحرير الوسائط المحددة
+ تحرير ويب هوك
+ إدراج رابط محلي
+ إدراج حرف
+ إدراج عنوان رسومي
+ إدراج صورة
+ إدراج رابط
+ انقر لإضافة ماكرو
+ إدراج جدول
+ سيؤدي هذا إلى حذف اللغة وجميع المحتوى المرتبط بها
+ تغيير الثقافة للغة قد تكون عملية مكلفة وسيؤدي إلى إعادة بناء ذاكرة التخزين المؤقت للمحتوى والفهارس
+ آخر تحرير
+ رابط
+ رابط داخلي:
+ عند استخدام الروابط المحلية، أدخل "#" قبل الرابط
+ فتح في نافذة جديدة؟
+ هذا الماكرو لا يحتوي على أي خصائص يمكنك تحريرها
+ لصق
+ تحرير الأذونات لـ
+ تعيين الأذونات لـ
+ تعيين الأذونات لـ %0% لمجموعة المستخدمين %1%
+ حدد مجموعات المستخدمين التي تريد تعيين الأذونات لها
+ يتم الآن حذف العناصر في سلة المحذوفات. يرجى عدم إغلاق هذه النافذة أثناء هذه العملية
+ سلة المحذوفات فارغة الآن
+ عند حذف العناصر من سلة المحذوفات، سيتم إزالتها إلى الأبد
+
+ regexlib.com تواجه خدمة الويب بعض المشاكل، والتي ليس لدينا أي سيطرة عليها. نأسف لهذا الإزعاج.]]>
+ ابحث عن تعبير عادي لإضافة التحقق إلى حقل النموذج. مثال: 'بريد إلكتروني', 'رمز بريدي', 'رابط'.
+ إزالة الماكرو
+ حقل مطلوب
+ تمت إعادة فهرسة الموقع
+ تم تحديث ذاكرة التخزين المؤقت للموقع. جميع محتويات النشر محدثة الآن. بينما تبقى جميع المحتويات غير المنشورة غير منشورة
+ سيتم تحديث ذاكرة التخزين المؤقت للموقع. سيتم تحديث جميع المحتويات المنشورة، بينما تبقى المحتويات غير المنشورة غير منشورة.
+ عدد الأعمدة
+ عدد الصفوف
+ انقر على الصورة لرؤية الحجم الكامل
+ اختر عنصرًا
+ عرض عنصر الذاكرة المؤقتة
+ ربط بالأصل
+ تضمين العناصر الفرعية
+ أكثر المجتمعات ودية
+ رابط إلى الصفحة
+ يفتح المستند المرتبط في نافذة أو علامة تبويب جديدة
+ رابط إلى الوسائط
+ حدد عقدة بداية المحتوى
+ حدد حدثًا
+ حدد وسائط
+ حدد نوع الوسائط
+ حدد أيقونة
+ حدد عنصرًا
+ حدد رابطًا
+ حدد ماكرو
+ حدد محتوى
+ حدد نوع المحتوى
+ حدد عقدة بداية الوسائط
+ حدد عضوًا
+ حدد مجموعة أعضاء
+ حدد نوع العضو
+ حدد عقدة
+ حدد اللغات
+ حدد الأقسام
+ حدد مستخدمًا
+ حدد المستخدمين
+ لم يتم العثور على أي أيقونات
+ لا توجد معلمات لهذا الماكرو
+ لا توجد وحدات ماكرو متاحة للإدراج
+ موفرو تسجيل الدخول الخارجيين
+ تفاصيل الاستثناء
+ تتبع المكدس
+ استثناء داخلي
+ ربط
+ إلغاء ربط
+ الحساب
+ حدد المحرر
+ حدد مقتطف
+ سيؤدي هذا إلى حذف العقدة وجميع لغاتها. إذا كنت تريد حذف لغة واحدة فقط، فيجب عليك إلغاء نشر العقدة بتلك اللغة بدلاً من ذلك.
+ %0%.]]>
+ %0% من مجموعة %1%]]>
+ نعم، إزالة
+ أنت تحذف التخطيط
+ سيؤدي تعديل التخطيط إلى فقدان البيانات لأي محتوى موجود يعتمد على هذا التكوين.
+ حدد التكوين
+
+
+
+ لاستيراد عنصر قاموس، ابحث عن ملف ".udt" على جهازك عن طريق النقر على زر "استيراد" (سيتم طلب التأكيد في الشاشة التالية)
+
+ عنصر القاموس غير موجود.
+ العنصر الأب غير موجود.
+ لا توجد عناصر قاموس.
+ لا توجد عناصر قاموس في هذا الملف.
+ لم يتم العثور على أي عناصر قاموس.
+ إنشاء عنصر قاموس
+
+
+ %0%' أدناه
+ ]]>
+ اسم الثقافة
+
+ نظرة عامة على القاموس
+
+
+ باحثون مكونون
+ يعرض خصائص وأدوات لأي باحث مكون (مثل باحث متعدد الفهارس)
+ قيم الحقل
+ حالة الصحة
+ حالة صحة الفهرس وما إذا كان يمكن قراءته
+ الفهارس
+ معلومات الفهرس
+ المحتوى في الفهرس
+ يسرد خصائص الفهرس
+ إدارة فهارس Examine
+ يسمح لك بمشاهدة تفاصيل كل فهرس ويوفر بعض الأدوات لإدارة الفهارس
+ إعادة بناء الفهرس
+
+ اعتمادًا على كمية المحتوى في موقعك، قد يستغرق هذا بعض الوقت.
+ لا يوصى بإعادة بناء الفهرس خلال أوقات حركة مرور عالية على الموقع أو عندما يقوم المحررون بتحرير المحتوى.
+ ]]>
+
+ الباحثون
+ ابحث في الفهرس وعرض النتائج
+ أدوات
+ أدوات لإدارة الفهرس
+ الحقول
+ لا يمكن قراءة الفهرس وسيحتاج إلى إعادة بناء
+ تستغرق العملية وقتًا أطول من المتوقع، تحقق من سجل Umbraco لمعرفة ما إذا كانت هناك أي أخطاء خلال هذه العملية
+ لا يمكن إعادة بناء هذا الفهرس لأنه لا يحتوي على
+ IIndexPopulator
+ لم يتم العثور على نتائج
+ عرض %0% - %1% من %2% نتيجة - الصفحة %3% من %4%
+
+
+ أدخل اسم المستخدم
+ أدخل كلمة المرور
+ تأكيد كلمة المرور
+ سمّ %0%...
+ أدخل اسمًا...
+ أدخل بريدًا إلكترونيًا...
+ أدخل اسم مستخدم...
+ تسمية...
+ أدخل وصفًا...
+ اكتب للبحث...
+ اكتب للتصفية...
+ اكتب لإضافة علامات (اضغط إدخال بعد كل علامة)...
+ أدخل بريدك الإلكتروني
+ أدخل رسالة...
+ اسم المستخدم الخاص بك هو عادةً بريدك الإلكتروني
+ #قيمة أو ?مفتاح=قيمة
+ أدخل اسمًا مستعارًا...
+ جارٍ إنشاء اسم مستعار...
+ إنشاء عنصر
+ تحرير
+ الاسم
+
+
+ إنشاء عرض قائمة مخصص
+ إزالة عرض قائمة مخصص
+ نوع محتوى أو نوع وسائط أو نوع عضو بهذا الاسم المستعار موجود بالفعل
+
+
+ تمت إعادة التسمية
+ أدخل اسم مجلد جديد هنا
+ تمت إعادة تسمية %0% إلى %1%
+
+
+ إضافة قيمة مسبقة
+ نوع بيانات قاعدة البيانات
+ معرف محرر الخصائص
+ محرر الخصائص
+ أزرار
+ تمكين الإعدادات المتقدمة لـ
+ تمكين قائمة السياق
+ الحد الأقصى للحجم الافتراضي للصور المدرجة
+ أوراق الأنماط ذات الصلة
+ إظهار التسمية
+ العرض والارتفاع
+ حدد المجلد لنقله
+ إلى في بنية الشجرة أدناه
+ تم نقله تحت
+ تعطيل تغيير محرر الخصائص على نوع بيانات بقيم مخزنة. للسماح بذلك يمكنك تغيير إعداد Umbraco:CMS:DataTypes:CanBeChanged في appsettings.json.
+ %0% سيحذف الخصائص وبياناتها من العناصر التالية]]>
+ أنا أفهم أن هذا الإجراء سيحذف الخصائص والبيانات المستندة إلى نوع البيانات هذا
+
+
+ تم حفظ بياناتك، ولكن قبل أن تتمكن من نشر هذه الصفحة، هناك بعض الأخطاء التي تحتاج إلى إصلاحها أولاً:
+ موفر العضوية الحالي لا يدعم تغيير كلمة المرور (يجب أن يكون EnablePasswordRetrieval صحيحًا)
+ %0% موجود بالفعل
+ كانت هناك أخطاء:
+ كانت هناك أخطاء:
+ يجب أن تكون كلمة المرور بطول %0% أحرف على الأقل وتحتوي على %1% حرف غير أبجدي رقمي على الأقل
+ %0% يجب أن يكون عددًا صحيحًا
+ حقل %0% في علامة التبويب %1% إلزامي
+ %0% حقل إلزامي
+ %0% في %1% ليس بتنسيق صحيح
+ %0% ليس بتنسيق صحيح
+
+
+ تم استلام خطأ من الخادم
+ تم منع نوع الملف المحدد من قبل المسؤول
+ ملاحظة! على الرغم من تمكين CodeMirror عن طريق التكوين، إلا أنه معطل في Internet Explorer لأنه غير مستقر بما فيه الكفاية.
+ يرجى ملء كل من الاسم المستعار والاسم على نوع الخاصية الجديد!
+ هناك مشكلة في حق الوصول للقراءة/الكتابة لملف أو مجلد معين
+ خطأ في تحميل نص Partial View (ملف: %0%)
+ الرجاء إدخال عنوان
+ الرجاء اختيار نوع
+ أنت على وشك جعل الصورة أكبر من الحجم الأصلي. هل أنت متأكد أنك تريد المتابعة؟
+ تم حذف عقدة البداية، يرجى الاتصال بمسؤولك
+ يرجى تحديد المحتوى قبل تغيير النمط
+ لا توجد أنماط نشطة متاحة
+ يرجى وضع المؤشر على يسار الخليتين اللتين ترغب في دمجهما
+ لا يمكنك تقسيم خلية لم يتم دمجها.
+ هذه الخاصية غير صالحة
+ حدث فشل غير معروف
+ فشل التزامن التفاؤلي، تم تعديل الكائن
+
+
+ حول
+ إجراء
+ إجراءات
+ إضافة
+ اسم مستعار
+ الكل
+ هل أنت متأكد؟
+ رجوع
+ العودة إلى النظرة العامة
+ حدود
+ بواسطة
+ إلغاء
+ هوامش الخلية
+ اختر
+ مسح
+ إغلاق
+ إغلاق النافذة
+ إغلاق اللوحة
+ تعليق
+ تأكيد
+ تقيد
+ تقيد النسب
+ محتوى
+ متابعة
+ نسخ
+ إنشاء
+ قاعدة البيانات
+ تاريخ
+ افتراضي
+ حذف
+ تم الحذف
+ جارٍ الحذف...
+ تصميم
+ قاموس
+ أبعاد
+ تجاهل
+ أسفل
+ تنزيل
+ تحرير
+ تم التحرير
+ عناصر
+ بريد إلكتروني
+ خطأ
+ حقل
+ بحث
+ أولاً
+ نقطة محورية
+ عام
+ مجموعات
+ مجموعة
+ ارتفاع
+ مساعدة
+ إخفاء
+ تاريخ
+ أيقونة
+ معرف
+ استيراد
+ البحث في هذا المجلد فقط
+ معلومات
+ هوامش داخلية
+ إدراج
+ تثبيت
+ غير صالح
+ محاذاة
+ تسمية
+ لغة
+ آخر
+ تخطيط
+ روابط
+ جارٍ التحميل...
+ مقفل
+ تسجيل الدخول
+ تسجيل الخروج
+ تسجيل الخروج
+ ماكرو
+ إلزامي
+ رسالة
+ نقل
+ اسم
+ جديد
+ التالي
+ لا
+ اسم العقدة
+ من
+ إيقاف
+ موافق
+ فتح
+ خيارات
+ تشغيل
+ أو
+ ترتيب حسب
+ كلمة المرور
+ مسار
+ لحظة من فضلك...
+ السابق
+ خصائص
+ قراءة المزيد
+ إعادة بناء
+ البريد الإلكتروني لاستلام بيانات النموذج
+ سلة المحذوفات
+ سلة المحذوفات فارغة
+ إعادة تحميل
+ متبقي
+ إزالة
+ إعادة تسمية
+ تجديد
+ مطلوب
+ استرجاع
+ إعادة المحاولة
+ أذونات
+ النشر المجدول
+ معلومات Umbraco
+ بحث
+ عذرًا، لا يمكننا العثور على ما تبحث عنه.
+ لم تتم إضافة أي عناصر
+ خادم
+ إعدادات
+ مشترك
+ إظهار
+ إظهار الصفحة عند الإرسال
+ حجم
+ فرز
+ حالة
+ إرسال
+ نجاح
+ نوع
+ اسم النوع
+ اكتب للبحث...
+ تحت
+ أعلى
+ تحديث
+ ترقية
+ رفع
+ رابط
+ مستخدم
+ اسم المستخدم
+ قيمة
+ عرض
+ مرحبًا...
+ عرض
+ نعم
+ مجلد
+ نتائج البحث
+ إعادة ترتيب
+ لقد انتهيت من إعادة الترتيب
+ معاينة
+ تغيير كلمة المرور
+ إلى
+ عرض القائمة
+ جارٍ الحفظ...
+ حالي
+ تضمين
+ محدد
+ آخر
+ مقالات
+ فيديوهات
+ صورة رمزية لـ
+ رأس
+ حقل نظام
+ آخر تحديث
+ انتقل إلى القائمة
+ انتقل إلى المحتوى
+ أساسي
+ تغيير
+ قسم القص
+ عام
+ وسائط
+ تراجع
+ تحقق
+
+
+ أزرق
+
+
+ إضافة مجموعة
+ إضافة خاصية
+ إضافة محرر
+ إضافة قالب
+ إضافة عقدة فرعية
+ إضافة فرعي
+ تحرير نوع البيانات
+ التنقل بين الأقسام
+ اختصارات
+ إظهار الاختصارات
+ تبديل عرض القائمة
+ تبديل السماح كجذر
+ تعليق/إلغاء تعليق الأسطر
+ إزالة سطر
+ نسخ الأسطر لأعلى
+ نسخ الأسطر لأسفل
+ نقل الأسطر لأعلى
+ نقل الأسطر لأسفل
+ عام
+ محرر
+ تبديل السماح بمتغيرات الثقافة
+ إضافة علامة تبويب
+
+
+ لون الخلفية
+ عريض
+ لون النص
+ خط
+ نص
+
+
+ صفحة
+
+
+ لا يمكن للمثبت الاتصال بقاعدة البيانات.
+ تعذر حفظ ملف web.config. يرجى تعديل سلسلة الاتصال يدويًا.
+ تم العثور على قاعدة البيانات الخاصة بك وهي معرفة كـ
+ تكوين قاعدة البيانات
+
+ تثبيت لتثبيت قاعدة بيانات Umbraco %0%
+ ]]>
+
+
+ التالي للمتابعة.]]>
+ لم يتم العثور على قاعدة البيانات! يرجى التحقق من أن المعلومات في "سلسلة الاتصال" لملف "web.config" صحيحة.
+
للمتابعة، يرجى تحرير ملف "web.config" (باستخدام Visual Studio أو محرر النصوص المفضل لديك)، انتقل إلى الأسفل، أضف سلسلة الاتصال لقاعدة البيانات الخاصة بك في المفتاح المسمى "UmbracoDbDSN" واحفظ الملف.
]]>
+
+ يرجى الاتصال بمزود خدمة الإنترنت الخاص بك إذا لزم الأمر.
+ إذا كنت تقوم بالتثبيت على جهاز محلي أو خادم، فقد تحتاج إلى معلومات من مسؤول النظام الخاص بك.]]>
+
+ اضغط على زر ترقية لترقية قاعدة البيانات الخاصة بك إلى Umbraco %0%
+
+ لا تقلق - لن يتم حذف أي محتوى وسيستمر كل شيء في العمل بعد ذلك!
+
+ ]]>
+ اضغط التالي للمتابعة. ]]>
+
+ التالي لمتابعة معالج التكوين]]>
+
+ يجب تغيير كلمة مرور المستخدم الافتراضي!]]>
+
+ تم تعطيل المستخدم الافتراضي أو ليس لديه حق الوصول إلى Umbraco!
لا يلزم اتخاذ أي إجراءات أخرى. انقر التالي للمتابعة.]]>
+
+ تم تغيير كلمة مرور المستخدم الافتراضي بنجاح منذ التثبيت!
لا يلزم اتخاذ أي إجراءات أخرى. انقر التالي للمتابعة.]]>
+ تم تغيير كلمة المرور!
+ احصل على بداية رائعة، شاهد مقاطع الفيديو التقديمية الخاصة بنا
+ بالنقر على الزر التالي (أو تعديل umbracoConfigurationStatus في web.config)، فإنك تقبل ترخيص هذا البرنامج كما هو محدد في المربع أدناه. لاحظ أن توزيعة Umbraco هذه تتكون من ترخيصين مختلفين، ترخيص MIT مفتوح المصدر للإطار وترخيص Umbraco المجاني الذي يغطي واجهة المستخدم.
+ غير مثبت بعد.
+الملفات والمجلدات المتأثرة
+مزيد من المعلومات حول إعداد أذونات Umbraco هنا
+تحتاج إلى منح ASP.NET أذونات التعديل للملفات/المجلدات التالية
+إعدادات الأذونات الخاصة بك مثالية تقريبًا!
+يمكنك تشغيل Umbraco دون مشاكل، ولكن لن تتمكن من تثبيت الحزم الموصى بها للاستفادة الكاملة من Umbraco.]]>
+كيفية الحل
+انقر هنا لقراءة النسخة النصية
+الفيديو التعليمي الخاص بنا حول إعداد أذونات المجلدات لـ Umbraco أو اقرأ النسخة النصية.]]>
+إعدادات الأذونات الخاصة بك قد تكون مشكلة!
+
+يمكنك تشغيل Umbraco دون مشاكل، ولكن لن تتمكن من إنشاء مجلدات أو تثبيت الحزم الموصى بها للاستفادة الكاملة من Umbraco.]]>
+إعدادات الأذونات الخاصة بك غير جاهزة لـ Umbraco!
+
+من أجل تشغيل Umbraco، ستحتاج إلى تحديث إعدادات الأذونات الخاصة بك.]]>
+إعدادات الأذونات الخاصة بك مثالية!
+أنت جاهز لتشغيل Umbraco وتثبيت الحزم!]]>
+حل مشكلة المجلد
+اتبع هذا الرابط لمزيد من المعلومات حول مشاكل ASP.NET وإنشاء المجلدات
+إعداد أذونات المجلدات
+
+أريد البدء من الصفر
+تعلم كيف)
+لا يزال بإمكانك اختيار تثبيت Runway لاحقًا. يرجى الذهاب إلى قسم المطور واختيار الحزم.
+]]>
+لقد قمت للتو بإعداد منصة Umbraco نظيفة. ماذا تريد أن تفعل بعد ذلك؟
+تم تثبيت Runway
+
+هذه قائمتنا الموصى بها من الوحدات، حدد تلك التي ترغب في تثبيتها، أو عرض القائمة الكاملة للوحدات
+]]>
+موصى به فقط للمستخدمين ذوي الخبرة
+أريد البدء بموقع ويب بسيط
+
+"Runway" هو موقع ويب بسيط يوفر بعض أنواع المستندات الأساسية والقوالب. يمكن للمثبت إعداد Runway لك تلقائيًا،
+ولكن يمكنك بسهولة تحريره أو توسيعه أو إزالته. ليس ضروريًا ويمكنك استخدام Umbraco بدونه بشكل مثالي. ومع ذلك،
+يقدم Runway أساسًا سهلاً مبنيًا على أفضل الممارسات لبدء أسرع من أي وقت مضى.
+إذا اخترت تثبيت Runway، يمكنك اختياريًا تحديد لبنات البناء الأساسية المسماة وحدات Runway لتعزيز صفحات Runway الخاصة بك.
+
+
+المضمن مع Runway: الصفحة الرئيسية، صفحة البدء، صفحة تثبيت الوحدات.
+الوحدات الاختيارية: التنقل العلوي، خريطة الموقع، الاتصال، المعرض.
+
+]]>
+ما هو Runway
+الخطوة 1/5 قبول الترخيص
+الخطوة 2/5: تكوين قاعدة البيانات
+الخطوة 3/5: التحقق من أذونات الملفات
+الخطوة 4/5: التحقق من أمان Umbraco
+الخطوة 5/5: Umbraco جاهز لبدء العمل
+شكرًا لك على اختيار Umbraco
+تصفح موقعك الجديد
+لقد قمت بتثبيت Runway، فلماذا لا ترى كيف يبدو موقعك الجديد.]]>
+مزيد من المساعدة والمعلومات
+احصل على المساعدة من مجتمعنا الحائز على جوائز، أو تصفح الوثائق أو شاهد بعض مقاطع الفيديو المجانية حول كيفية إنشاء موقع بسيط، وكيفية استخدام الحزم ودليل سريع لمصطلحات Umbraco]]>
+تم تثبيت Umbraco %0% وهو جاهز للاستخدام
+/web.config يدويًا وتحديث مفتاح AppSetting UmbracoConfigurationStatus في الأسفل إلى قيمة '%0%'.]]>
+البدء فورًا بالنقر على زر "تشغيل Umbraco" أدناه. إذا كنت جديدًا على Umbraco،
+يمكنك العثور على الكثير من الموارد في صفحات البداية الخاصة بنا.]]>
+تشغيل Umbraco
+لإدارة موقعك، ما عليك سوى فتح واجهة Umbraco الخلفية والبدء في إضافة المحتوى أو تحديث القوالب أو أوراق الأنماط أو إضافة وظائف جديدة]]>
+فشل الاتصال بقاعدة البيانات.
+Umbraco الإصدار 3
+Umbraco الإصدار 4
+شاهد
+Umbraco %0% لتثبيت جديد أو الترقية من الإصدار 3.0.
+