public/components/content-list-item/content-list-item.js (317 lines of code) (raw):
import { getContentLengthCategory, getCommissionedLengthTitle } from "./word-count-helpers.ts";
import { getArticleFormat, getArticleFormatTitle } from "../../lib/model/special-formats.ts"
import _ from 'lodash';
const OPHAN_PATH = 'https://dashboard.ophan.co.uk/summary?path=/',
LIVE_PATH = 'http://www.theguardian.com/';
function wfContentItemParser(config, wfFormatDateTime, statusLabels, sections) {
/*jshint validthis:true */
function getFullOfficeString(office) {
var offices = {
'AU': 'Australia',
'US': 'United States of America',
'UK': 'United Kingdom'
};
return offices[office];
}
function getViewerURL(path, isLive) {
return config.viewerUrl + '/' + (isLive ? 'live' : 'preview') + '/' + path;
}
function toTitleCase(str) {
return str.replace(/\b\w/g, function (txt) { return txt.toUpperCase(); });
}
function toInitials(str) {
if(str.includes("@")){
return str
.split("@")[0]
.toLowerCase()
.replace(".casual", "")
.replace(".freelancer", "")
.split(".")
.map(_ => _.substring(0,1).toUpperCase())
.join("");
}
if (str.length <= 3) { return str; }
var initials = str.match(/\b(\w)/g).join('');
// If we have more than 3 initials choose the first 2 and the last
if (initials.length > 3) { initials = initials.slice(0,2) + initials.slice(-1); }
return initials;
}
function stripHtml(text) {
return typeof(text) === 'string' ? text.replace(/<[^>]+>/gm, '') : '';
}
function getCommissioningDeskNames(commissioningDeskIdString) {
if (typeof commissioningDeskIdString === 'string' && !(/[a-z]/i.test(commissioningDeskIdString))) {
var commissioningDeskIds = commissioningDeskIdString.split(",");
return commissioningDeskIds.map((id) => {
return _wfConfig.commissioningDesks.filter(desk => desk.id === id.toNumber())[0];
});
} else {
return [];
}
}
var contentStatusValues = statusLabels.filter((status) => status.value !== 'Stub');
class ContentItemLinks {
constructor(item) {
if (item.composerId) {
this.composer = `${config.composerViewContent}/${item.composerId}`;
}
if (item.editorId) {
if (item.contentType === "media") {
this.mediaAtomMaker = `${config.mediaAtomMakerViewAtom}${item.editorId}`;
this.editor = this.mediaAtomMaker;
} else {
if (config.atomTypes.includes(item.contentType)) {
this.atomWorkshop = `${config.atomWorkshopViewAtom}/${item.contentType}/${item.editorId}/edit`;
this.editor = this.atomWorkshop;
}
}
}
if (item.path) {
this.preview = getViewerURL(item.path);
}
if (item.published && item.path) {
this.live = LIVE_PATH + item.path;
this.ophan = OPHAN_PATH + item.path;
}
}
}
class ContentItemView {
constructor(item) {
this.update(item);
}
update(item) {
// TODO: Stubs have a different structure to content items
this.id = item.id || item.stubId;
this.composerId = item.composerId;
this.editorId = item.editorId;
this.wordCount = item.wordCount;
this.printWordCount = item.printWordCount;
this.commissionedLength = item.commissionedLength;
this.missingCommissionedLengthReason = item.missingCommissionedLengthReason;
this.headline = item.headline;
this.standfirst = stripHtml(item.standfirst);
this.workingTitle = item.workingTitle || item.title;
this.priority = item.priority;
this.prioritySortValue = item.priority !== undefined ? item.priority : 0;
this.hasComments = !!(item.commentable);
this.commentsTitle = this.hasComments ? 'on' : 'off';
this.hasMainMedia = !!(item.hasMainMedia)
this.trailtext = stripHtml(item.trailtext);
this.trailImageUrl = item.trailImageUrl;
this.assignee = item.assignee;
this.assigneeEmail = item.assigneeEmail;
this.assigneeInitials = item.assignee && toInitials(item.assignee);
this.contentType = item.contentType;
this.contentTypeTitle = toTitleCase(item.contentType);
this.formatIcon = getArticleFormat(item.contentType, item.displayHint);
this.formatTitle = getArticleFormatTitle(item.contentType, item.displayHint);
this.office = item.prodOffice;
this.officeTitle = getFullOfficeString(item.prodOffice);
this.status = item.status || 'Stub';
this.statusValues = this.status === 'Stub' ? statusLabels : contentStatusValues;
this.section = sections.filter((section) => section.name === item.section)[0]; // Get section object
this.needsLegal = item.needsLegal;
this.needsPictureDesk = item.needsPictureDesk;
this.note = item.note;
this.commissioningDesks = getCommissioningDeskNames(item.commissioningDesks);
// TODO: Decide if this is due or deadline
this.deadline = item.due;
this.created = item.createdAt;
this.lastModified = item.lastModified;
this.lastModifiedBy = item.lastModifiedBy;
this.firstPublished = item.timePublished;
this.hasEmbargoedDate =
item.embargoedUntil &&
new Date(item.embargoedUntil).getTime() > (new Date()).getTime();
this.isTakenDown = item.takenDown;
this.isPublished = item.published;
this.isEmbargoed = this.hasEmbargoedDate || item.embargoedIndefinitely;
this.isScheduled = Boolean(item.scheduledLaunchDate);
var lifecycleState = this.lifecycleState(item);
this.lifecycleState = lifecycleState.display;
this.lifecycleStateKey = lifecycleState.key;
this.lifecycleStateSupl = lifecycleState.supl;
this.lifecycleStateSuplDate = lifecycleState.suplDate;
this.publishedStates = this.publishedStates(item);
this.links = new ContentItemLinks(item);
this.path = item.path;
this.isOwnedByInCopy = item.activeInInCopy;
this.storyBundleId = item.storyBundleId;
/* it may be linked with InCopy but owned by composer */
this.linkedWithIncopy = (typeof item.storyBundleId === "string" &&
item.storyBundleId.length > 0);
this.incopyTitle = this.linkedWithIncopy ?
'Linked with InCopy Story Bundle ' + this.storyBundleId :
'Not linked with InCopy';
this.optimisedForWeb = !!(item.optimisedForWeb);
this.optimisedForWebChanged = !!(item.optimisedForWebChanged);
this.sensitive = !!(item.sensitive);
this.legallySensitive = !!(item.legallySensitive);
if (this.optimisedForWebChanged) {
this.optimisedForWebTitle = 'Content has been modified since being optimised'
} else {
this.optimisedForWebTitle = this.optimisedForWeb ? 'Optimised for web' : 'Not optimised for web';
}
this.sensitiveTitle = 'This content features children, vulnerable people, or is on a topic that is likely to attract online abuse.';
this.legallySensitiveTitle = 'This content involves active criminal proceedings.';
this.shortActualPrintLocationDescription = item.shortActualPrintLocationDescription;
this.longActualPrintLocationDescription = item.longActualPrintLocationDescription;
this.actualNewspaperPageNumber = item.actualNewspaperPageNumber;
this.actualNewspaperPublicationDate = item.actualNewspaperPublicationDate;
this.shortPlannedPrintLocationDescription = item.shortPlannedPrintLocationDescription;
this.longPlannedPrintLocationDescription = item.longPlannedPrintLocationDescription;
this.plannedNewspaperPageNumber = item.plannedNewspaperPageNumber;
this.plannedNewspaperPublicationDate = item.plannedNewspaperPublicationDate;
this.statusInPrint = item.statusInPrint;
this.lastModifiedInPrintBy = item.lastModifiedInPrintBy;
// These are derived values used for display purposes.
const {
shortPrintLocationDescription,
newspaperPageNumber,
newspaperPublicationDate,
longPrintLocationDescription,
printLocationType
} = this.getPrintValues(item);
if (shortPrintLocationDescription) {
const newspaperPageNumberStr = newspaperPageNumber
? `p. ${newspaperPageNumber}`
: '';
this.printLocationDisplayString = `${shortPrintLocationDescription}<br />${newspaperPageNumberStr} ${wfFormatDateTime(newspaperPublicationDate, 'DD MMMM')}`;
// We use 8601 dates to make the date sortable.
this.printLocationBookSection = shortPrintLocationDescription;
this.printLocationPublicationDate =wfFormatDateTime(newspaperPublicationDate, 'ISO8601');
this.printLocationPageNumber = newspaperPageNumber !== undefined ? newspaperPageNumber : Number.MAX_VALUE;
this.longPrintLocationDescription = longPrintLocationDescription;
this.printLocationType = printLocationType;
}
this.item = item;
}
getPrintValues(item) {
const printLocationType = this.getPrintLocationType(item);
if (printLocationType === 'actual') {
return {
longPrintLocationDescription: item.longActualPrintLocationDescription,
shortPrintLocationDescription: item.shortActualPrintLocationDescription,
newspaperPageNumber: item.actualNewspaperPageNumber,
newspaperPublicationDate: new Date(item.actualNewspaperPublicationDate),
printLocationType
}
}
if (printLocationType === 'planned') {
return {
longPrintLocationDescription: item.longPlannedPrintLocationDescription,
shortPrintLocationDescription: item.shortPlannedPrintLocationDescription,
newspaperPageNumber: item.plannedNewspaperPageNumber,
newspaperPublicationDate: new Date(item.plannedNewspaperPublicationDate),
printLocationType
}
}
return {}; // For easy destructuring in the caller;
}
getPrintLocationType(item) {
if (item.shortActualPrintLocationDescription) {
return 'actual';
}
if (item.shortPlannedPrintLocationDescription) {
return 'planned';
}
return undefined;
}
allLifecycleState(item) {
var states = [
{ "display": "Published", "key": "published", "active": item.published && !item.takenDown, "suplDate": item.timePublished, "group": 1 },
{
"display": item.embargoedIndefinitely ? "Publication prevented": "Embargoed",
"key": "embargoed",
"active": this.isEmbargoed,
"suplDate": (!item.embargoedIndefinitely && this.hasEmbargoedDate) ? item.embargoedUntil : undefined,
"group": 2
},
{ "display": "Scheduled", "key": "scheduled", "active": this.isScheduled, "suplDate": item.scheduledLaunchDate, "group": 2 },
{ "display": "Taken down", "key": "takendown", "active": item.takenDown, "suplDate": item.timeTakenDown, "group": 3 },
{ "display": "", "key": "draft", "active": true, "group": 4 } // Base state
];
return states.filter((o) => { return o.active === true; });
}
lifecycleState(item) {
// Highest priority at the top!
const states = this.allLifecycleState(item);
return states[0];
}
publishedStates(item) {
// the group of states with the highest priority
const states = this.allLifecycleState(item);
const group = states[0].group;
return _.groupBy(states, "group")[group].map(state => {
return { display: state.display, suplDate: state.suplDate};
});
}
}
this.parse = function(item) {
return new ContentItemView(item);
};
}
/**
* Directive allowing the contentListItems to interact with the details drawer
* @param $rootScope
*/
var wfContentListItem = function ($rootScope, statuses, legalValues, pictureDeskValues, sections, config) {
return {
restrict: 'A',
template: () => {
return $rootScope.contentItemTemplate;
},
scope: {
contentItem: '=',
contentList: '=',
template: '='
},
controller: ($scope) => {
$scope.statusValues = statuses;
$scope.legalValues = legalValues;
$scope.pictureDeskValues = pictureDeskValues;
$scope.sections = sections;
$scope.isSupportedAtomType = config.atomTypes.includes($scope.contentItem.contentType);
const gridHost = window && window.location && window.location.host &&
window.location.host.toLowerCase().replace("workflow", "media").replace("code", "test");
$scope.pinboardInGridLink = `https://${gridHost}/search?pinboardId=${$scope.contentItem.id || $scope.contentItem.stubId}`
},
link: function ($scope, elem) {
/**
* Emit an event telling the details drawer to move itself to this element, update and display.
* @param {Object} contentItem - this contentItem
*/
elem.bind('click', () => {
$rootScope.$emit('contentItem.select', $scope.contentItem, elem);
});
}
};
};
/**
* Attribute directive: wf-content-item-update-action
*
* Listens to when an ng-model changes on the same control, then
* emits the action as an event to be captured in a controller elsewhere.
*/
function wfContentItemUpdateActionDirective() {
return {
restrict: 'A',
require: 'ngModel',
link: ($scope, $element, $attrs, ngModel) => {
var oldModelValue;
var $setter = ngModel.$setViewValue;
ngModel.$setViewValue = function() {
oldModelValue = ngModel.$modelValue;
$setter.apply(this, arguments);
};
ngModel.$viewChangeListeners.push(() => {
var field = $attrs.wfContentItemUpdateAction;
var msg = {
contentItem: $scope.contentItem,
data: {},
oldValues: {},
source: ngModel
};
msg.data[field] = ngModel.$modelValue;
msg.oldValues[field] = oldModelValue;
$scope.$emit('contentItem.update', msg);
});
}
};
}
function wfGetPriorityStringFilter (priorities) {
return function (priorityValue) {
return (priorities.filter((priority) => priority.value === priorityValue)[0].name).toLowerCase();
};
}
function wfCommissionedLengthCtrl ($scope) {
$scope.$watch('contentItem.wordCount', function (newWordCount) {
const commissionedLength = $scope.contentItem.commissionedLength;
$scope.lengthStatus = getContentLengthCategory(commissionedLength, newWordCount);
$scope.commissionedLengthTitle = getCommissionedLengthTitle(commissionedLength, newWordCount)
});
$scope.formatReason = (missingCommissionedLengthReason) => {
const reasons = {
"BreakingNews": "Breaking News",
}
return reasons[missingCommissionedLengthReason] || missingCommissionedLengthReason;
}
}
export { wfContentListItem, wfContentItemParser, wfContentItemUpdateActionDirective, wfGetPriorityStringFilter, wfCommissionedLengthCtrl };