From 9bcc2cfa0208117dac8eaff63fc95cdb38863016 Mon Sep 17 00:00:00 2001 From: Linty Date: Mon, 5 May 2025 21:40:59 +0200 Subject: [PATCH] fixes #2354 add profile standard page --- include/template.class.php | 2 +- language/en_UK/common.lang.php | 6 + language/fr_FR/common.lang.php | 6 + profile.php | 65 ++++- themes/default/template/profile_content.tpl | 11 + themes/standard_pages/js/profile.js | 156 ++++++++++++ themes/standard_pages/js/toaster.js | 32 +++ themes/standard_pages/template/profile.tpl | 255 ++++++++++++++++++++ themes/standard_pages/template/toaster.tpl | 47 ++++ themes/standard_pages/theme.css | 2 + 10 files changed, 569 insertions(+), 13 deletions(-) create mode 100644 themes/standard_pages/js/profile.js create mode 100644 themes/standard_pages/js/toaster.js create mode 100644 themes/standard_pages/template/profile.tpl create mode 100644 themes/standard_pages/template/toaster.tpl diff --git a/include/template.class.php b/include/template.class.php index 47b50f8e4..ba1f382bc 100644 --- a/include/template.class.php +++ b/include/template.class.php @@ -187,7 +187,7 @@ class Template // standard pages can't get the header to load the html header if ( 'default' != $theme - and in_array(script_basename(), array('identification', 'register', 'password')) + and in_array(script_basename(), array('identification', 'register', 'password', 'profile')) and (($themeconf['use_standard_pages'] ?? false) or conf_get_param('use_standard_pages', false)) ) { diff --git a/language/en_UK/common.lang.php b/language/en_UK/common.lang.php index f146fd883..ff6d4360f 100644 --- a/language/en_UK/common.lang.php +++ b/language/en_UK/common.lang.php @@ -471,3 +471,9 @@ $lang['Confirm my new password'] = 'Confirm my new password'; $lang['Confirm Password'] = 'Confirm Password'; $lang['Confirm new password'] = 'Confirm new password'; $lang['Set my password'] = 'Set my password'; +$lang['Account'] = 'Account'; +$lang['Manage your account'] = 'Manage your account'; +$lang['Choose how you want to see your gallery'] = 'Choose how you want to see your gallery'; +$lang['Change your password'] = 'Change your password'; +$lang['Options'] = 'Options'; +$lang['Your changes have been applied.'] = 'Your changes have been applied.'; diff --git a/language/fr_FR/common.lang.php b/language/fr_FR/common.lang.php index ab584bba9..28e6580d4 100644 --- a/language/fr_FR/common.lang.php +++ b/language/fr_FR/common.lang.php @@ -470,3 +470,9 @@ $lang['Confirm new password'] = 'Confirmer le nouveau mot de passe'; $lang['Set my password'] = 'Définir mon mot de passe'; $lang['Your password was successfully set'] = 'Votre mot de passe a été défini avec succès'; $lang['Your password was successfully reset'] = 'Votre mot de passe a été réinitialisé avec succès'; +$lang['Account'] = 'Mon compte'; +$lang['Manage your account'] = 'Gérer votre compte'; +$lang['Choose how you want to see your gallery'] = 'Choisissez comment vous voulez voir votre galerie'; +$lang['Change your password'] = 'Changez votre mot de passe'; +$lang['Options'] = 'Options'; +$lang['Your changes have been applied.'] = 'Vos changements ont été pris en compte.'; diff --git a/profile.php b/profile.php index 23ae8607b..86f182a92 100644 --- a/profile.php +++ b/profile.php @@ -30,22 +30,24 @@ if (!defined('PHPWG_ROOT_PATH')) trigger_notify('loc_begin_profile'); -// Reset to default (Guest) custom settings - if (isset($_POST['reset_to_default'])) - { - $fields = array( - 'nb_image_page', 'expand', - 'show_nb_comments', 'show_nb_hits', 'recent_period', 'show_nb_hits' - ); + $fields = array( + 'nb_image_page', 'expand', + 'show_nb_comments', 'show_nb_hits', 'recent_period', 'show_nb_hits' + ); - // Get the Guest custom settings - $query = ' + // Get the Guest custom settings + $query = ' SELECT '.implode(',', $fields).' FROM '.USER_INFOS_TABLE.' WHERE user_id = '.$conf['default_user_id'].' ;'; - $result = pwg_query($query); - $default_user = pwg_db_fetch_assoc($result); + $result = pwg_query($query); + $default_user = pwg_db_fetch_assoc($result); + $template->assign('DEFAULT_USER_VALUES', $default_user); + +// Reset to default (Guest) custom settings + if (isset($_POST['reset_to_default'])) + { $userdata = array_merge($userdata, $default_user); } @@ -68,10 +70,49 @@ SELECT '.implode(',', $fields).' $themeconf = $template->get_template_vars('themeconf'); if (!isset($themeconf['hide_menu_on']) OR !in_array('theProfilePage', $themeconf['hide_menu_on'])) { - include( PHPWG_ROOT_PATH.'include/menubar.inc.php'); + if ($themeconf['id'] !== 'standard_pages') + { + include( PHPWG_ROOT_PATH.'include/menubar.inc.php'); + } } include(PHPWG_ROOT_PATH.'include/page_header.php'); + + //Load language if cookie is set from login/register/password pages + if (isset($_COOKIE['lang']) and $user['language'] != $_COOKIE['lang']) + { + if (!array_key_exists($_COOKIE['lang'], get_languages())) + { + fatal_error('[Hacking attempt] the input parameter "'.$_COOKIE['lang'].'" is not valid'); + } + + $user['language'] = $_COOKIE['lang']; + load_language('common.lang', '', array('language'=>$user['language'])); + } + + //Get list of languages + foreach (get_languages() as $language_code => $language_name) + { + $language_options[$language_code] = $language_name; + } + + $template->assign(array( + 'language_options' => $language_options, + 'current_language' => $user['language'] + )); + + //Get link to doc + if ('fr' == substr($user['language'], 0, 2)) + { + $help_link = "https://doc-fr.piwigo.org/les-utilisateurs/se-connecter-a-piwigo"; + } + else + { + $help_link = "https://doc.piwigo.org/managing-users/log-in-to-piwigo"; + } + + $template->assign('HELP_LINK', $help_link); + trigger_notify('loc_end_profile'); flush_page_messages(); $template->pparse('profile'); diff --git a/themes/default/template/profile_content.tpl b/themes/default/template/profile_content.tpl index 0c4f26c10..ffecefaff 100644 --- a/themes/default/template/profile_content.tpl +++ b/themes/default/template/profile_content.tpl @@ -84,6 +84,17 @@ {/if} +{if isset($PLUGINS_PROFILE)} + {foreach from=$PLUGINS_PROFILE item=plugin_block} +
+ {$plugin_block.name} +
+ {include file=$plugin_block.template} +
+
+ {/foreach} +{/if} +

