diff --git a/admin/themes/default/js/people.js b/admin/themes/default/js/people.js index 5f9bf5559..b58a03585 100644 --- a/admin/themes/default/js/people.js +++ b/admin/themes/default/js/people.js @@ -122,26 +122,48 @@ $(function() { people_selection_refresh(); }); - $('#people_selection_delete').on('click', function() { + // action on the selection: the options shown depend on the chosen action + $('#people_selection_action_choice').on('change', function() { + people_selection_action_show($(this).val()); + }); + + // yes/no and confirmation checkboxes, same as the selection panel of the users + $('#people_selection').on('click', '.user-list-checkbox', function() { + const box = $(this); + if (box.closest('.yes_no_radio').length) { + box.siblings('.user-list-checkbox').removeAttr('data-selected'); + box.attr('data-selected', '1'); + } else { + box.attr('data-selected', '1' === box.attr('data-selected') ? null : '1'); + } + }); + + $('#people_selection_apply').on('click', function() { const ids = Object.keys(people_selected); if (ids.length === 0) return; + $('#people_selection_error').hide(); - delete_people(ids, function() { - people_selected = {}; - people_selection_refresh(); - }); - }); + switch ($('#people_selection_action_choice').val()) { + case 'merge': + merge_people(); + break; - $('#people_selection_merge').on('click', function() { - people_selection_merge_open(); - }); + case 'delete': + if ('1' !== $('#people_selection_action_delete .user-list-checkbox').attr('data-selected')) return; + delete_people(ids, function() { + people_selected = {}; + people_selection_action_reset(); + people_selection_refresh(); + }); + break; - $('#people_selection_merge_cancel').on('click', function() { - people_selection_merge_close(); - }); - - $('#people_selection_merge_confirm').on('click', function() { - merge_people(); + case 'visibility': + edit_selected_people({ + person_id: ids, + is_visible: '1' === $('#people_selection_action_visibility [data-name=visible_yes]').attr('data-selected'), + }); + break; + } }); }); @@ -155,7 +177,8 @@ function people_selection_switch(is_on) { add_person_btn.toggle(!is_on); $('.people-card-dropdown').hide(); - people_selection_merge_close(); + people_selection_action_reset(); + people_selection_refresh(); } function people_selection_refresh() { @@ -176,30 +199,40 @@ function people_selection_refresh() { ); }); - $('#people_selection_merge').toggleClass('unavailable', ids.length < 2); + // merge needs at least 2 people, its destination list follows the selection + const choice = $('#people_selection_action_choice'); + choice.find('option[value=merge]').prop('disabled', ids.length < 2); + if ('merge' === choice.val()) { + ids.length < 2 ? people_selection_action_reset() : people_selection_merge_fill(); + } - if (!$('#people_selection_merge_block').is(':visible')) { - $('#people_selection_nothing').toggle(ids.length === 0); - $('#people_selection_actions').toggle(ids.length > 0); + $('#people_selection_nothing').toggle(ids.length === 0); + $('#people_selection_actions').toggle(ids.length > 0); +} + +function people_selection_action_show(action) { + $('.people-selection-action .bulkAction, #people_selection_error').hide(); + if ('' !== action) { + $('#people_selection_action_' + action).show(); + } + $('#people_selection_apply').toggle('' !== action); + + if ('merge' === action) { + people_selection_merge_fill(); } } -function people_selection_merge_open() { - const ids = Object.keys(people_selected); - if (ids.length < 2) return; - - const choice = $('#people_selection_merge_choice').empty(); - $.each(ids, function(i, id) { - choice.append($('').val(id).text(people_selected[id])); - }); - - $('#people_selection_actions, #people_selection_nothing, #people_selection_error').hide(); - $('#people_selection_merge_block').show(); +function people_selection_action_reset() { + $('#people_selection_action_choice').val(''); + $('#people_selection_action_delete .user-list-checkbox').removeAttr('data-selected'); + people_selection_action_show(''); } -function people_selection_merge_close() { - $('#people_selection_merge_block, #people_selection_error').hide(); - people_selection_refresh(); +function people_selection_merge_fill() { + const choice = $('#people_selection_merge_choice').empty(); + $.each(Object.keys(people_selected), function(i, id) { + choice.append($('').val(id).text(people_selected[id])); + }); } // FUNCTIONS @@ -593,6 +626,29 @@ function edit_people(data) { }); } +// same values for all the selected people, the selection is kept +function edit_selected_people(data) { + data.pwg_token = PWG_TOKEN; + + $.ajax({ + url: 'ws.php?format=json&method=pwg.people.edit', + type: 'POST', + dataType: 'json', + data: data, + success: function(res) { + if (res.stat !== 'ok') { + return people_toast(res.message ?? str_people_error_occured, true); + } + + people_toast(1 === data.person_id.length ? str_person_updated : str_people_updated); + load_people(); + }, + error: function() { + people_toast(str_people_error_occured, true); + } + }); +} + function merge_people() { const destination_id = $('#people_selection_merge_choice').val(); const destination_name = people_selected[destination_id]; @@ -613,7 +669,8 @@ function merge_people() { } people_selected = {}; - people_selection_merge_close(); + people_selection_action_reset(); + people_selection_refresh(); people_toast(sprintf(str_people_merged, destination_name)); load_people(); }, diff --git a/admin/themes/default/template/people.tpl b/admin/themes/default/template/people.tpl index 00fd6578b..b9d952418 100644 --- a/admin/themes/default/template/people.tpl +++ b/admin/themes/default/template/people.tpl @@ -13,6 +13,7 @@ let str_people_no_identified_photo = "{'No identified photo for this person.'|translate|escape:javascript}"; let str_person_added = "{'Person added'|translate|escape:javascript}"; let str_person_updated = "{'Person updated'|translate|escape:javascript}"; + let str_people_updated = "{'People updated'|translate|escape:javascript}"; let str_person_deleted = "{'Person deleted'|translate|escape:javascript}"; let str_people_deleted = "{'%d people deleted'|translate|escape:javascript}"; let str_people_merged = "{"People merged into \"%s\""|translate|escape:javascript}"; @@ -88,17 +89,51 @@ - diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css index f1ff1ceb8..b8a4a1431 100644 --- a/admin/themes/default/theme.css +++ b/admin/themes/default/theme.css @@ -8780,46 +8780,78 @@ color:#FF7B00; cursor: pointer; } -.people-selection-button { - display: block; - width: 180px; - margin: 20px auto; - padding: 13px 30px; - border: 1px solid #e7e7e7; - background-color: transparent; - color: inherit; - font-size: 12px; - font-weight: 700; - cursor: pointer; +.people-selection-action { + margin-top: 30px; + text-align: left; } -.people-selection-button:hover { - border-color: #ffa744; - background-color: #ffa744; - color: #3c3c3c; +.people-selection-action-title { + margin-bottom: 10px !important; + font-weight: bold; + font-size: 15px; } -.people-selection-button.unavailable { - opacity: 0.3; - pointer-events: none; +.people-selection-action .user-action-select-container { + position: relative; +} + +.people-selection-action .user-action-select { + -webkit-appearance: none; + width: 100%; + padding: 5px 10px; +} + +.people-selection-action .bulkAction { + margin-top: 15px; + text-align: center; +} + +.people-selection-action .bulkAction p { + margin-bottom: 10px !important; } .people-selection-info { - margin-bottom: 30px !important; + margin-top: 10px !important; font-style: italic; } -.people-selection-merge select { - width: 100%; - padding: 8px; +.people-selection-action .user-list-checkbox { + user-select: none; + cursor: pointer; } -.people-selection-cancel { +.people-selection-action .user-list-checkbox i { + margin-left: 0; +} + +.people-selection-action .user-list-checkbox-label { + margin-left: 5px; + vertical-align: top; + font-size: 14px; +} + +.people-selection-action .yes_no_radio .user-list-checkbox .user-list-checkbox-label { + margin-left: 0; + margin-right: 10px; +} + +.people-selection-apply { + display: block; + width: 100%; + margin-top: 30px; + padding: 13px; + border: none; + background-color: #ffa744; + color: #3c3c3c; font-size: 12px; font-weight: 700; cursor: pointer; } +.people-selection-apply:hover { + background-color: #ff7700; +} + .people-selection-error { margin-top: 10px !important; font-weight: bold; diff --git a/admin/themes/roma/theme.css b/admin/themes/roma/theme.css index fec334f3e..caaa9afed 100644 --- a/admin/themes/roma/theme.css +++ b/admin/themes/roma/theme.css @@ -2607,18 +2607,7 @@ div.error .jGrowl-popup-icon, border-color: #c76a23; } -/* same colors as the selection panel of the tags */ -.people-page .people-selection-list, -.people-page .people-selection-button, -.people-page .people-selection-cancel { +/* same color as the selection list of the tags */ +.people-page .people-selection-list { color: #c0c0c0; } - -.people-page .people-selection-button:hover { - color: #3c3c3c; -} - -.people-page .people-selection-merge select { - background-color: #1a1a1a; - box-shadow: 0 0 2px #c5c5c5; -} diff --git a/include/functions_people.inc.php b/include/functions_people.inc.php index 728c95c6c..70ba75a27 100644 --- a/include/functions_people.inc.php +++ b/include/functions_people.inc.php @@ -204,6 +204,92 @@ function edit_person(int $id, array $data) return true; } +/** + * Set the same values to several people, only the fields they can share. + * The edit_person event is triggered for each person, like in edit_person(). + * + * @param int[] $person_ids + * @param array $data + * - bool is_visible (optional) + * - string source (optional) + * @return true|array ['code', 'error'] + */ +function edit_people(array $person_ids, array $data) +{ + $person_ids = array_values(array_unique($person_ids)); + if (count($person_ids) == 0) + { + return array('code' => 403, 'error' => 'No person given.'); + } + + foreach ($person_ids as $person_id) + { + if (!preg_match('/^\d+$/', $person_id)) + { + return array('code' => 403, 'error' => 'Person ids must be digits.'); + } + } + + $validated_data = array(); + + if (isset($data['is_visible'])) + { + if (!is_bool($data['is_visible'])) + { + return array('code' => 403, 'error' => 'is_visible must be: true or false.'); + } + $validated_data['is_visible'] = boolean_to_string($data['is_visible']); + } + + if (!empty($data['source'])) + { + if (!validate_people_source($data['source'])) + { + return array('code' => 403, 'error' => 'source must be: manual, auto or imported.'); + } + $validated_data['source'] = $data['source']; + } + + $query_people = ' +SELECT * + FROM '.PERSONS_TABLE.' + WHERE id IN ('.implode(',', $person_ids).') +;'; + $people_before = query2array($query_people, 'id'); + + if (count($people_before) != count($person_ids)) + { + return array('code' => 404, 'error' => 'Person not found.'); + } + + if (count($validated_data) == 0) + { + return true; + } + + $set = array(); + foreach ($validated_data as $field => $value) + { + $set[] = $field.' = \''.$value.'\''; + } + + $query = ' +UPDATE '.PERSONS_TABLE.' + SET '.implode(', ', $set).' + WHERE id IN ('.implode(',', $person_ids).') +;'; + pwg_query($query); + + pwg_activity('people', $person_ids, 'edit'); + + foreach (query2array($query_people, 'id') as $person_id => $person) + { + trigger_notify('edit_person', $person, $people_before[$person_id]); + } + + return true; +} + /** * @param int[]|int $people_ids * @return bool diff --git a/include/ws_functions/pwg.people.php b/include/ws_functions/pwg.people.php index 6d7d3f0ac..f71168eed 100644 --- a/include/ws_functions/pwg.people.php +++ b/include/ws_functions/pwg.people.php @@ -221,8 +221,25 @@ function ws_people_edit($params, &$service) { return new PwgError(403, 'Invalid security token'); } - - $is_edited = edit_person($params['person_id'], $params); + + $person_ids = array_values(array_unique($params['person_id'])); + + if (count($person_ids) == 1) + { + $is_edited = edit_person($person_ids[0], $params); + } + else + { + foreach (array('name', 'user_id', 'cover_image_person_id') as $field) + { + if (isset($params[$field])) + { + return new PwgError(WS_ERR_INVALID_PARAM, $field.' can only be set for one person.'); + } + } + + $is_edited = edit_people($person_ids, $params); + } if (isset($is_edited['error'])) { @@ -230,7 +247,7 @@ function ws_people_edit($params, &$service) } return array( - 'id' => $params['person_id'], + 'id' => $person_ids, ); } diff --git a/language/en_UK/admin.lang.php b/language/en_UK/admin.lang.php index 32984bf25..e73bcfaff 100644 --- a/language/en_UK/admin.lang.php +++ b/language/en_UK/admin.lang.php @@ -1480,5 +1480,6 @@ $lang['%d people deleted'] = '%d people deleted'; $lang['People merged into "%s"'] = 'People merged into "%s"'; $lang['Cover updated'] = 'Cover updated'; $lang['Delete person "%s"?'] = 'Delete person "%s"?'; +$lang['People updated'] = 'People updated'; // Leave this line empty \ No newline at end of file diff --git a/language/fr_FR/admin.lang.php b/language/fr_FR/admin.lang.php index c6f009372..62bcb5b75 100644 --- a/language/fr_FR/admin.lang.php +++ b/language/fr_FR/admin.lang.php @@ -1487,5 +1487,6 @@ $lang['%d people deleted'] = '%d personnes supprimées'; $lang['People merged into "%s"'] = 'Personnes fusionnées dans « %s »'; $lang['Cover updated'] = 'Couverture mise à jour'; $lang['Delete person "%s"?'] = 'Supprimer la personne « %s » ?'; +$lang['People updated'] = 'Personnes mises à jour'; // Leave this line empty diff --git a/ws.php b/ws.php index eca1e6f91..4c8fcbd90 100644 --- a/ws.php +++ b/ws.php @@ -1770,6 +1770,8 @@ enabled_high, registration_date, registration_date_string, registration_date_sin array( 'person_id' => array( 'type' => WS_TYPE_ID, + 'flags' => WS_PARAM_FORCE_ARRAY, + 'info' => 'several ids only for is_visible and source', ), 'name' => array( 'flags' => WS_PARAM_OPTIONAL,