/* tslint:disable */ declare module 'superdesk-api' { // TYPESCRIPT TYPES export interface DeepReadonlyArray extends ReadonlyArray> {}; export type DeepReadonlyObject = { readonly [P in keyof T]: DeepReadonly; } export type DeepReadonly = T extends Function ? T : T extends Array ? DeepReadonlyArray : DeepReadonlyObject; export type Omit = Pick>; export type ICallable = (...args: Array) => any; // EXTENSIONS export type onSpikeMiddlewareResult = {warnings?: Array<{text: string}>}; export type onPublishMiddlewareResult= {warnings?: Array<{text: string}>}; /** * float number 0 < x < 1. Larger the number, closer the component will be rendered to its side. * for example, if we had a list with 'start' positioned items with the following priorities [0.1, 0.2, 0.3] * we could add an item so it's the first in the list by setting priority to be less than 0.1, for example, 0.05. * to insert an item between 0.2 and 0.3 we could set its priority to 0.25 * See [[sortByDisplayPriority]] for debug information. */ export type IDisplayPriority = number; export interface IArticleAction { groupId?: string; // action lists can specify which groups they wanna render via an id priority?: IDisplayPriority; icon?: string; label: string; onTrigger(): void; } export interface IArticleActionBulk { priority?: IDisplayPriority; label: string; icon: string; group?: {label: string, icon: string}; onTrigger(): void; } export interface IMonitoringFilter { label: string; query: {[key: string]: any}; displayOptions?: { ignoreMatchesInSavedSearchMonitoringGroups?: boolean; }; } export interface IPersonalSpaceSection { label: string; id: string, query: {[key: string]: any}; } export interface IAuthoringSideWidget { _id: string; // required for configuring widget visibility in content profile label: string; order: number; // Integer. icon: string; component: React.ComponentType<{article: IArticle}>; isAllowed?(article: IArticle): boolean; // enables limiting widgets depending on article data } export interface AuthoringHeaderItem { _id: string; label: string; order: number; component: React.ComponentType<{article: IArticle}>; } export interface IExtensionActivationResult { contributions?: { globalMenuHorizontal?: Array; editor3?: { annotationInputTabs?: Array; } articleListItemWidgets?: Array>; articleGridItemWidgets?: Array>; authoringTopbarWidgets?: Array>; authoringSideWidgets?: Array; authoringHeaderComponents?: Array; authoringTopbar2Widgets?: Array>; mediaActions?: Array>; pages?: Array; workspaceMenuItems?: Array; customFieldTypes?: Array; notifications?: { [id: string]: (notification) => { body: string; actions: Array<{label: string; onClick(): void;}>; }; }; entities?: { article?: { getActions?(article: IArticle): Promise>; getActionsBulk?(articles: Array): Promise>; onPatchBefore?(id: IArticle['_id'], patch: Partial, dangerousOptions?: IDangerousArticlePatchingOptions,): Promise>; // can alter patch(immutably), can cancel patching onSpike?(item: IArticle): Promise; onSpikeMultiple?(items: Array): Promise; onPublish?(item: IArticle): Promise; onRewriteAfter?(item: IArticle): Promise; onSendBefore?(items: Array, desk: IDesk): Promise; }; }; iptcMapping?(data: Partial, item: Partial, parent?: IArticle): Promise>; searchPanelWidgets?: Array>; authoring?: { /** * Updates can be intercepted and modified. Return value will be used to compute a patch. * * Example: onUpdateBefore = (current, next) => ({...next, priority: next.headline.includes('important') ? 10 : 1}) */ onUpdateBefore?(current: IArticle, next: IArticle): Promise; /** Called after the update. */ onUpdateAfter?(previous: IArticle, current: IArticle): void; }; monitoring?: { getFilteringButtons?(deskId: string): Array; }; personalSpace?: { getSections?(): Array; }; } } export type ISearchPanelWidgetProps = { provider: string; params: T; setParams: (params: Partial) => void; }; export type IExtension = DeepReadonly<{ id: string; activate: (superdesk: ISuperdesk) => Promise; exposes?: {[key: string]: any}; }>; export type IExtensionObject = { extension: IExtension; activationResult: IExtensionActivationResult; }; export type IExtensions = {[key: string]: IExtensionObject}; export type ISideMenuItem = DeepReadonly<{ label: string; url: string; }>; // ENTITIES export interface IAuthor { // !!! _id is optional. It will not be present in ingested items. _id?: Array; // user id, role name: string; scheme: any | null; user: IUser; role?: string; parent?: string; } // to use as a value, use enum inside 'scripts/apps/search/interfaces.ts' export enum ITEM_STATE { /** * Item created in user workspace. */ DRAFT = 'draft', /** * Ingested item in ingest collection, not production. */ INGESTED = 'ingested', /** * Automatically ingested to desk. */ ROUTED = 'routed', /** * Item manually fetched from ingest to desk. */ FETCHED = 'fetched', /** * Item is sent to a desk. */ SUBMITTED = 'submitted', /** * Work started on a desk. */ IN_PROGRESS = 'in_progress', /** * Removed from a desk. */ SPIKED = 'spiked', /** * Published. */ PUBLISHED = 'published', /** * Scheduled for publishing. */ SCHEDULED = 'scheduled', /** * Correction is published. */ CORRECTED = 'corrected', /** * Killed, never publish again. */ KILLED = 'killed', /** * Sort of killed, never publish again. */ RECALLED = 'recalled', /** * Unpublished, might be published again. */ UNPUBLISHED = 'unpublished', /** * Correction, If Correction workflow is true, correction, copy of published article which we can edit. */ CORRECTION = 'correction', /** * being_corrected, If Correction workflow is true, being_corrected, the item is being corrected. */ BEING_CORRECTED = 'being_corrected', } export interface IRelatedArticle { _id: IArticle['_id']; type: IArticle['type']; order: number, } export interface IRendition { href: string; mimetype: string; /** media storage id, set when item is stored in superdesk */ media?: string; // picture and video only width?: number; height?: number; // video id, set when item is stored in video server video_editor_id?: string; }; export interface IArticle extends IBaseRestApiResponse { _id: string; _current_version: number; _type?: 'ingest' | 'archive' | 'published' | 'archived' | 'legal_archive' | 'externalsource' | string; uri?: string; // uri is external id which stays when image is fetched from provider/ingest guid: string; family_id: string; translated_from?: string; translation_id?: string; // if C is translated from B which is translated from A, all will have the same translation_id translations?: Array; // direct translations only, not all items with same translation_id usageterms?: any; keywords?: any; language: any; slugline: string; genre: any; anpa_take_key?: any; place: Array; object?: Array; person?: Array; organisation?: Array; event?: Array; priority?: any; urgency: any; anpa_category?: any; subject?: Array; company_codes?: Array; ednote?: string; authors?: Array; headline: string; sms?: string; abstract?: string; attachments?: Array<{attachment: string}>; byline: string; dateline?: { day?: string; date?: string; source?: string; located?: { dateline?: string; city?: string; city_code?: string; state_code?: string; country?: string; country_code?: string; tz?: string; state?: string; }; text?: string; }; body_html?: string; footer?: string; firstcreated: any; versioncreated: any; body_footer?: string; is_spiked?: any; expiry: any; copyrightholder?: string; copyrightnotice?: string; sign_off: string; feature_media?: any; media_description?: string; description_text?: string; associations?: { 'featuremedia': IArticle; // IArticle is used for media galleries and IRelatedArticle for linking articles. [id: string]: IArticle | IRelatedArticle; }; type: | 'text' | 'picture' | 'video' | 'audio' | 'preformatted' | 'graphic' | 'composite'; firstpublished?: string; linked_in_packages?: Array<{ package: string; package_type: string; // deprecated }>; gone?: any; lock_action: any; lock_user: any; lock_session: any; rewritten_by?: IArticle['_id']; rewrite_of?: IArticle['_id']; profile: string; word_count?: number; lines_count?: number; version_creator: string; state: ITEM_STATE; embargo?: any; signal?: any; broadcast?: { master_id?: any; // original story this broadcast version was created from status?: any; }; flags: any; source: string; /** correction counter, is reset on rewrite */ correction_sequence?: number; /** rewrite counter */ rewrite_sequence?: number; fetch_endpoint?: any; task_id?: any; ingest_provider?: any; archive_item?: IArticle; item_id?: string; // id of corresponding item in 'published' collection marked_desks?: Array<{ date_marked: string; desk_id: IDesk['_id']; user_marked: IUser['_id']; }>; highlights?: Array; highlight?: any; sms_message?: string; // storage for custom fields created by users extra?: {[key: string]: any}; task: { desk?: IDesk['_id']; stage?: IStage['_id']; user?: IUser['_id']; }; // might be only used for client-side state created?: any; archived?: any; unique_name: any; pubstatus: any; schedule_settings: any; format: any; fields_meta?: { [key: string]: { draftjsState?: any; } }; version: any; template: any; original_creator: string; unique_id: any; operation: any; lock_time: string; force_unlock?: boolean; order?: number; _status: any; _fetchable?: boolean; last_published_version?: any; /** * Wrapper for different renditions of non-textual content of the news object * * There can be multiple renditions for single item with different sizes/mimetypes. * * Picture renditions used in UI are generated automatically by Superdesk: * - **thumbnail** - used in lists * - **viewImage** - used in sidebar preview * - **baseImage** - used in media editor, full screen preview * * Video items can also provide **thumbnail** and **viewImage** renditions which will be * then used in list/preview. If there is **viewImage** it will use it for grid view/preview, * **thumbnail** will be used as poster when video is being loaded. When there is no **viewImage** * it will use **thumbnail** for both. */ renditions?: { /** Original binary uploaded by user. */ original?: IRendition; /** * Image rendition up to 220x120, used in lists. * * Could be bigger picture for video items, it's used as poster there. */ thumbnail?: IRendition; /** Image rendition up to 640x640, used in preview/grid view. */ viewImage?: IRendition; /** Image rendition up to 1400x1400, used for full screen preview. */ baseImage?: IRendition; /** Other renditions, could be custom, video, audio etc. */ [key: string]: IRendition; }; // media fields alt_text?: any; // planning extension assignment_id?: string; event_id?: any; // markForUser extension marked_for_user?: string | null; // remove when SDESK-4343 is done. selected?: any; es_highlight?: any; used?: boolean; used_count?: number; used_updated?: string; // other fields which don't exist in the database, don't belong to this entity and should be removed error?: any; _editable?: any; actioning?: { archive?: boolean; externalsource: boolean; archiveContent?: boolean; }; _autosave?: any; _locked?: boolean; attachments?: Array<{ attachment: string; }>; } export interface IDangerousArticlePatchingOptions { // When this option is set, an HTTP request will be sent immediately // even if the article is locked and is being edited. // Data received from the server will overwrite values edited by a user in case of a conflict. patchDirectlyAndOverwriteAuthoringValues?: boolean; } export interface IPublishedArticle extends IArticle { /** id in published collection, different for each correction */ item_id: string; /** item copy in archive collection, always the latest version of the item */ archive_item: IArticle; } export interface IPublishedArticle extends IArticle { /** id in published collection, different for each correction */ item_id: string; /** item copy in archive collection, always the latest version of the item */ archive_item: IArticle; } export interface IUserRole extends IBaseRestApiResponse { _id: string; name: string; privileges: any; author_role: string; editor_role: string; } export interface IDesk extends IBaseRestApiResponse { name: string; description?: string; members: Array; incoming_stage: IStage['_id']; working_stage: IStage['_id']; content_expiry?: number; source: string; monitoring_settings?: Array<{ _id: string; type: 'search' | 'stage' | 'scheduledDeskOutput' | 'deskOutput' | 'personal' | 'sentDeskOutput'; max_items: number; }>; desk_type: 'authoring' | 'production'; desk_metadata?: {[key: string]: any}; content_profiles: {[key: IContentProfile['_id']]: any}; desk_language?: string; monitoring_default_view?: 'list' | 'swimlane' | 'photogrid'; default_content_profile: string; default_content_template: string; slack_channel_name?: string; preferred_cv_items: {[key: string]: any}; preserve_published_content: boolean; } export interface IStage extends IBaseRestApiResponse { name: string; description: string; working_stage: boolean; default_incoming: boolean; task_status: 'todo' | 'in_progress' | 'done'; desk_order: number; desk: any; content_expiry: number; is_visible: boolean; local_readonly: boolean; incoming_macro: string; outgoing_macro: string; onstage_macro: string; } export interface IUser extends IBaseRestApiResponse { session_preferences?: {[key: string]: any}; _id: string; username: string; password: string; password_changed_on: string; first_name?: string; // not mandatory, empty when user is created programatically last_name?: string; // not mandatory, empty when user is created programatically display_name: string; email: string; phone: string; job_title: string; biography: string; facebook: string; instagram: string; twitter: string; jid: string; language: string; user_info: {}; picture_url: string; avatar: string; avatar_renditions: {}; role?: IUserRole['_id']; privileges: {}; user_type: 'user' | 'administrator'; is_support: boolean; is_author: boolean; is_active: boolean; is_enabled: boolean; needs_activation: boolean; desk: IDesk; sign_off: string; byline: string; invisible_stages: Array; slack_username: string; slack_user_id: string; last_activity_at?: string; } export interface IVocabularyTag { text: string; } export interface IVocabularyItem { name?: string; qcode?: string; is_active?: boolean; translations?: { name?: { [key: string]: string; } }; } export interface IVocabulary extends IBaseRestApiResponse { _deleted: boolean; display_name: string; helper_text?: string; popup_width?: number; type: string; items: Array; single_value?: boolean; schema_field?: string; dependent?: boolean; service: {}; priority?: number; unique_field: string; schema: {}; field_type: | 'text' | 'media' | 'date' | 'embed' | 'urls' | 'related_content' | 'custom'; field_options?: { // Used for related content fields allowed_types?: any; allowed_workflows?: { in_progress?: boolean; published?: boolean; }; multiple_items?: { enabled: boolean; max_items: number }; }; custom_field_type?: string; custom_field_config?: { [key: string]: any }; date_shortcuts?: Array<{ value: number; term: string; label: string }>; init_version?: number; preffered_items?: boolean; tags?: Array; disable_entire_category_selection?: boolean; } export interface IArticleField extends IVocabulary { single?: boolean; preview?: boolean; } export type IContentProfileEditorConfig = {[key: string]: IArticleField}; export interface IContentProfile { _id: string; label: string; description: string; schema: Object; editor: IContentProfileEditorConfig; widgets_config: Array<{widget_id: string; is_displayed: boolean}>; priority: number; enabled: boolean; is_used: boolean; created_by: string; updated_by: string; } export interface IMedia { _id: string; md5: string; name: string; filename: string; content_type: string; length: number; } export interface IAttachment extends IBaseRestApiResponse{ title: string; mimetype: string; filename: string; description: string; media: string | IMedia; internal: boolean; } // PAGE export type IPage = DeepReadonly<{ title: string; url: string; component: React.ComponentType; priority?: number; showTopMenu?: boolean; showSideMenu?: boolean; addToMainMenu?: boolean; // defaults to true }>; export type IWorkspaceMenuItem = DeepReadonly<{ href: string; icon: string; label: string; order?: number; shortcut?: string; privileges?: Array; }>; // REST API export interface IHttpRequestOptions { method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; url: string; // absolute url payload?: {}; headers?: {[key: string]: any}; urlParams?: {[key: string]: any}; abortSignal?: AbortSignal; } export interface IHttpRequestOptionsLocal extends Omit { path: string; // relative to application server } export interface IHttpRequestJsonOptionsLocal extends IHttpRequestOptionsLocal { // JSON not available with DELETE method method: 'GET' | 'POST' | 'PATCH' | 'PUT'; } export interface IBaseRestApiResponse { _created: string; _updated: string; _etag: string; _links: { parent?: any; collection?: any; self?: any; }; _id: string; } export interface IRestApiLink { title: string; href: string; } // Eve properties export interface IRestApiResponse { _items: Array; _links: { last?: IRestApiLink; parent: IRestApiLink; next?: IRestApiLink; self: IRestApiLink; prev?: IRestApiLink; }; _meta: { max_results: number; page: number; total: number; }; } export interface IQueryElasticParameters { endpoint: string; page: { from: number; size: number; }; sort: Array<{[field: string]: 'asc' | 'desc'}>; // can use deep references like {'a.b.c': []} filterValues: {[fieldName: string]: Array}; // can use deep references like {'a.b.c': []} // generates must_not statements filterValuesNegative?: {[fieldName: string]: Array}; aggregations: boolean; } interface IElasticSearchAggregationResult { buckets: Array<{key: string; doc_count: number}>; doc_count_error_upper_bound: number; sum_other_doc_count: number; } export type IArticleQuery = Omit; interface IArticleQueryResult extends IRestApiResponse { _aggregations: { category?: IElasticSearchAggregationResult; desk?: IElasticSearchAggregationResult; genre?: IElasticSearchAggregationResult; legal?: IElasticSearchAggregationResult; priority?: IElasticSearchAggregationResult; sms?: IElasticSearchAggregationResult; source?: IElasticSearchAggregationResult; type?: IElasticSearchAggregationResult; urgency?: IElasticSearchAggregationResult; }; } // GENERIC FORM export interface IPropsGenericForm> { formConfig: IFormGroup; defaultSortOption: ISortOption; defaultFilters?: Partial; renderRow(key: string, item: T, page: IGenericListPageComponent): JSX.Element; // Allows initializing a new item with some fields already filled. getNewItemTemplate?(page: IGenericListPageComponent): Partial; refreshOnEvents?: Array; fieldForSearch?: IFormField; // must be present in formConfig disallowCreatingNewItem?: true; disallowFiltering?: true; } export enum FormFieldType { textSingleLine = 'textSingleLine', textEditor3 = 'textEditor3', vocabularySingleValue = 'vocabularySingleValue', checkbox = 'checkbox', contentFilterSingleValue = 'contentFilterSingleValue', deskSingleValue = 'deskSingleValue', stageSingleValue = 'stage_singstageSingleValuele_value', macroSingleValue = 'macroSingleValue', yesNo = 'yesNo', } export interface IFormField { // don't forget to update runtime type checks type: FormFieldType; required?: boolean; // custom components for some fields might not require a label or want include a custom one label?: string; field: string; // can be used to pass read-only fields or display specific flags // component theme, variant or initial state could be set using this component_parameters?: {[key: string]: any}; } export interface IFormGroupCollapsible { // don't forget to update runtime type checks label: string; openByDefault: boolean; } export interface IFormGroup { // don't forget to update runtime type checks direction: 'vertical' | 'horizontal'; type: 'inline' | IFormGroupCollapsible; form: Array; } // CRUD MANAGER export type ICrudManagerFilters = {[fieldName: string]: any}; export interface ISortOption { field: string; direction: 'ascending' | 'descending'; } export interface ICrudManagerState extends IRestApiResponse { activeFilters: ICrudManagerFilters; activeSortOption?: ISortOption; } export interface ICrudManagerMethods { read( page: number, sort: ISortOption, filterValues?: ICrudManagerFilters, ): Promise>; update(item: Entity): Promise; create(item: Entity): Promise; delete(item: Entity): Promise; refresh(): Promise>; sort(nextSortOption: ISortOption): Promise>; removeFilter(fieldName: string): Promise>; goToPage(nextPage: number): Promise>; } export interface ICrudManager extends ICrudManagerState, ICrudManagerMethods { // allow exposing it as one interface for consumer components } // REACT COMPONENTS export interface IConfigurableUiComponents { UserAvatar?: React.ComponentType<{user: Partial}>; AuthoringAttachmentsWidget?: React.ComponentType; } export interface IConfigurableAlgorithms { countLines?(plainText: string, lineLength: number): number; } export interface IListItemProps { onClick?(): void; className?: string; inactive?: boolean; noHover?: boolean; noShadow?: boolean; noBackground?: boolean; fullWidth?: boolean; 'data-test-id'?: string; } export interface IPropsListItemColumn { ellipsisAndGrow?: boolean; noBorder?: boolean; justifyContent?: string; bold?: boolean; } export interface IPropsWidgetHeading { widgetName: string; editMode: boolean; } export interface IGridComponentProps { columns: number; boxed?: boolean; children: React.ReactNodeArray; } export interface IAlertComponentProps { type: 'info' | 'warning' | 'error'; message: string; title?: string; /** actions will be rendered as small icon-buttons on the right */ actions?: Array<{ label: string; onClick(): void; icon?: string; }>; size?: 'small'; hollow?: boolean; } export interface IFigureComponentProps { caption: string; onRemove?: () => void; children?: React.ReactNode; } export interface IDropZoneComponentProps { label?: string; className?: string; onDrop: (event: DragEvent) => void; canDrop: (event: DragEvent) => boolean; onFileSelect?: (files: FileList) => void; fileAccept?: string; } export interface IModalProps { 'data-test-id'?: string; size?: 'large' | 'extra-large' | 'fill' | 'full-screen'; } export interface IPropsModalHeader { onClose?(): void; } export interface IGenericListPageComponent> { openPreview(id: string): void; startEditing(id: string): void; closePreview(): void; setFiltersVisibility(nextValue: boolean): void; handleFilterFieldChange(field: string, nextValue: any, callback): void; openNewItemForm(): void; closeNewItemForm(): void; deleteItem(item: T): void; getActiveFilters(): Partial; removeFilter(fieldName: string): void; } export interface IPropsSelectUser { onSelect(user: IUser): void; selectedUserId?: string; disabled?: boolean; autoFocus?: boolean; horizontalSpacing?: boolean; } export interface IDropdownTreeGroup { render(): JSX.Element | null; items: Array>; } export interface IPropsDropdownTree { groups: Array>; getToggleElement(isOpen: boolean, onClick: () => void): JSX.Element; renderItem(key: string, item: T, closeDropdown: () => void): JSX.Element; wrapperStyles?: React.CSSProperties; 'data-test-id'?: string; } export interface ISpacingProps { margin?: number; marginTop?: number; marginRight?: number; marginBottom?: number; padding?: number; paddingTop?: number; paddingRight?: number; paddingBottom?: number; } interface IPropsBadge extends ISpacingProps { type: 'default' | 'primary' | 'success' | 'warning' | 'alert' | 'highlight' | 'light'; square?: boolean; } export interface IPropsIcon { className: string; size?: number; } export interface IPropsSpacer { type: 'horizontal' | 'vertical'; spacing: 'medium'; align?: 'start' | 'end' | 'center' | 'stretch'; children: Array; } export interface IAttachmentsWrapperProps { item: IArticle; attachments: Array; } export interface IAttachmentsWidgetProps extends IAttachmentsWrapperProps { // These props are passed in from the `AuthoringDirective` scope addAttachments(attachments: Array): void; removeAttachment(attachment: IAttachment): void; updateAttachment(attachment: IAttachment): void; updateItem?(updates: Partial): void; readOnly?: boolean; isWidget: boolean; editable: boolean; isLocked: boolean; isLockedByMe: boolean; isUploadValid(files: Array): boolean; } // HELPERS export interface ITreeNode { value: T; parent?: ITreeNode; children?: Array>; } // EDITOR3 export interface IEditor3AnnotationInputTab { label: string; selectedByDefault(annotationText: string, mode: 'create' | 'edit'): Promise; component: React.ComponentType; onAnnotationCreate(language: string, annotationText: string, definitionHtml: string): void; } export interface IPropsAnnotationInputComponent { annotationText: string; annotationInputComponent: React.ReactElement; annotationTypeSelect: JSX.Element; mode: 'create' | 'edit'; onCancel(): void; onApplyAnnotation(html: string): void; } // DATA API export interface IDataApi { findOne(endpoint: string, id: string): Promise; create(endpoint: string, item: Partial, urlParams?: Dictionary): Promise; query( endpoint: string, page: number, sortOption: ISortOption, filterValues: ICrudManagerFilters, max_results?: number, formatFiltersForServer?: (filters: ICrudManagerFilters) => ICrudManagerFilters, ): Promise>; queryRawJson(endpoint, params?: Dictionary): Promise; queryRaw(endpoint, params?: Dictionary): Promise; patch(endpoint, current: T, next: Partial): Promise; patchRaw(endpoint, id: T['_id'], etag: T['_etag'], patch: Partial): Promise; delete(endpoint, item: T): Promise; uploadFileWithProgress(endpoint: string, data: FormData, onProgress: (event: ProgressEvent) => void): Promise; } // EVENTS export interface IArticleUpdateEvent { user: string; items: {[itemId: string]: 1}; desks: {[itemId: string]: 1}; stages: {[itemId: string]: 1}; } export interface IResourceUpdateEvent { fields: {[key: string]: 1}; resource: string; _id: string; } export interface IResourceCreatedEvent { resource: string; _id: string; } export interface IResourceDeletedEvent { resource: string; _id: string; } export interface IEvents { articleEditStart: IArticle; articleEditEnd: IArticle; // Attachments attachmentsAdded: Array; attachmentRemoved: IAttachment; attachmentUpdated: IAttachment; } export interface IWebsocketMessage { event: string; extra: T; _created: string; _process: string; } export interface IPublicWebsocketMessages { 'content:update': IWebsocketMessage; 'resource:created': IWebsocketMessage; 'resource:updated': IWebsocketMessage; 'resource:deleted': IWebsocketMessage; } export interface INotifyMessageOptions { button?: { onClick(): void; label: string; } } export interface IElasticExistsQueryParams { field: string; } export interface IElasticExistsQuery { exists: IElasticExistsQueryParams; } export interface IElasticTermQueryParams { field: string; value: string; boost?: number; // Defaults to 1.0 } export interface IElasticTermQuery { term: {[field: string]: Omit}; } export interface IElasticTermsQueryParams { field: string; value: Array; boost?: number; // Defaults to 1.0 } export interface IElasticTermsQuery { terms: { [field: string]: IElasticTermsQuery['value']; boost?: IElasticTermsQuery['boost']; }; } export interface IElasticMatchPhraseQueryParams { field: string; query: string; analyzer?: string; // Defaults to the default analyzer } export interface IElasticMatchPhraseQuery { match_phrase: { [field: string]: Omit; } } export interface IElasticMatchQueryParams { field: string; query: string; analyzer?: string; // Defaults to default analyzer auto_generate_synonyms_phrase_query?: boolean; // Defaults to true fuzziness?: string; max_expansions?: number; // Defaults to 50 prefix_length?: number; // Defaults to 0 fuzzy_transpositions?: boolean; // Defaults to true fuzzy_rewrite?: string; lenient?: boolean; // Defaults to false operator?: 'OR' | 'AND'; // Defaults to `OR` minimum_should_match?: number | string; zero_terms_query?: 'none' | 'all'; // Defaults to `none` } export interface IElasticMatchQuery { match: { [field: string]: Omit; }; } export interface IElasticRangeQueryParams { field: string; gt?: string | number; gte?: string | number; lt?: string | number; lte?: string | number; format?: string; relation?: 'INTERSECTS' | 'CONTAINS' | 'WITHIN'; // Defaults to `INTERSECTS` time_zone?: string; boost?: number; // Defaults to 1.0 } export interface IElasticRangeQuery { range: { [field: string]: Omit; } } export interface IElasticQueryStringParams { query: string; default_field?: string; allow_leading_wildcard?: boolean; // Defaults to true analyze_wildcard?: boolean; // Defaults to false analyzer?: string; // Defaults to default analyzer auto_generate_synonyms_phrase_query?: boolean; // Defaults to true boost?: number; // Defaults to 1.0 default_operator?: 'OR' | 'AND'; // Defaults to 'OR' enable_position_increments?: boolean; // Defaults to true fields?: Array; fuzziness?: string; fuzzy_max_expansions?: number; // Defaults to 50 fuzzy_prefix_length?: number; // Defaults to 0 fuzzy_transpositions?: boolean; // Defaults to true lenient?: boolean; // Defaults to false max_determinized_states?: number; // Defaults to 10000 minimum_should_match?: string; quote_analyzer?: string; phrase_slop?: number; // Defaults to 0 quote_field_suffix?: string; rewrite?: string; time_zone?: string; } export interface IElasticQueryStringQuery { query_string: IElasticQueryStringParams; } export type IElasticQuery = IElasticExistsQuery | IElasticTermQuery | IElasticTermsQuery | IElasticMatchPhraseQuery | IElasticMatchQuery | IElasticRangeQuery | IElasticQueryStringQuery; export interface IElasticBoolQueryParams { must: Array; must_not: Array; filter?: Array; should?: Array; minimum_should_match?: string; boost?: number; // Defaults to 1.0 } export interface IElasticBoolQuery { bool: IElasticBoolQueryParams; } export interface IRootElasticQuery { query: { bool: IElasticBoolQueryParams; }; size?: number; from?: number; } export interface IElasticSearchApi { exists(params: IElasticExistsQueryParams): IElasticExistsQuery; term(params: IElasticTermQueryParams): IElasticTermQuery; terms(params: IElasticTermsQueryParams): IElasticTermsQuery; matchPhrase(params: IElasticMatchPhraseQueryParams): IElasticMatchPhraseQuery; match(params: IElasticMatchQueryParams): IElasticMatchQuery; range(params: IElasticRangeQueryParams): IElasticRangeQuery; queryString(params: IElasticQueryStringParams): IElasticQueryStringQuery; bool(params: IElasticBoolQueryParams): IElasticBoolQuery; } // Copied from 'superdesk-ui-framework/react/components/DatePicker.tsx // Otherwise we have to import it here, which causes issues with extensions export interface DatePickerLocaleSettings { firstDayOfWeek?: number; dayNames: string[]; dayNamesShort: string[]; dayNamesMin: string[]; monthNames: string[]; monthNamesShort: string[]; } // APPLICATION API export interface IAttachmentsApi { byArticle(article: IArticle): Promise>; byId(id: IAttachment['_id']): Promise; create(attachment: Partial): Promise; save(original: IAttachment, updates: Partial): Promise; delete(attachment: IAttachment): Promise; upload(attachment: Partial, file: File, onProgress?: (event: ProgressEvent) => void): Promise; download(attachment: IAttachment): void; getMediaId(attachment: IAttachment): IMedia['_id']; } export type ISuperdesk = DeepReadonly<{ dataApi: IDataApi, dataApiByEntity: { article: { query(parameters: IArticleQuery): Promise; }; }; elasticsearch: IElasticSearchApi; httpRequestJsonLocal(options: IHttpRequestJsonOptionsLocal): Promise; state: { articleInEditMode?: IArticle['_id']; }; instance: { config: ISuperdeskGlobalConfig }; ui: { article: { view(id: IArticle['_id']): void; // This isn't implemented for all fields accepting images. addImage(field: string, image: IArticle): void; /** * Programatically triggers saving of an article in edit mode. * Runs the same code as if "save" button was clicked manually. */ save(): void; }; alert(message: string): Promise; confirm(message: string, title?: string): Promise; showModal(component: React.ComponentType<{closeModal(): void}>): Promise; notify: { info(text: string, displayDuration?: number, options?: INotifyMessageOptions): void; success(text: string, displayDuration?: number, options?: INotifyMessageOptions): void; warning(text: string, displayDuration?: number, options?: INotifyMessageOptions): void; error(text: string, displayDuration?: number, options?: INotifyMessageOptions): void; }, framework: { getLocaleForDatePicker(targetLocale?: string): DatePickerLocaleSettings; }; }; entities: { article: { isLocked(article: IArticle): boolean; // returns true if locked by anyone, including the current user isLockedInCurrentSession(article: IArticle): boolean; isLockedInOtherSession(article: IArticle): boolean; isPersonal(article: IArticle): boolean; patch( article: IArticle, patch: Partial, dangerousOptions?: IDangerousArticlePatchingOptions, ): Promise; isArchived(article: IArticle): boolean; isPublished(article: IArticle): boolean; }; contentProfile: { get(id: string): Promise; }; vocabulary: { getIptcSubjects(): Promise>; getVocabulary(id: string): Promise>; }; desk: { getStagesOrdered(deskId: IDesk['_id']): Promise>; }; attachment: IAttachmentsApi; users: { getUsersByIds(ids: Array): Promise>; }; }; helpers: { assertNever(x: never): never; filterUndefined(values: Partial): Partial; filterKeys(original: T, keys: Array): Partial; stringToNumber(value?: string, radix?: number): number | undefined; numberToString(value?: number): string | undefined; notNullOrUndefined(x: null | undefined | T): x is T; }, components: { UserHtmlSingleLine: React.ComponentType<{html: string}>; getGenericListPageComponent(resource: string, formConfig: IFormGroup): React.ComponentType>; connectCrudManager( WrappedComponent: React.ComponentType, name: string, endpoint: string, formatFiltersForServer?: (filters: ICrudManagerFilters) => ICrudManagerFilters, ): React.ComponentType; ListItem: React.ComponentType; ListItemColumn: React.ComponentType; ListItemActionsMenu: React.ComponentType; List: { Item: React.ComponentType<{onClick: any}>; Row: React.ComponentType; Column: React.ComponentType<{grow: boolean}>; }, Grid: React.ComponentType; Alert: React.ComponentType; Figure: React.ComponentType; DropZone: React.ComponentType; Modal: React.ComponentType; ModalHeader: React.ComponentType; ModalBody: React.ComponentType; ModalFooter: React.ComponentType; Badge: React.ComponentType; SelectUser: React.ComponentType; UserAvatar: React.ComponentType<{userId: string}>; ArticleItemConcise: React.ComponentType<{article: IArticle}>; GroupLabel: React.ComponentType; Icon: React.ComponentType; TopMenuDropdownButton: React.ComponentType<{onClick: () => void; disabled?: boolean; active: boolean; pulsate?: boolean; 'data-test-id'?: string;}>; getDropdownTree: () => React.ComponentType>; Spacer: React.ComponentType; WidgetHeading: React.ComponentType; }; forms: { FormFieldType: typeof FormFieldType; generateFilterForServer(type: FormFieldType, value: any): any; isIFormGroupCollapsible(x: "inline" | IFormGroupCollapsible): x is IFormGroupCollapsible; isIFormGroup(x: IFormGroup | IFormField): x is IFormGroup; isIFormField(x: IFormGroup | IFormField): x is IFormField; getFormFieldPreviewComponent( item: { readonly [key: string]: any; }, formFieldConfig: any, options: {showAsPlainText?: boolean} = {} ): JSX.Element; }; localization: { gettext(message: string, params?: {[key: string]: string | number}): string; gettextPlural(count: number, singular: string, plural: string, params?: {[key: string]: string | number}): string; formatDate(date: Date | string): string; formatDateTime(date: Date): string; longFormatDateTime(date: Date | string): string; getRelativeOrAbsoluteDateTime( datetimeString: string, format: string, relativeDuration: number = 1, relativeUnit: string = 'days' ): string; }; privileges: { getOwnPrivileges(): Promise; hasPrivilege(privilege: string): boolean; }; preferences: { get(key: string): Promise; set( key: string, value: any, ): Promise; }; session: { getToken(): string; getCurrentUser(): Promise; getSessionId(): String; getCurrentUserId(): String; }; browser: { location: { getPage(): string; setPage(page: string); urlParams: { // Strings getString(field: string, defaultValue?: string): string | undefined; setString(field: string, value?: string); // Numbers getNumber(field: string, defaultValue?: number): number | undefined; setNumber(field: string, value?: number); // Booleans getBoolean(field: string, defaultValue?: boolean): boolean | undefined; setBoolean(field: string, value?: boolean); // Dates getDate(field: string, defaultValue?: Date): Date | undefined; setDate(field: string, value?: Date); // JSON getJson(field: string, defaultValue?: T): T | undefined; setJson(field: string, value?: T); }; }; }; utilities: { CSS: { getClass(originalName: string): string; getId(originalName: string): string; }; logger: { error(error: Error): void; warn(message: string, json: {[key: string]: any}): void; }; dateToServerString(date: Date): string; // outputs a string for parsing by the server memoize(func: T, maxCacheEntryCount = 1): T; generatePatch(a: Partial, b: Partial): Partial; stripHtmlTags(htmlString: string): string; getLinesCount(plainText: string): number | null; downloadBlob(data: BinaryType, mimetype: string, filename: string): void; arrayToTree( itemsFlat: Array, getId: (item: T) => string, getParentId: (item: T) => string | undefined | null, ): {result: Array>, errors: Array}; treeToArray(tree: Array>): Array; }; addWebsocketMessageListener( eventName: T, handler: (event: T extends keyof IPublicWebsocketMessages ? CustomEvent : CustomEvent> ) => void ): () => void; // returns a function to remove event listener addEventListener(eventName: T, fn: (arg: IEvents[T]) => void): void; removeEventListener(eventName: T, fn: (arg: IEvents[T]) => void): void; }>; export interface IAuthorsFieldOptions { displayField: keyof IUser; includeRoles: Array; // qcodes } // Use a union type to add more fields. Listing them explicitly here will help to get static typing in the instance config file. export type IListViewFieldWithOptions = { field: 'authors'; options: IAuthorsFieldOptions; }; export interface ISuperdeskGlobalConfig { // FROM SERVER default_language: string; disallowed_characters: Array; // applies to slugline schema: any; editor: { vidible?: any; picture?: any; }; feedback_url: any; override_ednote_for_corrections: any; override_ednote_template: any; default_genre: any; japanese_characters_per_minute: any; validator_media_metadata: any; publish_content_expiry_minutes: any; high_priority_queue_enabled: any; attachments_max_size: any; attachments_max_files: any; ingest_expiry_minutes: any; content_expiry_minutes: any; xmpp_auth: any; saml_auth: any; google_auth: any; saml_label: any; oidc_auth: any; keycloak_config: any; archive_autocomplete: boolean; /** allow updates for items which aren't published yet */ workflow_allow_multiple_updates: boolean; /** allow users who are not members of a desk to duplicate its content */ workflow_allow_duplicate_non_members: boolean; /** allow users to copy from desk to personal space */ workflow_allow_copy_to_personal: boolean; allow_updating_scheduled_items: boolean; corrections_workflow: boolean; // TANSA SERVER CONFIG tansa?: { base_url: string; app_id: string; app_version: string; user_id: string; profile_id: number; license_key: string; profiles: {[language: string]: number}; }, // FROM CLIENT server: { url: string; ws: any; }; apps: any; defaultRoute: string; startingDay: any; features: { swimlane?: { defaultNumberOfColumns: number; }; editor3?: boolean; qumu?: boolean; editorAttachments?: boolean; editorInlineComments?: boolean; editorSuggestions?: boolean; useTansaProofing?: boolean; editFeaturedImage?: any; validatePointOfInterestForImages?: any; autopopulateByline?: any; noPublishOnAuthoringDesk?: any; confirmMediaOnUpdate?: any; noMissingLink?: any; hideRoutedDesks?: any; autorefreshContent?: any; elasticHighlight?: any; onlyEditor3?: any; nestedItemsInOutputStage?: boolean; keepMetaTermsOpenedOnClick?: boolean; showCharacterLimit?: number; sendToPersonal?: boolean; publishFromPersonal?: boolean; customAuthoringTopbar?: { toDesk?: boolean; publish?: boolean; closeAndContinue?: boolean; publishAndContinue?: boolean; }, }; auth: { google: boolean }; ingest: { PROVIDER_DASHBOARD_DEFAULTS: { show_log_messages: boolean; show_ingest_count: boolean; show_time: boolean; log_messages: 'error'; show_status: boolean; } DEFAULT_SCHEDULE: { minutes: number; seconds: number; } DEFAULT_IDLE_TIME: { hours: number; minutes: number; }; }; confirm_spike: boolean; defaultTimezone: any; search: { useDefaultTimezone: any; }; search_cvs: any; view: { dateformat: string; // a combination of YYYY, MM, and DD with a custom separator e.g. 'MM/DD/YYYY' timeformat: string; }; user: { sign_off_mapping?: string; username_pattern?: string; }; infoRemovedFields: {}; previewSubjectFilterKey: any; authoring?: { timeToRead?: any; lineLength?: number; preview?: { hideContentLabels: boolean; }; }; ui: { publishEmbargo?: any; sendAndPublish?: any; italicAbstract?: any; sendPublishSchedule?: boolean; sendEmbargo?: boolean; sendDefaultStage?: 'working' | 'incoming'; authoring?: { firstLine?: { wordCount?: boolean; }; }; }; list: { narrowView?: any; singleLineView?: any; singleLine?: any; priority?: Array; firstLine?: Array, secondLine?: Array, relatedItems?: { firstLine: Array, secondLine: Array, }; }; gridViewFields: Array; gridViewFooterFields: { left: Array; right: Array; }; swimlaneViewFields: any; item_profile: { change_profile: any; }; model: { timeformat: string; dateformat: string; }; monitoring: { scheduled: any; }; defaultSearch: any; profile: any; profileLanguages: Array; subscriptionLevel: any; workspace: any; activity: any; analytics: { piwik: { url: any; }; ga: { id: any; }; }; longDateFormat: any; shortTimeFormat: any; shortDateFormat: any; shortWeekFormat: any; ArchivedDateFormat: any; ArchivedDateOnCalendarYear: any; iframely: { key: any; }; raven: { dsn: any; }; version: any; releaseDate: any; isTestEnvironment: any; environmentName: any; paths: { superdesk: any; }; editor3: { browserSpellCheck: boolean; }; pictures?: { minWidth?: number; minHeight?: number; } langOverride: {[langCode: string]: {[originalString: string]: string}}; transmitter_types: Array<{ type: string; name: string; config: any; }>; userOnlineMinutes: number; iMatricsFields: { entities: { [key: string]: { name: string; order: number; }; }, others: { [key: string]: { name: string; order: number; }; } }; } // CUSTOM FIELD TYPES export interface IEditorComponentProps { item: IArticle; value: IValue; setValue: (value: IValue) => void; readOnly: boolean; config: IConfig; } export interface IPreviewComponentProps { item: IArticle; value: any; } // IConfig must be a plain object export interface IConfigComponentProps { config: IConfig | null; onChange(config: IConfig): void; } export interface ICustomFieldType { id: string; label: string; editorComponent: React.ComponentType>; previewComponent: React.ComponentType; configComponent?: React.ComponentType>; } // IPTC picture metadata export interface IPTCMetadata { // envelope Destination: string; ServiceIdentifier: string; ProductID: string; DateSent: string; TimeSent: string; // application ObjectName: string; EditStatus: string; Urgency: string; SubjectReference: string; Category: string; SupplementalCategories: string; Keywords: string; ContentLocationCode: string; ContentLocationName: string; ReleaseDate: string; ReleaseTime: string; ExpirationDate: string; ExpirationTime: string; SpecialInstructions: string; DateCreated: string; TimeCreated: string; 'By-line': string; 'By-lineTitle': string; City: string; 'Sub-location': string; 'Province-State': string; 'Country-PrimaryLocationCode': string; 'Country-PrimaryLocationName': string; OriginalTransmissionReference: string; Headline: string; Credit: string; Source: string; CopyrightNotice: string; Contact: string; 'Caption-Abstract': string; 'Writer-Editor': string; LanguageIdentifier: string; } export interface ISubject { name: string; description?: string; qcode: string; scheme?: string; translations?: {}; altids?: {[key: string]: string}; aliases?: Array; /** provider name, eg. imatrics */ source?: string; /** original source of the data, eg. wikidata */ original_source?: string; parent?: string; } }