From c8d7503d4e9e3487411933be178a7754d7c3b2fb Mon Sep 17 00:00:00 2001 From: Linty Date: Fri, 28 Jun 2024 18:49:34 +0200 Subject: [PATCH] related to #2165 new add user popin - add field for add user - in the `pwg.users.add` method, the params `send_password_by_mail` does nothing anymore. Because we no longer want to send passwords in clear text. - in the `pwg.users.add` add a new `auto_password` parameter to generate a random password when a user is created - use this parameter (`auto_password`) in user_list.js - change mail content et password page on first login --- admin/themes/default/js/user_list.js | 152 +++++++++--- admin/themes/default/template/user_list.tpl | 255 +++++++++++++------- admin/themes/default/theme.css | 5 +- admin/themes/roma/theme.css | 5 +- include/functions_mail.inc.php | 37 +++ include/functions_user.inc.php | 22 ++ include/ws_functions/pwg.users.php | 17 +- password.php | 6 + ws.php | 5 + 9 files changed, 385 insertions(+), 119 deletions(-) diff --git a/admin/themes/default/js/user_list.js b/admin/themes/default/js/user_list.js index 7305f82e5..6014546e9 100644 --- a/admin/themes/default/js/user_list.js +++ b/admin/themes/default/js/user_list.js @@ -55,6 +55,7 @@ jQuery('[data-selectize=groups]').selectize({ let groupSelectize = jQuery('[data-selectize=groups]')[0].selectize; let groupGuestSelectize = jQuery('[data-selectize=groups]')[1].selectize; +let groupAddUserSelectize = $('[data-selectize=groups]')[2].selectize; /*----------------- OnClick functions @@ -149,18 +150,6 @@ $( document ).ready(function() { $('.edit-guest-user-button').click(open_guest_user_list); $('.CloseGuestUserList').click(close_guest_user_list); $('#GuestUserList .close-update-button').click(close_guest_user_list); - - $("#show_password").click(function() { - if ($(this).hasClass("icon-eye")) { - $(this).removeClass("icon-eye"); - $(this).addClass("icon-eye-off"); - $("#AddUserPassword").get(0).type = "text"; - } else { - $(this).removeClass("icon-eye-off"); - $(this).addClass("icon-eye"); - $("#AddUserPassword").get(0).type = "password"; - } - }) /* Action */ jQuery("[id^=action_]").hide(); @@ -180,10 +169,6 @@ $( document ).ready(function() { $(this).siblings().attr("data-selected", "0"); } }) - $(".AddUserGenPassword").on('click', function() { - const password = gen_password(); - $("#AddUserPassword").val(password); - }); $('.EditUserGenPassword').on('click', function() { const password = gen_password(); $('#edit_user_password').val(password); @@ -818,7 +803,10 @@ function add_user_close() { } function add_user_open() { - $('#AddUser .AddUserInput').val(''); + $('#AddUserSuccessContainer').hide(); + $('#AddUserFieldContainer').show(); + $('#AddUser :input').val(''); + fill_new_user(); $("#AddUser").fadeIn(); $(".AddUserLabelUsername input").first().focus(); } @@ -1429,7 +1417,7 @@ function fill_user_edit_summary(user_to_edit, pop_in, isGuest) { $('#copy_password').hide(); $('.update-password-success').css('margin', '40px 0'); $('#result_send_mail_copy').hide(); - $('#result_send_mail_copy_btn') + $('#result_send_mail_copy_btn, #AddUserCopyPassword') .css({'cursor': 'not-allowed', 'background-color' : 'grey', 'color': '#ffffff'}) .attr('title', cantCopy) .on('mouseenter', function() { @@ -1680,6 +1668,27 @@ function fill_guest_edit() { fill_user_edit_update(user_to_edit, pop_in); } +function fill_new_user() { + // When you want to add an user: privacy level and groups + // by default are the same as Guest, not status + const addUserPopIn = $('#AddUser'); + const status_index = get_status_index('normal'); + const level_index = get_level_index(guest_user.level); + set_selected_groups(guest_user.groups); + groupAddUserSelectize.clear(); + groupAddUserSelectize.load(function(callback) { + callback(groupOptions); + }); + jQuery.each(jQuery.grep(groupOptions, function(group) { + return group.isSelected; + }), function(i, group) { + groupAddUserSelectize.addItem(group.value); + }); + addUserPopIn.find(`.user-property-status select option:eq(${status_index})`).prop("selected", true); + addUserPopIn.find(`.user-property-level select option:eq(${level_index})`).prop("selected", true); + addUserPopIn.find('.user-list-checkbox[name="hd_enabled"]').attr('data-selected', guest_user.enabled_high == 'true' ? '1' : '0'); +} + function fill_who_is_the_king(user_to_edit, pop_in) { const who_is_the_king = pop_in.find("#who_is_the_king"); // By default I'm an admin and I only see who is the Main User @@ -2115,17 +2124,30 @@ function update_user_list() { } function add_user() { - let ajax_data = { - pwg_token: pwg_token, - } + let ajax_data = {}; + let groups_selected = $('.AddUserInputContainer .user-property-group .selectize-input .item').map(function () { + return parseInt($(this).attr('data-value')); + } ).get(); ajax_data.username = $('.AddUserLabelUsername .user-property-input').val(); - ajax_data.password = $('#AddUserPassword').val(); ajax_data.email = $(".AddUserLabelEmail .user-property-input").val(); - ajax_data.send_password_by_mail = $('.user-list-checkbox[name="send_by_email"]').attr("data-selected") == "1" ? true : false; - jQuery.ajax({ + ajax_data.status = $(".AddUserInputContainer .user-property-status select").val(); + ajax_data.level = $(".AddUserInputContainer .user-property-level select").val(); + ajax_data.enabled_high = $(".AddUserInputContainer .user-list-checkbox[name=\"hd_enabled\"]").attr('data-selected') == '1' ? true : false; + ajax_data.group_id = groups_selected.length == 0 ? -1 : groups_selected; + ajax_data.auto_password = true; + + // for debug + // console.log(ajax_data); + + $.ajax({ url: "ws.php?format=json&method=pwg.users.add", type:"POST", - data: ajax_data, + data: { + username: ajax_data.username, + auto_password: true, + email: ajax_data.email, + pwg_token + }, beforeSend: function() { $("#AddUser .AddUserErrors").css("visibility", "hidden"); if ($(".AddUserLabelUsername .user-property-input").val() == "") { @@ -2136,12 +2158,41 @@ function add_user() { }, success: (raw_data) => { let data = jQuery.parseJSON(raw_data); + if (data.stat == 'ok') { + let new_user_id = data.result.users[0].id; + add_infos_to_new_user(new_user_id, ajax_data); + } + else { + $("#AddUser .AddUserErrors").html(data.message) + $("#AddUser .AddUserErrors").css("visibility", "visible"); + } + } + }); +} + +function add_infos_to_new_user(user_id, ajax_data) { + $.ajax({ + url: 'ws.php?format=json&method=pwg.users.setInfo', + type: 'POST', + data: { + user_id, + status: ajax_data.status, + level: ajax_data.level, + group_id: ajax_data.group_id, + enabled_high: ajax_data.enabled_high, + pwg_token + }, + success: function(response) { + const data = JSON.parse(response); if (data.stat == 'ok') { let new_user_id = data.result.users[0].id; update_user_list(); - add_user_close(); + // add_user_close(); + $('#AddUserUpdated').removeClass('icon-red icon-cancel').addClass('icon-green border-green icon-ok'); + $('#AddUserUpdatedText').html(user_added_str.replace("%s", ajax_data.username)); + send_new_user_password(new_user_id, ajax_data.email === '' ? false : true); $("#AddUser .user-property-input").val(""); - $("#AddUserSuccess .edit-now").unbind("click").click(() => { + $("#AddUserSuccess .edit-now").off("click").on("click", () => { last_user_id = new_user_id; last_user_index = get_container_index_from_uid(new_user_id); if (last_user_index != -1) { @@ -2163,6 +2214,53 @@ function add_user() { }); } +function send_new_user_password(user_id, send_by_mail) { + $.ajax({ + url: "ws.php?format=json", + dataType: "json", + data:{ + method: 'pwg.users.generateResetPasswordLink', + user_id: user_id, + send_by_mail: send_by_mail, + pwg_token: pwg_token + }, + success: function(response) { + if('ok' === response.stat) { + $('#AddUserFieldContainer').hide(); + $('#AddUserSuccessContainer').fadeIn(); + $('#AddUserPasswordLink').val(response.result.generated_link).trigger('focus'); + $('#AddUserTextField').html(send_by_mail ? validLinkMail : validLinkWithoutMail); + + if(send_by_mail && !response.result.send_by_mail) { + $('#AddUserUpdated').removeClass('icon-green border-green icon-ok').addClass('icon-red-error icon-cancel'); + $('#AddUserUpdatedText').html(errorMailSent); + } + + if (window.isSecureContext && navigator.clipboard) { + $('#AddUserCopyPassword').off('click').on('click', function() { + const successMsg = $('#AddUserUpdatedText'); + successMsg.fadeOut(); + copyToClipboard(response.result.generated_link); + $('#AddUserUpdated').removeClass('icon-red icon-cancel').addClass('icon-green border-green icon-ok'); + successMsg.html(copyLinkStr); + successMsg.fadeIn(); + }); + }; + $('#AddUserButton').off('click').on('click', function() { + add_user_close(); + }); + } else { + $('#AddUserUpdated').removeClass('icon-green border-green icon-ok').addClass('icon-red-error icon-cancel'); + $('#AddUserUpdatedText').html(response.message); + } + }, + error: function(response) { + $('#AddUserUpdated').removeClass('icon-green border-green icon-ok').addClass('icon-red-error icon-cancel'); + $('#AddUserUpdatedText').html(response.message); + } + }); +} + function delete_user(uid) { jQuery.ajax({ url: "ws.php?format=json&method=pwg.users.delete", diff --git a/admin/themes/default/template/user_list.tpl b/admin/themes/default/template/user_list.tpl index fda0f676d..55e3a77de 100644 --- a/admin/themes/default/template/user_list.tpl +++ b/admin/themes/default/template/user_list.tpl @@ -45,6 +45,8 @@ const mainUserUpgradeWebmaster = "{'This user must first be defined as the webma const errorStr = "{'an error happened'|@translate|escape:javascript}"; const copyLinkStr = "{'Copied link'|@translate|escape:javascript}"; const cantCopy = "{'You cannot copy the password if the connection to this site is not secure.'|@translate|escape:javascript}"; +const validLinkMail = "{'A link, valid for 3 days has already been sent. If the email fails, copy the link below and send it to the user so the password can be set.'|@translate|escape:javascript}"; +const validLinkWithoutMail = "{'Copy the link below and send it to the user so the password can be set'|@translate|escape:javascript}"; const registered_str = '{"Registered"|@translate|escape:javascript}'; const last_visit_str = '{"Last visit"|@translate|escape:javascript}'; @@ -1130,47 +1132,104 @@ $(document).ready(function() {
{'Add a new user'|@translate}
-
- -
-
-
- - +
+
+
- -
- {'Generate random password'|@translate} +
+ +
+ +
+
+ +
+ +
+
+
+ +
+
+

