Commit | Line | Data |
---|---|---|
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._); |