diff --git a/themes/standard_pages/js/profile.js b/themes/standard_pages/js/profile.js new file mode 100644 index 000000000..a7a2496b3 --- /dev/null +++ b/themes/standard_pages/js/profile.js @@ -0,0 +1,156 @@ +let PWG_TOKEN; +$(function() { + PWG_TOKEN = $('#pwg_token').val(); + $('.profile-section .display-btn').on('click', function () { + const display = $(this).data('display'); + const selector = $(`#${display}`); + const element = selector.get(0); + + if (selector.hasClass('open')) { + // close + element.style.maxHeight = element.scrollHeight + 'px'; + void element.offsetHeight; + element.style.maxHeight = '0px'; + selector.removeClass('open'); + $(this).addClass('close'); + } else { + // open + selector.addClass('open'); + element.style.maxHeight = element.scrollHeight + 'px'; + $(this).removeClass('close'); + if ('account-display' !== display) { + setTimeout(() => { + const el = $(`#${display.split('-')[0]}-section`).get(0); + el.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + }, 200); + } + } + }); + + $('#account-section .display-btn').trigger('click'); + + $('#save_account').on('click', function() { + const mail = $('#email').val(); + if (!mail || mail == '') { + $('#email_error').show(); + return; + } + setInfos({ email: mail }); + }); + + $('#save_preferences').on('click', function() { + const values = { + nb_image_page: $('#nb_image_page').val(), + theme: $('select[name="theme"]').val(), + language: $('select[name="language"]').val(), + recent_period: $('#recent_period').val(), + expand: $('#opt_album').is(':checked'), + show_nb_comments: $('#opt_comment').is(':checked'), + show_nb_hits: $('#opt_hits').is(':checked') + } + + if (values.nb_image_page == '') { + $('#error_nb_image').show(); + return; + } + + if (values.recent_period == '') { + $('#error_period').show(); + return; + } + + setInfos({...values}); + }); + + $('#save_password').on('click', function() { + const passwords = { + password: $('#password').val(), + new_password: $('#password_new').val(), + conf_new_password: $('#password_conf').val(), + } + if (passwords.password == '' || passwords.new_password == '' || passwords.conf_new_password == '') { + $('#password-section input').each((i, element) => { + const el = $(element); + if (el.val() == '') { + el.parent().siblings().show(); + } + }); + return; + } + setInfos({...passwords}); + $('#password-section input').val(''); + }); + + standardSaveSelector.forEach((selector, i) => { + // console.log(i, selector); + $(selector).on('click', function() { + const values = {}; + $(`#${i}-section`).find('input, textarea, select').each((i, element) => { + const el = $(element); + const inputName = el.attr('name'); + const inputValue = el.val(); + values[inputName] = inputValue; + }); + setInfos({...values}); + }); + }); + + + const userDefaultValues = { + nb_image_page: $('input[name="nb_image_page"]').val(), + theme: $('select[name="theme"]').val(), + language: $('select[name="language"]').val(), + recent_period: $('input[name="recent_period"]').val(), + opt_album: $('#opt_album').is(':checked'), + opt_comment: $('#opt_comment').is(':checked'), + opt_hits: $('#opt_hits').is(':checked'), + } + + $('#reset_preferences').on('click', function() { + $('input[name="nb_image_page"]').val(userDefaultValues.nb_image_page); + $('select[name="theme"]').val(userDefaultValues.theme); + $('select[name="language"]').val(userDefaultValues.language); + $('input[name="recent_period"]').val(userDefaultValues.recent_period); + $('#opt_album').prop('checked', userDefaultValues.opt_album); + $('#opt_comment').prop('checked', userDefaultValues.opt_comment); + $('#opt_hits').prop('checked', userDefaultValues.opt_hits); + }); + + $('#default_preferences').on('click', function() { + $('input[name="nb_image_page"]').val(preferencesDefaultValues.nb_image_page); + $('input[name="recent_period"]').val(preferencesDefaultValues.recent_period); + $('#opt_album').prop('checked', preferencesDefaultValues.opt_album); + $('#opt_comment').prop('checked', preferencesDefaultValues.opt_comment); + $('#opt_hits').prop('checked', preferencesDefaultValues.opt_hits); + }); +}); + +function setInfos(params, method='pwg.users.setMyInfo') { + // for debug + // console.log('setInfos', params); + const all_params = { + ...params, + pwg_token: PWG_TOKEN + } + $.ajax({ + url: `ws.php?format=json&method=${method}`, + type: "POST", + dataType: "json", + data: all_params, + success: (data) => { + if (data.stat == 'ok') { + pwgToaster({ text: data.result, icon: 'success' }); + } else if (data.stat == 'fail') { + pwgToaster({ text: data.message, icon: 'error' }); + } else { + pwgToaster({ text: 'Error try later...', icon: 'error' }); + } + }, + error: function (e) { + pwgToaster({ text: e.responseJSON?.message ?? 'Server Internal Error try later...', icon: 'error' }); + }, + }); +} \ No newline at end of file diff --git a/themes/standard_pages/js/toaster.js b/themes/standard_pages/js/toaster.js new file mode 100644 index 000000000..1d1396a54 --- /dev/null +++ b/themes/standard_pages/js/toaster.js @@ -0,0 +1,32 @@ +function pwgToaster(info) { + if (!info.text || !info.icon) { + console.log('set info.text or info.icon'); + return; + } + + if (typeof info.text !== 'string') { + console.log('info.text is not a string'); + return; + } + + if (info.icon !== 'success' && info.icon !== 'error') { + console.log('info.icon must be success or error'); + return; + } + + const template = $('#toast_template').clone(); + + template.find('.toast_text').html(info.text); + template.find('.toast_icon').addClass(info.icon === 'success' ? 'icon-ok' : 'icon-cancel'); + template.addClass(info.icon === 'success' ? info.icon : 'error'); + + template.removeClass('template'); + template.appendTo('#pwg_toaster'); + + const time = info.time ?? 3600; + setInterval(() => { + template.fadeOut(() => { + template.remove(); + }) + }, time); +} \ No newline at end of file diff --git a/themes/standard_pages/template/profile.tpl b/themes/standard_pages/template/profile.tpl new file mode 100644 index 000000000..da9b9658c --- /dev/null +++ b/themes/standard_pages/template/profile.tpl @@ -0,0 +1,255 @@ +{combine_css id='standard_pages_css' path="themes/standard_pages/css/standard_pages.css" order=100} +{combine_css path="themes/default/vendor/fontello/css/gallery-icon.css" order=-10} +{combine_css path="admin/themes/default/fontello/css/fontello.css" order=-11} + + +{combine_script id='standard_pages_js' load='async' require='jquery' path='themes/standard_pages/js/standard_pages.js'} +{combine_script id='standard_profile_js' load='async' require='jquery' path='themes/standard_pages/js/profile.js'} +{footer_script} +const standardSaveSelector = []; +const preferencesDefaultValues = { + nb_image_page: {$DEFAULT_USER_VALUES['nb_image_page']}, + recent_period: {$DEFAULT_USER_VALUES['recent_period']}, + opt_album: {$DEFAULT_USER_VALUES['expand']}, + opt_comment: {$DEFAULT_USER_VALUES['show_nb_comments']}, + opt_hits: {$DEFAULT_USER_VALUES['show_nb_hits']}, +}; +{/footer_script} + + +

+
+ + +
+
+ {'Help'|translate} + {include file='toaster.tpl'} +
+
+ +
+ +
+ + {'Return to the gallery'|translate} + + {* ACCOUNT *} +
+
+
+

{'Account'|translate}

+

{'Manage your account'|translate}

+
+ +
+
+
+ +
+ +

{$USERNAME}

+ +
+
+
+ +
+ + +
+

+ {'must not be empty'|translate}

+
+
+ +
+
+
+ + {* PREFERENCES *} + {if $ALLOW_USER_CUSTOMIZATION} +
+
+
+

{'Preferences'|translate}

+

{'Choose how you want to see your gallery'|translate}

+
+ +
+
+
+ +
+ + +
+

+ {'must not be empty'|translate}

+
+ +
+ +
+ + {html_options name=theme options=$template_options selected=$template_selection} +
+

{'must not be empty'|translate}

+
+ +
+ +
+ + {html_options name=language options=$language_options selected=$language_selection} +
+

{'must not be empty'|translate}

+
+ +
+ +
+ + +
+

+ {'must not be empty'|translate}

+
+ + {* OPTIONS *} + +
+
+ +

{'Expand all albums'|@translate}

+
+ + {if $ACTIVATE_COMMENTS} +
+ +

{'Show number of comments'|@translate}

+
+ {/if} + +
+ +

{'Show number of hits'|@translate}

+
+
+ +
+ +
+ + +
+
+
+
+ {/if} + + {* PASSWORD *} + {if not $SPECIAL_USER} +
+
+
+

{'Password'|translate}

+

{'Change your password'|translate}

+
+ +
+
+
+ +
+ + + +
+

{'must not be empty'|translate}

+
+ +
+ +
+ + + +
+

{'must not be empty'|translate}

+
+ +
+ +
+ + + +
+

{'must not be empty'|translate}

+
+ +
+ +
+
+
+ {/if} + + {if isset($PLUGINS_PROFILE)} + {foreach from=$PLUGINS_PROFILE item=plugin_block key=k_block} +
+
+
+

{$plugin_block.name}

+

{$plugin_block.desc}

+
+ +
+
+ {include file=$plugin_block.template} + {if $plugin_block.standard_show_save} +
+ +
+ {footer_script} + standardSaveSelector.push('#save_{$k_block}'); + {/footer_script} + {/if} +
+
+ {/foreach} + {/if} + + {if count($language_options) > 1} +
+
+ + {foreach from=$language_options key=$code item=$lang} + {$lang} + {/foreach} + +
+ {$language_options[$current_language]} +
+
+
+ {/if} + \ No newline at end of file diff --git a/themes/standard_pages/template/toaster.tpl b/themes/standard_pages/template/toaster.tpl new file mode 100644 index 000000000..a264802d1 --- /dev/null +++ b/themes/standard_pages/template/toaster.tpl @@ -0,0 +1,47 @@ +{combine_script id='toaster_js' load='async' require='jquery' path='themes/standard_pages/js/toaster.js'} +{html_style} +.toast.template { + display: none; +} + +.toaster { + position: absolute; + right: 15px; + max-width: 300px; + top: 40px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.toast { + display: flex; + gap: 5px; + padding: 10px; + border-radius: 5px; + align-items: center; + font-size: 15px; + width: fit-content; + align-self: flex-end; +} + +.toast i:before { + font-size: 33px; +} + +.toast.success { + background-color:#4CA530; + color:#D6FFCF; +} + +.toast.error { + background-color:#BE4949; + color:#FFC8C8; +} +{/html_style} +
+
+ +

+
+
\ No newline at end of file diff --git a/themes/standard_pages/theme.css b/themes/standard_pages/theme.css index 8b8d7b34b..06a5f0626 100644 --- a/themes/standard_pages/theme.css +++ b/themes/standard_pages/theme.css @@ -455,6 +455,8 @@ p.error-message{ .profile-section .username { width: fit-content; cursor: not-allowed; + background-color: transparent !important; + border: none !important; } .profile-section .input-container.radio {