1 (function(angular
, $, _
) {
3 var partialUrl = function(relPath
) {
4 return CRM
.resourceUrls
['civicrm'] + '/partials/crmCaseType/' + relPath
;
7 var crmCaseType
= angular
.module('crmCaseType', ['ngRoute', 'ui.utils', 'crmUi', 'unsavedChanges']);
9 // Note: This template will be passed to cloneDeep(), so don't put any funny stuff in here!
10 var newCaseTypeTemplate
= {
17 {name
: 'Open Case', max_instances
: 1},
25 name
: 'standard_timeline',
26 label
: 'Standard Timeline',
27 timeline
: '1', // Angular won't bind checkbox correctly with numeric 1
29 {name
: 'Open Case', status
: 'Completed' }
34 { name
: 'Case Coordinator', creator
: '1', manager
: '1'}
39 crmCaseType
.config(['$routeProvider',
40 function($routeProvider
) {
41 $routeProvider
.when('/caseType', {
42 templateUrl
: partialUrl('list.html'),
43 controller
: 'CaseTypeListCtrl',
45 caseTypes: function($route
, crmApi
) {
46 return crmApi('CaseType', 'get', {});
50 $routeProvider
.when('/caseType/:id', {
51 templateUrl
: partialUrl('edit.html'),
52 controller
: 'CaseTypeCtrl',
54 apiCalls: function($route
, crmApi
) {
56 reqs
.actStatuses
= ['OptionValue', 'get', {
57 option_group_id
: 'activity_status'
59 reqs
.actTypes
= ['OptionValue', 'get', {
60 option_group_id
: 'activity_type',
66 reqs
.relTypes
= ['RelationshipType', 'get', {
68 sort
: CRM
.crmCaseType
.REL_TYPE_CNAME
,
72 if ($route
.current
.params
.id
!== 'new') {
73 reqs
.caseType
= ['CaseType', 'getsingle', {
74 id
: $route
.current
.params
.id
84 // Add a new record by name.
85 // Ex: <crmAddName crm-options="['Alpha','Beta','Gamma']" crm-var="newItem" crm-on-add="callMyCreateFunction(newItem)" />
86 crmCaseType
.directive('crmAddName', function() {
89 template
: '<input class="add-activity" type="hidden" />',
90 link: function(scope
, element
, attrs
) {
91 /// Format list of options for select2's "data"
92 var getFormattedOptions = function() {
94 results
: _
.map(scope
[attrs
.crmOptions
], function(option
){
95 return {id
: option
, text
: option
};
100 var input
= $('input', element
);
102 scope
._resetSelection = function() {
103 $(input
).select2('close');
104 $(input
).select2('val', '');
105 scope
[attrs
.crmVar
] = '';
109 data
: getFormattedOptions
,
110 createSearchChoice: function(term
) {
111 return {id
: term
, text
: term
};
114 $(input
).on('select2-selecting', function(e
) {
115 scope
[attrs
.crmVar
] = e
.val
;
116 scope
.$evalAsync(attrs
.crmOnAdd
);
117 scope
.$evalAsync('_resetSelection()');
121 scope
.$watch(attrs
.crmOptions
, function(value
) {
122 $(input
).select2('data', getFormattedOptions
);
123 $(input
).select2('val', '');
129 crmCaseType
.controller('CaseTypeCtrl', function($scope
, crmApi
, apiCalls
) {
130 $scope
.partialUrl
= partialUrl
;
132 $scope
.activityStatuses
= _
.values(apiCalls
.actStatuses
.values
);
133 $scope
.activityTypes
= apiCalls
.actTypes
.values
;
134 $scope
.activityTypeNames
= _
.pluck(apiCalls
.actTypes
.values
, 'name');
135 $scope
.relationshipTypeNames
= _
.pluck(apiCalls
.relTypes
.values
, CRM
.crmCaseType
.REL_TYPE_CNAME
); // CRM_Case_XMLProcessor::REL_TYPE_CNAME
136 $scope
.locks
= {caseTypeName
: true, activitySetName
: true};
139 'timeline': 'Timeline',
140 'sequence': 'Sequence'
143 $scope
.caseType
= apiCalls
.caseType
? apiCalls
.caseType
: _
.cloneDeep(newCaseTypeTemplate
);
144 $scope
.caseType
.definition
= $scope
.caseType
.definition
|| [];
145 $scope
.caseType
.definition
.activityTypes
= $scope
.caseType
.definition
.activityTypes
|| [];
146 $scope
.caseType
.definition
.activitySets
= $scope
.caseType
.definition
.activitySets
|| [];
147 $scope
.caseType
.definition
.caseRoles
= $scope
.caseType
.definition
.caseRoles
|| [];
148 window
.ct
= $scope
.caseType
;
150 $scope
.addActivitySet = function(workflow
) {
151 var activitySet
= {};
152 activitySet
[workflow
] = '1';
153 activitySet
.activityTypes
= [];
156 var names
= _
.pluck($scope
.caseType
.definition
.activitySets
, 'name');
157 while (_
.contains(names
, workflow
+ '_' + offset
)) offset
++;
158 activitySet
.name
= workflow
+ '_' + offset
;
159 activitySet
.label
= (offset
== 1 ) ? $scope
.workflows
[workflow
] : ($scope
.workflows
[workflow
] + ' #' + offset
);
161 $scope
.caseType
.definition
.activitySets
.push(activitySet
);
163 $('.crmCaseType-acttab').tabs('refresh').tabs({active
: -1});
167 /// Add a new activity entry to an activity-set
168 $scope
.addActivity = function(activitySet
, activityType
) {
169 activitySet
.activityTypes
.push({
172 reference_activity
: 'Open Case',
173 reference_offset
: '1',
174 reference_select
: 'newest'
176 if (!_
.contains($scope
.activityTypeNames
, activityType
)) {
177 $scope
.activityTypeNames
.push(activityType
);
181 /// Add a new top-level activity-type entry
182 $scope
.addActivityType = function(activityType
) {
183 var names
= _
.pluck($scope
.caseType
.definition
.activityTypes
, 'name');
184 if (!_
.contains(names
, activityType
)) {
185 $scope
.caseType
.definition
.activityTypes
.push({
190 if (!_
.contains($scope
.activityTypeNames
, activityType
)) {
191 $scope
.activityTypeNames
.push(activityType
);
196 $scope
.addRole = function(roles
, roleName
) {
197 var names
= _
.pluck($scope
.caseType
.definition
.caseRoles
, 'name');
198 if (!_
.contains(names
, roleName
)) {
203 if (!_
.contains($scope
.relationshipTypeNames
, roleName
)) {
204 $scope
.relationshipTypeNames
.push(roleName
);
208 $scope
.onManagerChange = function(managerRole
) {
209 angular
.forEach($scope
.caseType
.definition
.caseRoles
, function(caseRole
) {
210 if (caseRole
!= managerRole
) {
211 caseRole
.manager
= '0';
216 $scope
.removeItem = function(array
, item
) {
217 var idx
= _
.indexOf(array
, item
);
219 array
.splice(idx
, 1);
223 $scope
.isForkable = function() {
224 return !$scope
.caseType
.id
|| $scope
.caseType
.is_forkable
227 $scope
.isNewActivitySetAllowed = function(workflow
) {
232 return 0 == _
.where($scope
.caseType
.definition
.activitySets
, {sequence
: '1'}).length
;
234 CRM
.console('warn', 'Denied access to unrecognized workflow: (' + workflow
+ ')');
239 $scope
.isActivityRemovable = function(activitySet
, activity
) {
240 if (activitySet
.name
== 'standard_timeline' && activity
.name
== 'Open Case') {
247 $scope
.isValidName = function(name
) {
248 return !name
|| name
.match(/^[a-zA-Z0-9_]+$/);
251 $scope
.getWorkflowName = function(activitySet
) {
252 var result
= 'Unknown';
253 _
.each($scope
.workflows
, function(value
, key
) {
254 if (activitySet
[key
]) result
= value
;
260 * Determine which HTML partial to use for a particular
262 * @return string URL of the HTML partial
264 $scope
.activityTableTemplate = function(activitySet
) {
265 if (activitySet
.timeline
) {
266 return partialUrl('timelineTable.html');
267 } else if (activitySet
.sequence
) {
268 return partialUrl('sequenceTable.html');
274 $scope
.dump = function() {
275 console
.log($scope
.caseType
);
278 $scope
.save = function() {
279 var result
= crmApi('CaseType', 'create', $scope
.caseType
, true);
280 result
.success(function(data
) {
281 if (data
.is_error
== 0) {
282 $scope
.caseType
.id
= data
.id
;
283 window
.location
.href
= '#/caseType';
288 $scope
.$watchCollection('caseType.definition.activitySets', function() {
290 $('.crmCaseType-acttab').tabs('refresh');
294 var updateCaseTypeName = function () {
295 if (!$scope
.caseType
.id
&& $scope
.locks
.caseTypeName
) {
296 // Should we do some filtering? Lowercase? Strip whitespace?
297 var t
= $scope
.caseType
.title
? $scope
.caseType
.title
: '';
298 $scope
.caseType
.name
= t
.replace(/ /g, '_').replace(/[^a
-zA
-Z0
-9_
]/g
, '').toLowerCase();
301 $scope
.$watch('locks.caseTypeName', updateCaseTypeName
);
302 $scope
.$watch('caseType.title', updateCaseTypeName
);
304 if (!$scope
.isForkable()) {
305 CRM
.alert(ts('The CiviCase XML file for this case-type prohibits editing the definition.'));
309 crmCaseType
.controller('CaseTypeListCtrl', function($scope
, crmApi
, caseTypes
) {
310 $scope
.caseTypes
= caseTypes
.values
;
311 $scope
.toggleCaseType = function (caseType
) {
312 caseType
.is_active
= (caseType
.is_active
== '1') ? '0' : '1';
313 crmApi('CaseType', 'create', caseType
, true)
314 .then(function (data
) {
316 caseType
.is_active
= (caseType
.is_active
== '1') ? '0' : '1'; // revert
321 $scope
.deleteCaseType = function (caseType
) {
322 crmApi('CaseType', 'delete', {id
: caseType
.id
}, {
323 error: function (data
) {
324 CRM
.alert(data
.error_message
, ts('Error'));
327 .then(function (data
) {
328 if (!data
.is_error
) {
329 delete caseTypes
.values
[caseType
.id
];
334 $scope
.revertCaseType = function (caseType
) {
335 caseType
.definition
= 'null';
336 caseType
.is_forked
= '0';
337 crmApi('CaseType', 'create', caseType
, true)
338 .then(function (data
) {
340 caseType
.is_forked
= '1'; // restore
347 })(angular
, CRM
.$, CRM
._
);