diff --git a/client/actions/events/api.ts b/client/actions/events/api.ts index 33d3e18b2..c0c51cdfa 100644 --- a/client/actions/events/api.ts +++ b/client/actions/events/api.ts @@ -657,7 +657,7 @@ const fetchEventTemplates = () => (dispatch, getState, {api}) => { }); }; -const createEventTemplate = (itemId) => (dispatch, getState, {api, modal, notify}) => { +const createEventTemplate = (item: IEventItem) => (dispatch, getState, {api, modal, notify}) => { modal.prompt(gettext('Template name')).then((templateName) => { api('events_template').query({ where: { @@ -671,7 +671,24 @@ const createEventTemplate = (itemId) => (dispatch, getState, {api, modal, notify const doSave = () => { api('events_template').save({ template_name: templateName, - based_on_event: itemId, + based_on_event: item._id, + data: { + embedded_planning: item.associated_plannings.map((planning) => ({ + coverages: planning.coverages.map((coverage) => ({ + coverage_id: coverage.coverage_id, + g2_content_type: coverage.planning.g2_content_type, + desk: coverage.assigned_to.desk, + user: coverage.assigned_to.user, + language: coverage.planning.language, + news_coverage_status: coverage.news_coverage_status.qcode, + scheduled: coverage.planning.scheduled, + genre: coverage.planning.genre?.qcode, + slugline: coverage.planning.slugline, + ednote: coverage.planning.ednote, + internal_note: coverage.planning.internal_note, + })), + })), + }, }) .then(() => { dispatch(fetchEventTemplates()); diff --git a/client/actions/main.ts b/client/actions/main.ts index 9218363f7..302aee95f 100644 --- a/client/actions/main.ts +++ b/client/actions/main.ts @@ -17,6 +17,7 @@ import { IWebsocketMessageData, ITEM_TYPE, IEventTemplate, + IEventItem, } from '../interfaces'; import { @@ -176,12 +177,44 @@ const createNew = (itemType, item = null, updateUrl = true, modal = false) => ( }, 'create', updateUrl, modal) ); +function getEventsAssociatedItems(template: IEventTemplate): IEventItem['associated_plannings'] | [] { + const embeddedPlanning = template.data?.embedded_planning; + + return embeddedPlanning + ? embeddedPlanning.map((embedded) => ({ + _id: generateTempId(), + slugline: template.data?.slugline, + language: template.data?.language, + coverages: embedded.coverages.map((coverage) => ({ + coverage_id: coverage.coverage_id, + planning: { + g2_content_type: coverage.g2_content_type, + scheduled: coverage.scheduled, + language: coverage.language, + genre: coverage.genre ? {qcode: coverage.genre} : undefined, + slugline: coverage.slugline, + ednote: coverage.ednote, + internal_note: coverage.internal_note, + }, + assigned_to: { + desk: coverage.desk, + user: coverage.user, + }, + news_coverage_status: { + qcode: coverage.news_coverage_status, + }, + })), + })) + : []; +} + function createEventFromTemplate(template: IEventTemplate) { return self.createNew(ITEM_TYPE.EVENT, { ...template.data, dates: { tz: template.data.dates?.tz }, + associated_plannings: self.getEventsAssociatedItems(template) }); } @@ -1663,6 +1696,7 @@ const self = { changeEditorAction, notifyPreconditionFailed, setUnsetUserInitiatedSearch, + getEventsAssociatedItems, }; export default self; diff --git a/client/components/Main/ItemEditor/EditorHeader.tsx b/client/components/Main/ItemEditor/EditorHeader.tsx index 2874078aa..62fd15662 100644 --- a/client/components/Main/ItemEditor/EditorHeader.tsx +++ b/client/components/Main/ItemEditor/EditorHeader.tsx @@ -395,6 +395,7 @@ export class EditorHeader extends React.Component { loading, itemManager, autoSave, + diff, } = this.props; const states = this.getItemStates(); @@ -428,7 +429,7 @@ export class EditorHeader extends React.Component { {!loading && !hideItemActions && ( { () => { const message = gettext('Save changes before creating a template?'); - dispatch(allActions.main.openActionModalFromEditor(item, message, (updatedItem) => { - dispatch(eventsApi.createEventTemplate(updatedItem._id)); + dispatch(allActions.main.openActionModalFromEditor(item, message, () => { + dispatch(eventsApi.createEventTemplate(item)); })); }, [EVENTS.ITEM_ACTIONS.MARK_AS_COMPLETED.actionName]: diff --git a/server/planning/events/events_template.py b/server/planning/events/events_template.py index fcb19ff6d..f920434f1 100644 --- a/server/planning/events/events_template.py +++ b/server/planning/events/events_template.py @@ -19,6 +19,7 @@ from superdesk.utils import ListCursor from planning.common import DUPLICATE_EVENT_IGNORED_FIELDS from apps.archive.common import get_user +from .events_schema import events_schema logger = logging.getLogger(__name__) @@ -31,6 +32,7 @@ class EventsTemplateResource(Resource): endpoint_name = "events_template" resource_methods = ["GET", "POST"] item_methods = ["GET", "DELETE", "PATCH", "PUT"] + privileges = { "GET": "planning_event_management", "POST": "planning_event_templates", @@ -81,7 +83,9 @@ class EventsTemplateResource(Resource): "readonly": True, }, "subject": {"type": "list", "schema": {"type": "dict"}, "readonly": True}, + "embedded_planning": events_schema["embedded_planning"], } + schema = { "template_name": { "type": "string", @@ -162,7 +166,7 @@ def _get_event(_id): def _fill_event_template(self, doc): event = self._get_event(doc["based_on_event"]) - doc["data"] = event.copy() + doc.setdefault("data", {}).update(event.copy()) for field in DUPLICATE_EVENT_IGNORED_FIELDS: doc["data"].pop(field, None)