Merge branch 'release/17.0' into v17/dev

This commit is contained in:
Jacob Overgaard
2025-10-22 09:50:45 +02:00
8 changed files with 48 additions and 25 deletions

View File

@@ -16,7 +16,6 @@ public class DocumentUrlFactory : IDocumentUrlFactory
private readonly UrlProviderCollection _urlProviders;
private readonly IPreviewService _previewService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly IAbsoluteUrlBuilder _absoluteUrlBuilder;
private readonly ILogger<DocumentUrlFactory> _logger;
public DocumentUrlFactory(
@@ -24,14 +23,12 @@ public class DocumentUrlFactory : IDocumentUrlFactory
UrlProviderCollection urlProviders,
IPreviewService previewService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IAbsoluteUrlBuilder absoluteUrlBuilder,
ILogger<DocumentUrlFactory> logger)
{
_publishedUrlInfoProvider = publishedUrlInfoProvider;
_urlProviders = urlProviders;
_previewService = previewService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_absoluteUrlBuilder = absoluteUrlBuilder;
_logger = logger;
}
@@ -39,7 +36,7 @@ public class DocumentUrlFactory : IDocumentUrlFactory
{
ISet<UrlInfo> urlInfos = await _publishedUrlInfoProvider.GetAllAsync(content);
return urlInfos
.Select(urlInfo => CreateDocumentUrlInfo(urlInfo, false))
.Select(CreateDocumentUrlInfo)
.ToArray();
}
@@ -89,18 +86,16 @@ public class DocumentUrlFactory : IDocumentUrlFactory
}
}
return CreateDocumentUrlInfo(previewUrlInfo, previewUrlInfo.IsExternal is false);
return CreateDocumentUrlInfo(previewUrlInfo);
}
private DocumentUrlInfo CreateDocumentUrlInfo(UrlInfo urlInfo, bool ensureAbsoluteUrl)
private DocumentUrlInfo CreateDocumentUrlInfo(UrlInfo urlInfo)
{
var url = urlInfo.Url?.ToString();
return new DocumentUrlInfo
{
Culture = urlInfo.Culture,
Url = ensureAbsoluteUrl && url is not null
? _absoluteUrlBuilder.ToAbsoluteUrl(url).ToString()
: url,
Url = url,
Message = urlInfo.Message,
Provider = urlInfo.Provider,
};

View File

@@ -144,7 +144,7 @@ public class NewDefaultUrlProvider : IUrlProvider
public Task<UrlInfo?> GetPreviewUrlAsync(IContent content, string? culture, string? segment)
=> Task.FromResult<UrlInfo?>(
UrlInfo.AsUrl(
$"/{Constants.System.UmbracoPathSegment}/preview?id={content.Key}&culture={culture}&segment={segment}",
$"preview?id={content.Key}&culture={culture}&segment={segment}",
Alias,
culture,
isExternal: false));
@@ -182,7 +182,7 @@ public class NewDefaultUrlProvider : IUrlProvider
// We have the published content now, so we can check if the culture is published, and thus avoid the DB hit.
string route;
var isDraft = _umbracoContextAccessor.GetRequiredUmbracoContext().InPreviewMode;
if(isDraft is false && string.IsNullOrWhiteSpace(culture) is false && content.Cultures.Any() && content.IsInvariantOrHasCulture(culture) is false)
if (isDraft is false && string.IsNullOrWhiteSpace(culture) is false && content.Cultures.Any() && content.IsInvariantOrHasCulture(culture) is false)
{
route = "#";
}

View File

@@ -10,9 +10,7 @@ import { UmbModalBaseElement, umbOpenModal } from '@umbraco-cms/backoffice/modal
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
const elementName = 'umb-restore-from-recycle-bin-modal';
@customElement(elementName)
@customElement('umb-restore-from-recycle-bin-modal')
export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
UmbRestoreFromRecycleBinModalData,
UmbRestoreFromRecycleBinModalValue
@@ -21,10 +19,13 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
private _isAutomaticRestore = false;
@state()
private _restoreItem?: any;
private _destinationItem?: any;
@state()
private _destinationItem?: any;
private _destinationItemName?: string;
@state()
private _restoreItemName?: string;
#recycleBinRepository?: UmbRecycleBinRepository;
@@ -34,7 +35,16 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
super.firstUpdated(_changedProperties);
if (!this.data?.unique) throw new Error('Cannot restore an item without a unique identifier.');
this._restoreItem = await this.#requestItem(this.data.unique);
const restoreItem = await this.#requestItem(this.data.unique);
if (this.data.itemDataResolver) {
const resolver = new this.data.itemDataResolver(this);
resolver.setData(restoreItem);
this._restoreItemName = await resolver.getName();
} else {
this._restoreItemName = restoreItem.name;
}
const unique = await this.#requestAutomaticRestoreDestination();
if (unique !== undefined) {
@@ -47,9 +57,7 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
// TODO: handle ROOT lookup. Currently, we can't look up the root in the item repository.
// This is a temp solution to show something in the UI.
if (unique === null) {
this._destinationItem = {
name: 'ROOT',
};
this._destinationItemName = 'Root';
this.#setDestinationValue({
unique: null,
@@ -61,6 +69,14 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
this._destinationItem = await this.#requestItem(unique);
if (!this._destinationItem) throw new Error('Cant find destination item.');
if (this.data?.itemDataResolver) {
const resolver = new this.data.itemDataResolver(this);
resolver.setData(this._destinationItem);
this._destinationItemName = await resolver.getName();
} else {
this._destinationItemName = this._destinationItem.name;
}
this.#setDestinationValue({
unique: this._destinationItem.unique,
entityType: this._destinationItem.entityType,
@@ -138,7 +154,7 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
<umb-body-layout headline="Restore">
<uui-box>
${this._isAutomaticRestore
? html` Restore ${this._restoreItem?.name} to ${this._destinationItem?.name}`
? html` Restore ${this._restoreItemName} to ${this._destinationItemName}`
: this.#renderCustomSelectDestination()}
</uui-box>
${this.#renderActions()}
@@ -151,8 +167,8 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
<h4>Cannot automatically restore this item.</h4>
<p>There is no location where this item can be automatically restored. You can select a new location below.</p>
<h5>Restore to:</h5>
${this._destinationItem
? html`<uui-ref-node name=${this._destinationItem.name}>
${this._destinationItem && this._destinationItemName
? html`<uui-ref-node name=${this._destinationItemName}>
<uui-action-bar slot="actions">
<uui-button @click=${() => (this._destinationItem = undefined)} label="Remove"
>${this.localize.term('general_remove')}</uui-button
@@ -186,6 +202,6 @@ export default UmbRestoreFromRecycleBinModalElement;
declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbRestoreFromRecycleBinModalElement;
['umb-restore-from-recycle-bin-modal']: UmbRestoreFromRecycleBinModalElement;
}
}

View File

@@ -1,4 +1,5 @@
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type { UmbItemDataResolverConstructor } from '@umbraco-cms/backoffice/entity-item';
import type { UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
@@ -7,6 +8,7 @@ export interface UmbRestoreFromRecycleBinModalData {
entityType: string;
recycleBinRepositoryAlias: string;
itemRepositoryAlias: string;
itemDataResolver?: UmbItemDataResolverConstructor;
pickerModal: UmbModalToken<UmbPickerModalData<any>, UmbPickerModalValue> | string;
}

View File

@@ -28,6 +28,7 @@ export class UmbRestoreFromRecycleBinEntityAction extends UmbEntityActionBase<Me
entityType: this.args.entityType,
recycleBinRepositoryAlias: this.args.meta.recycleBinRepositoryAlias,
itemRepositoryAlias: this.args.meta.itemRepositoryAlias,
itemDataResolver: this.args.meta.itemDataResolver,
pickerModal: this.args.meta.pickerModal,
},
});

View File

@@ -1,4 +1,5 @@
import type { ManifestEntityAction, MetaEntityActionDefaultKind } from '@umbraco-cms/backoffice/entity-action';
import type { UmbItemDataResolverConstructor } from '@umbraco-cms/backoffice/entity-item';
import type { UmbModalToken, UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal';
export interface ManifestEntityActionRestoreFromRecycleBinKind
@@ -10,6 +11,7 @@ export interface ManifestEntityActionRestoreFromRecycleBinKind
export interface MetaEntityActionRestoreFromRecycleBinKind extends MetaEntityActionDefaultKind {
recycleBinRepositoryAlias: string;
itemRepositoryAlias: string;
itemDataResolver?: UmbItemDataResolverConstructor;
pickerModal: UmbModalToken<UmbPickerModalData<any>, UmbPickerModalValue> | string;
}

View File

@@ -46,6 +46,7 @@ export const manifests: Array<UmbExtensionManifest> = [
forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],
meta: {
itemRepositoryAlias: UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS,
itemDataResolver: UmbDocumentItemDataResolver,
recycleBinRepositoryAlias: UMB_DOCUMENT_RECYCLE_BIN_REPOSITORY_ALIAS,
pickerModal: UMB_DOCUMENT_PICKER_MODAL,
},

View File

@@ -40,6 +40,7 @@ import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/documen
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import type { UmbVariantPropertyGuardRule } from '@umbraco-cms/backoffice/property';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
type ContentModel = UmbDocumentDetailModel;
type ContentTypeModel = UmbDocumentTypeDetailModel;
@@ -70,6 +71,7 @@ export class UmbDocumentWorkspaceContext
#isTrashedContext = new UmbIsTrashedEntityContext(this);
#documentSegmentRepository = new UmbDocumentSegmentRepository(this);
#actionEventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;
#localize = new UmbLocalizationController(this);
constructor(host: UmbControllerHost) {
super(host, {
@@ -350,7 +352,11 @@ export class UmbDocumentWorkspaceContext
}
if (previewUrlData.message) {
umbPeekError(this._host, { color: 'danger', headline: 'Preview error', message: previewUrlData.message });
umbPeekError(this._host, {
color: 'danger',
headline: this.#localize.term('general_preview'),
message: previewUrlData.message,
});
throw new Error(previewUrlData.message);
}
}