Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
continue;
}

if (string.IsNullOrEmpty(dto.Name))
{
dto.Name = content.Name;
}

Check warning on line 108 in src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ Getting worse: Complex Method

ConvertIntermediateToObject increases in cyclomatic complexity from 14 to 15, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
url = content.Url(_publishedUrlProvider);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,29 @@
when,
} from '@umbraco-cms/backoffice/external/lit';
import { simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import {
UmbDocumentItemRepository,
UmbDocumentUrlRepository,
UmbDocumentUrlsDataResolver,
} from '@umbraco-cms/backoffice/document';
import { UmbMediaItemRepository, UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media';
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router';
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { UmbDocumentUrlRepository, UmbDocumentUrlsDataResolver } from '@umbraco-cms/backoffice/document';
import { UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media';

/**
* @element umb-input-multi-url
* @fires change - when the value of the input changes
* @fires blur - when the input loses focus
* @fires focus - when the input gains focus
*/
const elementName = 'umb-input-multi-url';
@customElement(elementName)
@customElement('umb-input-multi-url')
export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, '') {
#sorter = new UmbSorterController<UmbLinkPickerLink>(this, {
getUniqueOfElement: (element) => {
Expand Down Expand Up @@ -131,14 +134,21 @@
this.#urls = [...data]; // Unfreeze data coming from State, so we can manipulate it.
super.value = this.#urls.map((x) => x.url).join(',');
this.#sorter.setModel(this.#urls);
this.#populateLinksUrl();
this.#populateLinksNameAndUrl();
}
get urls(): Array<UmbLinkPickerLink> {
return this.#urls;
}

#urls: Array<UmbLinkPickerLink> = [];

#documentItemRepository = new UmbDocumentItemRepository(this);
#documentUrlRepository = new UmbDocumentUrlRepository(this);
#documentUrlsDataResolver = new UmbDocumentUrlsDataResolver(this);

#mediaItemRepository = new UmbMediaItemRepository(this);
#mediaUrlRepository = new UmbMediaUrlRepository(this);

/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
Expand All @@ -163,6 +173,9 @@
@state()
private _modalRoute?: UmbModalRouteBuilder;

@state()
_resolvedLinkNames: Array<{ unique: string; name: string }> = [];

@state()
_resolvedLinkUrls: Array<{ unique: string; url: string }> = [];

Expand Down Expand Up @@ -235,26 +248,42 @@
});
}

#populateLinksUrl() {
// Documents and media have URLs saved in the local link format. Display the actual URL to align with what
// the user sees when they selected it initially.
#populateLinksNameAndUrl() {
this._resolvedLinkNames = [];
this._resolvedLinkUrls = [];

// Documents and media have URLs saved in the local link format.
// Display the actual URL to align with what the user sees when they selected it initially.
this.#urls.forEach(async (link) => {
if (!link.unique) return;

let name: string | undefined = undefined;
let url: string | undefined = undefined;

switch (link.type) {
case 'document': {
if (!link.name || link.name.length === 0) {
name = await this.#getNameForDocument(link.unique);
}
url = await this.#getUrlForDocument(link.unique);
break;
}
case 'media': {
if (!link.name || link.name.length === 0) {
name = await this.#getNameForMedia(link.unique);
}
url = await this.#getUrlForMedia(link.unique);
break;
}
default:
break;
}

if (name) {
const resolvedName = { unique: link.unique, name };
this._resolvedLinkNames = [...this._resolvedLinkNames, resolvedName];
}

Check warning on line 286 in src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

UmbInputMultiUrlElement.populateLinksNameAndUrl has a cyclomatic complexity of 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
if (url) {
const resolvedUrl = { unique: link.unique, url };
this._resolvedLinkUrls = [...this._resolvedLinkUrls, resolvedUrl];
Expand All @@ -263,30 +292,38 @@
}

