Merge pull request #23686 from demeritcowboy/primary-ts
[civicrm-core.git] / templates / CRM / Contact / Form / Merge.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 <div class="crm-block crm-form-block crm-contact-merge-form-block">
11 <div class="help">
12 {ts}Click <strong>Merge</strong> to move data from the Duplicate Contact on the left into the Main Contact. In addition to the contact data (address, phone, email...), you may choose to move all or some of the related activity records (groups, contributions, memberships, etc.).{/ts} {help id="intro"}
13 </div>
14
15 <div class="message status">
16 {icon icon="fa-info-circle"}{/icon}
17 <strong>{ts}WARNING: The duplicate contact record WILL BE DELETED after the merge is complete.{/ts}</strong>
18 </div>
19
20 {if $user}
21 <div class="message status">
22 {icon icon="fa-info-circle"}{/icon}
23 <strong>{ts 1=$config->userFramework}WARNING: There are %1 user accounts associated with both the original and duplicate contacts. Ensure that the %1 user you want to retain is on the right - if necessary use the 'Flip between original and duplicate contacts.' option at top to swap the positions of the two records before doing the merge.
24 The user record associated with the duplicate contact will not be deleted, but will be unlinked from the associated contact record (which will be deleted).
25 You will need to manually delete that user (click on the link to open the %1 user account in new screen). You may need to give thought to how you handle any content or contents associated with that user.{/ts}</strong>
26 </div>
27 {/if}
28
29 <div class="crm-submit-buttons">
30 {include file="CRM/common/formButtons.tpl" location="top"}
31 </div>
32
33 <div class="action-link">
34 {if $prev}<a href="{$prev}" class="crm-hover-button action-item"><i class="crm-i fa-chevron-left" aria-hidden="true"></i> {ts}Previous{/ts}</a>{/if}
35 {if $next}<a href="{$next}" class="crm-hover-button action-item">{ts}Next{/ts} <i class="crm-i fa-chevron-right" aria-hidden="true"></i></a>{/if}
36 <a href="{$flip}" class="action-item crm-hover-button">
37 <i class="crm-i fa-random" aria-hidden="true"></i>
38 {ts}Flip between original and duplicate contacts.{/ts}
39 </a>
40 </div>
41
42 <div class="action-link">
43 <a href="#" class="action-item crm-hover-button crm-notDuplicate" title={ts}Mark this pair as not a duplicate.{/ts} onClick="processDupes( {$main_cid|escape}, {$other_cid|escape}, 'dupe-nondupe', 'merge-contact', '{$browseUrl}' );return false;">
44 <i class="crm-i fa-times-circle" aria-hidden="true"></i>
45 {ts}Mark this pair as not a duplicate.{/ts}
46 </a>
47 </div>
48
49 <div class="action-link">
50 <a href="javascript:void(0);" class="action-item crm-hover-button toggle_equal_rows">
51 <i class="crm-i fa-eye-slash" aria-hidden="true"></i>
52 {ts}Show/hide rows with the same data on each contact record.{/ts}
53 </a>
54 </div>
55
56 <table class="row-highlight">
57 <tr class="columnheader">
58 <th>&nbsp;</th>
59 <th>{$otherContactTypeIcon} <a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=$other_cid"}">{$other_name|escape}</a> ({ts}duplicate{/ts})</th>
60 <th>{ts}Mark All{/ts}<br />=={$form.toggleSelect.html} ==&gt;</th>
61 <th>{$mainContactTypeIcon}<a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=$main_cid"}">{$main_name|escape}</a></th>
62 <th width="300">{ts}Add/overwrite?{/ts}</th>
63 </tr>
64
65
66 {foreach from=$summary_rows item=summaryRow}
67 <tr>
68 <td>{$summaryRow.label}</td>
69 <td>{$summaryRow.other_contact_value}</td>
70 <td></td>
71 <td>{$summaryRow.main_contact_value}</td>
72 <td></td>
73 </tr>
74 {/foreach}
75
76 {foreach from=$rows item=row key=field}
77
78 {if !$row.main && !$row.other}
79 <tr style="background-color: #fff !important; border-bottom:1px solid #ccc !important;" class="no-data">
80 <td>
81 <strong>{$row.title|escape}</strong>
82 </td>
83 {else}
84 {if $row.main eq $row.other}
85 <tr class="merge-row-equal crm-row-ok {cycle values="odd-row,even-row"}">
86 {else}
87 <tr class="crm-row-error {cycle values="odd-row,even-row"}">
88 {/if}
89 <td>
90 {$row.title|escape}
91 </td>
92 {/if}
93
94 {assign var=position value=$field|strrpos:'_'}
95 {assign var=blockId value=$field|substr:$position+1}
96 {assign var=blockName value=$field|substr:14:$position-14}
97
98 <td>
99 {* @TODO check if this is ever an array or a fileName? *}
100 {if $row.location_entity == "email" OR
101 $row.location_entity == "address"}
102 <span style="white-space: pre">
103 {else}
104 <span>
105 {/if}
106 {if !is_array($row.other)}
107 {$row.other|escape}
108 {elseif $row.other.fileName}
109 {$row.other.fileName|escape}
110 {else}
111 {', '|implode:$row.other}
112 {/if}
113 </span>
114 </td>
115
116 <td style='white-space: nowrap'>
117 {if $form.$field}=={$form.$field.html|crmAddClass:"select-row"}==&gt;{/if}
118 </td>
119
120 {* For location blocks *}
121 {if $row.location_entity}
122
123 <td>
124 {strip}
125 {if $row.location_entity == "email" OR
126 $row.location_entity == "address"}
127 <span style="white-space: pre" id="main_{$blockName|escape}_{$blockId|escape}">
128 {else}
129 <span id="main_{$blockName|escape}_{$blockId|escape}">
130 {/if}
131 {* @TODO check if this is ever an array or a fileName? *}
132 {if !is_array($row.main)}
133 {$row.main|escape}
134 {elseif $row.main.fileName}
135 {$row.main.fileName|escape}
136 {else}
137 {', '|implode:$row.main}
138 {/if}
139 </span>
140 {/strip}
141 </td>
142
143 <td>
144 {* Display location for fields with locations *}
145 {if $blockName eq 'email' || $blockName eq 'phone' || $blockName eq 'address' || $blockName eq 'im' }
146 {$form.location_blocks.$blockName.$blockId.locTypeId.html}&nbsp;
147 {/if}
148
149 {* Display other_type_id for websites, ims and phones *}
150 {if $blockName eq 'website' || $blockName eq 'im' || $blockName eq 'phone' }
151 {$form.location_blocks.$blockName.$blockId.typeTypeId.html}&nbsp;
152 {/if}
153
154 {* Display the overwrite/add/add new label *}
155 <span id="main_{$blockName}_{$blockId}_overwrite" class="location_block_controls">
156
157 <span class="location_primary">
158 {if $row.main && $row.main_is_primary == "1"}{ts}Primary{/ts}{/if}
159 </span>
160
161 <span class="location_block_controls_options">
162 <span class="location_operation_description">
163 {if $row.main}({ts}overwrite{/ts}){else}({ts}add{/ts}){/if}
164 </span>
165 <span style="display: block" class="location_operation_checkbox">
166 {if $row.main && ($blockName eq 'email' || $blockName eq 'phone')}
167 {$form.location_blocks.$blockName.$blockId.operation.html}
168 {/if}
169 </span>
170 <span style="display: block" class="location_set_other_primary">
171 {if $blockName neq 'website' && (($row.main && $row.main_is_primary != "1") || !$row.main)}
172 {$form.location_blocks.$blockName.$blockId.set_other_primary.html}
173 {/if}
174 </span>
175 </span>
176 </span>
177
178 </td>
179
180 {* For non-location blocks *}
181 {else}
182
183 <td>
184 <span>
185 {if !is_array($row.main)}
186 {$row.main|escape}
187 {elseif $row.main.fileName}
188 {$row.main.fileName|escape}
189 {else}
190 {', '|implode:$row.main}
191 {/if}
192 </span>
193 </td>
194
195 <td>
196 {if $row.main || $row.other}
197 <span>
198 {if $row.main == $row.other}
199 <span class="action_label">({ts}match{/ts})</span><br />
200 {elseif $row.main}
201 <span class="action_label">({ts}overwrite{/ts})</span><br />
202 {else}
203 <span class="action_label">({ts}add{/ts})</span>
204 {/if}
205 </span>
206 {/if}
207 </td>
208
209 {/if}
210
211 </tr>
212 {/foreach}
213
214 {foreach from=$rel_tables item=params key=paramName}
215 {if $paramName eq 'move_rel_table_users'}
216 <tr class="{cycle values="even-row,odd-row"}">
217 <td><strong>{ts}Move related...{/ts}</strong></td><td>{if $otherUfId}<a target="_blank" href="{$params.other_url}">{$otherUfName}</a></td><td style='white-space: nowrap'>=={$form.$paramName.html|crmAddClass:"select-row"}==&gt;{else}<td style='white-space: nowrap'></td>{/if}</td><td>{if $mainUfId}<a target="_blank" href="{$params.main_url}">{$mainUfName}</a>{/if}</td>
218 <td>({ts}migrate{/ts})</td>
219 </tr>
220 {else}
221 <tr class="{cycle values="even-row,odd-row"}">
222 <td><strong>{ts}Move related...{/ts}</strong></td><td><a href="{$params.other_url}">{$params.title}</a></td><td style='white-space: nowrap'>=={$form.$paramName.html|crmAddClass:"select-row"}==&gt;</td><td><a href="{$params.main_url}">{$params.title}</a>{if $form.operation.$paramName.add.html}&nbsp;{$form.operation.$paramName.add.html}{/if}</td>
223 <td>({ts}migrate{/ts})</td>
224 </tr>
225 {/if}
226 {/foreach}
227 </table>
228
229 <div class="crm-submit-buttons">
230 {include file="CRM/common/formButtons.tpl" location="bottom"}
231 </div>
232 </div>
233
234 {literal}
235 <script type="text/javascript">
236
237 var locationBlockInfo = {/literal}{$locationBlockInfo}{literal};
238 var allBlock = {/literal}{$mainLocBlock}{literal};
239
240 /**
241 * Triggered when a 'location' or 'type' destination is changed, and when
242 * the operation or 'set primary' checkboxes are changed.
243 *
244 * Check to see if the 'main' contact record has a corresponding location
245 * block when the destination of a field is changed. Allow existing location
246 * fields to be overwritten with data from the 'other' contact.
247 *
248 * @param blockName string
249 * The name of the entity.
250 * @param blockId int
251 * The block ID being affected.
252 * @param event object
253 * The event that triggered the update.
254 */
255 function updateMainLocationBlock(blockName, blockId, event) {
256
257 // Get type of select list that's been changed (location or type)
258 var locTypeId = CRM.$('select#location_blocks_' + blockName + '_' + blockId + '_locTypeId').val();
259 var typeTypeId = CRM.$('select#location_blocks_' + blockName + '_' + blockId + '_typeTypeId').val();
260
261 // @todo Fix this 'special handling' for websites (no location id)
262 if (!locTypeId) {
263 locTypeId = 0;
264 }
265
266 // Look for a matching block on the main contact
267 var mainBlockId = 0;
268 var mainBlockDisplay = '';
269 var mainBlock = findBlock(blockName, locTypeId, typeTypeId);
270 if (mainBlock != false) {
271 mainBlockDisplay = mainBlock['display'];
272 mainBlockId = mainBlock['id'];
273 }
274
275 // Update main location display and id
276 CRM.$("input[name='location_blocks[" + blockName + "][" + blockId + "][mainContactBlockId]']").val(mainBlockId);
277 CRM.$("#main_" + blockName + "_" + blockId).html(mainBlockDisplay);
278
279 // Update controls area
280
281 // Get the parent block once for speed
282 var this_controls = CRM.$("#main_" + blockName + "_" + blockId + "_overwrite");
283
284 // Update primary label
285 if (mainBlock != false && mainBlock['is_primary'] == '1') {
286 this_controls.find(".location_primary").text('{/literal}{ts escape='js'}Primary{/ts}{literal}');
287 }
288 else {
289 this_controls.find(".location_primary").text('');
290 }
291
292 // Update operation description
293 var operation_description = "{/literal}{ts escape='js'}add{/ts}{literal}";
294 var add_new_check_length = this_controls.find(".location_operation_checkbox input:checked").length;
295 if (mainBlock != false) {
296 if (add_new_check_length > 0) {
297 operation_description = "{/literal}{ts escape='js'}add new{/ts}{literal}";
298 }
299 else {
300 operation_description = "{/literal}{ts escape='js'}overwrite{/ts}{literal}";
301 }
302 }
303 this_controls.find(".location_operation_description").text("(" + operation_description + ")");
304
305 // Skip if the 'add new' or 'set primary' checkboxes were clicked
306 if (event.target.id.match(/(operation|set_other_primary)/) === null) {
307 // Display 'Add new' checkbox if there is a main block, and this is an
308 // email or phone type.
309 if (mainBlock != false && (blockName == 'email' || blockName == 'phone')) {
310 var op_id = 'location_blocks[' + blockName + '][' + blockId + '][operation]';
311 this_controls.find(".location_operation_checkbox").html(
312 '<input id="' + op_id + '" name="' + op_id + '" type="checkbox" value="1" class="crm-form-checkbox"><label for="' + op_id + '">{/literal}{ts escape='js'}Add new{/ts}{literal}</label>'
313 );
314 }
315 else {
316 this_controls.find(".location_operation_checkbox").html('');
317 }
318 }
319
320 // Skip if 'set primary' was clicked
321 if (event.target.id.match(/(set_other_primary)/) === null) {
322 // Display 'Set primary' checkbox if applicable
323 if (blockName != 'website' && (mainBlock == false || mainBlock['is_primary'] != "1" || add_new_check_length > 0)) {
324 var prim_id = 'location_blocks[' + blockName + '][' + blockId + '][set_other_primary]';
325 this_controls.find(".location_set_other_primary").html(
326 '<input id="' + prim_id + '" name="' + prim_id + '" type="checkbox" value="1" class="crm-form-checkbox"><label for="' + prim_id + '">{/literal}{ts escape='js'}Set as primary{/ts}{literal}</label>'
327 );
328 }
329 else {
330 this_controls.find(".location_set_other_primary").html('');
331 }
332 }
333
334 }
335
336 /**
337 * Look for a matching 'main' contact location block by entity, location and
338 * type
339 *
340 * @param entName string
341 * The entity name to lookup.
342 * @param locationID int
343 * The location ID to lookup.
344 * @param typeID int
345 * The type ID to lookup.
346 *
347 * @returns boolean|object
348 * Returns false if no match, otherwise an object with the location ID and
349 * display value.
350 */
351 function findBlock(entName, locationID, typeID) {
352 var entityArray = allBlock[entName];
353 var result = false;
354 for (var i = 0; i < entityArray.length; i++) {
355 // Match based on location and type ID, depending on the entity info
356 if (locationBlockInfo[entName]['hasLocation'] == false || locationID == entityArray[i]['location_type_id']) {
357 if (locationBlockInfo[entName]['hasType'] == false || typeID == entityArray[i][locationBlockInfo[entName]['hasType']]) {
358 result = {
359 display: entityArray[i][locationBlockInfo[entName]['displayField']],
360 id: entityArray[i]['id'],
361 is_primary: entityArray[i]['is_primary']
362 };
363 break;
364 }
365 }
366 }
367 return result;
368 }
369
370 /**
371 * Called when a 'set primary' checkbox is clicked in order to disable any
372 * other 'set primary' checkboxes for blocks of the same entity. So don't let
373 * users try to set two different phone numbers as primary on the form.
374 *
375 * @param event object
376 * The event that triggered the update
377 */
378 function updateSetPrimaries(event) {
379 var nameSplit = event.target.name.split('[');
380 var blockName = nameSplit[1].slice(0, -1);
381 var controls = CRM.$('span.location_block_controls[id^="main_' + blockName + '"]');
382
383 // Enable everything
384 controls.find('input[id$="[set_other_primary]"]:not(:checked)').removeAttr("disabled");
385
386 // If one is checked, disable the others
387 if (controls.find('input[id$="[set_other_primary]"]:checked').length > 0) {
388 controls.find('input[id$="[set_other_primary]"]:not(:checked)').attr("disabled", "disabled");
389 }
390 }
391
392 /**
393 * Toggle the location type and the is_primary on & off depending on whether the merge box is ticked.
394 *
395 * @param element
396 */
397 function toggleRelatedLocationFields(element) {
398 relatedElements = CRM.$(element).parent().siblings('td').find('input,select,label,hidden');
399 if (CRM.$(element).is(':checked')) {
400 relatedElements.removeClass('disabled').attr('disabled', false);
401
402 }
403 else {
404 relatedElements.addClass('disabled').attr('disabled', true);
405 }
406
407 }
408
409 CRM.$(function($) {
410 $('input.crm-form-checkbox[data-is_location]').on('click', function(){
411 toggleRelatedLocationFields(this)
412 });
413
414 // Show/hide matching data rows
415 $('.toggle_equal_rows').click(function() {
416 $('tr.merge-row-equal').toggle();
417 });
418
419 // Call mergeBlock whenever a location type is changed
420 // (This is applied to the body because the inputs can be added dynamically
421 // to the form, and we need to catch when they change.)
422 $('body').on('change', 'select[id$="locTypeId"],select[id$="typeTypeId"],input[id$="[operation]"],input[id$="[set_other_primary]"]', function(event){
423
424 // All the information we need is held in the id, separated by underscores
425 var nameSplit = this.name.split('[');
426
427 // Lookup the main value, if any are available
428 if (allBlock[nameSplit[1].slice(0, -1)] != undefined) {
429 updateMainLocationBlock(nameSplit[1].slice(0, -1), nameSplit[2].slice(0, -1), event);
430 }
431
432 // Update all 'set primary' checkboxes
433 updateSetPrimaries(event);
434
435 });
436
437 });
438
439 </script>
440 {/literal}
441
442 {* process the dupe contacts *}
443 {include file="CRM/common/dedupe.tpl"}