Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

As a user I want to have the Coming Up field active when I create a new story in save as a template. [SDBELGA-466] #4033

Merged
merged 6 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 95 additions & 35 deletions scripts/apps/templates/controllers/CreateTemplateController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,55 @@
import notifySaveError from '../helpers';
import {extensions} from 'appConfig';
import {IArticle, ICustomFieldType, IVocabulary} from 'superdesk-api';

/**
* Iterate content profile fields.
* Check if field is a custom field from extension.
* If it is, run `onTemplateCreate` middleware on it
* and update the value.
*/
function applyMiddleware(_item: IArticle, content, vocabularies): Promise<IArticle> {
// Custom field types with `onTemplateCreate` defined. From all extensions.
const fieldTypes: {[id: string]: ICustomFieldType<any>} = {};

Object.values(extensions).forEach((ext) => {
ext?.activationResult?.contributions?.customFieldTypes?.forEach((customField: ICustomFieldType<any>) => {
if (customField.onTemplateCreate != null) {
fieldTypes[customField.id] = customField;
}
});
});

return vocabularies.getVocabularies().then((_vocabularies: Array<IVocabulary>) => {
const fakeScope: any = {};

return content.setupAuthoring(_item.profile, fakeScope, _item).then(() => {
let itemNext: IArticle = {..._item};

for (const fieldId of Object.keys(fakeScope.editor)) {
const vocabulary = _vocabularies.find(({_id}) => _id === fieldId);

if (vocabulary != null && fieldTypes[vocabulary.custom_field_type] != null) {
const config = vocabulary.custom_field_config ?? {};
const customField = fieldTypes[vocabulary.custom_field_type];

itemNext = {
...itemNext,
extra: {
...itemNext.extra,
[fieldId]: customField.onTemplateCreate(
itemNext?.extra?.[fieldId],
config,
),
},
};
}
}

return itemNext;
});
});
}

