import _ from 'lodash'; import {IArticle} from 'superdesk-api'; import {dataApi} from 'core/helpers/CrudManager'; /** * @ngdoc service * @module superdesk.apps.archive * @name family * @requires api * @requires desks * @description Family Service is responsible for returning related items of a given story */ FamilyService.$inject = ['api', 'desks']; export function FamilyService(api, desks) { const repo = 'archive,published'; /** * @ngdoc method * @name family#fetchItems * @public * @description Returns duplicates of a given story * @param {string} familyId * @param {Object} excludeItem * @returns {Object} */ this.fetchItems = (familyId, excludeItem) => { let filter: Array = [ {not: {term: {state: 'spiked'}}}, {term: {family_id: familyId}}, ]; if (excludeItem && excludeItem.unique_id) { filter.push({not: {term: {unique_id: excludeItem.unique_id}}}); } return query(filter, 'versioncreated', 'desc'); }; /** * @ngdoc method * @name family#fetchMediaUsedItems * @public * @description Returns stories which have used a media item * @param {string} mediaUniqueId * @returns {Array} */ this.fetchMediaUsedItems = (mediaUniqueId) => { let filter = [ {not: {term: {state: 'spiked'}}}, {term: {'associations.featuremedia.unique_id': mediaUniqueId}}, ]; return query(filter, 'versioncreated', 'desc'); }; this.fetchLinks = (item: IArticle) => dataApi.query('links', 1, null, {uri: item.uri, guid: item.guid || item._id}) .then((resp) => { if (resp._items.length === 0 && item.used) { // fallback return this.fetchMediaUsedItems(item.unique_id); } return resp; }).then((resp) => resp._items); /** * @ngdoc method * @name family#query * @private * @description Creates the query object and performs the query * @param {Object} filter * @param {string} sortField * @param {string} order * @param {Object} queryString * @returns {Object} */ const query = (filter: any, sortField: string, order: string, queryString?: string) => { let params: any = { repo: repo, source: { query: {filtered: {filter: { and: filter, }}}, size: 200, from: 0, }, }; if (queryString) { params.source.query.filtered.query = queryString; } params.source.sort = {}; params.source.sort[sortField] = order; return api.query('search', params); }; /** * @ngdoc method * @name family#fetchRelatedItems * @public * @description Returns takes, updates, corrections and kills of a given event_id * @param {Object} item - story to get related items of * @returns {Object} */ this.fetchRelatedItems = (item) => { let filter = [ {not: {term: {state: 'spiked'}}}, {term: {event_id: item.event_id}}, {not: {term: {type: 'composite'}}}, ]; return query(filter, 'versioncreated', 'asc'); }; this.fetchRelatedByState = (item: IArticle, state: Array) => api.query('archive_related', { source: {query: {bool: {must: {terms: {state: state}}}}}, }, item) .then((data) => data._items); /** * @ngdoc method * @name family#fetchRelatableItems * @public * @description Returns any story potentially linkable * @param {string} keyword - slugline to be matched * @param {string} sluglineMatch - type of matching rule for slugline * @param {item} item - authoring item that the user is trying to link * @param {string} modificationDateAfter - filter for versioncreated * @returns {Object} */ this.fetchRelatableItems = (keyword, sluglineMatch, item, modificationDateAfter) => { let filter: Array = [ {not: {term: {state: 'spiked'}}}, {not: {term: {event_id: item.event_id}}}, {not: {term: {type: 'composite'}}}, {not: {term: {last_published_version: 'false'}}}, {term: {type: item.type}}, ]; let queryString = null; let queryRelatedItem = []; let sanitizedKeyword = keyword.replace(/[\\:]/g, '').replace(/\//g, '\\/'); let queryWords = sanitizedKeyword.split(' '); const addSlugs = function(list, words) { words.forEach((w) => { if (w) { list.push('slugline:(' + w + ')'); } }); }; // process creation date if (modificationDateAfter) { let dateQuery: any = {}; dateQuery.versioncreated = { gte: modificationDateAfter, }; filter.push({range: dateQuery}); } switch (sluglineMatch) { case 'ANY': // any words in the slugline if (keyword.indexOf(' ') >= 0) { queryRelatedItem.push('slugline:("' + sanitizedKeyword + '")'); } addSlugs(queryRelatedItem, queryWords); if (queryRelatedItem.length) { queryString = { query_string: { query: queryRelatedItem.join(' '), lenient: true, default_operator: 'OR', }, }; } break; case 'PREFIX': // phrase prefix queryString = { match_phrase_prefix: { 'slugline.phrase': sanitizedKeyword, }, }; break; default: // exact match on slugline queryString = { query_string: { query: 'slugline.phrase:("' + sanitizedKeyword + '")', lenient: true, }, }; } return query(filter, 'firstcreated', 'asc', queryString); }; /** * @ngdoc method * @name family#fetchDesks * @public * @description Returns the fetched desk list of a given story * @param {Object} item - story * @param {bookean} excludeSelf * @returns {Object} */ this.fetchDesks = (item, excludeSelf) => this.fetchItems(item.state === 'ingested' ? item._id : item.family_id, excludeSelf ? item : undefined) .then((items) => { let deskList = []; let deskIdList = []; _.each(items._items, (i) => { if (i.task && i.task.desk && desks.deskLookup[i.task.desk]) { if (deskIdList.indexOf(i.task.desk) < 0) { var _isMember = !_.isEmpty(_.find(desks.userDesks, {_id: i.task.desk})); deskList.push( { desk: desks.deskLookup[i.task.desk], count: 1, itemId: i._id, isUserDeskMember: _isMember, item: i, }); deskIdList.push(i.task.desk); } else { deskList[deskIdList.indexOf(i.task.desk)].count += 1; } } }); return deskList; }); }