async #getUrlForDocument(unique: string) {
const documentUrlRepository = new UmbDocumentUrlRepository(this);
const { data: documentUrlData } = await documentUrlRepository.requestItems([unique]);
const urlsItem = documentUrlData?.[0];
const dataResolver = new UmbDocumentUrlsDataResolver(this);
dataResolver.setData(urlsItem?.urls);
const resolvedUrls = await dataResolver.getUrls();
const { data: data } = await this.#documentUrlRepository.requestItems([unique]);

this.#documentUrlsDataResolver.setData(data?.[0]?.urls);

const resolvedUrls = await this.#documentUrlsDataResolver.getUrls();
return resolvedUrls?.[0]?.url ?? '';
}

async #getUrlForMedia(unique: string) {
const mediaUrlRepository = new UmbMediaUrlRepository(this);
const { data: mediaUrlData } = await mediaUrlRepository.requestItems([unique]);
return mediaUrlData?.[0].url ?? '';
const { data } = await this.#mediaUrlRepository.requestItems([unique]);
return data?.[0].url ?? '';
}

async #getNameForDocument(unique: string) {
const { data } = await this.#documentItemRepository.requestItems([unique]);
return data?.[0]?.name ?? '';
}

async #requestRemoveItem(index: number) {
async #getNameForMedia(unique: string) {
const { data } = await this.#mediaItemRepository.requestItems([unique]);
return data?.[0]?.name ?? '';
}

async #requestRemoveItem(index: number, name?: string) {
const item = this.#urls[index];
if (!item) throw new Error('Could not find item at index: ' + index);

await umbConfirmModal(this, {
color: 'danger',
headline: `Remove ${item.name}?`,
content: 'Are you sure you want to remove this item',
confirmLabel: 'Remove',
headline: `Remove ${name || item.name || 'item'}?`,
content: 'Are you sure you want to remove this item?',
confirmLabel: '#general_remove',
});

this.#removeItem(index);
Expand Down Expand Up @@ -320,6 +357,17 @@
this.dispatchEvent(new UmbChangeEvent());
}

#getResolvedItemName(link: UmbLinkPickerLink): string {
return (link.name || this._resolvedLinkNames.find((name) => name.unique === link.unique)?.name) ?? '';
}

#getResolvedItemUrl(link: UmbLinkPickerLink): string {
return (
(this._resolvedLinkUrls.find((url) => url.unique === link.unique)?.url ?? link.url ?? '') +
(link.queryString || '')
);
}

override render() {
return html`${this.#renderItems()} ${this.#renderAddButton()}`;
}
Expand Down Expand Up @@ -356,13 +404,15 @@
#renderItem(link: UmbLinkPickerLink, index: number) {
const unique = this.#getUnique(link);
const href = this.readonly ? undefined : (this._modalRoute?.({ index }) ?? undefined);
const resolvedUrl = this._resolvedLinkUrls.find((url) => url.unique === link.unique)?.url ?? '';
const name = this.#getResolvedItemName(link);
const url = this.#getResolvedItemUrl(link);

return html`
<uui-ref-node
id=${unique}
href=${ifDefined(href)}
name=${link.name || ''}
detail=${resolvedUrl + (link.queryString || '')}
name=${name || url}
detail=${ifDefined(name ? url : undefined)}
?readonly=${this.readonly}>
<umb-icon slot="icon" name=${link.icon || 'icon-link'}></umb-icon>
${when(
Expand All @@ -371,7 +421,7 @@
<uui-action-bar slot="actions">
<uui-button
label=${this.localize.term('general_remove')}
@click=${() => this.#requestRemoveItem(index)}></uui-button>
@click=${() => this.#requestRemoveItem(index, name)}></uui-button>
</uui-action-bar>
`,
)}
Expand All @@ -390,6 +440,6 @@

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbInputMultiUrlElement;
'umb-input-multi-url': UmbInputMultiUrlElement;
}
}
Loading