Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata widget #4212

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scripts/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {templates} from './templates';
import {time} from './time';
import {user} from './user';
import {vocabularies} from './vocabularies';
import {filters} from './filters';

/**
* This is core API, not extensions API.
Expand All @@ -29,4 +30,5 @@ export const sdApi = {
user,
vocabularies,
highlights,
filters,
};
84 changes: 81 additions & 3 deletions scripts/api/vocabularies.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {OrderedMap} from 'immutable';
import ng from 'core/services/ng';
import {IVocabulary} from 'superdesk-api';
import {IArticle, IVocabulary, IVocabularyItem} from 'superdesk-api';
import {getVocabularyItemNameTranslated} from 'core/utils';

function getAll(): OrderedMap<IVocabulary['_id'], IVocabulary> {
return OrderedMap<string, IVocabulary>(
Expand All @@ -14,18 +15,95 @@ function isCustomFieldVocabulary(vocabulary: IVocabulary): boolean {
return vocabulary.field_type != null || vocabulary.custom_field_type != null;
}

function getVocabularyItemLabel(term: IVocabularyItem, item: IArticle): string {
if (!term) {
return 'None';
}

// Item can be anything here. It might be an article object or search filters object
// depending where the function is called from.
// It's checked if language is a string in order not to confuse it when language
// is an array when called from global search filters.
const language = typeof item.language === 'string' ? item.language : undefined;

return getVocabularyItemNameTranslated(term, language);
}

const vocabularyItemsToString = (
array: Array<IVocabularyItem>,
propertyName?: keyof IVocabularyItem,
schemeName?: string,
): string =>
getVocabularyItemsByPropertyName(array, propertyName, schemeName).join(', ');

const getVocabularyItemsByPropertyName = (
array: Array<IVocabularyItem>,
propertyName?: keyof IVocabularyItem,
schemeName?: string,
): Array<string> => {
let subjectMerged = [];

array.forEach((item) => {
const value = propertyName == null ? item : item[propertyName];

if (value) {
subjectMerged.push(value);

if ((schemeName?.length ?? 0) < 1 && item.scheme !== schemeName) {
subjectMerged.pop();
}
}
});

return subjectMerged;
};

const getVocabularyItemsPreview = (
array: Array<IVocabularyItem>,
propertyName?: keyof IVocabularyItem,
schemeName?: string,
returnArray?: boolean,
): Array<string> | string => {
if (returnArray) {
return getVocabularyItemsByPropertyName(array, propertyName, schemeName);
} else {
return vocabularyItemsToString(array, propertyName, schemeName);
}
};

/**
* Selection vocabularies may be configured to be included in content profiles.
*/
function isSelectionVocabulary(vocabulary: IVocabulary) {
function isSelectionVocabulary(vocabulary: IVocabulary): boolean {
return !isCustomFieldVocabulary(vocabulary) && (
vocabulary.selection_type === 'multi selection'
|| vocabulary.selection_type === 'single selection'
);
}

export const vocabularies = {
interface IVocabulariesApi {
getAll: () => OrderedMap<IVocabulary['_id'], IVocabulary>;
isCustomFieldVocabulary:(vocabulary: IVocabulary) => boolean;
isSelectionVocabulary: (vocabulary: IVocabulary) => boolean;
getVocabularyItemLabel: (term: IVocabularyItem, item: IArticle) => string;
getVocabularyItemsPreview: (
array: Array<IVocabularyItem>,
propertyName?: keyof IVocabularyItem,
schemeName?: string,
returnArray?: boolean
) => Array<string> | string;
vocabularyItemsToString: (
array: Array<IVocabularyItem>,
propertyName?: keyof IVocabularyItem,
schemeName?: string,
) => string;
}

export const vocabularies: IVocabulariesApi = {
getAll,
isCustomFieldVocabulary,
isSelectionVocabulary,
getVocabularyItemLabel,
getVocabularyItemsPreview,
vocabularyItemsToString,
};
11 changes: 10 additions & 1 deletion scripts/apps/archive/directives/HtmlPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {getAnnotationsFromItem} from 'core/editor3/helpers/editor3CustomData';
import {META_FIELD_NAME} from 'core/editor3/helpers/fieldsMeta';
import ng from 'core/services/ng';
import {gettext} from 'core/utils';
import {IArticle} from 'superdesk-api';

function getAnnotationTypesAsync(scope) {
ng.get('metadata').initialize()
Expand All @@ -16,7 +17,15 @@ function getAnnotationTypesAsync(scope) {
});
}

function getAllAnnotations(item) {
interface IAnotationData {
body: string;
id: number;
index: number;
styleName: string;
type: string;
}

export function getAllAnnotations(item: IArticle): Array<IAnotationData> {
const annotations = [];

for (const field in item[META_FIELD_NAME]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {getAllAnnotations} from 'apps/archive/directives/HtmlPreview';
import {Spacer} from 'core/ui/components/Spacer';
import {gettext} from 'core/utils';
import React from 'react';
import {IArticle} from 'superdesk-api';
import {Label, ToggleBox} from 'superdesk-ui-framework/react';
import './annotations-preview.scss';

interface IProps {
article: IArticle;
}

export class AnnotationsPreview extends React.Component<IProps> {
render(): React.ReactNode {
const {article} = this.props;

return (
<div>
<div dangerouslySetInnerHTML={{__html: article.archive_description}} />
<ToggleBox title={gettext('Annotations')}>
{
(article.annotations?.length ?? 0) > 0 && (
getAllAnnotations(article).map((annotation) => (
<Spacer h gap="4" key={annotation.id} noWrap>
<Label text={annotation.type} style="hollow" type="primary" />
<div>
<span
className="annotation-body-react"
dangerouslySetInnerHTML={{__html: annotation.body}}
/>
<sup className="annotation-id">
{annotation.id}
</sup>
</div>
</Spacer>
))
)
}
</ToggleBox>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.annotation-body-react {
border-bottom: 1px dotted;
p {
display: inline;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Spacer} from 'core/ui/components/Spacer';
import React from 'react';
import {ContentDivider, Heading} from 'superdesk-ui-framework/react';

interface IProps {
label: string;
value: string | number | JSX.Element;
}

export class MetadataItem extends React.Component<IProps> {
render(): React.ReactNode {
const {label, value} = this.props;

return (
<>
<Spacer h gap="32" justifyContent="space-between" alignItems="center" noWrap>
<Heading type="h6" align="start">
{label.toUpperCase()}
</Heading>
<div>{value}</div>
</Spacer>
<ContentDivider border type="dotted" margin="x-small" />
</>
);
}
}
Loading