CreateTemplateController.$inject = [
'item',
Expand All @@ -10,6 +61,8 @@ CreateTemplateController.$inject = [
'lodash',
'privileges',
'session',
'content',
'vocabularies',
];
export function CreateTemplateController(
item,
Expand All @@ -21,6 +74,8 @@ export function CreateTemplateController(
_,
privileges,
session,
content,
vocabularies,
) {
var self = this;

Expand Down Expand Up @@ -76,43 +131,48 @@ export function CreateTemplateController(
|| self.canEdit() !== true;

function save() {
var data = {
template_name: self.name,
template_type: self.type,
template_desks: self.is_public ? [self.desk] : null,
is_public: self.is_public,
data: templates.pickItemData(item),
};

var template = self.template ? self.template : data;
var diff: any = self.template ? data : null;

// in case there is old template but user renames it
// or user is not allowed to edit it - create a new one
if (self.willCreateNew()) {
template = data;
diff = null;

if (self.canEdit() !== true) {
template.is_public = false;
template.user = session.identity._id;
template.template_desks = null;
const _item: IArticle = JSON.parse(JSON.stringify(templates.pickItemData(item)));
const sessionId = session.identity._id;

return applyMiddleware(_item, content, vocabularies).then((itemAfterMiddleware) => {
var data = {
template_name: self.name,
template_type: self.type,
template_desks: self.is_public ? [self.desk] : null,
is_public: self.is_public,
data: itemAfterMiddleware,
};

var template = self.template ? self.template : data;
var diff: any = self.template ? data : null;

// in case there is old template but user renames it
// or user is not allowed to edit it - create a new one
if (self.willCreateNew()) {
template = data;
diff = null;

if (self.canEdit() !== true) {
template.is_public = false;
template.user = sessionId;
template.template_desks = null;
}
}
}

// if template is made private, set current user as template owner
if (template.is_public === true && diff?.is_public === false) {
diff.user = session.identity._id;
}
// if template is made private, set current user as template owner
if (template.is_public === true && diff?.is_public === false) {
diff.user = sessionId;
}

return api.save('content_templates', template, diff)
.then((_data) => {
self._issues = null;
return _data;
}, (response) => {
notifySaveError(response, notify);
self._issues = response.data._issues;
return $q.reject(self._issues);
});
return api.save('content_templates', template, diff)
.then((_data) => {
self._issues = null;
return _data;
}, (response) => {
notifySaveError(response, notify);
self._issues = response.data._issues;
return $q.reject(self._issues);
});
});
}
}
62 changes: 42 additions & 20 deletions scripts/apps/templates/tests/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@ describe('templates', () => {
describe('templates widget', () => {
var existingTemplate = {template_name: 'template1', template_desks: ['sports'], is_public: true, user: 'foo'};

beforeEach(inject((desks, api, $q, session, privileges) => {
beforeEach(inject((desks, api, $q, session, privileges, vocabularies, content) => {
spyOn(desks, 'fetchCurrentUserDesks').and.returnValue($q.when({_items: []}));
spyOn(api, 'save').and.returnValue($q.when({}));
spyOn(api, 'find').and.returnValue($q.when(existingTemplate));
spyOn(privileges, 'userHasPrivileges').and.returnValue(true);
session.identity = {_id: 'foo', user_type: 'user'};
spyOn(vocabularies, 'getVocabularies').and.returnValue(Promise.resolve([]));
spyOn(content, 'setupAuthoring').and.callFake((profile, scope, item) => {
scope.editor = {};
scope.schema = {};

return Promise.resolve({});
});
}));

it('can create template', inject(($controller, api) => {
it('can create template', (done) => inject(($controller, api) => {
var item = _.create({slugline: 'FOO', headline: 'foo'});
var ctrl = $controller('CreateTemplateController', {item: item});

Expand All @@ -28,20 +35,25 @@ describe('templates', () => {
ctrl.desk = 'news';
ctrl.hasCrops = true;
ctrl.save();
expect(api.save).toHaveBeenCalledWith('content_templates', {
template_name: 'test',
template_type: 'create',
template_desks: null,
is_public: false,
user: 'foo',
data: {
headline: 'foo',
slugline: 'FOO',
},
}, null);

setTimeout(() => {
expect(api.save).toHaveBeenCalledWith('content_templates', {
template_name: 'test',
template_type: 'create',
template_desks: null,
is_public: false,
user: 'foo',
data: {
headline: 'foo',
slugline: 'FOO',
},
}, null);

done();
}, 200);
}));

it('can update template', inject(($controller, api, $rootScope, session) => {
it('can update template', (done) => inject(($controller, api, $rootScope, session) => {
var item = _.create({slugline: 'FOO', template: '123'});
var ctrl = $controller('CreateTemplateController', {item: item});

Expand All @@ -53,10 +65,15 @@ describe('templates', () => {
expect(ctrl.type).toBe('create');
expect(ctrl.desk).toBe('sports');
ctrl.save();
expect(api.save.calls.argsFor(0)[1]).toBe(existingTemplate);

setTimeout(() => {
expect(api.save.calls.argsFor(0)[1]).toBe(existingTemplate);

done();
}, 200);
}));

it('can create new using old template data', inject(($controller, api, $rootScope, session) => {
it('can create new using old template data', (done) => inject(($controller, api, $rootScope, session) => {
var item = _.create({slugline: 'foo', template: '123'});
var ctrl = $controller('CreateTemplateController', {item: item});

Expand All @@ -66,10 +83,15 @@ describe('templates', () => {
ctrl.name = 'rename it';
ctrl.is_public = true;
ctrl.save();
expect(api.save.calls.argsFor(0)[1]).not.toBe(existingTemplate);
expect(api.save.calls.argsFor(0)[1].is_public).toBe(true);
expect(api.save.calls.argsFor(0)[1].template_desks[0]).toBe('sports');
expect(api.save.calls.argsFor(0)[1].template_desks.length).toBe(1);

setTimeout(() => {
expect(api.save.calls.argsFor(0)[1]).not.toBe(existingTemplate);
expect(api.save.calls.argsFor(0)[1].is_public).toBe(true);
expect(api.save.calls.argsFor(0)[1].template_desks[0]).toBe('sports');
expect(api.save.calls.argsFor(0)[1].template_desks.length).toBe(1);

done();
}, 200);
}));
});

Expand Down
3 changes: 3 additions & 0 deletions scripts/core/superdesk-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2160,6 +2160,9 @@ declare module 'superdesk-api' {
previewComponent: React.ComponentType<IPreviewComponentProps>;
configComponent?: React.ComponentType<IConfigComponentProps<IConfig>>;
templateEditorComponent?: React.ComponentType<ITemplateEditorComponentProps<IConfig>>;

// may intercept template creation and return modified value
onTemplateCreate?(value: any, config: IConfig): any;
}


Expand Down
7 changes: 7 additions & 0 deletions scripts/extensions/datetimeField/src/extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export const defaultDateTimeConfig: IDateTimeFieldConfig = {
increment_steps: [],
};

function onTemplateCreate(_value: string, config: IDateTimeFieldConfig) {
const initialOffset = config.initial_offset_minutes;

return `{{ now|add_timedelta(minutes=${initialOffset})|iso_datetime }}`;
}

const extension: IExtension = {
activate: (superdesk: ISuperdesk) => {
const gettext = superdesk.localization.gettext;
Expand All @@ -56,6 +62,7 @@ const extension: IExtension = {
previewComponent: getDateTimePreviewComponent(superdesk),
configComponent: getConfigComponent(superdesk),
templateEditorComponent: getToggleDateTimeField(superdesk),
onTemplateCreate: onTemplateCreate,
},
],
},
Expand Down