{'Privacy level'|@translate}

+
+ +
+
+
+ +
+
+

{'Groups'|@translate}

+
+ +
+
+
+ +
+
+ + + + {'High definition enabled'|translate} +
+
+ +
+
+ +
+
+ {'Cancel'|@translate} +
+ +
+ {'Add User'|@translate} +
-
- + -
- - - - {'Send connection settings by email'|translate} -
- -
-
- -
- {'Add User'|@translate} -
- -
@@ -1181,13 +1240,6 @@ $(document).ready(function() { cursor: help; } -#show_password { - position: absolute; - left: 240px; - top: 29px; - z-index: 100; {*used to fix firefox auto fill input*} -} - /* general */ .no-flex-grow { flex-grow:0 !important; @@ -1754,6 +1806,10 @@ $(document).ready(function() { margin-bottom:5px; } +#AddUser .user-property-label { + margin-top: 0; +} + .user-property-label span, .dates-infos { color: #ff7700; @@ -1833,6 +1889,10 @@ $(document).ready(function() { margin-left: 418px; } +#AddUser .user-property-select-container::before { + margin-left: 328px; +} + .user-action-select-container { position:relative; } @@ -1856,6 +1916,12 @@ $(document).ready(function() { cursor:pointer; } +#AddUser .user-list-checkbox { + margin-top: 5px; + margin-bottom: 5px; + align-self: start; +} + /* summary section */ .edit-user-icons { width: 100%; @@ -1975,7 +2041,8 @@ $(document).ready(function() { width: 100%; } -.user-property-password-choice .head-button-2 { +.user-property-password-choice .head-button-2, +#AddUserSuccessContainer .head-button-2 { display: block; text-align: center; margin-right: 0; @@ -1997,7 +2064,8 @@ $(document).ready(function() { padding-top: 30px; } -.edit-password-success .edit-password-success-input { +.edit-password-success .edit-password-success-input, +.AddUserPasswordInput { background-color: transparent; font-size: 15px; border: none; @@ -2005,7 +2073,8 @@ $(document).ready(function() { padding: 0 5px; } -.edit-password-success .edit-password-success-reset-link { +.edit-password-success .edit-password-success-reset-link, +.AddUserPasswordInputContainer { display: flex; align-items: center; width: 100%; @@ -2013,12 +2082,14 @@ $(document).ready(function() { background-color: #F3F3F3; } -.edit-password-success .edit-password-success-reset-link span { +.edit-password-success .edit-password-success-reset-link span, +.AddUserPasswordInputContainer span { padding: 5px; cursor: pointer; } -.edit-password-success .edit-password-success-reset-link span:hover { +.edit-password-success .edit-password-success-reset-link span:hover, +.AddUserPasswordInputContainer span:hover { color: #ffffff; background-color: #ff7700; } @@ -2070,7 +2141,8 @@ $(document).ready(function() { .edit-password-success-ok .icon-button, .edit-username-success-ok .icon-button, -.user-property-main-user-close .icon-button { +.user-property-main-user-close .icon-button, +#AddUserSuccessContainer .icon-button { padding: 10px 20px; font-size: 1.1em; font-weight: bold; @@ -2079,16 +2151,22 @@ $(document).ready(function() { color: #777; } +#AddUserSuccessContainer .icon-button { + align-self: center; +} + .edit-password-success-ok .icon-button:hover, .edit-username-success-ok .icon-button:hover, -.user-property-main-user-close .icon-button:hover { +.user-property-main-user-close .icon-button:hover, +#AddUserSuccessContainer .icon-button:hover { background-color: #ddd; color: #3A3A3A; } .edit-password-success-ok .icon-button::before, .edit-username-success-ok .icon-button::before, -.user-property-main-user-close .icon-button::before { +.user-property-main-user-close .icon-button::before, +#AddUserSuccessContainer .icon-button::before { margin-left: 0; } @@ -2509,7 +2587,32 @@ $(document).ready(function() { flex-direction:column; border-radius:15px; align-items:center; - width: 270px; + width: 350px; +} + +#AddUserFieldContainer { + width: 100%; +} + +#AddUserSuccessContainer { + display: flex; + flex-direction: column; + width: 100%; + text-align: center; +} + +.AddUserPasswordInputContainer { + margin: 0; +} +.AddUserSubmitContainer { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; +} + +.AddUserTextField { + margin: 0 0 10px 0; } .AddIconContainer { @@ -2533,7 +2636,7 @@ $(document).ready(function() { .AddUserInputContainer { display: flex; flex-direction: column; - margin: 20px 0px; + margin: 5px 0px; width:100%; } @@ -2548,38 +2651,8 @@ $(document).ready(function() { padding: 10px 5px; } -.AddUserPasswordWrapper { - display:flex; - justify-content:space-between; - position: relative; -} - -.AddUserPasswordWrapper span { - font-size:1.3em; - cursor:pointer; -} - - -.AddUserPasswordWrapper:hover { - color:#ffa646; -} - -.AddUserGenPassword { - margin-top: 5px; - font-size: 1.1em; - cursor:pointer; -} -.AddUserGenPassword:hover, .AddUserGenPassword:active { - color:#ffa646; -} - -.AddUserGenPassword span { - margin-right:10px; -} - .AddUserErrors { visibility:hidden; - width:100%; padding:5px; border-left:solid 3px red; } @@ -2590,29 +2663,37 @@ $(document).ready(function() { color: #3F3E40; background-color: #FFA836; padding: 10px; - margin: 20px; + margin-top: 5px; font-size:1em; margin-bottom:0; } .AddUserCancel { - color: #3F3E40; + color: #A4A4A4; font-weight: bold; cursor: pointer; font-size:1em; } +.AddUserResult { + padding: 10px; + text-align: start; +} + /* Selectize Inputs (groups) */ #UserList .user-property-group .selectize-input, -#GuestUserList .user-property-group .selectize-input { +#GuestUserList .user-property-group .selectize-input, +#AddUser .user-property-group .selectize-input { overflow-y: scroll; } #UserList .item, #UserList .item.active, #GuestUserList .item, -#GuestUserList .item.active { +#GuestUserList .item.active, +#AddUser .item, +#AddUser .item.active { background-image:none; background-color: #ffa646; border-color: transparent; @@ -2621,7 +2702,8 @@ $(document).ready(function() { } #UserList .item .remove, -#GuestUserList .item .remove { +#GuestUserList .item .remove, +#AddUser .item .remove { background-color: transparent; border-top-right-radius: 20px; border-bottom-right-radius: 20px; @@ -2630,7 +2712,8 @@ $(document).ready(function() { } #UserList .item .remove:hover, -#GuestUserList .item .remove:hover { +#GuestUserList .item .remove:hover, +#AddUser .item .remove:hover { background-color: #ff7700; } diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css index dc733a840..902503522 100644 --- a/admin/themes/default/theme.css +++ b/admin/themes/default/theme.css @@ -5618,7 +5618,7 @@ input:checked + .slider:before, input:checked + .slider::after { position: relative; } -.AddUserErrors, .update-user-fail, .AddAlbumErrors, .EditUserErrors, .update-password-fail { +.AddUserErrors, .update-user-fail, .AddAlbumErrors, .EditUserErrors, .update-password-fail, .icon-red-error { background-color: #ffcfcf; color: #ff5252; border-left:solid 3px red; @@ -5635,7 +5635,8 @@ input:checked + .slider:before, input:checked + .slider::after { } .update-user-success, .update-password-success, -.update-username-success { +.update-username-success, +.border-green { border-left: 2px solid #00FF00; } #UserList .AddUserBlock p, diff --git a/admin/themes/roma/theme.css b/admin/themes/roma/theme.css index bbb6eb348..e1b01105e 100644 --- a/admin/themes/roma/theme.css +++ b/admin/themes/roma/theme.css @@ -1868,7 +1868,7 @@ li.plupload_delete a:hover {background: url("images/cancelhover.svg")!important; background-color: #343434; } -.AddUserErrors, .update-user-fail, .EditUserErrors, .update-password-fail { +.AddUserErrors, .update-user-fail, .EditUserErrors, .update-password-fail, .icon-red-error { background-color: #f22;; color: #ffd5dc; border-left:solid 3px #f22; @@ -1903,7 +1903,8 @@ li.plupload_delete a:hover {background: url("images/cancelhover.svg")!important; .update-user-success, .update-password-success, -.update-username-success { +.update-username-success, +.border-green { border-left: 2px solid #a9f6e3; } diff --git a/include/functions_mail.inc.php b/include/functions_mail.inc.php index edd7bda95..1c15ecabc 100644 --- a/include/functions_mail.inc.php +++ b/include/functions_mail.inc.php @@ -1041,6 +1041,43 @@ function pwg_generate_reset_password_mail($username, $reset_password_link, $gall ); } +/** + * Generate content mail for set password + * + * Return the content mail to send + * @since 15 + * @param string $username + * @param string $reset_password_link + * @param string $gallery_title + * @return string mail content + */ +function pwg_generate_set_password_mail($username, $set_password_link, $gallery_title) +{ + set_make_full_url(); + + $message = l10n('Someone requested that the password be set for the following user account:') . "\r\n\r\n"; + $message.= l10n( + 'Username "%s" on gallery %s', + $username, + get_gallery_home_url() + ); + $message.= "\r\n\r\n"; + $message.= l10n('To set your password, visit the following address:') . "\r\n"; + $message.= $set_password_link; + $message.= "\r\n\r\n"; + $message.= l10n('If this was a mistake, just ignore this email and nothing will happen.')."\r\n"; + + unset_make_full_url(); + + $message = trigger_change('render_lost_password_mail_content', $message); + + return array( + 'subject' => l10n('Welcome to ') . '['.$gallery_title.']', + 'content' => $message, + 'email_format' => 'text/plain', + ); +} + trigger_notify('functions_mail_included'); ?> diff --git a/include/functions_user.inc.php b/include/functions_user.inc.php index d4f1bf9ee..ab01015a4 100644 --- a/include/functions_user.inc.php +++ b/include/functions_user.inc.php @@ -1905,4 +1905,26 @@ function userprefs_get_param($param, $default_value=null) return $default_value; } + +/** + * See if this is the first time the user has logged on + * + * @since 15 + * @param int $user_id + * @return bool true if first connexion else false + */ +function first_connexion($user_id) +{ + $query = ' +SELECT COUNT(*) + FROM '.ACTIVITY_TABLE.' + WHERE action = \'login\' and performed_by = '.$user_id.''; + + list($logged_in) = pwg_db_fetch_row(pwg_query($query)); + if ($logged_in > 0) + { + return false; + } + return true; +} ?> diff --git a/include/ws_functions/pwg.users.php b/include/ws_functions/pwg.users.php index 2c9c38822..59d08c6f1 100644 --- a/include/ws_functions/pwg.users.php +++ b/include/ws_functions/pwg.users.php @@ -392,13 +392,18 @@ function ws_users_add($params, &$service) } } + if ($params['auto_password']) + { + $params['password'] = generate_key(rand(15, 20)); + } + $user_id = register_user( $params['username'], $params['password'], $params['email'], false, // notify admin $errors, - $params['send_password_by_mail'] + false // $params['send_password_by_mail'] ); if (!$user_id) @@ -1006,7 +1011,15 @@ function ws_users_generate_reset_password_link($params, &$service) if ($params['send_by_mail'] and !empty($user_lost['email'])) { - $email_params = pwg_generate_reset_password_mail($user_lost['username'], $generate_link['reset_password_link'], $conf['gallery_title']); + $first_login = first_connexion($params['user_id']); + if ($first_login) + { + $email_params = pwg_generate_set_password_mail($user_lost['username'], $generate_link['reset_password_link'], $conf['gallery_title']); + } + else + { + $email_params = pwg_generate_reset_password_mail($user_lost['username'], $generate_link['reset_password_link'], $conf['gallery_title']); + } // Here we remove the display of errors because they prevent the response from being parsed if (@pwg_mail($user_lost['email'], $email_params)) { diff --git a/password.php b/password.php index ee9262adc..c44421eb0 100644 --- a/password.php +++ b/password.php @@ -224,12 +224,14 @@ if (isset($_GET['key']) and !is_a_guest()) if (isset($_GET['key']) and !isset($_POST['submit'])) { + $first_login = false; $user_id = check_password_reset_key($_GET['key']); if (is_numeric($user_id)) { $userdata = getuserdata($user_id, false); $page['username'] = $userdata['username']; $template->assign('key', $_GET['key']); + $first_login = first_connexion($user_id); if (!isset($page['action'])) { @@ -278,6 +280,10 @@ if ('lost' == $page['action']) $template->assign('username_or_email', htmlspecialchars(stripslashes($_POST['username_or_email']))); } } +else if ('reset' == $page['action'] and isset($first_login) and $first_login) +{ + $title = l10n('Welcome'); +} $page['body_id'] = 'thePasswordPage'; diff --git a/ws.php b/ws.php index 3b1ce4437..99e5721a4 100644 --- a/ws.php +++ b/ws.php @@ -1155,6 +1155,11 @@ enabled_high, registration_date, registration_date_string, registration_date_sin 'ws_users_add', array( 'username' => array(), + 'auto_password' => array( + 'default'=>false, + 'flags'=>WS_TYPE_BOOL, + 'info' => 'if true ignores password and confirm password' + ), 'password' => array('default'=>null), 'password_confirm' => array('flags'=>WS_PARAM_OPTIONAL), 'email' => array('default'=>null),