Skip to content

Commit

Permalink
Merge branch 'develop' into templates-e2e-test
Browse files Browse the repository at this point in the history
  • Loading branch information
dzonidoo committed Apr 11, 2024
2 parents 7a6ec1c + 557744b commit 8e5aa50
Show file tree
Hide file tree
Showing 21 changed files with 182 additions and 2,819 deletions.
27 changes: 27 additions & 0 deletions e2e/client/playwright/article-versions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {test, expect} from '@playwright/test';
import {Monitoring} from './page-object-models/monitoring';
import {restoreDatabaseSnapshot, s} from './utils';

test.describe('article versions', async () => {
test('reverting to a previous article version', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot();
await page.goto('/#/workspace/monitoring');
await monitoring.selectDeskOrWorkspace('Sports');

await monitoring.executeActionOnMonitoringItem(
page.locator(s('article-item=story 2')),
'Edit',
);
await page.locator(s('authoring', 'field-slugline')).fill('story 2.1');
await page.locator(s('authoring-topbar', 'save')).click();
await expect(page.locator(s('authoring', 'field-slugline'))).toHaveValue('story 2.1');

await page.locator(s('navigation-tabs', 'authoring-widget=Versions/History')).click();
await page
.locator(s('authoring-widget-panel=Versions/History', 'article-version=3'))
.getByRole('button', {name: 'revert'}).click();
await expect(page.locator(s('authoring', 'field-slugline'))).toHaveValue('story 2');
});
});
2,848 changes: 52 additions & 2,796 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"angular-contenteditable": "0.3.9",
"angular-dynamic-locale": "0.1.32",
"angular-embed": "github:superdesk/angular-embed#d75968e",
"angular-embedly": "github:superdesk/angular-embedly#0.0.8",
"angular-embedly": "github:superdesk/angular-embedly#master",
"angular-gettext": "github:superdesk/angular-gettext#master",
"angular-i18n": "1.6.9",
"angular-mocks": "1.6.9",
Expand Down Expand Up @@ -122,7 +122,7 @@
"sass-loader": "6.0.6",
"shortid": "2.2.8",
"style-loader": "0.20.2",
"superdesk-ui-framework": "^3.0.77",
"superdesk-ui-framework": "^3.0.79",
"ts-loader": "3.5.0",
"typescript": "4.9.5",
"uuid": "8.3.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {CustomEditor3Entity} from 'core/editor3/constants';
import {getEntityMap} from 'core/editor3/helpers/get-entity-map';
import {IEditorDragDropArticleEmbed} from 'core/editor3/reducers/editor3';
import {ContentState} from 'draft-js';
import {IArticle} from 'superdesk-api';

export function copyEmbeddedArticlesIntoAssociations(contentState: ContentState, article: IArticle): void {
/**
* Putting embedded article into associations.
* It's meant for external use after item is published.
* see {@link AtomicBlockParser.parse}
*/
getEntityMap(contentState).forEach((entity) => {
if (entity.getType() === CustomEditor3Entity.ARTICLE_EMBED) {
const data = entity.getData() as IEditorDragDropArticleEmbed['data'];
const entityItem = data.item;

if (article.associations == null) {
article.associations = {};
}

article.associations[entityItem._id] = entityItem;
}
});
}
6 changes: 6 additions & 0 deletions scripts/apps/authoring-react/field-adapters/body_html.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {IArticle, IAuthoringFieldV2, IFieldAdapter, IEditor3Config, IEditor3ValueStorage} from 'superdesk-api';
import {gettext} from 'core/utils';
import {retrieveStoredValueEditor3Generic, storeEditor3ValueBase} from '.';
import {copyEmbeddedArticlesIntoAssociations} from '../copy-embedded-articles-into-associations';
import {editor3ToOperationalFormat} from '../fields/editor3';

