mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Legacy url conflict UI improvements (#114172)
This commit is contained in:
parent
0861a5d6a0
commit
080b2199f4
42 changed files with 364 additions and 372 deletions
|
@ -213,6 +213,10 @@ readonly links: {
|
|||
mappingRolesFieldRules: string;
|
||||
runAsPrivilege: string;
|
||||
}>;
|
||||
readonly spaces: Readonly<{
|
||||
kibanaLegacyUrlAliases: string;
|
||||
kibanaDisableLegacyUrlAliasesApi: string;
|
||||
}>;
|
||||
readonly watcher: Record<string, string>;
|
||||
readonly ccs: Record<string, string>;
|
||||
readonly plugins: Record<string, string>;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -356,6 +356,10 @@ export class DocLinksService {
|
|||
mappingRolesFieldRules: `${ELASTICSEARCH_DOCS}role-mapping-resources.html#mapping-roles-rule-field`,
|
||||
runAsPrivilege: `${ELASTICSEARCH_DOCS}security-privileges.html#_run_as_privilege`,
|
||||
},
|
||||
spaces: {
|
||||
kibanaLegacyUrlAliases: `${KIBANA_DOCS}legacy-url-aliases.html`,
|
||||
kibanaDisableLegacyUrlAliasesApi: `${KIBANA_DOCS}spaces-api-disable-legacy-url-aliases.html`,
|
||||
},
|
||||
watcher: {
|
||||
jiraAction: `${ELASTICSEARCH_DOCS}actions-jira.html`,
|
||||
pagerDutyAction: `${ELASTICSEARCH_DOCS}actions-pagerduty.html`,
|
||||
|
@ -702,6 +706,10 @@ export interface DocLinksStart {
|
|||
mappingRolesFieldRules: string;
|
||||
runAsPrivilege: string;
|
||||
}>;
|
||||
readonly spaces: Readonly<{
|
||||
kibanaLegacyUrlAliases: string;
|
||||
kibanaDisableLegacyUrlAliasesApi: string;
|
||||
}>;
|
||||
readonly watcher: Record<string, string>;
|
||||
readonly ccs: Record<string, string>;
|
||||
readonly plugins: Record<string, string>;
|
||||
|
|
|
@ -682,6 +682,10 @@ export interface DocLinksStart {
|
|||
mappingRolesFieldRules: string;
|
||||
runAsPrivilege: string;
|
||||
}>;
|
||||
readonly spaces: Readonly<{
|
||||
kibanaLegacyUrlAliases: string;
|
||||
kibanaDisableLegacyUrlAliasesApi: string;
|
||||
}>;
|
||||
readonly watcher: Record<string, string>;
|
||||
readonly ccs: Record<string, string>;
|
||||
readonly plugins: Record<string, string>;
|
||||
|
|
|
@ -236,7 +236,7 @@ describe('embeddable', () => {
|
|||
...savedVis,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'conflict',
|
||||
errorJSON: '{targetType: "lens", sourceId: "1", targetSpace: "space"}',
|
||||
sourceId: '1',
|
||||
aliasTargetId: '2',
|
||||
},
|
||||
} as ResolvedLensSavedObjectAttributes);
|
||||
|
|
|
@ -286,8 +286,9 @@ export class Embeddable
|
|||
defaultMessage: `You've encountered a URL conflict`,
|
||||
}),
|
||||
longMessage: (
|
||||
<this.deps.spaces.ui.components.getSavedObjectConflictMessage
|
||||
json={sharingSavedObjectProps.errorJSON!}
|
||||
<this.deps.spaces.ui.components.getEmbeddableLegacyUrlConflict
|
||||
targetType={DOC_TYPE}
|
||||
sourceId={sharingSavedObjectProps.sourceId!}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
|
|
@ -48,7 +48,7 @@ export function getLensAttributeService(
|
|||
outcome,
|
||||
alias_target_id: aliasTargetId,
|
||||
} = await savedObjectStore.load(savedObjectId);
|
||||
const { attributes, references, type, id } = savedObject;
|
||||
const { attributes, references, id } = savedObject;
|
||||
const document = {
|
||||
...attributes,
|
||||
references,
|
||||
|
@ -57,14 +57,7 @@ export function getLensAttributeService(
|
|||
const sharingSavedObjectProps = {
|
||||
aliasTargetId,
|
||||
outcome,
|
||||
errorJSON:
|
||||
outcome === 'conflict'
|
||||
? JSON.stringify({
|
||||
targetType: type,
|
||||
sourceId: id,
|
||||
targetSpace: (await startDependencies.spaces.getActiveSpace()).id,
|
||||
})
|
||||
: undefined,
|
||||
sourceId: id,
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -27,7 +27,7 @@ export const getPersisted = async ({
|
|||
lensServices: LensAppServices;
|
||||
history?: History<unknown>;
|
||||
}): Promise<
|
||||
{ doc: Document; sharingSavedObjectProps: Omit<SharingSavedObjectProps, 'errorJSON'> } | undefined
|
||||
{ doc: Document; sharingSavedObjectProps: Omit<SharingSavedObjectProps, 'sourceId'> } | undefined
|
||||
> => {
|
||||
const { notifications, spaces, attributeService } = lensServices;
|
||||
let doc: Document;
|
||||
|
|
|
@ -43,7 +43,7 @@ export interface LensAppState extends EditorFrameState {
|
|||
savedQuery?: SavedQuery;
|
||||
searchSessionId: string;
|
||||
resolvedDateRange: DateRange;
|
||||
sharingSavedObjectProps?: Omit<SharingSavedObjectProps, 'errorJSON'>;
|
||||
sharingSavedObjectProps?: Omit<SharingSavedObjectProps, 'sourceId'>;
|
||||
}
|
||||
|
||||
export type DispatchSetState = (state: Partial<LensAppState>) => {
|
||||
|
|
|
@ -834,5 +834,5 @@ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandle
|
|||
export interface SharingSavedObjectProps {
|
||||
outcome?: 'aliasMatch' | 'exactMatch' | 'conflict';
|
||||
aliasTargetId?: string;
|
||||
errorJSON?: string;
|
||||
sourceId?: string;
|
||||
}
|
||||
|
|
|
@ -364,8 +364,9 @@ export class MapEmbeddable
|
|||
iconType="alert"
|
||||
iconColor="danger"
|
||||
data-test-subj="embeddable-maps-failure"
|
||||
body={spaces.ui.components.getSavedObjectConflictMessage({
|
||||
json: sharingSavedObjectProps.errorJSON!,
|
||||
body={spaces.ui.components.getEmbeddableLegacyUrlConflict({
|
||||
targetType: MAP_SAVED_OBJECT_TYPE,
|
||||
sourceId: sharingSavedObjectProps.sourceId!,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -14,12 +14,11 @@ import { checkForDuplicateTitle, OnSaveProps } from '../../../../src/plugins/sav
|
|||
import { getCoreOverlays, getEmbeddableService, getSavedObjectsClient } from './kibana_services';
|
||||
import { extractReferences, injectReferences } from '../common/migrations/references';
|
||||
import { MapByValueInput, MapByReferenceInput } from './embeddable/types';
|
||||
import { getSpacesApi } from './kibana_services';
|
||||
|
||||
export interface SharingSavedObjectProps {
|
||||
outcome?: 'aliasMatch' | 'exactMatch' | 'conflict';
|
||||
aliasTargetId?: string;
|
||||
errorJSON?: string;
|
||||
sourceId?: string;
|
||||
}
|
||||
|
||||
type MapDoc = MapSavedObjectAttributes & {
|
||||
|
@ -88,14 +87,7 @@ export function getMapAttributeService(): MapAttributeService {
|
|||
sharingSavedObjectProps: {
|
||||
aliasTargetId,
|
||||
outcome,
|
||||
errorJSON:
|
||||
outcome === 'conflict' && getSpacesApi()
|
||||
? JSON.stringify({
|
||||
targetType: MAP_SAVED_OBJECT_TYPE,
|
||||
sourceId: savedObjectId,
|
||||
targetSpace: (await getSpacesApi()!.getActiveSpace()).id,
|
||||
})
|
||||
: undefined,
|
||||
sourceId: savedObjectId,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -19,3 +19,7 @@ export const getSpacesFeatureDescription = () => {
|
|||
|
||||
return spacesFeatureDescription;
|
||||
};
|
||||
|
||||
export const DEFAULT_OBJECT_NOUN = i18n.translate('xpack.spaces.shareToSpace.objectNoun', {
|
||||
defaultMessage: 'object',
|
||||
});
|
||||
|
|
|
@ -20,8 +20,9 @@ export type {
|
|||
CopyToSpaceSavedObjectTarget,
|
||||
} from './copy_saved_objects_to_space';
|
||||
|
||||
export type { LegacyUrlConflictProps, EmbeddableLegacyUrlConflictProps } from './legacy_urls';
|
||||
|
||||
export type {
|
||||
LegacyUrlConflictProps,
|
||||
ShareToSpaceFlyoutProps,
|
||||
ShareToSpaceSavedObjectTarget,
|
||||
} from './share_saved_objects_to_space';
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { EmbeddableLegacyUrlConflictProps } from '../types';
|
||||
import type { InternalProps } from './embeddable_legacy_url_conflict_internal';
|
||||
|
||||
export const getEmbeddableLegacyUrlConflict = async (
|
||||
internalProps: InternalProps
|
||||
): Promise<React.FC<EmbeddableLegacyUrlConflictProps>> => {
|
||||
const { EmbeddableLegacyUrlConflictInternal } = await import(
|
||||
'./embeddable_legacy_url_conflict_internal'
|
||||
);
|
||||
return (props: EmbeddableLegacyUrlConflictProps) => {
|
||||
return <EmbeddableLegacyUrlConflictInternal {...{ ...internalProps, ...props }} />;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiCodeBlock,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiTextAlign,
|
||||
} from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import type { StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import type { PluginsStart } from '../../plugin';
|
||||
import type { SpacesManager } from '../../spaces_manager';
|
||||
import type { EmbeddableLegacyUrlConflictProps } from '../types';
|
||||
|
||||
export interface InternalProps {
|
||||
spacesManager: SpacesManager;
|
||||
getStartServices: StartServicesAccessor<PluginsStart>;
|
||||
}
|
||||
|
||||
export const EmbeddableLegacyUrlConflictInternal = (
|
||||
props: InternalProps & EmbeddableLegacyUrlConflictProps
|
||||
) => {
|
||||
const { spacesManager, getStartServices, targetType, sourceId } = props;
|
||||
|
||||
const [expandError, setExpandError] = useState(false);
|
||||
|
||||
const { value: asyncParams } = useAsync(async () => {
|
||||
const [{ docLinks }] = await getStartServices();
|
||||
const { id: targetSpace } = await spacesManager.getActiveSpace();
|
||||
const docLink = docLinks.links.spaces.kibanaDisableLegacyUrlAliasesApi;
|
||||
const aliasJsonString = JSON.stringify({ targetSpace, targetType, sourceId }, null, 2);
|
||||
return { docLink, aliasJsonString };
|
||||
}, [getStartServices, spacesManager]);
|
||||
const { docLink, aliasJsonString } = asyncParams ?? {};
|
||||
|
||||
if (!aliasJsonString || !docLink) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.embeddableLegacyUrlConflict.messageText"
|
||||
defaultMessage="We found 2 saved objects for this panel. Disable the legacy URL alias to fix this error."
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{expandError ? (
|
||||
<EuiTextAlign textAlign="left">
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.embeddableLegacyUrlConflict.calloutTitle"
|
||||
defaultMessage="Copy this JSON and use it with the {documentationLink}"
|
||||
values={{
|
||||
documentationLink: (
|
||||
<EuiLink external href={docLink} target="_blank">
|
||||
{'_disable_legacy_url_aliases API'}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
>
|
||||
<EuiCodeBlock fontSize="s" language="json" isCopyable={true} paddingSize="none">
|
||||
{aliasJsonString}
|
||||
</EuiCodeBlock>
|
||||
</EuiCallOut>
|
||||
</EuiTextAlign>
|
||||
) : (
|
||||
<EuiButtonEmpty onClick={() => setExpandError(true)}>
|
||||
{i18n.translate('xpack.spaces.embeddableLegacyUrlConflict.detailsButton', {
|
||||
defaultMessage: `View details`,
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,4 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { DocumentationLinksService } from './documentation_links';
|
||||
export { getEmbeddableLegacyUrlConflict } from './embeddable_legacy_url_conflict';
|
||||
export { getLegacyUrlConflict } from './legacy_url_conflict';
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import type { StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import { DEFAULT_OBJECT_NOUN } from '../../constants';
|
||||
import type { PluginsStart } from '../../plugin';
|
||||
import type { LegacyUrlConflictProps } from '../types';
|
||||
|
||||
export interface InternalProps {
|
||||
getStartServices: StartServicesAccessor<PluginsStart>;
|
||||
}
|
||||
|
||||
export const LegacyUrlConflictInternal = (props: InternalProps & LegacyUrlConflictProps) => {
|
||||
const {
|
||||
getStartServices,
|
||||
objectNoun = DEFAULT_OBJECT_NOUN,
|
||||
currentObjectId,
|
||||
otherObjectId,
|
||||
otherObjectPath,
|
||||
} = props;
|
||||
|
||||
const [isDismissed, setIsDismissed] = useState(false);
|
||||
|
||||
const { value: asyncParams } = useAsync(async () => {
|
||||
const [{ application: applicationStart, docLinks }] = await getStartServices();
|
||||
const appId = await applicationStart.currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject
|
||||
const docLink = docLinks.links.spaces.kibanaLegacyUrlAliases;
|
||||
return { applicationStart, appId, docLink };
|
||||
}, [getStartServices]);
|
||||
const { docLink, applicationStart, appId } = asyncParams ?? {};
|
||||
|
||||
if (!applicationStart || !appId || !docLink || isDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function clickLinkButton() {
|
||||
applicationStart!.navigateToApp(appId!, { path: otherObjectPath });
|
||||
}
|
||||
|
||||
function clickDismissButton() {
|
||||
setIsDismissed(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyUrlConflict.calloutTitle"
|
||||
defaultMessage="2 saved objects use this URL"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyUrlConflict.calloutBodyText"
|
||||
defaultMessage="Check that this is the {objectNoun} that you are looking for. Otherwise, go to the other one. {documentationLink}"
|
||||
values={{
|
||||
objectNoun,
|
||||
documentationLink: (
|
||||
<EuiLink external href={docLink} target="_blank">
|
||||
{i18n.translate('xpack.spaces.legacyUrlConflict.documentationLinkText', {
|
||||
defaultMessage: 'Learn more',
|
||||
})}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
delay="long"
|
||||
content={i18n.translate('xpack.spaces.legacyURLConflict.toolTipText', {
|
||||
defaultMessage:
|
||||
'This {objectNoun} has [id={currentObjectId}]. The other {objectNoun} has [id={otherObjectId}].',
|
||||
values: { objectNoun, currentObjectId, otherObjectId },
|
||||
})}
|
||||
>
|
||||
<EuiButton
|
||||
color="warning"
|
||||
size="s"
|
||||
onClick={clickLinkButton}
|
||||
data-test-subj="legacy-url-conflict-go-to-other-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyUrlConflict.linkButton"
|
||||
defaultMessage="Go to other {objectNoun}"
|
||||
values={{ objectNoun }}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
color="warning"
|
||||
size="s"
|
||||
onClick={clickDismissButton}
|
||||
data-test-subj="legacy-url-conflict-dismiss-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyUrlConflict.dismissButton"
|
||||
defaultMessage="Dismiss"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -5,4 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { getEmbeddableLegacyUrlConflict, getLegacyUrlConflict } from './components';
|
||||
export { createRedirectLegacyUrl } from './redirect_legacy_url';
|
||||
|
||||
export type { EmbeddableLegacyUrlConflictProps, LegacyUrlConflictProps } from './types';
|
|
@ -10,9 +10,9 @@ import { first } from 'rxjs/operators';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import type { PluginsStart } from '../../plugin';
|
||||
import type { SpacesApiUi } from '../../ui_api';
|
||||
import { DEFAULT_OBJECT_NOUN } from '../components/constants';
|
||||
import { DEFAULT_OBJECT_NOUN } from '../constants';
|
||||
import type { PluginsStart } from '../plugin';
|
||||
import type { SpacesApiUi } from '../ui_api';
|
||||
|
||||
export function createRedirectLegacyUrl(
|
||||
getStartServices: StartServicesAccessor<PluginsStart>
|
||||
|
@ -22,10 +22,10 @@ export function createRedirectLegacyUrl(
|
|||
const { currentAppId$, navigateToApp } = application;
|
||||
const appId = await currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject
|
||||
|
||||
const title = i18n.translate('xpack.spaces.shareToSpace.redirectLegacyUrlToast.title', {
|
||||
const title = i18n.translate('xpack.spaces.redirectLegacyUrlToast.title', {
|
||||
defaultMessage: `We redirected you to a new URL`,
|
||||
});
|
||||
const text = i18n.translate('xpack.spaces.shareToSpace.redirectLegacyUrlToast.text', {
|
||||
const text = i18n.translate('xpack.spaces.redirectLegacyUrlToast.text', {
|
||||
defaultMessage: `The {objectNoun} you're looking for has a new location. Use this URL from now on.`,
|
||||
values: { objectNoun },
|
||||
});
|
46
x-pack/plugins/spaces/public/legacy_urls/types.ts
Normal file
46
x-pack/plugins/spaces/public/legacy_urls/types.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties for the LegacyUrlConflict component.
|
||||
*/
|
||||
export interface LegacyUrlConflictProps {
|
||||
/**
|
||||
* The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different
|
||||
* **object**_.
|
||||
*
|
||||
* Default value is 'object'.
|
||||
*/
|
||||
objectNoun?: string;
|
||||
/**
|
||||
* The ID of the object that is currently shown on the page.
|
||||
*/
|
||||
currentObjectId: string;
|
||||
/**
|
||||
* The ID of the other object that the legacy URL alias points to.
|
||||
*/
|
||||
otherObjectId: string;
|
||||
/**
|
||||
* The path within your application to use for the new URL, optionally including `search` and/or `hash` URL components. Do not include
|
||||
* `/app/my-app` or the current base path.
|
||||
*/
|
||||
otherObjectPath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the EmbeddableLegacyUrlConflict component.
|
||||
*/
|
||||
export interface EmbeddableLegacyUrlConflictProps {
|
||||
/**
|
||||
* The target type of the legacy URL alias.
|
||||
*/
|
||||
targetType: string;
|
||||
/**
|
||||
* The source ID of the legacy URL alias.
|
||||
*/
|
||||
sourceId: string;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { docLinksServiceMock } from 'src/core/public/mocks';
|
||||
|
||||
import { DocumentationLinksService } from './documentation_links';
|
||||
|
||||
describe('DocumentationLinksService', () => {
|
||||
const setup = () => {
|
||||
const docLinks = docLinksServiceMock.createStartContract();
|
||||
const service = new DocumentationLinksService(docLinks);
|
||||
return { docLinks, service };
|
||||
};
|
||||
|
||||
describe('#getKibanaPrivilegesDocUrl', () => {
|
||||
it('returns expected value', () => {
|
||||
const { service } = setup();
|
||||
expect(service.getKibanaPrivilegesDocUrl()).toMatchInlineSnapshot(
|
||||
`"https://www.elastic.co/guide/en/kibana/mocked-test-branch/kibana-privileges.html"`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { DocLinksStart } from 'src/core/public';
|
||||
|
||||
export class DocumentationLinksService {
|
||||
private readonly kbnPrivileges: string;
|
||||
|
||||
constructor(docLinks: DocLinksStart) {
|
||||
this.kbnPrivileges = `${docLinks.links.security.kibanaPrivileges}`;
|
||||
}
|
||||
|
||||
public getKibanaPrivilegesDocUrl() {
|
||||
return `${this.kbnPrivileges}`;
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ const createApiUiComponentsMock = () => {
|
|||
getSpaceList: jest.fn(),
|
||||
getLegacyUrlConflict: jest.fn(),
|
||||
getSpaceAvatar: jest.fn(),
|
||||
getSavedObjectConflictMessage: jest.fn(),
|
||||
getEmbeddableLegacyUrlConflict: jest.fn(),
|
||||
};
|
||||
|
||||
return mock;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const DEFAULT_OBJECT_NOUN = i18n.translate('xpack.spaces.shareToSpace.objectNoun', {
|
||||
defaultMessage: 'object',
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { SavedObjectConflictMessageProps } from '../types';
|
||||
|
||||
export const getSavedObjectConflictMessage = async (): Promise<
|
||||
React.FC<SavedObjectConflictMessageProps>
|
||||
> => {
|
||||
const { SavedObjectConflictMessage } = await import('./saved_object_conflict_message');
|
||||
return (props: SavedObjectConflictMessageProps) => {
|
||||
return <SavedObjectConflictMessage {...props} />;
|
||||
};
|
||||
};
|
|
@ -6,5 +6,3 @@
|
|||
*/
|
||||
|
||||
export { getShareToSpaceFlyoutComponent } from './share_to_space_flyout';
|
||||
export { getSavedObjectConflictMessage } from './get_saved_object_conflict_message';
|
||||
export { getLegacyUrlConflict } from './legacy_url_conflict';
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import type { ApplicationStart, StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import type { PluginsStart } from '../../plugin';
|
||||
import type { LegacyUrlConflictProps } from '../types';
|
||||
import { DEFAULT_OBJECT_NOUN } from './constants';
|
||||
|
||||
export interface InternalProps {
|
||||
getStartServices: StartServicesAccessor<PluginsStart>;
|
||||
}
|
||||
|
||||
export const LegacyUrlConflictInternal = (props: InternalProps & LegacyUrlConflictProps) => {
|
||||
const {
|
||||
getStartServices,
|
||||
objectNoun = DEFAULT_OBJECT_NOUN,
|
||||
currentObjectId,
|
||||
otherObjectId,
|
||||
otherObjectPath,
|
||||
} = props;
|
||||
|
||||
const [applicationStart, setApplicationStart] = useState<ApplicationStart>();
|
||||
const [isDismissed, setIsDismissed] = useState(false);
|
||||
const [appId, setAppId] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
async function setup() {
|
||||
const [{ application }] = await getStartServices();
|
||||
const appIdValue = await application.currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject
|
||||
setApplicationStart(application);
|
||||
setAppId(appIdValue);
|
||||
}
|
||||
setup();
|
||||
}, [getStartServices]);
|
||||
|
||||
if (!applicationStart || !appId || isDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function clickLinkButton() {
|
||||
applicationStart!.navigateToApp(appId!, { path: otherObjectPath });
|
||||
}
|
||||
|
||||
function clickDismissButton() {
|
||||
setIsDismissed(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.legacyUrlConflictTitle"
|
||||
defaultMessage="2 objects are associated with this URL"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.legacyUrlConflictBody"
|
||||
defaultMessage="You're currently looking at {objectNoun} [id={currentObjectId}]. A legacy URL for this page shows a different {objectNoun} [id={otherObjectId}]."
|
||||
values={{ objectNoun, currentObjectId, otherObjectId }}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="warning"
|
||||
size="s"
|
||||
onClick={clickLinkButton}
|
||||
data-test-subj="legacy-url-conflict-go-to-other-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.legacyUrlConflictLinkButton"
|
||||
defaultMessage="Go to other {objectNoun}"
|
||||
values={{ objectNoun }}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
color="warning"
|
||||
size="s"
|
||||
onClick={clickDismissButton}
|
||||
data-test-subj="legacy-url-conflict-dismiss-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.legacyUrlConflictDismissButton"
|
||||
defaultMessage="Dismiss"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiButtonEmpty, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import type { SavedObjectConflictMessageProps } from '../types';
|
||||
|
||||
export const SavedObjectConflictMessage = ({ json }: SavedObjectConflictMessageProps) => {
|
||||
const [expandError, setExpandError] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.legacyURLConflict.longMessage"
|
||||
defaultMessage="Disable the {documentationLink} associated with this object."
|
||||
values={{
|
||||
documentationLink: (
|
||||
<EuiLink
|
||||
external
|
||||
href="https://www.elastic.co/guide/en/kibana/master/legacy-url-aliases.html"
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate('xpack.spaces.legacyURLConflict.documentationLinkText', {
|
||||
defaultMessage: 'legacy URL alias',
|
||||
})}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{expandError ? (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.spaces.legacyURLConflict.expandErrorText', {
|
||||
defaultMessage: `This object has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`,
|
||||
values: { json },
|
||||
})}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
/>
|
||||
) : (
|
||||
<EuiButtonEmpty onClick={() => setExpandError(true)}>
|
||||
{i18n.translate('xpack.spaces.legacyURLConflict.expandError', {
|
||||
defaultMessage: `Show more`,
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -26,7 +26,6 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
|
||||
import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common';
|
||||
import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants';
|
||||
import { DocumentationLinksService } from '../../lib';
|
||||
import { getSpaceAvatarComponent } from '../../space_avatar';
|
||||
import { useSpaces } from '../../spaces_context';
|
||||
import type { SpacesDataEntry } from '../../types';
|
||||
|
@ -135,9 +134,7 @@ export const SelectableSpacesControl = (props: Props) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const kibanaPrivilegesUrl = new DocumentationLinksService(
|
||||
docLinks!
|
||||
).getKibanaPrivilegesDocUrl();
|
||||
const docLink = docLinks?.links.security.kibanaPrivileges;
|
||||
return (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
|
@ -146,7 +143,7 @@ export const SelectableSpacesControl = (props: Props) => {
|
|||
defaultMessage="To view hidden spaces, you need {additionalPrivilegesLink}."
|
||||
values={{
|
||||
additionalPrivilegesLink: (
|
||||
<EuiLink href={kibanaPrivilegesUrl} target="_blank">
|
||||
<EuiLink href={docLink} target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.unknownSpacesLabel.additionalPrivilegesLink"
|
||||
defaultMessage="additional privileges"
|
||||
|
|
|
@ -22,7 +22,6 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { ALL_SPACES_ID } from '../../../common/constants';
|
||||
import { DocumentationLinksService } from '../../lib';
|
||||
import { useSpaces } from '../../spaces_context';
|
||||
import type { SpacesDataEntry } from '../../types';
|
||||
import type { ShareOptions } from '../types';
|
||||
|
@ -85,10 +84,7 @@ export const ShareModeControl = (props: Props) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const kibanaPrivilegesUrl = new DocumentationLinksService(
|
||||
docLinks!
|
||||
).getKibanaPrivilegesDocUrl();
|
||||
|
||||
const docLink = docLinks?.links.security.kibanaPrivileges;
|
||||
return (
|
||||
<>
|
||||
<EuiCallOut
|
||||
|
@ -108,7 +104,7 @@ export const ShareModeControl = (props: Props) => {
|
|||
values={{
|
||||
objectNoun,
|
||||
readAndWritePrivilegesLink: (
|
||||
<EuiLink href={kibanaPrivilegesUrl} target="_blank">
|
||||
<EuiLink href={docLink} target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.shareToSpace.privilegeWarningLink"
|
||||
defaultMessage="read and write privileges"
|
||||
|
|
|
@ -28,6 +28,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
import type { SavedObjectReferenceWithContext, ToastsStart } from 'src/core/public';
|
||||
|
||||
import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants';
|
||||
import { DEFAULT_OBJECT_NOUN } from '../../constants';
|
||||
import { getCopyToSpaceFlyoutComponent } from '../../copy_saved_objects_to_space';
|
||||
import { useSpaces } from '../../spaces_context';
|
||||
import type { SpacesManager } from '../../spaces_manager';
|
||||
|
@ -38,7 +39,6 @@ import type {
|
|||
ShareToSpaceSavedObjectTarget,
|
||||
} from '../types';
|
||||
import { AliasTable } from './alias_table';
|
||||
import { DEFAULT_OBJECT_NOUN } from './constants';
|
||||
import { RelativesFooter } from './relatives_footer';
|
||||
import { ShareToSpaceForm } from './share_to_space_form';
|
||||
import type { InternalLegacyUrlAliasTarget } from './types';
|
||||
|
|
|
@ -5,15 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export {
|
||||
getShareToSpaceFlyoutComponent,
|
||||
getLegacyUrlConflict,
|
||||
getSavedObjectConflictMessage,
|
||||
} from './components';
|
||||
export { createRedirectLegacyUrl } from './utils';
|
||||
export type {
|
||||
LegacyUrlConflictProps,
|
||||
ShareToSpaceFlyoutProps,
|
||||
ShareToSpaceSavedObjectTarget,
|
||||
SavedObjectConflictMessageProps,
|
||||
} from './types';
|
||||
export { getShareToSpaceFlyoutComponent } from './components';
|
||||
export type { ShareToSpaceFlyoutProps, ShareToSpaceSavedObjectTarget } from './types';
|
||||
|
|
|
@ -18,31 +18,6 @@ export interface ShareSavedObjectsToSpaceResponse {
|
|||
[spaceId: string]: SavedObjectsImportResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the LegacyUrlConflict component.
|
||||
*/
|
||||
export interface LegacyUrlConflictProps {
|
||||
/**
|
||||
* The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different
|
||||
* **object**_.
|
||||
*
|
||||
* Default value is 'object'.
|
||||
*/
|
||||
objectNoun?: string;
|
||||
/**
|
||||
* The ID of the object that is currently shown on the page.
|
||||
*/
|
||||
currentObjectId: string;
|
||||
/**
|
||||
* The ID of the other object that the legacy URL alias points to.
|
||||
*/
|
||||
otherObjectId: string;
|
||||
/**
|
||||
* The path to use for the new URL, optionally including `search` and/or `hash` URL components.
|
||||
*/
|
||||
otherObjectPath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the ShareToSpaceFlyout.
|
||||
*/
|
||||
|
@ -140,10 +115,3 @@ export interface ShareToSpaceSavedObjectTarget {
|
|||
*/
|
||||
noun?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the SavedObjectConflictMessage component.
|
||||
*/
|
||||
export interface SavedObjectConflictMessageProps {
|
||||
json: string;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,9 @@ import React from 'react';
|
|||
import type { StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import { getCopyToSpaceFlyoutComponent } from '../copy_saved_objects_to_space';
|
||||
import { getEmbeddableLegacyUrlConflict, getLegacyUrlConflict } from '../legacy_urls';
|
||||
import type { PluginsStart } from '../plugin';
|
||||
import {
|
||||
getLegacyUrlConflict,
|
||||
getSavedObjectConflictMessage,
|
||||
getShareToSpaceFlyoutComponent,
|
||||
} from '../share_saved_objects_to_space';
|
||||
import { getShareToSpaceFlyoutComponent } from '../share_saved_objects_to_space';
|
||||
import { getSpaceAvatarComponent } from '../space_avatar';
|
||||
import { getSpaceListComponent } from '../space_list';
|
||||
import { getSpacesContextProviderWrapper } from '../spaces_context';
|
||||
|
@ -55,8 +52,10 @@ export const getComponents = ({
|
|||
getShareToSpaceFlyout: wrapLazy(getShareToSpaceFlyoutComponent, { showLoadingSpinner: false }),
|
||||
getCopyToSpaceFlyout: wrapLazy(getCopyToSpaceFlyoutComponent, { showLoadingSpinner: false }),
|
||||
getSpaceList: wrapLazy(getSpaceListComponent),
|
||||
getEmbeddableLegacyUrlConflict: wrapLazy(() =>
|
||||
getEmbeddableLegacyUrlConflict({ spacesManager, getStartServices })
|
||||
),
|
||||
getLegacyUrlConflict: wrapLazy(() => getLegacyUrlConflict({ getStartServices })),
|
||||
getSpaceAvatar: wrapLazy(getSpaceAvatarComponent),
|
||||
getSavedObjectConflictMessage: wrapLazy(() => getSavedObjectConflictMessage()),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import type { StartServicesAccessor } from 'src/core/public';
|
||||
|
||||
import { createRedirectLegacyUrl } from '../legacy_urls';
|
||||
import type { PluginsStart } from '../plugin';
|
||||
import { createRedirectLegacyUrl } from '../share_saved_objects_to_space';
|
||||
import { useSpaces } from '../spaces_context';
|
||||
import type { SpacesManager } from '../spaces_manager';
|
||||
import { getComponents } from './components';
|
||||
|
|
|
@ -10,11 +10,8 @@ import type { ReactElement } from 'react';
|
|||
import type { CoreStart } from 'src/core/public';
|
||||
|
||||
import type { CopyToSpaceFlyoutProps } from '../copy_saved_objects_to_space';
|
||||
import type {
|
||||
LegacyUrlConflictProps,
|
||||
SavedObjectConflictMessageProps,
|
||||
ShareToSpaceFlyoutProps,
|
||||
} from '../share_saved_objects_to_space';
|
||||
import type { EmbeddableLegacyUrlConflictProps, LegacyUrlConflictProps } from '../legacy_urls';
|
||||
import type { ShareToSpaceFlyoutProps } from '../share_saved_objects_to_space';
|
||||
import type { SpaceAvatarProps } from '../space_avatar';
|
||||
import type { SpaceListProps } from '../space_list';
|
||||
import type { SpacesContextProps, SpacesReactContextValue } from '../spaces_context';
|
||||
|
@ -88,6 +85,12 @@ export interface SpacesApiUiComponent {
|
|||
* Note: must be rendered inside of a SpacesContext.
|
||||
*/
|
||||
getSpaceList: LazyComponentFn<SpaceListProps>;
|
||||
/**
|
||||
* Displays a callout that needs to be used if an embeddable component call to `SavedObjectsClient.resolve()` results in an `"conflict"`
|
||||
* outcome, which indicates that the user has loaded an embeddable which is associated directly with one object (A), *and* with a legacy
|
||||
* URL that points to a different object (B).
|
||||
*/
|
||||
getEmbeddableLegacyUrlConflict: LazyComponentFn<EmbeddableLegacyUrlConflictProps>;
|
||||
/**
|
||||
* Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which
|
||||
* indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a
|
||||
|
@ -110,8 +113,4 @@ export interface SpacesApiUiComponent {
|
|||
* Displays an avatar for the given space.
|
||||
*/
|
||||
getSpaceAvatar: LazyComponentFn<SpaceAvatarProps>;
|
||||
/**
|
||||
* Displays a saved object conflict message that directs user to disable legacy URL alias
|
||||
*/
|
||||
getSavedObjectConflictMessage: LazyComponentFn<SavedObjectConflictMessageProps>;
|
||||
}
|
||||
|
|
|
@ -24209,10 +24209,6 @@
|
|||
"xpack.spaces.shareToSpace.currentSpaceBadge": "現在",
|
||||
"xpack.spaces.shareToSpace.featureIsDisabledTooltip": "この機能はこのスペースでは無効です。",
|
||||
"xpack.spaces.shareToSpace.flyoutTitle": "{objectNoun}をスペースに割り当てる",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictBody": "現在、{objectNoun} [id={currentObjectId}]を表示しています。このページのレガシーURLは別の{objectNoun} [id={otherObjectId}]を示しています。",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictDismissButton": "閉じる",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictLinkButton": "他の{objectNoun}に移動",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictTitle": "2つのオブジェクトがこのURLに関連付けられています",
|
||||
"xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.linkText": "新しいスペースを作成",
|
||||
"xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.text": "オブジェクトを共有するには、{createANewSpaceLink}できます。",
|
||||
"xpack.spaces.shareToSpace.objectNoun": "オブジェクト",
|
||||
|
@ -24221,8 +24217,6 @@
|
|||
"xpack.spaces.shareToSpace.privilegeWarningBody": "この{objectNoun}のスペースを編集するには、すべてのスペースで{readAndWritePrivilegesLink}が必要です。",
|
||||
"xpack.spaces.shareToSpace.privilegeWarningLink": "読み書き権限",
|
||||
"xpack.spaces.shareToSpace.privilegeWarningTitle": "追加の権限が必要です",
|
||||
"xpack.spaces.shareToSpace.redirectLegacyUrlToast.text": "検索している{objectNoun}は新しい場所にあります。今後はこのURLを使用してください。",
|
||||
"xpack.spaces.shareToSpace.redirectLegacyUrlToast.title": "新しいURLに移動しました",
|
||||
"xpack.spaces.shareToSpace.saveButton": "保存して閉じる",
|
||||
"xpack.spaces.shareToSpace.shareErrorTitle": "{objectNoun}の更新エラー",
|
||||
"xpack.spaces.shareToSpace.shareModeControl.buttonGroupLegend": "この共有方法を選択",
|
||||
|
|
|
@ -24614,10 +24614,6 @@
|
|||
"xpack.spaces.shareToSpace.currentSpaceBadge": "当前",
|
||||
"xpack.spaces.shareToSpace.featureIsDisabledTooltip": "此功能在此工作区中已禁用。",
|
||||
"xpack.spaces.shareToSpace.flyoutTitle": "将 {objectNoun} 分配给工作区",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictBody": "当前您正在查看 {objectNoun} [id={currentObjectId}]。此页面的旧 URL 显示不同的 {objectNoun} [id={otherObjectId}]。",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictDismissButton": "关闭",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictLinkButton": "前往其他 {objectNoun}",
|
||||
"xpack.spaces.shareToSpace.legacyUrlConflictTitle": "2 个对象与此 URL 关联",
|
||||
"xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.linkText": "创建新工作区",
|
||||
"xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.text": "您可以{createANewSpaceLink},用于共享您的对象。",
|
||||
"xpack.spaces.shareToSpace.objectNoun": "对象",
|
||||
|
@ -24626,8 +24622,6 @@
|
|||
"xpack.spaces.shareToSpace.privilegeWarningBody": "要编辑此 {objectNoun} 的工作区,您在所有工作区中都需要{readAndWritePrivilegesLink}。",
|
||||
"xpack.spaces.shareToSpace.privilegeWarningLink": "读写权限",
|
||||
"xpack.spaces.shareToSpace.privilegeWarningTitle": "需要其他权限",
|
||||
"xpack.spaces.shareToSpace.redirectLegacyUrlToast.text": "您正在寻找的{objectNoun}具有新的位置。从现在开始使用此 URL。",
|
||||
"xpack.spaces.shareToSpace.redirectLegacyUrlToast.title": "我们已将您重定向到新 URL",
|
||||
"xpack.spaces.shareToSpace.relativesControl.description": "{relativesCount} 个相关{relativesCount, plural, other {对象}}也将更改。",
|
||||
"xpack.spaces.shareToSpace.saveButton": "保存并关闭",
|
||||
"xpack.spaces.shareToSpace.shareErrorTitle": "更新 {objectNoun} 时出错",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue