Rename extension (ang `msgtplui` => `crmMsgadm`, hyphen-form)
[civicrm-core.git] / ext / msgtplui / ang / msgtplui / Edit.js
CommitLineData
2abfa17f
TO
1(function(angular, $, _) {
2
946846bb
TO
3 var TRANSLATED = ['msg_subject', 'msg_html', 'msg_text'];
4
5 /**
6 * Create an APIv4 request to replace a series of translations.
7 *
8 * @param id
9 * The ID of the translated entity.
10 * @param lang
11 * The language of the translation ('fr_CA', 'en_GB').
12 * @param status
13 * The status of the translation ('active', 'draft').
14 * @param values
15 * Key-value pairs. ({msg_title: 'Cheerio'})
16 * @returns []
17 * An API call which replaces the translations ([entity,action,params]).
18 */
19 function reqReplaceTranslations(id, lang, status, values) {
b7c61807 20 if (!values._exists) {
882e3016
TO
21 return reqDeleteTranslations(id, lang, status);
22 }
946846bb 23 var records = [];
b7c61807
TO
24 angular.forEach(TRANSLATED, function(key) {
25 records.push({"entity_field":key, "string":values[key]});
946846bb
TO
26 });
27 return ['Translation', 'replace', {
28 records: records,
29 where: [
30 ["entity_table", "=", "civicrm_msg_template"],
31 ["entity_id", "=", id],
32 ["language", "=", lang],
33 ["status_id:name", "=", status]
34 ]
35 }];
36 }
37
38 function reqDeleteTranslations(id, lang, status) {
39 return ['Translation', 'delete', {
40 where: [
41 ["entity_table", "=", "civicrm_msg_template"],
42 ["entity_id", "=", id],
43 ["language", "=", lang],
44 ["status_id:name", "=", status]
45 ]
46 }];
47 }
48
49 /**
50 * Create an APIv4 request to read a series of translations.
51 *
52 * @param lang
53 * The language of the translation ('fr_CA', 'en_GB').
54 * @param status
55 * The status of the translation ('active', 'draft').
56 * @returns []
57 * An API call which replaces the translations ([entity,action,params]).
58 */
59 function reqChainTranslations(lang, status) {
1d86bc3c
TO
60 return ["Translation", "get", {
61 "select": ['id', 'entity_field', 'string'],
62 "where": [
63 ['entity_table', '=', 'civicrm_msg_template'],
64 ['entity_id', '=', '$id'],
65 ['language', '=', lang],
66 ['status_id:name', '=', status]
67 ]
68 }];
69 }
70
946846bb 71 function respMergeTranslations(prefetch) {
b7c61807
TO
72 angular.forEach(prefetch, function(results, queryName) {
73 var forceExists = (queryName === 'txActive');
1d86bc3c
TO
74 angular.forEach(results, function(result) {
75 if (result.translations) {
76 angular.forEach(result.translations, function(tx) {
77 result[tx.entity_field] = tx.string;
78 });
b7c61807 79 result._exists = forceExists || (result.translations.length > 0);
1d86bc3c
TO
80 }
81 });
82 });
83 return prefetch;
84 }
85
86 function pickFirsts(prefetch) {
87 return _.reduce(prefetch, function(all, record, key){
88 all[key] = record[0] || undefined;
89 return all;
90 }, {});
91 }
92
882e3016 93 function copyTranslations(src, dest) {
b7c61807 94 dest._exists = false; // Starting assumption - prove otherwise in a moment.
882e3016 95 TRANSLATED.forEach(function(fld) {
b7c61807
TO
96 if (src[fld] !== undefined) {
97 dest._exists = true;
98 dest[fld] = src[fld];
882e3016
TO
99 }
100 else {
b7c61807 101 delete dest[fld];
882e3016
TO
102 }
103 });
104 }
105
2abfa17f
TO
106 angular.module('msgtplui').config(function($routeProvider) {
107 $routeProvider.when('/edit', {
108 controller: 'MsgtpluiEdit',
109 controllerAs: '$ctrl',
110 templateUrl: '~/msgtplui/Edit.html',
111
112 // If you need to look up data when opening the page, list it out
113 // under "resolve".
114 resolve: {
1d86bc3c
TO
115 prefetch: function(crmApi4, crmStatus, $location) {
116 var args = $location.search();
117 var requests = {};
118
119 requests.main = ['MessageTemplate', 'get', {
120 where: [['id', '=', args.id]],
121 }];
122
123 requests.original = ['MessageTemplate', 'get', {
124 join: [["MessageTemplate AS other", "INNER", null, ["workflow_name", "=", "other.workflow_name"]]],
125 where: [["other.id", "=", args.id], ["is_reserved", "=", true]],
126 limit: 25
127 }];
128
129 if (args.lang) {
130 requests.txActive = ['MessageTemplate', 'get', {
b7c61807 131 select: TRANSLATED,
1d86bc3c 132 where: [['id', '=', args.id]],
946846bb 133 chain: {translations: reqChainTranslations(args.lang, 'active')}
1d86bc3c
TO
134 }];
135
136 requests.txDraft = ['MessageTemplate', 'get', {
b7c61807 137 select: TRANSLATED,
1d86bc3c 138 where: [['id', '=', args.id]],
946846bb 139 chain: {translations: reqChainTranslations(args.lang, 'draft')}
1d86bc3c
TO
140 }];
141 }
142
946846bb 143 return crmStatus({start: ts('Loading...'), success: ''}, crmApi4(requests).then(respMergeTranslations).then(pickFirsts));
beed9144
TO
144 },
145 tokenList: function () {
146 return CRM.crmMailing.mailTokens;
2abfa17f
TO
147 }
148 }
149 });
150 }
151 );
152
b48aae2d 153 angular.module('msgtplui').controller('MsgtpluiEdit', function($q, $scope, crmApi4, crmBlocker, crmStatus, crmUiAlert, crmUiHelp, $location, prefetch, tokenList, $rootScope, dialogService) {
50d12767 154 var block = $scope.block = crmBlocker();
2abfa17f 155 var ts = $scope.ts = CRM.ts('msgtplui');
bdff1ee3 156 var hs = $scope.hs = crmUiHelp({file: 'CRM/MessageAdmin/Edit'}); // See: templates/CRM/MessageAdmin/Edit.hlp
818b072b 157 var $ctrl = this;
1d86bc3c 158 var args = $location.search();
2abfa17f 159
834c02da 160 $ctrl.locales = CRM.msgtplui.allLanguages;
818b072b 161 $ctrl.records = prefetch;
beed9144 162 $ctrl.tokenList = tokenList;
1d86bc3c 163 if (args.lang) {
818b072b 164 $ctrl.lang = args.lang;
6b9bb2c1 165 $ctrl.tab = (args.status === 'draft' && $ctrl.records.txDraft && $ctrl.records.txDraft._exists) ? 'txDraft' : 'txActive';
1d86bc3c
TO
166 }
167 else {
818b072b
TO
168 $ctrl.lang = null;
169 $ctrl.tab = 'main';
1d86bc3c 170 }
50d12767 171
4873aa5e
TO
172 var revisionTypes = [
173 {name: 'original', label: ts('Original')},
174 {name: 'main', label: ts('Standard')},
08de7486
TO
175 {name: 'txActive', label: ts('%1 - Current translation', {1: $ctrl.locales[$ctrl.lang] || $ctrl.lang})},
176 {name: 'txDraft', label: ts('%1 - Draft translation', {1: $ctrl.locales[$ctrl.lang] || $ctrl.lang})}
4873aa5e
TO
177 ];
178
818b072b
TO
179 $ctrl.switchTab = function switchTab(tgt) {
180 $ctrl.tab = tgt;
b5124320 181 // Experimenting with action buttons in the tab-bar. This makes the scroll unnecessary.
768e4eb3 182 // $('html, body').animate({scrollTop: $("a[name=crm-msgadm-tabs]").offset().top - $('#civicrm-menu').height()}, 200);
882e3016 183 };
5730735f
TO
184 $ctrl.allowDraft = function allowDraft() {
185 return !!$ctrl.lang;
186 };
818b072b 187 $ctrl.hasDraft = function hasDraft() {
5730735f 188 return $ctrl.allowDraft() && $ctrl.records.txDraft && $ctrl.records.txDraft._exists;
882e3016 189 };
4873aa5e
TO
190 $ctrl.hasRevType = function hasRevType(name) {
191 switch (name) {
192 case 'txDraft': return $ctrl.hasDraft();
193 case 'txActive': return !!$ctrl.lang;
194 case 'original': return !!$ctrl.records.original;
195 case 'main': return !$ctrl.lang; // !!$ctrl.records.main;
196 }
197 };
818b072b
TO
198 $ctrl.createDraft = function createDraft(src) {
199 copyTranslations(src, $ctrl.records.txDraft);
200 $ctrl.switchTab('txDraft');
4207b76f 201 crmStatus({success: ts('Beginning draft...')}, $q.resolve());
882e3016 202 };
818b072b
TO
203 $ctrl.deleteDraft = function deleteDraft() {
204 copyTranslations({}, $ctrl.records.txDraft);
205 $ctrl.switchTab('txActive');
4207b76f 206 crmStatus({error: ts('Abandoned draft.')}, $q.reject()).then(function(){},function(){});
882e3016 207 };
818b072b
TO
208 $ctrl.activateDraft = function activateDraft() {
209 copyTranslations($ctrl.records.txDraft, $ctrl.records.txActive);
210 copyTranslations({}, $ctrl.records.txDraft);
211 $ctrl.switchTab('txActive');
6bf782d6 212 crmStatus({success: ts('Activated draft.')}, $q.resolve());
882e3016
TO
213 };
214
818b072b 215 $ctrl.save = function save() {
946846bb 216 var requests = {};
818b072b 217 if ($ctrl.lang) {
b7c61807
TO
218 requests.txActive = reqReplaceTranslations($ctrl.records.main.id, $ctrl.lang, 'active', $ctrl.records.txActive);
219 requests.txDraft = reqReplaceTranslations($ctrl.records.main.id, $ctrl.lang, 'draft', $ctrl.records.txDraft);
946846bb
TO
220 }
221 else {
222 requests.main = ['MessageTemplate', 'update', {
818b072b
TO
223 where: [['id', '=', $ctrl.records.main.id]],
224 values: $ctrl.records.main
946846bb
TO
225 }];
226 }
227 return block(crmStatus({start: ts('Saving...'), success: ts('Saved')}, crmApi4(requests)));
50d12767 228 };
818b072b 229 $ctrl.cancel = function() {
50d12767
TO
230 window.location = '#/workflow';
231 };
818b072b 232 $ctrl.delete = function() {
946846bb 233 var requests = {};
818b072b
TO
234 if ($ctrl.lang) {
235 requests.txActive = reqDeleteTranslations($ctrl.records.main.id, $ctrl.lang, 'active');
236 requests.txDraft = reqDeleteTranslations($ctrl.records.main.id, $ctrl.lang, 'draft');
946846bb
TO
237 }
238 else {
818b072b 239 requests.main = ['MessageTemplate', 'delete', {where: [['id', '=', $ctrl.records.main.id]]}];
946846bb
TO
240 }
241 return block(crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, crmApi4(requests)))
818b072b 242 .then($ctrl.cancel);
50d12767 243 };
b48aae2d
TO
244
245 // Ex: $rootScope.$emit('previewMsgTpl', {revisionName: 'txDraft', formatName: 'msg_text'})
246 function onPreview(event, args) {
b48aae2d 247 var defaults = {
97e6b7ec
TO
248 // exampleName: 'fix-this-example',
249 // examples: [
250 // {id: 0, name: 'fix-this-example', title: ts('Fix this example')},
251 // {id: 1, name: 'another-example', title: ts('Another example')}
252 // ],
b48aae2d
TO
253 formatName: 'msg_html',
254 formats: [
255 {id: 0, name: 'msg_html', label: ts('HTML')},
256 {id: 1, name: 'msg_text', label: ts('Text')}
257 ],
258 revisionName: $ctrl.tab,
4873aa5e
TO
259 revisions: _.reduce(revisionTypes, function(acc, revType){
260 if ($ctrl.hasRevType(revType.name)) {
261 acc.push(angular.extend({id: acc.length, rec: $ctrl.records[revType.name]}, revType));
262 }
b48aae2d
TO
263 return acc;
264 }, []),
265 title: ts('Preview')
266 };
97e6b7ec 267
08de7486 268 crmApi4({
d037a503 269 examples: ['ExampleData', 'get', {
08de7486 270 // FIXME: workflow name
5f863fa3
TO
271 where: [["tags", "CONTAINS", "preview"], ["name", "LIKE", "workflow/" + $ctrl.records.main.workflow_name + "/%"]],
272 select: ['name', 'title', 'data']
08de7486
TO
273 }],
274 adhoc: ['WorkflowMessage', 'getTemplateFields', {
275 workflow: $ctrl.records.main.workflow_name,
276 format: 'example'
277 }]
278 }).then(function(resp) {
279 console.log('resp',resp);
280 if ((!resp.examples || resp.examples.length === 0) && resp.adhoc) {
c2c610b4
TO
281 // In the future, if Preview dialog allows editing adhoc examples, then we can show the dialog. But for now, it won't work without explicit examples.
282 crmUiAlert({
283 title: ts('Preview unavailable'),
284 text: ts('Generating a preview for this message requires example data. Please talk to a developer about adding example data for "%1".', {1: $ctrl.records.main.workflow_name}),
285 type: 'error'
286 });
287 return;
08de7486
TO
288 }
289 defaults.exampleName = resp.examples.length > 0 ? (resp.examples)[0].name : null;
97e6b7ec 290 var i = 0;
08de7486 291 angular.forEach(resp.examples, function(ex) {
97e6b7ec
TO
292 ex.id = i++;
293 });
08de7486 294 defaults.examples = resp.examples;
97e6b7ec
TO
295
296 var model = angular.extend({}, defaults, args);
297 var options = CRM.utils.adjustDialogDefaults({
768e4eb3 298 dialogClass: 'crm-msgadm-dialog',
97e6b7ec
TO
299 autoOpen: false,
300 height: '90%',
301 width: '90%'
302 });
303 return dialogService.open('previewMsgDlg', '~/msgtplui/Preview.html', model, options)
304 // Nothing to do but hide warnings. The field was edited live.
305 .then(function(){}, function(){});
306 }, function(failure) {
307 // handle failure
b48aae2d 308 });
97e6b7ec 309
b48aae2d
TO
310 }
311 $rootScope.$on('previewMsgTpl', onPreview);
312 $rootScope.$on('$destroy', function (){
313 $rootScope.$off('previewMsgTpl', onPreview);
314 });
2abfa17f
TO
315 });
316
317})(angular, CRM.$, CRM._);