export const body_html: IFieldAdapter<IArticle> = {
getFieldV2: (fieldEditor, fieldSchema) => {
Expand Down Expand Up @@ -56,6 +58,10 @@ export const body_html: IFieldAdapter<IArticle> = {
// (only applicable to body_html field)
articleUpdated.annotations = result.annotations;

const contentState = editor3ToOperationalFormat(value, item.language).contentState;

copyEmbeddedArticlesIntoAssociations(contentState, articleUpdated);

return articleUpdated;
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="article-versions" ng-controller="VersioningWidgetCtrl">
<ul class="versions-list boxed-list sd-padding--2">
<li class="boxed-list__item boxed-list__item--clickable boxed-list__item--comfortable" ng-class="{'boxed-list__item--selected': selected === version}" ng-repeat="version in versions" ng-click="openVersion(version)" ng-disabled="dirty">
<li class="boxed-list__item boxed-list__item--clickable boxed-list__item--comfortable" ng-class="{'boxed-list__item--selected': selected === version}" ng-repeat="version in versions" ng-click="openVersion(version)" ng-disabled="dirty" data-test-id="article-version" data-test-value="{{ version._current_version }}">
<div class="boxed-list__item-content">
<div class="boxed-list__item-content-row boxed-list__item-content-row--fixed">
<time class="sd-text__date-time sd-text__date-time--small" sd-absdate title="{{ :: version._created | date:'medium' }}" datetime="version._created"></time>
Expand All @@ -24,4 +24,4 @@
</div>
</li>
</ul>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
'active-right': active,
'right-extended': active.extended}">
<div class="sd-sidetab-menu sd-sidetab-menu--right" ng-if="item">
<ul class="navigation-tabs">
<ul class="navigation-tabs" data-test-id="navigation-tabs">
<li ng-repeat="widget in widgets | orderBy:'order'"
ng-class="{active: widget === active, pinned: widget.pinned}">
<a class="sd-sidetab-menu__btn" type="submit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export class ContentProfileFieldsConfig extends React.Component<IProps, IState>
};

allSchemaFieldKeys.forEach((_property) => {
field[_property] = schema[fieldId][_property];
field[_property] = schema[fieldId]?.[_property];
});

return field;
Expand Down
4 changes: 1 addition & 3 deletions scripts/core/editor3/actions/editor3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ export function dragDrop(
if (res.ok === true) {
const payload: IEditorDragDropPayload = {
data: {
id: res.src._id,
name: getArticleLabel(res.src),
html: res.src.body_html ?? '',
item: res.src,
},
blockKey,
contentType: 'article-embed',
Expand Down
10 changes: 5 additions & 5 deletions scripts/core/editor3/components/article-embed/article-embed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {ContentBlock, ContentState, EditorState} from 'draft-js';
import {IEditorDragDropArticleEmbed} from 'core/editor3/reducers/editor3';
import {openArticle} from 'core/get-superdesk-api-implementation';
import {Card} from 'core/ui/components/Card';
import {gettext} from 'core/utils';
import {getArticleLabel, gettext} from 'core/utils';

interface IProps {
block: ContentBlock;
Expand All @@ -18,14 +18,14 @@ export class ArticleEmbed extends React.Component<IProps> {
const {block, contentState} = this.props;
const entityKey = block.getEntityAt(0);
const entity = contentState.getEntity(entityKey);
const {id, name, html} = entity.getData() as IEditorDragDropArticleEmbed['data'];
const {item} = entity.getData() as IEditorDragDropArticleEmbed['data'];

const heading = (
<Spacer h gap="32" justifyContent="space-between" noWrap>
<div>
{gettext('Embedding from:')}
&nbsp;
<strong>{name}</strong>
<strong>{getArticleLabel(item)}</strong>
</div>

<div style={{flexShrink: 0}}>
Expand All @@ -34,7 +34,7 @@ export class ArticleEmbed extends React.Component<IProps> {
ariaValue={gettext('open in a new window')}
size="small"
onClick={() => {
openArticle(id, 'edit-new-window');
openArticle(item._id, 'edit-new-window');
}}
/>
</div>
Expand All @@ -44,7 +44,7 @@ export class ArticleEmbed extends React.Component<IProps> {
return (
<Card heading={heading} width="100%">
<div className="article-embed">
<div dangerouslySetInnerHTML={{__html: html}} />
<div dangerouslySetInnerHTML={{__html: item.body_html}} />
</div>
</Card>

Expand Down
14 changes: 13 additions & 1 deletion scripts/core/editor3/components/links/LinkDecorator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import {attachmentsApi} from 'apps/authoring/attachments/attachmentsService';
import {addEventListener, removeEventListener} from 'core/get-superdesk-api-implementation';
import {IAttachment} from 'superdesk-api';
import {appConfig} from 'appConfig';

/**
* @name LinkStrategy
Expand Down Expand Up @@ -87,7 +88,18 @@ class LinkComponent extends React.Component<any, any> {
);
}

return <a href={this.link.href} title={this.link.href}>{this.props.children}</a>;
return appConfig.linksBlankTarget
? (
<a
target="_blank"
rel="noopener noreferrer"
href={this.link.href}
title={this.link.href}
>
{this.props.children}
</a>
)
: (<a href={this.link.href} title={this.link.href}>{this.props.children}</a>);
}
}

Expand Down
3 changes: 2 additions & 1 deletion scripts/core/editor3/components/links/LinkInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ng from 'core/services/ng';
import {gettext} from 'core/utils';
import {IEditorStore} from 'core/editor3/store';
import {IArticle} from 'superdesk-api';
import {appConfig} from 'appConfig';

/**
* @ngdoc React
Expand Down Expand Up @@ -114,7 +115,7 @@ export class LinkInputComponent extends React.Component<IProps, any> {
const isLocalDomain = (localDomains || []).some((item) => url.includes(item.domain));

link = {href: url};
if (!isLocalDomain && localDomains != null) {
if ((appConfig.linksBlankTarget === true) || (!isLocalDomain && localDomains != null)) {
link.target = '_blank';
}
} else if (linkType === linkTypes.attachement) {
Expand Down
4 changes: 4 additions & 0 deletions scripts/core/editor3/directive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {TextStatisticsConnected} from 'apps/authoring/authoring/components/text-
import {getLabelNameResolver} from 'apps/workspace/helpers/getLabelForFieldId';
import {ValidateCharactersConnected} from 'apps/authoring/authoring/ValidateCharactersConnected';
import {Spacer} from 'core/ui/components/Spacer';
import {copyEmbeddedArticlesIntoAssociations} from 'apps/authoring-react/copy-embedded-articles-into-associations';

/**
* @ngdoc directive
Expand Down Expand Up @@ -103,6 +104,9 @@ function generateHtml(
objectToUpdate[fieldName] = editor3StateToHtml(
contentStatePreparedForExport,
);

copyEmbeddedArticlesIntoAssociations(contentStatePreparedForExport, item);

generateAnnotations(item);
}
}
Expand Down
28 changes: 28 additions & 0 deletions scripts/core/editor3/helpers/get-entity-map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {ContentState} from 'draft-js';
import {Map} from 'immutable';

// .getEntityMap doesn't work
// .entityMap appears to be private and only works sometimes. Other times it returns what appears to be a entity class.

type DraftEntityInstance = ReturnType<ContentState['getEntity']>;

export function getEntityMap(contentState: ContentState): Map<string, DraftEntityInstance> {
let entityMap = Map<string, DraftEntityInstance>();

let endReached = false;
let i = 1;

while (!endReached) {
try {
const entity = contentState.getEntity(i.toString());

entityMap = entityMap.set(i.toString(), entity);

i++;
} catch (e) {
endReached = true;
}
}

return entityMap;
}
5 changes: 4 additions & 1 deletion scripts/core/editor3/html/to-html/AtomicBlockParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export class AtomicBlockParser {
case CustomEditor3Entity.MULTI_LINE_QUOTE:
return this.parseMultiLineQuote(getData(this.contentState, contentBlock.getKey())).trim();
case CustomEditor3Entity.ARTICLE_EMBED:
return (data as IEditorDragDropArticleEmbed['data']).html;
// eslint-disable-next-line no-case-declarations
const item = (data as IEditorDragDropArticleEmbed['data']).item;

return `<div data-association-key="${item._id}">${item.body_html}</div>`;
default:
logger.warn(`Editor3: Cannot generate HTML for entity type of ${entity.getType()}`, data);
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/core/editor3/html/to-html/editor3StateToHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {trimStartExact, trimEndExact} from 'core/helpers/utils';
export const editor3StateToHtml = (
contentState: ContentState,
disabled: Array<string> = [], // A set of disabled elements (ie. ['table'] will ignore
) => {
): string => {
const annotationsByStyleName = getAnnotationsFromContentState(contentState)
.reduce((accumulator, item) => ({...accumulator, [item.styleName]: item}), {});

Expand Down
5 changes: 2 additions & 3 deletions scripts/core/editor3/reducers/editor3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {logger} from 'core/services/logger';
import {EditorLimit, IActionPayloadSetExternalOptions} from '../actions';
import {assertNever} from 'core/helpers/typescript-helpers';
import {CustomEditor3Entity} from '../constants';
import {IArticle} from 'superdesk-api';

/**
* @description Contains the list of editor related reducers.
Expand Down Expand Up @@ -366,9 +367,7 @@ export interface IEditorDragDropMedia {
export interface IEditorDragDropArticleEmbed {
contentType: 'article-embed';
data: {
id: string;
name: string;
html: string;
item: IArticle;
};
blockKey: string;
}
Expand Down
3 changes: 3 additions & 0 deletions scripts/core/superdesk-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3130,6 +3130,9 @@ declare module 'superdesk-api' {
oidc_auth: any;
keycloak_config: any;

/** Allow default target for links to be _blank. */
linksBlankTarget?: boolean;

/**
* Enable autocomplete for supported text fields in authoring.
* Values of published items are used for autocomplete suggestions.
Expand Down
2 changes: 1 addition & 1 deletion scripts/core/ui/components/MultiSelectTreeWithTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class MultiSelectTreeWithTemplate<T> extends React.PureComponent<IProps<T
getId={getId}
selectBranchWithChildren={false}
optionTemplate={(item) => <OptionTemplate item={item} />}
valueTemplate={(item) => <ValueTemplate item={item} />}
valueTemplate={(item, Wrapper) => <Wrapper><ValueTemplate item={item} /></Wrapper>}
allowMultiple={this.props.allowMultiple}
singleLevelSearch
readOnly={this.props.readOnly}
Expand Down
2 changes: 1 addition & 1 deletion scripts/extensions/broadcasting/src/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ export class RundownsPage extends React.PureComponent<IProps, IState> {
<Layout.OverlayPanel />
</Layout.LayoutContainer>

<Layout.ContentSplitter visible={true} />
<Layout.ContentSplitter visible={true} disabled />
</React.Fragment>
)
}
Expand Down
1 change: 1 addition & 0 deletions styles/sass/layouts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ $slide-pane-width: 400px;
transition: right 0.3s ease-in-out;
border-radius: var(--b-radius--x-large);
margin: 12px 0;
z-index: 1050;
&.show {
inset-inline-end: 12px;
}
Expand Down

0 comments on commit 8e5aa50

Please sign in to comment.