Skip to content

Commit

Permalink
Metadata widget (#4212)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecalcc authored Apr 5, 2023
1 parent b18b550 commit 4e9b998
Show file tree
Hide file tree
Showing 13 changed files with 676 additions and 40 deletions.
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

0 comments on commit 4e9b998

Please sign in to comment.