No need for crm-button wrappers for real buttons
[civicrm-core.git] / templates / CRM / Contact / Form / Contact.tpl
1 {*
2 +--------------------------------------------------------------------+
3 | Copyright CiviCRM LLC. All rights reserved. |
4 | |
5 | This work is published under the GNU AGPLv3 license with some |
6 | permitted exceptions and without any warranty. For full license |
7 | and copyright information, see https://civicrm.org/licensing |
8 +--------------------------------------------------------------------+
9 *}
10 {* This form is for Contact Add/Edit interface *}
11 {if $addBlock}
12 {include file="CRM/Contact/Form/Edit/$blockName.tpl"}
13 {else}
14 {if $contactId}
15 {include file="CRM/Contact/Form/Edit/Lock.tpl"}
16 {/if}
17 <div class="crm-form-block crm-search-form-block">
18 {if call_user_func(array('CRM_Core_Permission','check'), 'administer CiviCRM') }
19 <a href='{crmURL p="civicrm/admin/setting/preferences/display" q="reset=1"}' title="{ts}Click here to configure the panes.{/ts}"><i class="crm-i fa-wrench" aria-hidden="true"></i></a>
20 {/if}
21 <span style="float:right;"><a href="#expand" id="expand">{ts}Expand all tabs{/ts}</a></span>
22 <div class="crm-submit-buttons">
23 {include file="CRM/common/formButtons.tpl" location="top"}
24 </div>
25
26 <div class="crm-accordion-wrapper crm-contactDetails-accordion">
27 <div class="crm-accordion-header">
28 {ts}Contact Details{/ts}
29 </div><!-- /.crm-accordion-header -->
30 <div class="crm-accordion-body" id="contactDetails">
31 <div id="contactDetails">
32 <div class="crm-section contact_basic_information-section">
33 {include file="CRM/Contact/Form/Edit/$contactType.tpl"}
34 </div>
35 <table class="crm-section contact_information-section form-layout-compressed">
36 {foreach from=$blocks item="label" key="block"}
37 {include file="CRM/Contact/Form/Edit/$block.tpl"}
38 {/foreach}
39 </table>
40 <table class="crm-section contact_source-section form-layout-compressed">
41 <tr class="last-row">
42 <td>{$form.contact_source.label} {help id="id-source"}<br />
43 {$form.contact_source.html|crmAddClass:twenty}
44 </td>
45 <td>{$form.external_identifier.label}&nbsp;{help id="id-external-id"}<br />
46 {$form.external_identifier.html}
47 </td>
48 {if $contactId}
49 <td>
50 <label for="internal_identifier_display">{ts}Contact ID{/ts} {help id="id-contact-id"}</label><br />
51 <input id="internal_identifier_display" type="text" class="crm-form-text six" size="6" readonly="readonly" value="{$contactId}">
52 </td>
53 {/if}
54 </tr>
55 </table>
56 <table class="image_URL-section form-layout-compressed">
57 <tr>
58 <td>
59 {$form.image_URL.label}&nbsp;&nbsp;{help id="id-upload-image" file="CRM/Contact/Form/Contact.hlp"}<br />
60 {$form.image_URL.html|crmAddClass:twenty}
61 {if !empty($imageURL)}
62 {include file="CRM/Contact/Page/ContactImage.tpl"}
63 {/if}
64 </td>
65 </tr>
66 </table>
67
68 {*add dupe buttons *}
69 {$form._qf_Contact_refresh_dedupe.html}
70 {if $isDuplicate}
71 &nbsp;&nbsp;
72 {$form._qf_Contact_upload_duplicate.html}
73 {/if}
74 <div class="spacer"></div>
75 </div>
76 </div><!-- /.crm-accordion-body -->
77 </div><!-- /.crm-accordion-wrapper -->
78
79 {foreach from = $editOptions item = "title" key="name"}
80 {if $name eq 'CustomData' }
81 <div id='customData'>{include file="CRM/Contact/Form/Edit/CustomData.tpl"}</div>
82 {else}
83 {include file="CRM/Contact/Form/Edit/$name.tpl"}
84 {/if}
85 {/foreach}
86 <div class="crm-submit-buttons">
87 {include file="CRM/common/formButtons.tpl" location="bottom"}
88 </div>
89 </div>
90 {literal}
91
92 <script type="text/javascript" >
93 CRM.$(function($) {
94 var $form = $("form.{/literal}{$form.formClass}{literal}"),
95 action = {/literal}{$action|intval}{literal},
96 cid = {/literal}{$contactId|intval}{literal},
97 _ = CRM._;
98
99 $('.crm-accordion-body').each( function() {
100 //remove tab which doesn't have any element
101 if ( ! $.trim( $(this).text() ) ) {
102 ele = $(this);
103 prevEle = $(this).prev();
104 $(ele).remove();
105 $(prevEle).remove();
106 }
107 //open tab if form rule throws error
108 if ( $(this).children().find('span.crm-error').text().length > 0 ) {
109 $(this).parents('.collapsed').crmAccordionToggle();
110 }
111 });
112 if (action === 2) {
113 $('.crm-accordion-wrapper').not('.crm-accordion-wrapper .crm-accordion-wrapper').each(function() {
114 highlightTabs(this);
115 });
116 $('#crm-container').on('change click', '.crm-accordion-body :input, .crm-accordion-body a', function() {
117 highlightTabs($(this).parents('.crm-accordion-wrapper'));
118 });
119 }
120 function highlightTabs(tab) {
121 //highlight the tab having data inside.
122 $('.crm-accordion-body :input', tab).each( function() {
123 var active = false;
124 switch($(this).prop('type')) {
125 case 'checkbox':
126 case 'radio':
127 if($(this).is(':checked') && !$(this).is('[id$=IsPrimary],[id$=IsBilling]')) {
128 $('.crm-accordion-header:first', tab).addClass('active');
129 return false;
130 }
131 break;
132
133 case 'text':
134 case 'textarea':
135 if($(this).val()) {
136 $('.crm-accordion-header:first', tab).addClass('active');
137 return false;
138 }
139 break;
140
141 case 'select-one':
142 case 'select-multiple':
143 if($(this).val() && $('option[value=""]', this).length > 0) {
144 $('.crm-accordion-header:first', tab).addClass('active');
145 return false;
146 }
147 break;
148
149 case 'file':
150 if($(this).next().html()) {
151 $('.crm-accordion-header:first', tab).addClass('active');
152 return false;
153 }
154 break;
155 }
156 $('.crm-accordion-header:first', tab).removeClass('active');
157 });
158 }
159
160 $('a#expand').click( function() {
161 if( $(this).attr('href') == '#expand') {
162 var message = {/literal}"{ts escape='js'}Collapse all tabs{/ts}"{literal};
163 $(this).attr('href', '#collapse');
164 $('.crm-accordion-wrapper.collapsed').crmAccordionToggle();
165 }
166 else {
167 var message = {/literal}"{ts escape='js'}Expand all tabs{/ts}"{literal};
168 $('.crm-accordion-wrapper:not(.collapsed)').crmAccordionToggle();
169 $(this).attr('href', '#expand');
170 }
171 $(this).html(message);
172 return false;
173 });
174
175 $('.customDataPresent').change(function() {
176 var values = $("#contact_sub_type").val();
177 CRM.buildCustomData({/literal}"{$contactType}"{literal}, values).one('crmLoad', function() {
178 highlightTabs(this);
179 loadMultiRecordFields(values);
180 });
181 });
182
183 function loadMultiRecordFields(subTypeValues) {
184 if (subTypeValues === false) {
185 subTypeValues = null;
186 }
187 else if (!subTypeValues) {
188 subTypeValues = {/literal}"{$paramSubType}"{literal};
189 }
190 function loadNextRecord(i, groupValue, groupCount) {
191 if (i < groupCount) {
192 CRM.buildCustomData({/literal}"{$contactType}"{literal}, subTypeValues, null, i, groupValue, true).one('crmLoad', function() {
193 highlightTabs(this);
194 loadNextRecord(i+1, groupValue, groupCount);
195 });
196 }
197 }
198 {/literal}
199 {foreach from=$customValueCount item="groupCount" key="groupValue"}
200 {if $groupValue}{literal}
201 loadNextRecord(1, {/literal}{$groupValue}{literal}, {/literal}{$groupCount}{literal});
202 {/literal}
203 {/if}
204 {/foreach}
205 {literal}
206 }
207
208 loadMultiRecordFields();
209
210 {/literal}{if $oldSubtypes}{literal}
211 $('button[name=_qf_Contact_upload_view], button[name=_qf_Contact_upload_new]').click(function() {
212 var submittedSubtypes = $('#contact_sub_type').val();
213 var oldSubtypes = {/literal}{$oldSubtypes}{literal};
214
215 var warning = false;
216 $.each(oldSubtypes, function(index, subtype) {
217 if ( $.inArray(subtype, submittedSubtypes) < 0 ) {
218 warning = true;
219 }
220 });
221 if ( warning ) {
222 return confirm({/literal}'{ts escape="js"}One or more contact subtypes have been de-selected from the list for this contact. Any custom data associated with de-selected subtype will be removed as long as the contact does not have a contact subtype still selected. Click OK to proceed, or Cancel to review your changes before saving.{/ts}'{literal});
223 }
224 return true;
225 });
226 {/literal}{/if}{literal}
227
228 // Handle delete of multi-record custom data
229 $form.on('click', '.crm-custom-value-del', function(e) {
230 e.preventDefault();
231 var $el = $(this),
232 msg = '{/literal}{ts escape="js"}The record will be deleted immediately. This action cannot be undone.{/ts}{literal}';
233 CRM.confirm({title: $el.attr('title'), message: msg})
234 .on('crmConfirm:yes', function() {
235 var url = CRM.url('civicrm/ajax/customvalue');
236 var request = $.post(url, $el.data('post'));
237 CRM.status({success: '{/literal}{ts escape="js"}Record Deleted{/ts}{literal}'}, request);
238 var addClass = '.add-more-link-' + $el.data('post').groupID;
239 $el.closest('div.crm-custom-accordion').remove();
240 $('div' + addClass).last().show();
241 });
242 });
243
244 {/literal}{* Ajax check for matching contacts *}
245 {if $checkSimilar == 1}
246 var contactType = {$contactType|@json_encode},
247 rules = {*$ruleFields|@json_encode*}{literal}[
248 'first_name',
249 'last_name',
250 'nick_name',
251 'household_name',
252 'organization_name',
253 'email'
254 ],
255 ruleFields = {},
256 $ruleElements = $(),
257 matchMessage,
258 dupeTpl = _.template($('#duplicates-msg-tpl').html()),
259 runningCheck = 0;
260 $.each(rules, function(i, field) {
261 // Match regular fields
262 var $el = $('#' + field + ', #' + field + '_1_' + field, $form).filter(':input');
263 // Match custom fields
264 if (!$el.length && field.lastIndexOf('_') > 0) {
265 var pieces = field.split('_');
266 field = 'custom_' + pieces[pieces.length-1];
267 $el = $('#' + field + ', [name=' + field + '_-1]', $form).filter(':input');
268 }
269 if ($el.length) {
270 ruleFields[field] = $el;
271 $ruleElements = $ruleElements.add($el);
272 }
273 });
274 // Check for matches on input when action == ADD
275 if (action === 1) {
276 $ruleElements.on('change', function () {
277 if ($(this).is('input[type=text]') && $(this).val().length < 3) {
278 return;
279 }
280 checkMatches().done(function (data) {
281 var params = {
282 title: data.count == 1 ? {/literal}"{ts escape='js'}Similar Contact Found{/ts}" : "{ts escape='js'}Similar Contacts Found{/ts}"{literal},
283 info: "{/literal}{ts escape='js'}If the contact you were trying to add is listed below, click their name to view or edit their record{/ts}{literal}:",
284 contacts: data.values,
285 cid: cid
286 };
287 if (data.count) {
288 openDupeAlert(params);
289 }
290 });
291 });
292 }
293
294 // Call the api to check for matching contacts
295 function checkMatches(rule) {
296 var match = {contact_type: contactType},
297 response = $.Deferred(),
298 checkNum = ++runningCheck,
299 params = {
300 options: {sort: 'sort_name'},
301 return: ['display_name', 'email']
302 };
303 $.each(ruleFields, function(fieldName, ruleField) {
304 if (ruleField.length > 1) {
305 match[fieldName] = ruleField.filter(':checked').val();
306 } else if (ruleField.is('input[type=text]')) {
307 if (ruleField.val().length > 2) {
308 match[fieldName] = ruleField.val() + (rule ? '' : '%');
309 }
310 } else {
311 match[fieldName] = ruleField.val();
312 }
313 });
314 // CRM-20565 - Need a good default matching rule before using the dedupe engine for checking on-the-fly.
315 // Defaulting to contact.get.
316 var action = rule ? 'duplicatecheck' : 'get';
317 if (rule) {
318 params.rule_type = rule;
319 params.match = match;
320 params.exclude = cid ? [cid] : [];
321 } else {
322 _.extend(params, match);
323 }
324 CRM.api3('contact', action, params).done(function(data) {
325 // If a new request has started running, cancel this one.
326 if (checkNum < runningCheck) {
327 response.reject();
328 } else {
329 response.resolve(data);
330 }
331 });
332 return response;
333 }
334
335 // Open an alert about possible duplicate contacts
336 function openDupeAlert(data, iconType) {
337 // Close msg if it exists
338 matchMessage && matchMessage.close && matchMessage.close();
339 matchMessage = CRM.alert(dupeTpl(data), _.escape(data.title), iconType, {expires: false});
340 $('.matching-contacts-actions', '#crm-notification-container').on('click', 'a', function() {
341 // No confirmation dialog on click
342 $('[data-warn-changes=true]').attr('data-warn-changes', 'false');
343 });
344 }
345
346 // Update the duplicate alert after getting results
347 function updateDupeAlert(data, iconType) {
348 var $alert = $('.matching-contacts-actions', '#crm-notification-container')
349 .closest('.ui-notify-message');
350 $alert
351 .removeClass('crm-msg-loading success info alert error')
352 .addClass(iconType)
353 .find('h1').text(data.title);
354 $alert
355 .find('.notify-content')
356 .html(dupeTpl(data));
357 }
358
359 // Ajaxify the "Check for Matching Contact(s)" button
360 $('#_qf_Contact_refresh_dedupe').click(function(e) {
361 var placeholder = {{/literal}
362 title: "{ts escape='js'}Fetching Matches{/ts}",
363 info: "{ts escape='js'}Checking for similar contacts...{/ts}",
364 contacts: []
365 {literal}};
366 openDupeAlert(placeholder, 'crm-msg-loading');
367 checkMatches('Supervised').done(function(data) {
368 var params = {
369 title: data.count ? {/literal}"{ts escape='js'}Similar Contact Found{/ts}" : "{ts escape='js'}None Found{/ts}"{literal},
370 info: data.count ?
371 "{/literal}{ts escape='js'}If the contact you were trying to add is listed below, click their name to view or edit their record{/ts}{literal}:" :
372 "{/literal}{ts escape='js'}No matches found using the default Supervised deduping rule.{/ts}{literal}",
373 contacts: data.values,
374 cid: cid
375 };
376 updateDupeAlert(params, data.count ? 'alert' : 'success');
377 });
378 e.preventDefault();
379 });
380 {/literal}{/if}{literal}
381 });
382 </script>
383
384 <script type="text/template" id="duplicates-msg-tpl">
385 <em><%- info %></em>
386 <ul class="matching-contacts-actions">
387 <% _.forEach(contacts, function(contact) { %>
388 <li>
389 <a href="<%= CRM.url('civicrm/contact/view', {reset: 1, cid: contact.id}) %>">
390 <%- contact.display_name %>
391 </a>
392 <%- contact.email %>
393 <% if (cid) { %>
394 <% var params = {reset: 1, action: 'update', oid: contact.id > cid ? contact.id : cid, cid: contact.id > cid ? cid : contact.id }; %>
395 (<a href="<%= CRM.url('civicrm/contact/merge', params) %>">{/literal}{ts}Merge{/ts}{literal}</a>)
396 <% } %>
397 </li>
398 <% }); %>
399 </ul>
400 </script>
401 {/literal}
402
403 {* jQuery validate *}
404 {include file="CRM/Form/validate.tpl"}
405
406 {* include common additional blocks tpl *}
407 {include file="CRM/common/additionalBlocks.tpl"}
408
409 {/if}