diff --git a/admin/include/functions.php b/admin/include/functions.php index 331e21a8b..71b3c2706 100644 --- a/admin/include/functions.php +++ b/admin/include/functions.php @@ -1929,7 +1929,7 @@ function fill_lounge($images, $categories) */ function empty_lounge($invalidate_user_cache=true) { - global $logger; + global $logger, $conf; if (isset($conf['empty_lounge_running'])) { @@ -2734,6 +2734,17 @@ function get_newsletter_subscribe_base_url($language='en_UK') return PHPWG_URL.'/announcement/subscribe/'; } +/** + * Get url on piwigo.org for old newsletters + * + * @param string $language (unused) + * @return string + */ +function get_old_newsletters_base_url($language='en_UK') +{ + return PHPWG_URL.'/newsletter'; +} + /** * Return admin menu id for accordion. * diff --git a/admin/intro.php b/admin/intro.php index 4cfcb02b5..c9629a5fe 100644 --- a/admin/intro.php +++ b/admin/intro.php @@ -104,6 +104,7 @@ if ($conf['show_newsletter_subscription'] and userprefs_get_param('show_newslett array( 'EMAIL' => $user['email'], 'SUBSCRIBE_BASE_URL' => get_newsletter_subscribe_base_url($user['language']), + 'OLD_NEWSLETTERS_URL' => get_old_newsletters_base_url($user['language']), ) ); } diff --git a/admin/themes/default/images/promote-newsletter.png b/admin/themes/default/images/promote-newsletter.png new file mode 100644 index 000000000..6b37d63e8 Binary files /dev/null and b/admin/themes/default/images/promote-newsletter.png differ diff --git a/admin/themes/default/template/intro.tpl b/admin/themes/default/template/intro.tpl index eff014028..28ba8a705 100644 --- a/admin/themes/default/template/intro.tpl +++ b/admin/themes/default/template/intro.tpl @@ -13,6 +13,9 @@ const storage_total = {$STORAGE_TOTAL}; const storage_details = {$STORAGE_CHART_DATA|json_encode}; const translate_files = "{'%d files'|translate|escape:javascript}"; let translate_type = {}; +{if isset($SUBSCRIBE_BASE_URL)} + const newsletter_base_url = "{$SUBSCRIBE_BASE_URL}"; +{/if} {literal} jQuery().ready(function(){ jQuery('.cluetip').cluetip({ @@ -42,10 +45,37 @@ jQuery().ready(function(){ } }); {/if} + +{if isset($SUBSCRIBE_BASE_URL)} + jQuery(".eiw").prepend(` +
+
+ + + +
+ {"Subscribe to our newsletter and stay updated!"|@translate|escape:javascript} + + {"See previous newsletters"|@translate|escape:javascript} +
+ +
+ +
`); + +{/if} + {literal} - jQuery('.newsletter-subscription a').click(function() { - jQuery('.newsletter-subscription').hide(); + jQuery("#newsletterSubscribeInput").change(function(){ + jQuery("#newsletterSubscribeLink").attr("href", newsletter_base_url + jQuery("#newsletterSubscribeInput").val()) + }) + + jQuery('.newsletter-hide').click(function() { + jQuery('.promote-newsletter').hide(); jQuery.ajax({ type: 'GET', @@ -247,7 +277,4 @@ translate_type['{$type_to_translate}'] = "{$type_to_translate|translate}"; {/if} -{if isset($SUBSCRIBE_BASE_URL)} -
{'Subscribe %s to Piwigo Announcements Newsletter'|@translate:$EMAIL} {'... or hide this link'|translate} -{/if}

\ No newline at end of file diff --git a/admin/themes/default/template/updates_ext.tpl b/admin/themes/default/template/updates_ext.tpl index 5c1b8dc32..a89aa0241 100644 --- a/admin/themes/default/template/updates_ext.tpl +++ b/admin/themes/default/template/updates_ext.tpl @@ -103,6 +103,34 @@ function updateExtension(type, id, revision) { }); }; +const targetNode = document.getElementById("theAdminPage"); + +const config = { attributes: false, childList: true, subtree: true }; + +const callback = (mutationList, observer) => { + for (const mutation of mutationList) { + if (mutation.type === "childList") { + let popup = jQuery("#jGrowl").children(); + for (let i = 0; i < popup.length; i++){ + if ((jQuery(popup[i])).hasClass("success")){ + if (! ((jQuery(popup[i]).children(":first")).hasClass("jGrowl-popup-icon icon-ok"))){ + jQuery(popup[i]).prepend('
') + } + }; + + if ((jQuery(popup[i])).hasClass("error")){ + if (! ((jQuery(popup[i]).children(":first")).hasClass("jGrowl-popup-icon icon-cancel"))){ + jQuery(popup[i]).prepend('
') + } + } + }; + } + } +}; + +const observer = new MutationObserver(callback); +observer.observe(targetNode, config); + function ignoreExtension(type, id) { queuedManager.add({ type: 'GET', diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css index 41e5357a1..89c50770a 100644 --- a/admin/themes/default/theme.css +++ b/admin/themes/default/theme.css @@ -2317,10 +2317,79 @@ h2:lang(en) { text-transform:capitalize; } border-left:4px solid #00529b; } +.jGrowl-popup-icon{ + position: absolute; + font-size: 4em; + left: -25px; +} + .success { - color: #6DCE5E; - background-color:#D6FFCF; - border-left:4px solid #6DCE5E; + color: #3C3C3C; +} +div.jGrowl div.jGrowl-notification.success{ + background: none; + background-color: #D6FFCF; + opacity: 1; + text-align: left; + font-family: 'Open Sans'; + box-shadow: 0px 5px 10px 5px rgba(0,0,0,0.1); + margin-bottom: 1em; +} +div.success .jGrowl-popup-icon{ + color: #4CA530; +} + +.error{ + color: #3C3C3C; +} +div.jGrowl div.jGrowl-notification.error{ + background: none; + background-color: #FFC8C8; + opacity: 1; + text-align: left; + font-family: 'Open Sans'; + box-shadow: 0px 5px 10px 5px rgba(0,0,0,0.1); + margin-bottom: 1em; +} +div.error .jGrowl-popup-icon{ + color: #BE4949; +} + +div.jGrowl div.jGrowl-notification div.jGrowl-message { + font-size: 1.2em !important; + margin: 0% 0% 0% 28%; +} +div.jGrowl div.jGrowl-notification div.jGrowl-header { + font-size: 1.2em !important; + font-weight: 800 !important; + margin: 5% 0% 2% 28%; +} +div.success div.jGrowl-close, div.error div.jGrowl-close { + font-size: 2em !important; + font-weight: 1000 !important; + position: relative; + top: -7px; + right: 1px; +} + +div.jGrowl div.jGrowl-closer { + visibility: hidden; +} + +div.jGrowl div.jGrowl-notification{ + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=85); + zoom: 1; + width: 21vw; + height: 100%; + padding: 18%; + margin-top: 5px; + margin-bottom: 5px; + font-size: 1em; + + display: none; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; } .metasync-success { @@ -7364,7 +7433,7 @@ color:#FF7B00; color: black !important; } -.promote-apps { +.promote-apps{ display: flex; flex-direction: column; justify-content: flex-end; @@ -7379,6 +7448,71 @@ color:#FF7B00; box-shadow: 0px 5px 10px 5px rgba(0,0,0,0.1) } +.promote-newsletter{ + display: flex; + flex-direction: column; + justify-content: flex-end; + + border-radius: 5px; + margin: 0 20px 15px 20px; + + background: #f5d8b0; + position: relative; + + box-shadow: 0px 5px 10px 5px rgba(0,0,0,0.1) +} + +.promote-newsletter-content{ + display: flex; + flex-direction: column; + justify-content: center; + flex: 2; + + margin-left: 8.45%; + margin-right: 16.32%; +} + +.promote-link { + color: #E18C32; + font-size: 15px; + margin-block: 3.5%; + font-weight: 700; +} + +.promote-newsletter-title{ + font-size: 24px; + font-weight: 700; + text-align: left; + margin-top: 3.5%; + margin-bottom: 1.75%; + color: #493C21; +} + +.subscribe-newsletter{ + font-size: 15px; + max-height: 17.5%; + display: flex; + gap: 4.64%; + margin-block: 1.75%; +} + +.promote-image{ + height: auto; + width: 25%; + object-fit: contain; +} + +#newsletterSubscribeInput{ + font-weight: 700; + background-color: #FFFFFF; + color: #000000; + padding-inline: 4%; + padding-block: 2%; + border-radius: 2px; + border-width: 2px; + border-color: #D3D3D3; +} + .close-apps { padding: 1px; margin-left: auto; @@ -7434,7 +7568,7 @@ color:#FF7B00; flex-direction: column; font-size: 30px; font-weight: 700; - color: #000; + color: #493C21; justify-content: center; flex: 2; } @@ -7452,11 +7586,11 @@ color:#FF7B00; .go-to-porg { cursor: pointer; padding: 7px 14px; - font-size: 18px; + font-size: 15px; font-weight: bold; - background-color: #ffa744; + max-width: 41.06%; + background-color: #f4ab4f; color: #3c3c3c; - width: 150px; text-align: center; white-space: nowrap; } @@ -7464,6 +7598,9 @@ color:#FF7B00; .go-to-porg::before { margin-right: 10px; } +.go-to-porg:hover { + color: #3c3c3c; +} .promote-content .right-side .promote-text { display: flex; @@ -7639,6 +7776,40 @@ color:#FF7B00; } } +@media (max-width: 393px) { + .promote-content{ + display: flex; + flex-direction: column; + justify-content: center; + gap: 15%; + flex: 2; + } + + .promote-newsletter-content { + display: flex; + flex-direction: column; + justify-content: center; + flex: 2; + margin-inline: 3.8%; + } + + .promote-image{ + height: auto; + width: 77%; + object-fit: contain; + align-self: center; + } + + .go-to-porg{ + max-width: 100%; + margin-top: 5%; + } + + .promote-link{ + margin-block: 10%; + } +} + /* Css for whats new popin */ #whats_new{ display: none; diff --git a/i.php b/i.php index 333c25baa..cd80e5652 100644 --- a/i.php +++ b/i.php @@ -234,7 +234,7 @@ function parse_request() { ierror('Invalid crop', 400); } - $greatest = ImageStdParams::get_by_type(IMG_XXLARGE); + $greatest = ImageStdParams::get_by_type(IMG_4XLARGE); $key = array(); $params->add_url_tokens($key); diff --git a/identification.php b/identification.php index c1699caef..dc84521a8 100644 --- a/identification.php +++ b/identification.php @@ -74,6 +74,8 @@ if (isset($_POST['login'])) // {redirect (final) = http://localhost/piwigo/git/admin.php} $root_url = get_absolute_root_url(); + $_SESSION['connected_with'] = 'pwg_ui'; + redirect( empty($redirect_to) ? get_gallery_home_url() diff --git a/include/config_default.inc.php b/include/config_default.inc.php index b1e27bc6a..21336b389 100644 --- a/include/config_default.inc.php +++ b/include/config_default.inc.php @@ -460,6 +460,34 @@ $conf['session_use_ip_address'] = true; // session"). $conf['session_gc_probability'] = 1; +// +-----------------------------------------------------------------------+ +// | api key | +// +-----------------------------------------------------------------------+ + +// api_key_duration: available duration options (in days) for API key creation. +// Array of predefined durations that will be displayed in the select dropdown +// when creating a new API key. Use 'custom' to allow users to set a specific +// expiration date with a date picker input. +$conf['api_key_duration'] = ['30', '90', '180', '365', 'custom']; + +// The following API methods are prohibited when making requests with an API key. +// These restrictions are in place for security reasons and to prevent unauthorized +// access to sensitive operations that require higher-level authentication. +$conf['api_key_forbidden_methods'] = array( + // users + 'pwg.users.generatePasswordLink', + 'pwg.users.getAuthKey', + 'pwg.users.setMainUser', + 'pwg.users.setInfo', + // plugins + 'pwg.plugins.performAction', + // themes + 'pwg.themes.performAction', + // extensions + 'pwg.extensions.ignoreUpdate', + 'pwg.extensions.update', +); + // +-----------------------------------------------------------------------+ // | debug/performance | // +-----------------------------------------------------------------------+ diff --git a/include/derivative_std_params.inc.php b/include/derivative_std_params.inc.php index 44acedee6..b39428940 100644 --- a/include/derivative_std_params.inc.php +++ b/include/derivative_std_params.inc.php @@ -20,6 +20,8 @@ define('IMG_MEDIUM', 'medium'); define('IMG_LARGE', 'large'); define('IMG_XLARGE', 'xlarge'); define('IMG_XXLARGE', 'xxlarge'); +define('IMG_3XLARGE', '3xlarge'); +define('IMG_4XLARGE', '4xlarge'); define('IMG_CUSTOM', 'custom'); @@ -53,7 +55,7 @@ final class ImageStdParams /** @var string[] */ private static $all_types = array( IMG_SQUARE, IMG_THUMB, IMG_XXSMALL, IMG_XSMALL, IMG_SMALL, - IMG_MEDIUM, IMG_LARGE, IMG_XLARGE, IMG_XXLARGE + IMG_MEDIUM, IMG_LARGE, IMG_XLARGE, IMG_XXLARGE, IMG_3XLARGE, IMG_4XLARGE ); /** @var DerivativeParams[] */ private static $all_type_map = array(); @@ -214,6 +216,8 @@ final class ImageStdParams IMG_LARGE => new DerivativeParams( SizingParams::classic(1008,756) ), IMG_XLARGE => new DerivativeParams( SizingParams::classic(1224,918) ), IMG_XXLARGE => new DerivativeParams( SizingParams::classic(1656,1242) ), + IMG_3XLARGE => new DerivativeParams( SizingParams::classic(2232,1674) ), + IMG_4XLARGE => new DerivativeParams( SizingParams::classic(3000,2250) ), ); $now = time(); foreach($arr as $params) diff --git a/include/functions_session.inc.php b/include/functions_session.inc.php index b36b39743..f7718a100 100644 --- a/include/functions_session.inc.php +++ b/include/functions_session.inc.php @@ -153,6 +153,13 @@ SELECT data */ function pwg_session_write($session_id, $data) { + // when the request is authenticated via api_key (PWG_API_KEY_REQUEST), + // you do not want the session to be written to the database (no user session persistence) + // this avoids polluting the session table with stateless API accesses + if (defined('PWG_API_KEY_REQUEST')) + { + return true; + } $query = ' REPLACE INTO '.SESSIONS_TABLE.' (id,data,expiration) diff --git a/include/functions_user.inc.php b/include/functions_user.inc.php index 5ffe9d757..350c44638 100644 --- a/include/functions_user.inc.php +++ b/include/functions_user.inc.php @@ -1661,14 +1661,28 @@ function get_recent_photos_sql($db_field) * * @return bool */ -function auth_key_login($auth_key) +function auth_key_login($auth_key, $connection_by_header=false) { global $conf, $user, $page; - if (!preg_match('/^[a-z0-9]{30}$/i', $auth_key)) + $valid_key = false; + $secret_key = null; + if (preg_match('/^[a-z0-9]{30}$/i', $auth_key)) { - return false; + $valid_key = 'auth_key'; } + else if ( + preg_match('/^pkid-\d{8}-[a-z0-9]{20}:[a-z0-9]{40}$/i', $auth_key) + and $connection_by_header + ) + { + $valid_key = 'api_key'; + $tmp_key = explode(':', $auth_key); + $auth_key = $tmp_key[0]; + $secret_key = $tmp_key[1]; + } + + if (!$valid_key) return false; $query = ' SELECT @@ -1689,6 +1703,22 @@ SELECT $key = $keys[0]; + // the key is an api_key + if ('api_key' === $valid_key) + { + // check secret + if (!pwg_password_verify($secret_key, $key['apikey_secret'])) + { + return false; + } + + // is the key is revoked? + if (null != $key['revoked_on']) + { + return false; + } + } + // is the key still valid? if (strtotime($key['expired_on']) < strtotime($key['dbnow'])) { @@ -1697,12 +1727,34 @@ SELECT } // admin/webmaster/guest can't get connected with authentication keys - if (!in_array($key['status'], array('normal','generic'))) + if ('auth_key' === $valid_key and !in_array($key['status'], array('normal','generic'))) { return false; } $user['id'] = $key['user_id']; + + // update last used key + single_update( + USER_AUTH_KEYS_TABLE, + array('last_used_on' => $key['dbnow']), + array( + 'user_id' => $user['id'], + 'auth_key' => $key['auth_key'] + ), + ); + + // set the type of connection + $_SESSION['connected_with'] = $valid_key; + + // if the connection is made via an API key in the header, + // access is authenticated without creating a persistent user session + // this enables stateless authentication for API calls + if ($connection_by_header) + { + return true; + } + log_user($user['id'], false); trigger_notify('login_success', $key['username']); @@ -1771,6 +1823,7 @@ SELECT 'created_on' => $now, 'duration' => $conf['auth_key_duration'], 'expired_on' => $expiration, + 'key_type' => 'auth_key', ); single_insert(USER_AUTH_KEYS_TABLE, $key); @@ -1799,6 +1852,7 @@ UPDATE '.USER_AUTH_KEYS_TABLE.' SET expired_on = NOW() WHERE user_id = '.$user_id.' AND expired_on > NOW() + AND key_type = \'auth_key\' ;'; pwg_query($query); } @@ -2028,4 +2082,555 @@ SELECT COUNT(*) } return true; } + +/** + * Check all user infos and save parameters + * + * @since 16 + * @param mixed[] $params + * @option string username (optional) + * @option string password (optional) + * @option string email (optional) + * @option string status (optional) + * @option int level (optional) + * @option string language (optional) + * @option string theme (optional) + * @option int nb_image_page (optional) + * @option int recent_period (optional) + * @option bool expand (optional) + * @option bool show_nb_comments (optional) + * @option bool show_nb_hits (optional) + * @option bool enabled_high (optional) + */ +function check_and_save_user_infos($params) +{ + if (isset($params['username']) and strlen(str_replace( " ", "", $params['username'])) == 0) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'Name field must not be empty'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'Name field must not be empty' + ) + ); + } + + global $conf, $user, $service; + + include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); + + $updates = $updates_infos = array(); + $update_status = null; + + if (count($params['user_id']) == 1) + { + if (get_username($params['user_id'][0]) === false) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'This user does not exist.'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'This user does not exist.' + ) + ); + } + + if (!empty($params['username'])) + { + $user_id = get_userid($params['username']); + if ($user_id and $user_id != $params['user_id'][0]) + { + // return new PwgError(WS_ERR_INVALID_PARAM, l10n('this login is already used')); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => l10n('this login is already used') + ) + ); + } + if ($params['username'] != strip_tags($params['username'])) + { + // return new PwgError(WS_ERR_INVALID_PARAM, l10n('html tags are not allowed in login')); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => l10n('html tags are not allowed in login') + ) + ); + } + $updates[ $conf['user_fields']['username'] ] = $params['username']; + } + + if (!empty($params['email'])) + { + if ( ($error = validate_mail_address($params['user_id'][0], $params['email'])) != '') + { + // return new PwgError(WS_ERR_INVALID_PARAM, $error); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => $error + ) + ); + } + $updates[ $conf['user_fields']['email'] ] = $params['email']; + } + + if (!empty($params['password'])) + { + if (!is_webmaster()) + { + $password_protected_users = array($conf['guest_id']); + + $query = ' +SELECT + user_id + FROM '.USER_INFOS_TABLE.' + WHERE status IN (\'webmaster\', \'admin\') +;'; + $admin_ids = query2array($query, null, 'user_id'); + + // we add all admin+webmaster users BUT the user herself + $password_protected_users = array_merge($password_protected_users, array_diff($admin_ids, array($user['id']))); + + if (in_array($params['user_id'][0], $password_protected_users)) + { + // return new PwgError(403, 'Only webmasters can change password of other "webmaster/admin" users'); + return array( + 'error' => array( + 'code' => 403, + 'message' => 'Only webmasters can change password of other "webmaster/admin" users' + ) + ); + } + } + + $updates[ $conf['user_fields']['password'] ] = $conf['password_hash']($params['password']); + } + } + + if (!empty($params['status'])) + { + if (in_array($params['status'], array('webmaster', 'admin')) and !is_webmaster() ) + { + // return new PwgError(403, 'Only webmasters can grant "webmaster/admin" status'); + return array( + 'error' => array( + 'code '=> 403, + 'message' => 'Only webmasters can grant "webmaster/admin" status' + ) + ); + } + + if ( !in_array($params['status'], array('guest','generic','normal','admin','webmaster')) ) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid status'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'Invalid status' + ) + ); + } + + $protected_users = array( + $user['id'], + $conf['guest_id'], + $conf['webmaster_id'], + ); + + // an admin can't change status of other admin/webmaster + if ('admin' == $user['status']) + { + $query = ' +SELECT + user_id + FROM '.USER_INFOS_TABLE.' + WHERE status IN (\'webmaster\', \'admin\') +;'; + $protected_users = array_merge($protected_users, query2array($query, null, 'user_id')); + } + + // status update query is separated from the rest as not applying to the same + // set of users (current, guest and webmaster can't be changed) + $params['user_id_for_status'] = array_diff($params['user_id'], $protected_users); + + $update_status = $params['status']; + } + + if (!empty($params['level']) or @$params['level']===0) + { + if ( !in_array($params['level'], $conf['available_permission_levels']) ) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid level'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'Invalid level' + ) + ); + } + $updates_infos['level'] = $params['level']; + } + + if (!empty($params['language'])) + { + if ( !in_array($params['language'], array_keys(get_languages())) ) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid language'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'Invalid language' + ) + ); + } + $updates_infos['language'] = $params['language']; + } + + if (!empty($params['theme'])) + { + if ( !in_array($params['theme'], array_keys(get_pwg_themes())) ) + { + // return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid theme'); + return array( + 'error' => array( + 'code' => WS_ERR_INVALID_PARAM, + 'message' => 'Invalid theme' + ) + ); + } + $updates_infos['theme'] = $params['theme']; + } + + if (!empty($params['nb_image_page'])) + { + $updates_infos['nb_image_page'] = $params['nb_image_page']; + } + + if (!empty($params['recent_period']) or @$params['recent_period']===0) + { + $updates_infos['recent_period'] = $params['recent_period']; + } + + if (!empty($params['expand']) or @$params['expand']===false) + { + $updates_infos['expand'] = boolean_to_string($params['expand']); + } + + if (!empty($params['show_nb_comments']) or @$params['show_nb_comments']===false) + { + $updates_infos['show_nb_comments'] = boolean_to_string($params['show_nb_comments']); + } + + if (!empty($params['show_nb_hits']) or @$params['show_nb_hits']===false) + { + $updates_infos['show_nb_hits'] = boolean_to_string($params['show_nb_hits']); + } + + if (!empty($params['enabled_high']) or @$params['enabled_high']===false) + { + $updates_infos['enabled_high'] = boolean_to_string($params['enabled_high']); + } + + // perform updates + single_update( + USERS_TABLE, + $updates, + array($conf['user_fields']['id'] => $params['user_id'][0]) + ); + + if (isset($updates[ $conf['user_fields']['password'] ])) + { + deactivate_user_auth_keys($params['user_id'][0]); + } + + if (isset($updates[ $conf['user_fields']['email'] ])) + { + deactivate_password_reset_key($params['user_id'][0]); + } + + if (isset($update_status) and count($params['user_id_for_status']) > 0) + { + $query = ' +UPDATE '. USER_INFOS_TABLE .' SET + status = "'. $update_status .'" + WHERE user_id IN('. implode(',', $params['user_id_for_status']) .') +;'; + pwg_query($query); + + // we delete sessions, ie disconnect, for users if status becomes "guest". + // It's like deactivating the user. + if ('guest' == $update_status) + { + foreach ($params['user_id_for_status'] as $user_id_for_status) + { + delete_user_sessions($user_id_for_status); + } + } + } + + if (count($updates_infos) > 0) + { + $query = ' +UPDATE '. USER_INFOS_TABLE .' SET '; + + $first = true; + foreach ($updates_infos as $field => $value) + { + if (!$first) $query.= ', '; + else $first = false; + $query.= $field .' = "'. $value .'"'; + } + + $query.= ' + WHERE user_id IN('. implode(',', $params['user_id']) .') +;'; + pwg_query($query); + } + + // manage association to groups + if (!empty($params['group_id'])) + { + $query = ' +DELETE + FROM '.USER_GROUP_TABLE.' + WHERE user_id IN ('.implode(',', $params['user_id']).') +;'; + pwg_query($query); + + // we remove all provided groups that do not really exist + $query = ' +SELECT + id + FROM `'.GROUPS_TABLE.'` + WHERE id IN ('.implode(',', $params['group_id']).') +;'; + $group_ids = array_from_query($query, 'id'); + + // if only -1 (a group id that can't exist) is in the list, then no + // group is associated + + if (count($group_ids) > 0) + { + $inserts = array(); + + foreach ($group_ids as $group_id) + { + foreach ($params['user_id'] as $user_id) + { + $inserts[] = array('user_id' => $user_id, 'group_id' => $group_id); + } + } + + mass_inserts(USER_GROUP_TABLE, array_keys($inserts[0]), $inserts); + } + } + + invalidate_user_cache(); + + pwg_activity('user', $params['user_id'], 'edit'); + + return array( + 'user_id' => $params['user_id'], + 'infos' => $updates_infos, + 'account' => $updates + ); +} + +/** + * Create a new api_key + * + * @since 16 + * @param int $user_id + * @param int|null $duration + * @param string $key_name + * @return array auth_key / apikey_secret / apikey_name / + * user_id / created_on / duration / expired_on / key_type + */ +function create_api_key($user_id, $duration, $key_name) +{ + $key_id = 'pkid-'.date('Ymd').'-'.generate_key(20); + $key_secret = generate_key(40); + + list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();')); + + $key = array( + 'auth_key' => $key_id, + 'apikey_secret' => pwg_password_hash($key_secret), + 'apikey_name' => $key_name, + 'user_id' => $user_id, + 'created_on' => $dbnow, + 'key_type' => 'api_key' + ); + + if (!empty($duration)) + { + $query = ' +SELECT + ADDDATE(NOW(), INTERVAL '.($duration * 60 * 60 * 24).' SECOND) +;'; + list($expiration) = pwg_db_fetch_row(pwg_query($query)); + $key['duration'] = $duration; + } + $key['expired_on'] = $expiration; + + single_insert(USER_AUTH_KEYS_TABLE, $key); + + $key['apikey_secret'] = $key_secret; + return $key; +} + +/** + * Revoke a api_key + * + * @since 16 + * @param int $user_id + * @param string $pkid + * @return string|bool + */ +function revoke_api_key($user_id, $pkid) +{ + $query = ' +SELECT + COUNT(*), + NOW() + FROM `'.USER_AUTH_KEYS_TABLE.'` + WHERE auth_key = "'.$pkid.'" + AND user_id = '.$user_id.' +;'; + + list($key, $now) = pwg_db_fetch_row(pwg_query($query)); + if ($key == 0) + { + return l10n('API Key not found'); + } + + single_update( + USER_AUTH_KEYS_TABLE, + array('revoked_on' => $now), + array( + 'auth_key' => $pkid, + 'user_id' => $user_id + ) + ); + + return true; +} + +/** + * Edit a api_key + * + * @since 16 + * @param int $user_id + * @param string $pkid + * @return string|bool + */ +function edit_api_key($user_id, $pkid, $api_name) +{ + $query = ' +SELECT + COUNT(*) + FROM `'.USER_AUTH_KEYS_TABLE.'` + WHERE auth_key = "'.$pkid.'" + AND user_id = '.$user_id.' +;'; + + list($key) = pwg_db_fetch_row(pwg_query($query)); + if ($key == 0) + { + return l10n('API Key not found'); + } + + single_update( + USER_AUTH_KEYS_TABLE, + array('apikey_name' => $api_name), + array( + 'auth_key' => $pkid, + 'user_id' => $user_id + ) + ); + + return true; +} + +/** + * Get all api_key + * + * @since 16 + * @param string $user_id + * @return array|false + */ +function get_api_key($user_id) +{ + $query = ' +SELECT * + FROM `'.USER_AUTH_KEYS_TABLE.'` + WHERE user_id = '.$user_id.' + AND key_type = "api_key" +;'; + + $api_keys = query2array($query); + if (!$api_keys) return false; + + $query = ' +SELECT + NOW() +;'; + list($now) = pwg_db_fetch_row(pwg_query($query)); + + foreach ($api_keys as $i => $api_key) + { + $api_key['apikey_secret'] = str_repeat("*", 40); + unset($api_key['auth_key_id'], $api_key['user_id'], $api_key['key_type']); + + $api_key['created_on_format'] = format_date($api_key['created_on'], array('day', 'month', 'year')); + $api_key['expired_on_format'] = format_date($api_key['expired_on'], array('day', 'month', 'year')); + $api_key['last_used_on_since'] = + $api_key['last_used_on'] + ? time_since($api_key['last_used_on'], 'day') + : l10n('Never'); + + $expired_on = str2DateTime($api_key['expired_on']); + $now = str2DateTime($now); + + $api_key['is_expired'] = $expired_on < $now; + if ($api_key['is_expired']) + { + $api_key['expiration'] = l10n('Expired'); + } + else + { + $diff = dateDiff($now, $expired_on); + if ($diff->days > 0) + { + $api_key['expiration'] = l10n('%d days', $diff->days); + } + elseif ($diff->h > 0) + { + $api_key['expiration'] = l10n('%d hours', $diff->h); + } + else + { + $api_key['expiration'] = l10n('%d minutes', $diff->i); + } + } + + $api_key['expired_on_since'] = time_since($api_key['expired_on'], 'day'); + + $api_key['revoked_on_since'] = + $api_key['revoked_on'] + ? time_since($api_key['revoked_on'], 'day') + : null; + + $api_key['revoked_on_message'] = + $api_key['revoked_on'] + ? l10n('This API key was manually revoked on %s', format_date($api_key['revoked_on'], array('day', 'month', 'year'))) + : null; + + $api_keys[$i] = $api_key; + } + + return $api_keys; +} ?> 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/include/user.inc.php b/include/user.inc.php index c3be8418f..8bb62e76c 100644 --- a/include/user.inc.php +++ b/include/user.inc.php @@ -56,6 +56,44 @@ if (isset($_GET['auth'])) auth_key_login($_GET['auth']); } +// HTTP_AUTHORIZATION api_key +if ( + defined('IN_WS') + and isset($_SERVER['HTTP_AUTHORIZATION']) + and !empty($_SERVER['HTTP_AUTHORIZATION']) + and isset($_REQUEST['method']) +) +{ + $auth_header = pwg_db_real_escape_string($_SERVER['HTTP_AUTHORIZATION']) ?? null; + + if ($auth_header) + { + $authenticate = auth_key_login($auth_header, true); + if (!$authenticate) + { + include_once(PHPWG_ROOT_PATH.'include/ws_init.inc.php'); + $service->sendResponse(new PwgError(401, 'Invalid api_key')); + exit; + } + define('PWG_API_KEY_REQUEST', true); + + // set pwg_token for api_key request + if (isset($_POST['pwg_token'])) + { + $_POST['pwg_token'] = get_pwg_token(); + } + + if (isset($_GET['pwg_token'])) + { + $_GET['pwg_token'] = get_pwg_token(); + } + + // logger + global $logger; + $logger->info('[api_key][pkid='.explode(':', $auth_header)[0].'][method='.$_REQUEST['method'].']'); + } +} + if ( defined('IN_WS') and isset($_REQUEST['method']) @@ -70,6 +108,7 @@ if ( $service->sendResponse(new PwgError(999, 'Invalid username/password')); exit(); } + $_SESSION['connected_with'] = 'pwg.images.uploadAsync'; } $page['user_use_cache'] = true; diff --git a/include/ws_core.inc.php b/include/ws_core.inc.php index 3fa2bea39..8130035fe 100644 --- a/include/ws_core.inc.php +++ b/include/ws_core.inc.php @@ -517,6 +517,11 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF return new PwgError(401, 'Access denied'); } + if (!$this->isAuthorizedMethodForAPIKEY()) + { + return new PwgError(401, 'Access denied'); + } + // parameter check and data correction $signature = $method['signature']; $missing_params = array(); @@ -679,5 +684,27 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF } return $res; } + + function isAuthorizedMethodForAPIKEY() + { + global $conf; + + // if the request is made with an API key (via header or session API key), + // we check whether the requested method is on the + // list of prohibited methods ($conf['api_key_forbidden_methods']) for API keys + // if it is, access is refused (false) + if ( + defined('PWG_API_KEY_REQUEST') + OR (isset($_SESSION['connected_with']) AND 'ws_session_login_api_key' === $_SESSION['connected_with']) + ) + { + if (in_array($_REQUEST['method'], $conf['api_key_forbidden_methods'])) + { + return false; + } + } + + return true; + } } ?> diff --git a/include/ws_functions/pwg.php b/include/ws_functions/pwg.php index b155e07a4..c1886b189 100644 --- a/include/ws_functions/pwg.php +++ b/include/ws_functions/pwg.php @@ -347,8 +347,24 @@ DELETE FROM '. RATE_TABLE .' */ function ws_session_login($params, &$service) { - if (try_log_user($params['username'], $params['password'], false)) + if (defined('PWG_API_KEY_REQUEST')) { + return new PwgError(401, 'Cannot use this method with an api key'); + } + + if (preg_match('/^pkid-\d{8}-[a-z0-9]{20}$/i', $params['username'])) + { + $secret = pwg_db_real_escape_string($params['password']); + $authenticate = auth_key_login($params['username'].':'.$secret); + if ($authenticate) + { + $_SESSION['connected_with'] = 'ws_session_login_api_key'; + return true; + } + } + else if (try_log_user($params['username'], $params['password'], false)) + { + $_SESSION['connected_with'] = 'ws_session_login'; return true; } return new PwgError(999, 'Invalid username/password'); @@ -362,6 +378,11 @@ function ws_session_login($params, &$service) */ function ws_session_logout($params, &$service) { + if (defined('PWG_API_KEY_REQUEST')) + { + return new PwgError(401, 'Cannot use this method with an api key'); + } + if (!is_a_guest()) { logout_user(); @@ -390,11 +411,13 @@ function ws_session_getStatus($params, &$service) $res['current_datetime'] = $dbnow; $res['version'] = PHPWG_VERSION; $res['save_visits'] = do_log(); + $res['connected_with'] = $_SESSION['connected_with'] ?? null; // Piwigo Remote Sync does not support receiving the new (version 14) output "save_visits" if (isset($_SERVER['HTTP_USER_AGENT']) and preg_match('/^PiwigoRemoteSync/', $_SERVER['HTTP_USER_AGENT'])) { unset($res['save_visits']); + unset($res['connected_with']); } // Piwigo Remote Sync does not support receiving the available sizes @@ -1151,4 +1174,5 @@ SELECT 'summary' => $search_summary ); } -?> + +?> \ No newline at end of file diff --git a/include/ws_functions/pwg.users.php b/include/ws_functions/pwg.users.php index 65a7686f1..baf44f14d 100644 --- a/include/ws_functions/pwg.users.php +++ b/include/ws_functions/pwg.users.php @@ -532,270 +532,124 @@ function ws_users_setInfo($params, &$service) return new PwgError(403, 'Invalid security token'); } - if (isset($params['username']) and strlen(str_replace( " ", "", $params['username'])) == 0) { - return new PwgError(WS_ERR_INVALID_PARAM, 'Name field must not be empty'); - } + $updated_users = check_and_save_user_infos($params); - global $conf, $user; - - include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); - - $updates = $updates_infos = array(); - $update_status = null; - - if (count($params['user_id']) == 1) + if (isset($updated_users['error'])) { - if (get_username($params['user_id'][0]) === false) - { - return new PwgError(WS_ERR_INVALID_PARAM, 'This user does not exist.'); - } - - if (!empty($params['username'])) - { - $user_id = get_userid($params['username']); - if ($user_id and $user_id != $params['user_id'][0]) - { - return new PwgError(WS_ERR_INVALID_PARAM, l10n('this login is already used')); - } - if ($params['username'] != strip_tags($params['username'])) - { - return new PwgError(WS_ERR_INVALID_PARAM, l10n('html tags are not allowed in login')); - } - $updates[ $conf['user_fields']['username'] ] = $params['username']; - } - - if (!empty($params['email'])) - { - if ( ($error = validate_mail_address($params['user_id'][0], $params['email'])) != '') - { - return new PwgError(WS_ERR_INVALID_PARAM, $error); - } - $updates[ $conf['user_fields']['email'] ] = $params['email']; - } - - if (!empty($params['password'])) - { - if (!is_webmaster()) - { - $password_protected_users = array($conf['guest_id']); - - $query = ' -SELECT - user_id - FROM '.USER_INFOS_TABLE.' - WHERE status IN (\'webmaster\', \'admin\') -;'; - $admin_ids = query2array($query, null, 'user_id'); - - // we add all admin+webmaster users BUT the user herself - $password_protected_users = array_merge($password_protected_users, array_diff($admin_ids, array($user['id']))); - - if (in_array($params['user_id'][0], $password_protected_users)) - { - return new PwgError(403, 'Only webmasters can change password of other "webmaster/admin" users'); - } - } - - $updates[ $conf['user_fields']['password'] ] = $conf['password_hash']($params['password']); - } + return new PwgError($updated_users[ 'error' ][ 'code' ], $updated_users[ 'error' ][ 'message' ]); } - if (!empty($params['status'])) - { - if (in_array($params['status'], array('webmaster', 'admin')) and !is_webmaster() ) - { - return new PwgError(403, 'Only webmasters can grant "webmaster/admin" status'); - } - - if ( !in_array($params['status'], array('guest','generic','normal','admin','webmaster')) ) - { - return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid status'); - } - - $protected_users = array( - $user['id'], - $conf['guest_id'], - $conf['webmaster_id'], - ); - - // an admin can't change status of other admin/webmaster - if ('admin' == $user['status']) - { - $query = ' -SELECT - user_id - FROM '.USER_INFOS_TABLE.' - WHERE status IN (\'webmaster\', \'admin\') -;'; - $protected_users = array_merge($protected_users, query2array($query, null, 'user_id')); - } - - // status update query is separated from the rest as not applying to the same - // set of users (current, guest and webmaster can't be changed) - $params['user_id_for_status'] = array_diff($params['user_id'], $protected_users); - - $update_status = $params['status']; - } - - if (!empty($params['level']) or @$params['level']===0) - { - if ( !in_array($params['level'], $conf['available_permission_levels']) ) - { - return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid level'); - } - $updates_infos['level'] = $params['level']; - } - - if (!empty($params['language'])) - { - if ( !in_array($params['language'], array_keys(get_languages())) ) - { - return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid language'); - } - $updates_infos['language'] = $params['language']; - } - - if (!empty($params['theme'])) - { - if ( !in_array($params['theme'], array_keys(get_pwg_themes())) ) - { - return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid theme'); - } - $updates_infos['theme'] = $params['theme']; - } - - if (!empty($params['nb_image_page'])) - { - $updates_infos['nb_image_page'] = $params['nb_image_page']; - } - - if (!empty($params['recent_period']) or @$params['recent_period']===0) - { - $updates_infos['recent_period'] = $params['recent_period']; - } - - if (!empty($params['expand']) or @$params['expand']===false) - { - $updates_infos['expand'] = boolean_to_string($params['expand']); - } - - if (!empty($params['show_nb_comments']) or @$params['show_nb_comments']===false) - { - $updates_infos['show_nb_comments'] = boolean_to_string($params['show_nb_comments']); - } - - if (!empty($params['show_nb_hits']) or @$params['show_nb_hits']===false) - { - $updates_infos['show_nb_hits'] = boolean_to_string($params['show_nb_hits']); - } - - if (!empty($params['enabled_high']) or @$params['enabled_high']===false) - { - $updates_infos['enabled_high'] = boolean_to_string($params['enabled_high']); - } - - // perform updates - single_update( - USERS_TABLE, - $updates, - array($conf['user_fields']['id'] => $params['user_id'][0]) - ); - - if (isset($updates[ $conf['user_fields']['password'] ])) - { - deactivate_user_auth_keys($params['user_id'][0]); - } - - if (isset($updates[ $conf['user_fields']['email'] ])) - { - deactivate_password_reset_key($params['user_id'][0]); - } - - if (isset($update_status) and count($params['user_id_for_status']) > 0) - { - $query = ' -UPDATE '. USER_INFOS_TABLE .' SET - status = "'. $update_status .'" - WHERE user_id IN('. implode(',', $params['user_id_for_status']) .') -;'; - pwg_query($query); - - // we delete sessions, ie disconnect, for users if status becomes "guest". - // It's like deactivating the user. - if ('guest' == $update_status) - { - foreach ($params['user_id_for_status'] as $user_id_for_status) - { - delete_user_sessions($user_id_for_status); - } - } - } - - if (count($updates_infos) > 0) - { - $query = ' -UPDATE '. USER_INFOS_TABLE .' SET '; - - $first = true; - foreach ($updates_infos as $field => $value) - { - if (!$first) $query.= ', '; - else $first = false; - $query.= $field .' = "'. $value .'"'; - } - - $query.= ' - WHERE user_id IN('. implode(',', $params['user_id']) .') -;'; - pwg_query($query); - } - - // manage association to groups - if (!empty($params['group_id'])) - { - $query = ' -DELETE - FROM '.USER_GROUP_TABLE.' - WHERE user_id IN ('.implode(',', $params['user_id']).') -;'; - pwg_query($query); - - // we remove all provided groups that do not really exist - $query = ' -SELECT - id - FROM `'.GROUPS_TABLE.'` - WHERE id IN ('.implode(',', $params['group_id']).') -;'; - $group_ids = array_from_query($query, 'id'); - - // if only -1 (a group id that can't exist) is in the list, then no - // group is associated - - if (count($group_ids) > 0) - { - $inserts = array(); - - foreach ($group_ids as $group_id) - { - foreach ($params['user_id'] as $user_id) - { - $inserts[] = array('user_id' => $user_id, 'group_id' => $group_id); - } - } - - mass_inserts(USER_GROUP_TABLE, array_keys($inserts[0]), $inserts); - } - } - - invalidate_user_cache(); - - pwg_activity('user', $params['user_id'], 'edit'); - return $service->invoke('pwg.users.getList', array( - 'user_id' => $params['user_id'], - 'display' => 'basics,'.implode(',', array_keys($updates_infos)), - )); + 'user_id' => $updated_users['user_id'], + 'display' => 'basics,'.implode(',', array_keys($updated_users['infos'])), + )); +} + +/** + * API method + * Update user + * @since 16 + * @param mixed[] $params + * @option string email (optional) + * @option int nb_image_page (optional) + * @option string theme (optional) + * @option string language (optional) + * @option int recent_period (optional) + * @option bool expand (optional) + * @option bool show_nb_comments (optional) + * @option bool show_nb_hits (optional) + * @option string password (optional) + * @option string new_password (optional) + * @option string conf_new_password (optional) + */ +function ws_users_setMyInfo($params, &$service) +{ + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, 'Invalid security token'); + } + + if (is_a_guest()) + { + return new PwgError(401, 'Access Denied'); + } + + global $user, $conf; + + // ACTIVATE_COMMENTS + if (!$conf['activate_comments']) + { + unset($params['show_nb_comments']); + } + + // ALLOW_USER_CUSTOMIZATION + if (!$conf['allow_user_customization']) + { + unset( + $params['nb_image_page'], + $params['theme'], + $params['language'], + $params['recent_period'], + $params['expand'], + $params['show_nb_comments'], + $params['show_nb_hits'] + ); + } + + // SPECIAL_USER + $special_user = in_array($user['id'], array($conf['guest_id'], $conf['default_user_id'])); + if ($special_user) + { + unset( + $params['password'], + $params['theme'], + $params['language'] + ); + } + + if (!empty($params['password'])) + { + if ($params['new_password'] != $params['conf_new_password']) + { + return new PwgError(403, l10n('The passwords do not match')); + } + + $query = ' +SELECT '.$conf['user_fields']['password'].' AS password + FROM '.USERS_TABLE.' + WHERE '.$conf['user_fields']['id'].' = \''.$user['id'].'\' +;'; + list($current_password) = pwg_db_fetch_row(pwg_query($query)); + + if (!$conf['password_verify']($params['password'], $current_password)) + { + return new PwgError(403, l10n('Current password is wrong')); + } + + $params['password'] = $params['new_password']; + } + + + // Unset admin field also new and conf password + unset( + $params['new_password'], + $params['conf_new_password'], + $params['username'], + $params['status'], + $params['level'], + $params['group_id'], + $params['enabled_high'] + ); + + $params['user_id'] = [$user['id']]; + $updated_users = check_and_save_user_infos($params); + + if (isset($updated_users['error'])) + { + return new PwgError($updated_users[ 'error' ][ 'code' ], $updated_users[ 'error' ][ 'message' ]); + } + + return l10n('Your changes have been applied.'); } /** @@ -1097,4 +951,158 @@ function ws_set_main_user($params, &$service) conf_update_param('webmaster_id', $params['user_id']); return 'The main user has been changed.'; } + +/** + * API method + * Create a new api key for the current user + * @since 15 + * @param mixed[] $params + */ +function ws_create_api_key($params, &$service) +{ + global $user, $logger; + + if (is_a_guest() OR !can_manage_api_key()) return new PwgError(401, 'Acces Denied'); + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, 'Invalid security token'); + } + + if ($params['duration'] < 1 OR $params['duration'] > 999999) + { + return new PwgError(400, 'Invalid duration max days is 999999'); + } + + if (strlen($params['key_name']) > 100) + { + return new PwgError(400, 'Key name is too long'); + } + + $key_name = pwg_db_real_escape_string($params['key_name']); + $duration = 0 == $params['duration'] ? 1 : $params['duration']; + + $secret = create_api_key($user['id'], $duration, $key_name); + + $logger->info('[api_key][user_id='.$user['id'].'][action=create][key_name='.$params['key_name'].']'); + + return $secret; +} + +/** + * API method + * Revoke a api key for the current user + * @since 15 + * @param mixed[] $params + */ +function ws_revoke_api_key($params, &$service) +{ + global $user, $logger; + + if (is_a_guest() OR !can_manage_api_key()) return new PwgError(401, 'Acces Denied'); + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, l10n('Invalid security token')); + } + + if (!preg_match('/^pkid-\d{8}-[a-z0-9]{20}$/i', $params['pkid'])) + { + return new PwgError(403, l10n('Invalid pkid format')); + } + + $revoked_key = revoke_api_key($user['id'], $params['pkid']); + + if (true !== $revoked_key) + { + return new PwgError(403, $revoked_key); + } + + $logger->info('[api_key][user_id='.$user['id'].'][action=revoke][pkid='.$params['pkid'].']'); + + return l10n('API Key has been successfully revoked.'); +} + +/** + * API method + * Edit a api key for the current user + * @since 15 + * @param mixed[] $params + */ +function ws_edit_api_key($params, &$service) +{ + global $user, $logger; + + if (is_a_guest()) + { + return new PwgError(401, 'Acces Denied'); + } + + if (!can_manage_api_key()) + { + return new PwgError(401, 'Acces Denied'); + } + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, l10n('Invalid security token')); + } + + if (!preg_match('/^pkid-\d{8}-[a-z0-9]{20}$/i', $params['pkid'])) + { + return new PwgError(403, l10n('Invalid pkid format')); + } + + $key_name = pwg_db_real_escape_string($params['key_name']); + $edited_key = edit_api_key($user['id'], $params['pkid'], $key_name); + + if (true !== $edited_key) + { + return new PwgError(403, $edited_key); + } + + $logger->info('[api_key][user_id='.$user['id'].'][action=edit][pkid='.$params['pkid'].'][new_name='.$key_name.']'); + + return l10n('API Key has been successfully edited.'); +} + +/** + * API method + * Get all api key for the current user + * @since 15 + * @param mixed[] $params + */ +function ws_get_api_key($params, &$service) +{ + global $user; + + if (is_a_guest()) + { + return new PwgError(401, 'Acces Denied'); + } + + if (!can_manage_api_key()) + { + return new PwgError(401, 'Acces Denied'); + } + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, 'Invalid security token'); + } + + $api_keys = get_api_key($user['id']); + + return $api_keys ?? l10n('No API key found'); +} + +function can_manage_api_key() +{ + // You can manage your api key only if you are connected via identification.php + if (isset($_SESSION['connected_with']) and 'pwg_ui' === $_SESSION['connected_with']) + { + return true; + } + return false; +} ?> diff --git a/install/db/175-database.php b/install/db/175-database.php index 3cfb50ac6..69542ba7f 100644 --- a/install/db/175-database.php +++ b/install/db/175-database.php @@ -13,9 +13,8 @@ if (!defined('PHPWG_ROOT_PATH')) $upgrade_description = 'add config parameter to override Theme Login & Registration Pages'; -// we set it to false in this upgrade script, as opposed to the default value -// for a new installation, because it was the default behavior before Piwigo 16 -conf_update_param('use_standard_pages', false); +// Force use of standard pages on update +conf_update_param('use_standard_pages', true); echo "\n".$upgrade_description."\n"; diff --git a/install/db/176-database.php b/install/db/176-database.php new file mode 100644 index 000000000..813d96603 --- /dev/null +++ b/install/db/176-database.php @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/install/db/177-database.php b/install/db/177-database.php new file mode 100644 index 000000000..49004fb84 --- /dev/null +++ b/install/db/177-database.php @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/language/en_UK/common.lang.php b/language/en_UK/common.lang.php index f146fd883..a62397ed6 100644 --- a/language/en_UK/common.lang.php +++ b/language/en_UK/common.lang.php @@ -471,3 +471,50 @@ $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.'; +$lang['Create API Keys to secure your acount'] = 'Create API Keys to secure your account'; +$lang['API Keys'] = 'API Keys'; +$lang['Created at'] = 'Created at'; +$lang['Last use'] = 'Last use'; +$lang['Expires in'] = 'Expires in'; +$lang['Expired on'] = 'Expired on'; +$lang['Never'] = 'Never'; +$lang['New API Key'] = 'New API Key'; +$lang['Show expired keys'] = 'Show expired keys'; +$lang['Hide expired keys'] = 'Hide expired keys'; +$lang['Generate API Key'] = 'Generate API Key'; +$lang['Create a new API key to secure your account.'] = 'Create a new API key to secure your account.'; +$lang['API Key name'] = 'API Key name'; +$lang['Duration'] = 'Duration'; +$lang['Custom date'] = 'Custom date'; +$lang['Generate key'] = 'Generate key'; +$lang['Save your secret Key and ID'] = 'Save your secret Key and ID'; +$lang['This will not be displayed again. You must copy it to continue.'] = 'This will not be displayed again. You must copy it to continue.'; +$lang['Done'] = 'Done'; +$lang['Public key copied.'] = 'Public key copied.'; +$lang['Secret key copied. Keep it in a safe place.'] = 'Secret key copied. Keep it in a safe place.'; +$lang['Impossible to copy automatically. Please copy manually.'] = 'Impossible to copy automatically. Please copy manually.'; +$lang['The api key has been successfully created.'] = 'The API key has been successfully created.'; +$lang['API Key not found'] = 'API Key not found'; +$lang['Expired'] = 'Expired'; +$lang['API Key has been successfully revoked.'] = 'API Key has been successfully revoked.'; +$lang['API Key has been successfully edited.'] = 'API Key has been successfully edited.'; +$lang['No expiration'] = 'No expiration'; +$lang['must not be empty'] = 'must not be empty'; +$lang['The secret key can no longer be displayed.'] = 'The secret key can no longer be displayed.'; +$lang['Revoked'] = 'Revoked'; +$lang['Revoke'] = 'Revoke'; +$lang['The email %s will be used to notify you when your API key is about to expire.'] = 'The email %s will be used to notify you when your API key is about to expire.'; +$lang['You have no email address, so you will not be notified when your API key is about to expire.'] = 'You have no email address, so you will not be notified when your API key is about to expire.'; +$lang['you must choose a date'] = 'you must choose a date'; +$lang['This API key was manually revoked on %s'] = 'This API key was manually revoked on %s'; +$lang['Edit API Key'] = 'Edit API Key'; +$lang['Do you really want to revoke the "%s" API key?'] = 'Do you really want to revoke the "%s" API key?'; +$lang['To manage your API keys, please log in with your username/password.'] = 'To manage your API keys, please log in with your username/password.'; +$lang['3xlarge'] = '3XL - extra huge'; +$lang['4xlarge'] = '4XL - gigantique'; \ No newline at end of file diff --git a/language/fr_FR/common.lang.php b/language/fr_FR/common.lang.php index ab584bba9..f139b2c5e 100644 --- a/language/fr_FR/common.lang.php +++ b/language/fr_FR/common.lang.php @@ -470,3 +470,50 @@ $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.'; +$lang['Create API Keys to secure your acount'] = 'Créez des clés API pour sécuriser votre compte'; +$lang['API Keys'] = 'Clés API'; +$lang['Created at'] = 'Crée le'; +$lang['Last use'] = 'Dernière utilisation'; +$lang['Expires in'] = 'Expire dans'; +$lang['Expired on'] = 'Expiré le'; +$lang['Never'] = 'Jamais'; +$lang['New API Key'] = 'Nouvelle clé API'; +$lang['Show expired keys'] = 'Afficher les clés expirées'; +$lang['Hide expired keys'] = 'Masquer les clés expirées'; +$lang['Generate API Key'] = 'Générer une clé API'; +$lang['Create a new API key to secure your account.'] = 'Créez une nouvelle clé API pour sécuriser votre compte.'; +$lang['API Key name'] = 'Nom de la clé API'; +$lang['Duration'] = 'Durée'; +$lang['Custom date'] = 'Date personnalisée'; +$lang['Generate key'] = 'Générer la clé'; +$lang['Save your secret Key and ID'] = 'Enregistrez votre clé secrète et votre identifiant'; +$lang['This will not be displayed again. You must copy it to continue.'] = 'La clé secrete ne sera plus affichée. Vous devez la copier pour continuer.'; +$lang['Done'] = 'Terminé'; +$lang['Public key copied.'] = 'Clé publique copiée.'; +$lang['Secret key copied. Keep it in a safe place.'] = 'Clé secrète copiée. Gardez-la dans un endroit sûr.'; +$lang['Impossible to copy automatically. Please copy manually.'] = 'Impossible de copier automatiquement. Veuillez copier manuellement.'; +$lang['The api key has been successfully created.'] = 'La clé API a été créée avec succès.'; +$lang['API Key not found'] = 'Clé API non trouvée'; +$lang['Expired'] = 'Expirée'; +$lang['API Key has been successfully revoked.'] = 'La clé API a été révoquée avec succès.'; +$lang['API Key has been successfully edited.'] = 'La clé API a été modifiée avec succès.'; +$lang['No expiration'] = 'Pas d’expiration'; +$lang['must not be empty'] = 'ne doit pas être vide'; +$lang['The secret key can no longer be displayed.'] = 'La clé secrète ne peut plus être affichée.'; +$lang['Revoked'] = 'Révoqué'; +$lang['Revoke'] = 'Révoquer'; +$lang['The email %s will be used to notify you when your API key is about to expire.'] = 'L\'email %s sera utilisé pour vous notifier que votre clé API est sur le point d\'expirer.'; +$lang['You have no email address, so you will not be notified when your API key is about to expire.'] = 'Vous n\'avez pas d\'adresse email, vous ne serez donc pas notifié lorsque votre clé API sera sur le point d\'expirer.'; +$lang['you must choose a date'] = 'vous devez choisir une date'; +$lang['This API key was manually revoked on %s'] = 'Cette clé API a été révoquée manuellement le %s'; +$lang['Edit API Key'] = 'Modifier la clé API'; +$lang['Do you really want to revoke the "%s" API key?'] = 'Voulez-vous vraiment révoquer la clé API "%s" ?'; +$lang['To manage your API keys, please log in with your username/password.'] = 'Pour gérer vos clés API, veuillez vous connecter avec votre nom d\'utilisateur/mot de passe.'; +$lang['3xlarge'] = '3XL - très énorme'; +$lang['4xlarge'] = '4XL - gigantesque'; diff --git a/profile.php b/profile.php index 23ae8607b..f8e0d4a84 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'); @@ -301,7 +342,7 @@ function save_profile_from_post($userdata, &$errors) */ function load_profile_in_template($url_action, $url_redirect, $userdata, $template_prefixe=null) { - global $template, $conf; + global $template, $conf, $user; $template->assign('radio_options', array( @@ -341,6 +382,47 @@ function load_profile_in_template($url_action, $url_redirect, $userdata, $templa $template->assign('SPECIAL_USER', $special_user); $template->assign('IN_ADMIN', defined('IN_ADMIN')); + // api key expiration choice + list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT ADDDATE(NOW(), INTERVAL 1 DAY);')); + $template->assign('API_CURRENT_DATE', explode(' ', $dbnow)[0]); + + $duration = array(); + $display_duration = array(); + $has_custom = false; + foreach ($conf['api_key_duration'] as $day) + { + if ('custom' === $day) + { + $has_custom = true; + continue; + } + $duration[] = 'ADDDATE(NOW(), INTERVAL '.$day.' DAY) as `'.$day.'`'; + } + + $query = ' +SELECT + '.implode(', ', $duration).' +;'; + $result = query2array($query)[0]; + foreach ($result as $day => $date) + { + $display_duration[ $day ] = l10n('%d days', $day) . ' (' . format_date($date, array('day', 'month', 'year')) . ')'; + } + + if ($has_custom) + { + $display_duration['custom'] = l10n('Custom date'); + } + $template->assign('API_EXPIRATION', $display_duration); + $template->assign('API_SELECTED_EXPIRATION', array_key_first($display_duration)); + $template->assign('API_CAN_MANAGE', 'pwg_ui' === ($_SESSION['connected_with'] ?? null)); + + $email_notifications_infos = $user['email'] ? + l10n('The email %s will be used to notify you when your API key is about to expire.', $user['email']) + : l10n('You have no email address, so you will not be notified when your API key is about to expire.'); + $template->assign('API_EMAIL_INFOS', $email_notifications_infos); + + // allow plugins to add their own form data to content trigger_notify( 'load_profile_in_template', $userdata ); 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/default/vendor/fontello/config.json b/themes/default/vendor/fontello/config.json index b042aac9f..1cb3dd9a9 100644 --- a/themes/default/vendor/fontello/config.json +++ b/themes/default/vendor/fontello/config.json @@ -346,6 +346,34 @@ "code": 59394, "src": "fontawesome" }, + { + "uid": "9082609133b3870fa82169c62f258711", + "css": "hash", + "code": 59715, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M748.2 443.9C738.6 443.9 730.4 450.7 728.6 460.2L714.3 535.1C712 547.4 721.4 558.8 734 558.8H866.7C877.8 558.8 886.7 567.8 886.7 578.8V712.6C886.7 723.6 877.8 732.6 866.7 732.6H692C682.5 732.6 674.3 739.3 672.4 748.6L625 983.9C623.1 993.3 614.9 1000 605.4 1000H451C438.4 1000 428.9 988.5 431.4 976.1L475 756.5C477.5 744.1 468 732.6 455.4 732.6H374C364.4 732.6 356.1 739.4 354.3 748.8L308.9 983.8C307.1 993.2 298.9 1000 289.3 1000H135.7C123.1 1000 113.6 988.5 116.1 976.2L159.1 756.4C161.6 744.1 152.1 732.6 139.5 732.6H20C9 732.6 0 723.6 0 712.6V578.8C0 567.8 9 558.8 20 558.8H183C192.6 558.8 200.8 552.1 202.7 542.7L217.4 467.8C219.8 455.4 210.3 443.9 197.7 443.9H68.8C57.7 443.9 48.8 435 48.8 423.9V286.1C48.8 275 57.7 266.1 68.8 266.1H241.3C250.8 266.1 259.1 259.3 260.9 249.9L307 16.1C308.8 6.8 317 0 326.6 0H480.8C493.4 0 502.9 11.6 500.4 24L456.2 242.1C453.7 254.5 463.2 266.1 475.8 266.1H555.8C565.3 266.1 573.6 259.3 575.4 249.9L621.5 16.1C623.3 6.8 631.5 0 641.1 0H795.4C808 0 817.5 11.5 815 23.9L771.4 242.2C768.9 254.5 778.4 266.1 791 266.1H913.3C924.4 266.1 933.3 275 933.3 286.1V423.9C933.3 435 924.4 443.9 913.3 443.9H748.2ZM398.4 535.1C396 547.4 405.5 558.8 418 558.8H497.5C507.1 558.8 515.3 552.1 517.2 542.7L531.9 467.8C534.3 455.4 524.8 443.9 512.2 443.9H432.3C422.7 443.9 414.4 450.7 412.6 460.2L398.4 535.1Z", + "width": 933 + }, + "search": [ + "#" + ] + }, + { + "uid": "d577f127681b58b1a14c208b090ec5a7", + "css": "skull", + "code": 59716, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M812.5 779.1C926.8 698.8 1000 575.8 1000 437.5 1000 195.9 776.2 0 500 0 223.8 0 0 195.9 0 437.5 0 575.6 73.2 698.8 187.5 779.1 187.5 779.9 187.5 780.5 187.5 781.3V906.3C187.5 958 229.5 1000 281.3 1000H375V906.3C375 889.1 389.1 875 406.3 875 423.4 875 437.5 889.1 437.5 906.3V1000H562.5V906.3C562.5 889.1 576.6 875 593.8 875 610.9 875 625 889.1 625 906.3V1000H718.8C770.5 1000 812.5 958 812.5 906.3V781.3C812.5 780.5 812.5 779.9 812.5 779.1ZM187.5 500C187.5 466.8 200.7 435.1 224.1 411.6 247.6 388.2 279.3 375 312.5 375 345.7 375 377.4 388.2 400.9 411.6 424.3 435.1 437.5 466.8 437.5 500 437.5 533.2 424.3 564.9 400.9 588.4 377.4 611.8 345.7 625 312.5 625 279.3 625 247.6 611.8 224.1 588.4 200.7 564.9 187.5 533.2 187.5 500ZM687.5 375C720.6 375 752.4 388.2 775.9 411.6 799.3 435.1 812.5 466.8 812.5 500 812.5 533.2 799.3 564.9 775.9 588.4 752.4 611.8 720.6 625 687.5 625 654.3 625 622.6 611.8 599.1 588.4 575.7 564.9 562.5 533.2 562.5 500 562.5 466.8 575.7 435.1 599.1 411.6 622.6 388.2 654.3 375 687.5 375Z", + "width": 1000 + }, + "search": [ + "skull" + ] + }, { "uid": "5e45a2249ceb236bc7345886ddc46313", "css": "list-check", diff --git a/themes/default/vendor/fontello/css/gallery-icon-codes.css b/themes/default/vendor/fontello/css/gallery-icon-codes.css index 8e990703e..dc4812f0e 100644 --- a/themes/default/vendor/fontello/css/gallery-icon-codes.css +++ b/themes/default/vendor/fontello/css/gallery-icon-codes.css @@ -32,6 +32,8 @@ .gallery-icon-eye:before { content: '\e84d'; } /* '' */ .gallery-icon-pencil-1:before { content: '\e84f'; } /* '' */ .gallery-icon-search:before { content: '\e900'; } /* '' */ +.gallery-icon-hash:before { content: '\e943'; } /* '' */ +.gallery-icon-skull:before { content: '\e944'; } /* '' */ .gallery-icon-hdd:before { content: '\f0a0'; } /* '' */ .gallery-icon-star-half-alt:before { content: '\f123'; } /* '' */ .gallery-icon-crop:before { content: '\f125'; } /* '' */ diff --git a/themes/default/vendor/fontello/css/gallery-icon-embedded.css b/themes/default/vendor/fontello/css/gallery-icon-embedded.css index fdde5eff1..5b2082ea4 100644 --- a/themes/default/vendor/fontello/css/gallery-icon-embedded.css +++ b/themes/default/vendor/fontello/css/gallery-icon-embedded.css @@ -1,16 +1,16 @@ @charset "UTF-8"; @font-face { font-family: 'gallery-icon'; - src: url('../font/gallery-icon.eot?85636721'); - src: url('../font/gallery-icon.eot?85636721#iefix') format('embedded-opentype'), - url('../font/gallery-icon.svg?85636721#gallery-icon') format('svg'); + src: url('../font/gallery-icon.eot?12753861'); + src: url('../font/gallery-icon.eot?12753861#iefix') format('embedded-opentype'), + url('../font/gallery-icon.svg?12753861#gallery-icon') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'gallery-icon'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -18,7 +18,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'gallery-icon'; - src: url('../font/gallery-icon.svg?85636721#gallery-icon') format('svg'); + src: url('../font/gallery-icon.svg?12753861#gallery-icon') format('svg'); } } */ @@ -90,6 +90,8 @@ .gallery-icon-eye:before { content: '\e84d'; } /* '' */ .gallery-icon-pencil-1:before { content: '\e84f'; } /* '' */ .gallery-icon-search:before { content: '\e900'; } /* '' */ +.gallery-icon-hash:before { content: '\e943'; } /* '' */ +.gallery-icon-skull:before { content: '\e944'; } /* '' */ .gallery-icon-hdd:before { content: '\f0a0'; } /* '' */ .gallery-icon-star-half-alt:before { content: '\f123'; } /* '' */ .gallery-icon-crop:before { content: '\f125'; } /* '' */ diff --git a/themes/default/vendor/fontello/css/gallery-icon-ie7-codes.css b/themes/default/vendor/fontello/css/gallery-icon-ie7-codes.css index 5d4a8f63d..1c440bbe8 100644 --- a/themes/default/vendor/fontello/css/gallery-icon-ie7-codes.css +++ b/themes/default/vendor/fontello/css/gallery-icon-ie7-codes.css @@ -31,6 +31,8 @@ .gallery-icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-pencil-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.gallery-icon-hash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.gallery-icon-skull { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-hdd { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-star-half-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-crop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/themes/default/vendor/fontello/css/gallery-icon-ie7.css b/themes/default/vendor/fontello/css/gallery-icon-ie7.css index ec414a4f0..6ab9a60a3 100644 --- a/themes/default/vendor/fontello/css/gallery-icon-ie7.css +++ b/themes/default/vendor/fontello/css/gallery-icon-ie7.css @@ -42,6 +42,8 @@ .gallery-icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-pencil-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.gallery-icon-hash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.gallery-icon-skull { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-hdd { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-star-half-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .gallery-icon-crop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/themes/default/vendor/fontello/css/gallery-icon.css b/themes/default/vendor/fontello/css/gallery-icon.css index 987bd564f..530896968 100644 --- a/themes/default/vendor/fontello/css/gallery-icon.css +++ b/themes/default/vendor/fontello/css/gallery-icon.css @@ -1,12 +1,12 @@ @charset "UTF-8"; @font-face { font-family: 'gallery-icon'; - src: url('../font/gallery-icon.eot?1927029'); - src: url('../font/gallery-icon.eot?1927029#iefix') format('embedded-opentype'), - url('../font/gallery-icon.woff2?1927029') format('woff2'), - url('../font/gallery-icon.woff?1927029') format('woff'), - url('../font/gallery-icon.ttf?1927029') format('truetype'), - url('../font/gallery-icon.svg?1927029#gallery-icon') format('svg'); + src: url('../font/gallery-icon.eot?17614864'); + src: url('../font/gallery-icon.eot?17614864#iefix') format('embedded-opentype'), + url('../font/gallery-icon.woff2?17614864') format('woff2'), + url('../font/gallery-icon.woff?17614864') format('woff'), + url('../font/gallery-icon.ttf?17614864') format('truetype'), + url('../font/gallery-icon.svg?17614864#gallery-icon') format('svg'); font-weight: normal; font-style: normal; } @@ -16,7 +16,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'gallery-icon'; - src: url('../font/gallery-icon.svg?1927029#gallery-icon') format('svg'); + src: url('../font/gallery-icon.svg?17614864#gallery-icon') format('svg'); } } */ @@ -87,6 +87,8 @@ .gallery-icon-eye:before { content: '\e84d'; } /* '' */ .gallery-icon-pencil-1:before { content: '\e84f'; } /* '' */ .gallery-icon-search:before { content: '\e900'; } /* '' */ +.gallery-icon-hash:before { content: '\e943'; } /* '' */ +.gallery-icon-skull:before { content: '\e944'; } /* '' */ .gallery-icon-hdd:before { content: '\f0a0'; } /* '' */ .gallery-icon-star-half-alt:before { content: '\f123'; } /* '' */ .gallery-icon-crop:before { content: '\f125'; } /* '' */ diff --git a/themes/default/vendor/fontello/demo.html b/themes/default/vendor/fontello/demo.html index 200b841ef..212377408 100644 --- a/themes/default/vendor/fontello/demo.html +++ b/themes/default/vendor/fontello/demo.html @@ -146,11 +146,11 @@ } @font-face { font-family: 'gallery-icon'; - src: url('./font/gallery-icon.eot?21906900'); - src: url('./font/gallery-icon.eot?21906900#iefix') format('embedded-opentype'), - url('./font/gallery-icon.woff?21906900') format('woff'), - url('./font/gallery-icon.ttf?21906900') format('truetype'), - url('./font/gallery-icon.svg?21906900#gallery-icon') format('svg'); + src: url('./font/gallery-icon.eot?49463795'); + src: url('./font/gallery-icon.eot?49463795#iefix') format('embedded-opentype'), + url('./font/gallery-icon.woff?49463795') format('woff'), + url('./font/gallery-icon.ttf?49463795') format('truetype'), + url('./font/gallery-icon.svg?49463795#gallery-icon') format('svg'); font-weight: normal; font-style: normal; } @@ -323,20 +323,26 @@

+
+ gallery-icon-hash0xe943 +
+
+ gallery-icon-skull0xe944 +
gallery-icon-hdd0xf0a0
gallery-icon-star-half-alt0xf123
+
+
gallery-icon-crop0xf125
gallery-icon-left0xf177
-
-
gallery-icon-file-image0xf1c5
diff --git a/themes/default/vendor/fontello/font/gallery-icon.eot b/themes/default/vendor/fontello/font/gallery-icon.eot index 46c7fa296..83b9d1dae 100644 Binary files a/themes/default/vendor/fontello/font/gallery-icon.eot and b/themes/default/vendor/fontello/font/gallery-icon.eot differ diff --git a/themes/default/vendor/fontello/font/gallery-icon.svg b/themes/default/vendor/fontello/font/gallery-icon.svg index 5c882f000..5f994114b 100644 --- a/themes/default/vendor/fontello/font/gallery-icon.svg +++ b/themes/default/vendor/fontello/font/gallery-icon.svg @@ -70,6 +70,10 @@ + + + + diff --git a/themes/default/vendor/fontello/font/gallery-icon.ttf b/themes/default/vendor/fontello/font/gallery-icon.ttf index 89d5823af..d67d31248 100644 Binary files a/themes/default/vendor/fontello/font/gallery-icon.ttf and b/themes/default/vendor/fontello/font/gallery-icon.ttf differ diff --git a/themes/default/vendor/fontello/font/gallery-icon.woff b/themes/default/vendor/fontello/font/gallery-icon.woff index 8a1b62750..0f0520862 100644 Binary files a/themes/default/vendor/fontello/font/gallery-icon.woff and b/themes/default/vendor/fontello/font/gallery-icon.woff differ diff --git a/themes/default/vendor/fontello/font/gallery-icon.woff2 b/themes/default/vendor/fontello/font/gallery-icon.woff2 index bf6fa9225..28726b2fb 100644 Binary files a/themes/default/vendor/fontello/font/gallery-icon.woff2 and b/themes/default/vendor/fontello/font/gallery-icon.woff2 differ diff --git a/themes/standard_pages/css/standard_pages.css b/themes/standard_pages/css/standard_pages.css index c3afb853b..e69de29bb 100644 --- a/themes/standard_pages/css/standard_pages.css +++ b/themes/standard_pages/css/standard_pages.css @@ -1,508 +0,0 @@ -html{ - min-height:100%; -} - -#theHeader, -#copyright{ - display:none; -} - -#theIdentificationPage, -#theRegisterPage, -#thePasswordPage{ - width:100%; - height:100%; - min-height:100vh; - margin:0; - font-family:Arial, Helvetica, sans-serif; -} - -#theIdentificationPage #the_page, -#theRegisterPage #the_page, -#thePasswordPage #the_page{ - min-height:100vh; - height:100%; -} - -#mode{ - display:flex; - flex-direction:column; - height:100%; - min-height:100vh; - overflow-y:auto; -} - -#login-form, -#register-form, -#password-form{ - border-radius:15px; - width:400px; - margin:0 auto; - margin-top:50px; - box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.15); - padding:50px; -} - -#logo-section{ - max-width:300px; - margin:0 auto; - padding-top:50px; -} - -#logo-section img{ - width:100%; - min-width:300px; -} - -h1{ - font-size: 30px; - font-weight: 400; - margin:0; - margin-bottom:30px; - text-align:center; -} - -h1 i{ - margin:10px; -} - -#login-form form, -#register-form form, -#password-form form{ - display:flex; - justify-content:center; - flex-direction:column; -} - -#password-form p{ - text-align:center; - font-size:15px; - font-weight:500; - margin:0; -} - -#password-form .error-message{ - bottom:0; -} - -.input-container{ - border-radius:3px; - padding:5px 15px; - margin-bottom:25px; - flex-wrap:nowrap; -} - -.input-container input{ - background-color:transparent; - border:none; - width:100%; - line-height:25px; -} - -input:-webkit-autofill { - background-color: transparent !important; - color: inherit !important; -} - -input:-webkit-autofill::first-line { - color: inherit !important; -} - -.input-container input:focus{ - border:none; - outline:none; -} - -.input-container:focus-within{ - border:1px solid #ff7700!important; -} - -.input-container i { - font-size:15px; - margin-right:5px; -} - -.column-flex{ - display:flex; - flex-direction:column; - position:relative; -} - -.row-flex{ - display:flex; - flex-direction:row; - align-items:center; -} - - -.remember-me-container { - display: flex; - align-items: center; - cursor: pointer; - font-family: sans-serif; - font-size: 16px; - margin-bottom:5px; - margin-left:20px; -} - -.gallery-icon-checkmark { - width: 18px; - height: 18px; - border: 1px solid #777; - border-radius: 2px; - display: inline-block; - margin-right: 8px; - line-height: 18px; - font-size: 14px; -} - -.remember-me-container:hover label{ - cursor:pointer; -} - -.gallery-icon-checkmark::before { - opacity: 0; -} - -input[type="checkbox"]#remember_me { - display: none; -} - -input[type="checkbox"]#remember_me:checked + label ~ .gallery-icon-checkmark::before, -input[type="checkbox"]#remember_me:checked + .gallery-icon-checkmark::before { - opacity: 1; - font-size: 12px; - margin: 2px; - vertical-align: text-top; -} - -label{ -margin-bottom:5px; -font-size:15px; -font-weight:700; -} - -p.form-instructions{ - padding-bottom:25px; -} - -.btn-main{ - background-color:#ff7700!important; - color:white!important; - border:none; - padding:15px; - margin-top:15px; - cursor:pointer; - text-decoration:none!important; -} - -a.btn-main{ - display:block; - text-align:center; -} - -#return-to-gallery{ - margin: 30px auto; - display:block; - width:fit-content; -} - -.secondary-links{ - margin-top:40px; - text-align:center; -} - -#theIdentificationPage a, -#theRegisterPage a, -#thePasswordPage a{ - text-decoration:underline; -} - -#theIdentificationPage a:hover, -#theRegisterPage a:hover, -#thePasswordPage a:hover{ - border-bottom:none; -} - -#separator{ - width:300px; - border-bottom:1px solid #D8D8D8; - display:block; - margin: 15px auto; -} - -#header-options{ - position:fixed; - width:100%; - display:flex; - justify-content: space-between; - font-size:15px; -} - -#header-options > *{ - padding:15px; -} - -#theIdentificationPage #header-options .toggle-mode{ - cursor:pointer; -} - -.gallery-icon-sun{ - display:none; -} - -.gallery-icon-eye{ - cursor:pointer; -} - -#language-switch{ - display:flex; - justify-content: flex-end; - font-size:15px; - position: fixed; - right: 0; - bottom: 0; -} - -#lang-select{ - width:fit-content; - position:relative; -} - -#selected-language-container{ - padding:15px; -} - -#lang-select #other-languages{ - display:none; - border-radius: 5px; - padding: 15px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); - margin-right:11px; - width:max-content; - position:absolute; - bottom:40px; - right:0; -} - -#lang-select #other-languages span{ - text-decoration:none; - display:block; - padding:5px 0; -} - -#lang-select #other-languages span:hover{ - color:#ff7700; - cursor:pointer; -} - -#lang-select:hover #other-languages{ - display:block; -} - -#lang-select .gallery-icon-left-chevron:before{ - rotate: 90deg; -} - -#selected-language{ - margin-left:5px; - text-decoration:underline; -} - -.success-message{ - width: calc(100% - 34px); - display: block; - padding: 15px; - margin-bottom:15px; -} - -.error-message{ - text-align: left; - position: absolute; - bottom: 10px; - left:0; - margin: 0; - display:none; -} - -.error-message i, -p.error-message{ - color: #EB3223!important; -} - -.error_block_container { - position: absolute; - right: 15px; - max-width: 300px; - top:40px; -} - -.error_block { - display:flex; - background-color:#BE4949; - border-radius:5px; - color:#FFC8C8; - padding:15px; - position:relative; - align-items:center; -} - -.error_block p{ - margin:0; -} - -.error_block .gallery-icon-cancel:before{ - font-size:33px; - margin-left:0; - margin-right:15px; -} - -#password-form p.intro-paragraph{ - margin-bottom:15px; -} - - -/* Light */ -#theIdentificationPage .light, -#theRegisterPage .light, -#thePasswordPage .light{ - background: linear-gradient(75.69deg, #FFEACF 7.64%, #FFFAF3 77.87%); -} - -.light #login-form, -.light #register-form, -.light #password-form, -.light #lang-select #other-languages { - background-color:#ffffff; -} - -#theIdentificationPage .light a, -#theRegisterPage .light a, -#thePasswordPage .light a, -.light h1, -.light .input-container input, -.light .secondary-links, -.light .properties label, -.light .properties i, -.light #password-form p, -.light #lang-select #other-languages span{ - color:#3C3C3C; -} - -#theIdentificationPage .light a:hover, -#theRegisterPage .light a:hover, -#thePasswordPage .light a:hover{ - color:#ff7700; -} - -.light #header-options a, -.light #header-options .toggle-mode, -.light #selected-language-container{ - color:#ff7700; -} - -.light .input-container{ - background-color:#F0F0F0; - border:1px solid #F0F0F0; -} - -.light .input-container input:is(:-webkit-autofill, :autofill) -{ - box-shadow: 0 0 0 100px #F0F0F0 inset; - -webkit-box-shadow: 0 0 0px 1000px #F0F0F0 inset !important; - -webkit-text-fill-color: #3C3C3C; - color:#3C3C3C; -} - -.light #separator{ - border-bottom:1px solid #D8D8D8; -} - -.light .gallery-icon-eye{ - color:#898989; -} - -.light .success-message{ - background-color: #DBF6D7; - color: #6DCE5E; - border-left: 4px solid #6DCE5E; -} - -/* Dark */ -#theIdentificationPage .dark, -#theRegisterPage .dark, -#thePasswordPage .dark{ - background: linear-gradient(75.69deg, #1B1B1D 7.64%, #2F2F2F 77.87%); -} - -.dark #login-form, -.dark #register-form, -.dark #password-form{ - background-color:#3C3C3C; -} - -#theIdentificationPage .dark a, -#theRegisterPage .dark a, -#thePasswordPage .dark a, -.dark h1, -.dark .input-container input, -.dark .secondary-links, -.dark .properties label, -.dark .properties i, -.dark #password-form p, -.dark #lang-select #other-languages span{ - color:#D6D6D6; -} - -#theIdentificationPage .dark a:hover, -#theRegisterPage .dark a:hover, -#thePasswordPage .dark a:hover, -.dark #lang-select #other-languages span{ - color:#ff7700; -} - -.dark #header-options a, -.dark #header-options .toggle-mode, -.dark #selected-language-container{ - color:#FFEBD0; -} - -.dark .input-container{ - background-color:#303030; - border:1px solid #303030; -} - -.dark .input-container input:is(:-webkit-autofill, :autofill) -{ - box-shadow: 0 0 0 100px #303030 inset; - -webkit-box-shadow: 0 0 0px 1000px #303030 inset !important; - -webkit-text-fill-color: #D6D6D6; - color:#D6D6D6; -} - -.dark #separator{ - border-bottom:1px solid #303030; -} - -.dark .gallery-icon-eye{ - color:#898989; -} - -.dark #lang-select #other-languages { - background-color: #3C3C3C; -} - -.dark .success-message{ - background-color: #4EA590; - color: #AAF6E4; - border-left: 4px solid #AAF6E4; -} - -/*Responsive display*/ -@media (max-width: 768px) { - #login-form, - #register-form, - #password-form{ - max-width:300px; - } -} diff --git a/themes/standard_pages/fonts/OpenSans-VariableFont_wdth,wght.ttf b/themes/standard_pages/fonts/OpenSans-VariableFont_wdth,wght.ttf new file mode 100644 index 000000000..548c15f13 Binary files /dev/null and b/themes/standard_pages/fonts/OpenSans-VariableFont_wdth,wght.ttf differ diff --git a/themes/standard_pages/js/profile.js b/themes/standard_pages/js/profile.js new file mode 100644 index 000000000..c8a59e2fb --- /dev/null +++ b/themes/standard_pages/js/profile.js @@ -0,0 +1,560 @@ +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 = '1px'; + selector.removeClass('open'); + $(this).addClass('close'); + } else { + // open + selector.addClass('open'); + resetSection(display); + $(this).removeClass('close'); + } + }); + + $('#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) => { + $(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); + }); + + // API KEY BELOW + if (!can_manage_api) { + $('.can-manage').hide(); + $('#cant_manage_api').show(); + return; + }; + $('#new_apikey').on('click', function() { + openApiModal(); + }); + + $('#close_api_modal, #cancel_apikey').on('click', function() { + closeApiModal(); + }); + + $('#close_api_modal_edit').on('click', function() { + closeApiEditModal(); + }); + + $('#close_api_modal_revoke, #cancel_api_revoke').on('click', function() { + closeApiRevokeModal(); + }); + + $('#show_expired_list').on('click', function() { + const api_list_expired = $('#api_key_list_expired'); + const isOpen = $(this).data('show'); + if(!isOpen) { + api_list_expired.get(0).style.maxHeight = 'max-content'; + $(this).text(str_hide_expired); + } else { + api_list_expired.get(0).style.maxHeight = '0'; + $(this).text(str_show_expired); + } + + $(this).data('show', !isOpen); + + resetSection('apikey-display', false, true); + }); + + $(window).on('keydown', function(e) { + const haveApiModal = $('#api_modal').is(':visible'); + const haveApiEditModal = $('#api_modal_edit').is(':visible'); + const haveApiRevokeModal = $('#api_modal_revoke').is(':visible'); + if (haveApiModal && e.key === 'Escape') { + closeApiModal(); + } + if (haveApiEditModal && e.key === 'Escape') { + closeApiEditModal(); + } + if (haveApiRevokeModal && e.key === 'Escape') { + closeApiRevokeModal(); + } + }); + + $('select[name="api_expiration"]').on('change', function() { + const custom_date = $('#api_custom_date'); + const value = $(this).val(); + if ('custom' === value) { + custom_date.css('display', 'flex'); + } else { + custom_date.css('display', 'none'); + } + $('#error_api_key_date').hide(); + }); + + $('#api_expiration_date').on('change', function() { + $('#error_api_key_date').hide(); + }); + + getAllApiKeys(); +}); + +function setInfos(params, method='pwg.users.setMyInfo', callback=null, errCallback=null) { + // 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') { + if (typeof callback === 'function') { + callback(data.result); + return; + }; + pwgToaster({ text: data.result, icon: 'success' }); + } else if (data.stat == 'fail') { + pwgToaster({ text: data.message, icon: 'error' }); + } else { + pwgToaster({ text: str_handle_error, icon: 'error' }); + } + if (typeof callback === 'function') { + errCallback(data); + return; + } + }, + error: function (e) { + pwgToaster({ text: e.responseJSON?.message ?? str_handle_error, icon: 'error' }); + if (typeof callback === 'function') { + errCallback(e); + return; + } + }, + }); +} + +function getAllApiKeys(reset = false) { + $.ajax({ + url: 'ws.php?format=json&method=pwg.users.api_key.get', + type: "POST", + dataType: 'json', + data: { + pwg_token: PWG_TOKEN + }, + success: function(res) { + if (res.stat == 'ok') { + if (typeof res.result === 'string') { + // No keys + } else { + AddApiLine(res.result, reset); + } + } + }, + error: function(e) { + pwgToaster({ text: e.responseJSON?.message ?? str_handle_error + 'getAllApiKeys', icon: 'error' }); + } + }); +} + +function AddApiLine(lines, reset) { + const api_list = $('#api_key_list'); + const api_list_expired = $('#api_key_list_expired'); + + $('#api_key_list .api-tab-line:not(.template-api), #api_key_list .api-tab-collapse:not(.template-api)').remove(); + $('#api_key_list_expired .api-tab-line:not(.template-api), #api_key_list_expired .api-tab-collapse:not(.template-api)').remove(); + + lines.forEach((line, i) => { + const api_line = $('#api_line').clone(); + const api_collapse = $('#api_collapse').clone(); + const tmp_id = line.auth_key.slice(24, 34); + + api_line.removeClass('template-api').addClass('api-tab'); + api_line.attr('id', `api_${tmp_id}`); + api_line.find('.icon-collapse').data('api', tmp_id); + api_line.find('.api_name').text(line.apikey_name).attr('title', line.apikey_name); + api_line.find('.api_creation').text(line.created_on_format); + api_line.find('.api_last_use').text(line.last_used_on_since).attr('title', line.last_used_on_since); + api_line.find('.api_expiration').text(line.expiration); + api_line.find('.api-icon-action').attr('data-api', `api_${tmp_id}`); + api_line.find('.api-icon-action').attr('data-pkid', line.auth_key); + + api_collapse.attr('id', `api_collapse_${tmp_id}`); + api_collapse.removeClass('template-api'); + api_collapse.find('.api_key').text(line.auth_key); + api_collapse.find('.icon-clone').attr({ + 'data-copy': line.auth_key, + 'data-success': `api_copy_success_${tmp_id}` + }); + api_collapse.find('.api-copy').attr('id', `api_copy_success_${tmp_id}`); + + if (!line.revoked_on && !line.is_expired) { + api_list.append(api_line); + api_line.after(api_collapse); + } else { + api_list_expired.append(api_line); + api_line.after(api_collapse); + api_line.find('.api-icon-action').remove(); + if (line.is_expired) { + api_line.find('.api_expiration').html(` ${line.expired_on_since}`); + } else { + api_line.find('.api_expiration').html(` ${/\d/.test(line.revoked_on_since) ? line.revoked_on_since : no_time_elapsed} `); + } + } + + }); + + apiLineEvent(); + if (reset) { + resetSection('apikey-display'); + } +} + +function apiLineEvent() { + $('.icon-collapse').off('click').on('click', function() { + const api_collapse = $(`#api_collapse_${$(this).data('api')}`); + const api_line = $(`#api_${$(this).data('api')}`); + + if (api_collapse.is(':visible')) { + api_collapse.removeClass('open'); + api_line.removeClass('open'); + api_line.find('.icon-collapse').addClass('close'); + api_collapse.css('display', 'none'); + api_collapse.find('.api-copy').addClass('api-hide'); + } else { + api_collapse.addClass('open'); + api_line.addClass('open'); + api_line.find('.icon-collapse').removeClass('close'); + api_collapse.css('display', 'grid'); + } + + resetSection('apikey-display', false, true); + }); + + $('.api-tab-collapse .icon-clone').off('click').on('click', function() { + const data_to_copy = $(this).data('copy'); + const selector = $(this).data('success'); + copyToClipboard(data_to_copy, str_copy_key_id, `#${selector}`); + }); + + $('.api-tab-line .edit-mode').off('click').on('click', function() { + const selector = $(this).parent().data('api'); + openApiEditModal(`#${selector}`); + }); + + $('.api-tab-line .delete-mode').off('click').on('click', function() { + const selector = $(this).parent().data('api'); + openApiRevokeModal(`#${selector}`); + }); + +} + +function resetSection(selector, scroll = true, maxContent = false) { + const el = $(`#${selector}`); + const element = el.get(0); + const scrollH = maxContent ? 'max-content' : element.scrollHeight + 'px'; + element.style.maxHeight = scrollH; + + if ('account-display' !== selector && scroll) { + setTimeout(() => { + const el = $(`#${selector.split('-')[0]}-section`).get(0); + el.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + }, 200); + } +} + +function openApiModal() { + $('#api_modal').fadeIn(); + $('#api_key_name').trigger('focus'); + saveApiKeyEvent(); +} + +function closeApiModal() { + $('#api_modal').fadeOut(() => { + $('#api_key_name').val(''); + $('select[name="api_expiration"]').val(selected_date).trigger('change'); + $('#api_expiration_date').val(''); + + $('#api_secret_key').val(''); + $('#retrieves_keyapi').hide(); + $('#generate_keyapi').show(); + $('#done_apikey').attr('disabled', true); + $('#api_key_copy_success, #api_id_copy_success').addClass('api-hide'); + }); + unbindApiKeyEvents(); +} + +function successApiModal(secret, id) { + $('#api_secret_key').val(secret); + $('#api_id_key').val(id); + + $('#generate_keyapi').hide(); + $('#retrieves_keyapi').fadeIn(); + + $('#api_secret_copy').off('click').on('click', function() { + const copy = copyToClipboard(secret, str_copy_key_secret, '#api_key_copy_success'); + + $('#done_apikey').removeAttr('disabled'); + $('#done_apikey').on('click', closeApiModal); + }); + + $('#api_id_copy').off('click').on('click', function() { + const copy = copyToClipboard(id, str_copy_key_id, '#api_id_copy_success'); + }); +} + +//api edit modal +function openApiEditModal(selector) { + const value = $(selector).find('.api_name').text(); + const pkid = $(selector).find('.api-icon-action').data('pkid'); + $('#api_key_edit').val(value); + $('#api_modal_edit').fadeIn(); + $('#api_key_edit').trigger('focus'); + saveApiEditEvents(pkid); +} + +function closeApiEditModal() { + $('#api_modal_edit').fadeOut(() => { + $('#api_key_edit').val(''); + unbindApiEditEvents(); + }); +} + +function saveApiEditEvents(pkid) { + $('#save_api_edit').on('click', function() { + const value = $('#api_key_edit').val(); + + if ('' == value) { + $('#error_api_key_edit').show(); + return; + } + setInfos( + { + pkid, + key_name: value, + }, + 'pwg.users.api_key.edit', + (res) => { + pwgToaster({ text: str_api_edited, icon: 'success' }); + getAllApiKeys(true); + closeApiEditModal(); + } + ); + }); +} + +function unbindApiEditEvents() { + $('#save_api_edit').off('click'); +} + +// api revoke modal +function openApiRevokeModal(selector) { + const apiName = $(selector).find('.api_name').text(); + const pkid = $(selector).find('.api-icon-action').data('pkid'); + const text = sprintf(str_revoke_key, apiName); + $('#api_modal_revoke_title').text(text); + + $('#api_modal_revoke').fadeIn(); + saveApiRevokeEvents(pkid); +} + +function closeApiRevokeModal() { + $('#api_modal_revoke').fadeOut(() => { + $('#api_modal_revoke_title').text(''); + unbindApiRevokeEvents(); + }); +} + +function saveApiRevokeEvents(pkid) { + $('#revoke_api_key').on('click', function() { + setInfos( + { + pkid, + }, + 'pwg.users.api_key.revoke', + (res) => { + pwgToaster({ text: str_api_revoked, icon: 'success' }); + getAllApiKeys(true); + closeApiRevokeModal(); + } + ); + }); +} + +function unbindApiRevokeEvents() { + $('#revoke_api_key').off('click'); +} + +function copyToClipboard(copy, message, selector = null) { + if (window.isSecureContext && navigator.clipboard) { + navigator.clipboard.writeText(copy); + if (selector) { + $(selector).removeClass('api-hide'); + // auto hide + // setTimeout(() => { + // $(selector).addClass('api-hide'); + // }, 1000); + } else { + pwgToaster({ text: message, icon: 'success' }); + } + return true; + } else { + pwgToaster({ text: str_cant_copy, icon: 'error' }); + return false; + } +} + +function saveApiKeyEvent() { + const handler = () => { + const api_name = $('#api_key_name').val(); + let api_duration = $('select[name="api_expiration"]').val(); + + if (api_name == '') { + $('#error_api_key_name').show(); + return; + } + + if ('custom' === api_duration && !$('#api_expiration_date').val()) { + $('#error_api_key_date').show(); + return; + } + + + unbindApiKeyEvents(); + + if ('custom' === api_duration) { + const today = new Date(); + const custom_date = new Date($('#api_expiration_date').val()); + const one_day = 1000 * 60 * 60 * 24; + const days = Math.ceil((custom_date.getTime() - today.getTime() ) / (one_day)); + api_duration = days; + } else { + api_duration = Number(api_duration) ?? 1; + } + + setInfos( + { + key_name: api_name, + duration: api_duration + }, + 'pwg.users.api_key.create', + (res) => { + pwgToaster({ text: str_api_added, icon: 'success' }); + getAllApiKeys(true); + successApiModal(res.apikey_secret, res.auth_key); + }, + (err) => { + saveApiKeyEvent(); + } + ); + } + + $('#save_apikey').on('click.apikey', handler); + $(window).on('keydown.apikey', function(e) { + if (e.key === 'Enter') { + e.preventDefault(); + handler(); + } + }) +} + +function unbindApiKeyEvents() { + $('#api_modal').find('*').addBack().off('.apikey'); + $(window).off('.apikey'); +} 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..e83d2c9c6 --- /dev/null +++ b/themes/standard_pages/template/profile.tpl @@ -0,0 +1,472 @@ +{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'} +{combine_script id='common' load='footer' require='jquery' path='admin/themes/default/js/common.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']}, +}; +const selected_date = "{$API_SELECTED_EXPIRATION}"; +const can_manage_api = {($API_CAN_MANAGE) ? "true" : "false"}; + +const str_copy_key_id = "{"Public key copied."|translate|escape:javascript}"; +const str_copy_key_secret = "{"Secret key copied. Keep it in a safe place."|translate|escape:javascript}"; +const str_cant_copy = "{"Impossible to copy automatically. Please copy manually."|translate|escape:javascript}"; +const str_api_added = "{"The api key has been successfully created."|translate|escape:javascript}"; +const str_revoked = "{"Revoked"|translate|escape:javascript}"; +const str_show_expired = "{"Show expired keys"|translate|escape:javascript}"; +const str_hide_expired = "{"Hide expired keys"|translate|escape:javascript}"; +const str_handle_error = "{"An error has occured"|translate|escape:javascript}"; +const str_expires_in = "{"Expires in"|translate|escape:javascript}"; +const str_expires_on = "{"Expired on"|translate|escape:javascript}"; +const str_revoke_key = "{'Do you really want to revoke the "%s" API key?'|translate|escape:javascript}"; +const str_api_revoked = "{"API Key has been successfully revoked."|translate|escape:javascript}"; +const str_api_edited = "{"API Key has been successfully edited."|translate|escape:javascript}"; +const no_time_elapsed = "{"right now"|translate|escape:javascript}"; +{/footer_script} + + +
+
+ + +
+ +
+ +
+ +
+ + {'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} + + {* API KEY *} +
+
+
+

{'API Keys'|translate}

+

{'Create API Keys to secure your acount'|translate}

+
+ +
+ +
+
+

{'To manage your API keys, please log in with your username/password.'|translate|escape:html}

+
+ +
+ +
+
+
+ +

{'API Key name'|translate}

+

{'Created at'|translate}

+

{'Last use'|translate}

+

{'Expires in'|translate}

+ +
+
+ +
+
+ +
+

+

+

+

+
+ + +
+
+ +
+ +
+ +
+
+
+ +
+
+ + {* API KEY MODAL *} +
+
+ + +
+
+

{'Generate API Key'|translate}

+

{'Create a new API key to secure your account.'|translate}

+
+ +
+
+ +
+ + +
+

+ {'must not be empty'|translate}

+
+ +
+
+ +
+ + {html_options name=api_expiration options=$API_EXPIRATION} +
+

+ {'you must choose a date'|translate}

+
+ +
+ +
+ +
+
+
+ +

{$API_EMAIL_INFOS}

+ +
+ + +
+
+
+ +
+
+

{'Generate API Key'|translate}

+

{'Save your secret Key and ID'|translate}

+

+

+ + +
+ + + +
+ + +
+ + + +
+ +
+ +
+
+ +
+
+ + {* API KEY MODAL EDIT *} +
+
+ + +
+
+

{'Edit API Key'|translate}

+
+ +
+ +
+ + +
+

+ {'must not be empty'|translate}

+
+ +
+ +
+
+
+
+ {* API KEY MODAL REVOKE *} +
+
+ + +
+
+

+
+ +
+ + +
+
+
+
+
+ + {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} + {include file='toaster.tpl'} +
\ 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..a7e731192 --- /dev/null +++ b/themes/standard_pages/template/toaster.tpl @@ -0,0 +1,59 @@ +{combine_script id='toaster_js' load='async' require='jquery' path='themes/standard_pages/js/toaster.js'} +{html_style} +.toast.template { + display: none; +} + +.toaster { + position: fixed; + right: 15px; + max-width: 300px; + top: 40px; + display: flex; + flex-direction: column; + gap: 10px; + z-index: 9999; +} + +.toast { + display: flex; + gap: 5px; + padding: 10px; + border-radius: 5px; + align-items: center; + font-size: 15px; + width: fit-content; + align-self: flex-end; + box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.15); +} + +.toast i:before { + font-size: 33px; +} + +.light .toast.success { + background-color: #D6FFCF; + color: #4CA530; +} + +.light .toast.error { + background-color: #F8D7DC; + color: #EB3D33; +} + +.dark .toast.success { + background-color: #4EA590; + color: #AAF6E4; +} + +.dark .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 e69de29bb..c6635cd4a 100644 --- a/themes/standard_pages/theme.css +++ b/themes/standard_pages/theme.css @@ -0,0 +1,1121 @@ +@font-face { + font-family: Open_Sans; + src: url(fonts/OpenSans-VariableFont_wdth\,wght.ttf); +} + +html{ + min-height:100%; +} + +#theHeader, +#copyright, +#api_custom_date, +#retrieves_keyapi, +.template-section, +.template-api, +.api_name_edit { + display:none; +} + +.api-hide { + display: none !important; +} + +#theIdentificationPage, +#theRegisterPage, +#thePasswordPage, +#theProfilePage{ + width:100%; + height:100%; + min-height:100vh; + margin:0; + font-family: 'Open_Sans', sans-serif; +} + +#theIdentificationPage #the_page, +#theRegisterPage #the_page, +#thePasswordPage #the_page, +#theProfilePage #the_page{ + min-height:100vh; + height:100%; +} + +#mode{ + display:flex; + flex-direction:column; + height:100%; + min-height:100vh; + overflow-y:auto; +} + +#login-form, +#register-form, +#password-form, +.profile-section{ + border-radius:15px; + width:400px; + margin:0 auto; + margin-top:50px; + box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.15); + padding:50px; +} + +#logo-section{ + max-width:300px; + margin:0 auto; + padding-top:50px; +} + +#logo-section img{ + width:100%; + min-width:300px; +} + +h1{ + font-size: 30px; + font-weight: 400; + margin:0; + margin-bottom:30px; + text-align:center; +} + +h1 i{ + margin:10px; +} + +#login-form form, +#register-form form, +#password-form form{ + display:flex; + justify-content:center; + flex-direction:column; +} + +#password-form p, +.profile-section p{ + text-align:center; + font-size:15px; + font-weight:500; + margin:0; +} + +#password-form .error-message{ + bottom:0; +} + +.input-container, +.input-modal { + border-radius:3px; + padding:5px 15px; + margin-bottom:25px; + flex-wrap:nowrap; +} + +.input-container input, +.input-modal input, +.input-container select, +.input-container textarea{ + background-color:transparent; + border:none; + width:100%; + line-height:25px; +} + +.input-container textarea { + resize: none; +} + +input:-webkit-autofill { + background-color: transparent !important; + color: inherit !important; +} + +input:-webkit-autofill::first-line { + color: inherit !important; +} + +input[type='radio'] { + accent-color: #ff7700; +} + +.input-container input:focus, +.input-modal input:focus, +.profile-section .api-tab-line.edit input:focus, +.input-container select:focus, +.input-container textarea:focus{ + border:none; + outline:none; +} + +select { + padding: 5px 0; +} + +.input-container:focus-within, +.input-modal:focus-within{ + border:1px solid #ff7700!important; +} + +.input-container i, +.input-modal i { + font-size:15px; + margin-right:5px; +} + +.column-flex{ + display:flex; + flex-direction:column; + position:relative; +} + +.row-flex{ + display:flex; + flex-direction:row; + align-items:center; +} + +.remember-me-container { + display: flex; + align-items: center; + cursor: pointer; + font-family: 'Open_Sans', sans-serif; + font-size: 16px; + margin-bottom:5px; + margin-left:20px; +} + +.gallery-icon-checkmark { + width: 18px; + height: 18px; + border: 1px solid #777; + border-radius: 2px; + display: inline-block; + margin-right: 8px; + line-height: 18px; + font-size: 14px; +} + +.remember-me-container:hover label{ + cursor:pointer; +} + +.gallery-icon-checkmark::before { + opacity: 0; +} + +input[type="checkbox"]#remember_me { + display: none; +} + +input[type="checkbox"]#remember_me:checked + label ~ .gallery-icon-checkmark::before, +input[type="checkbox"]#remember_me:checked + .gallery-icon-checkmark::before { + opacity: 1; + font-size: 12px; + margin: 2px; + vertical-align: text-top; +} + +label{ +margin-bottom:5px; +font-size:15px; +font-weight:700; +} + +p.form-instructions{ + padding-bottom:25px; +} + +.btn-main{ + background-color:#ff7700!important; + color:white!important; + border:none; + padding:15px; + margin-top:15px; + cursor:pointer; + text-decoration:none!important; +} + +.btn-secondary { + background-color: #ECECEC!important; + color: #3C3C3C!important; +} + +.btn-cancel, +.btn-link { + background: none; + border: none; + padding: 0; + margin: 0; + font: inherit; + color: inherit; + cursor: pointer; + font-size: 14px; + line-height: 1; + height: fit-content; +} + +.btn-link { + text-decoration: underline; + margin-top: 15px; +} + +a.btn-main{ + display:block; + text-align:center; +} + +.btn-main:disabled { + background-color:#aaaaaa!important; + color: #3C3C3C !important; + cursor: not-allowed; +} + +#return-to-gallery{ + margin: 30px auto; + display:block; + width:fit-content; +} + +.secondary-links{ + margin-top:40px; + text-align:center; +} + +#theIdentificationPage a, +#theRegisterPage a, +#thePasswordPage a, +#theProfilePage a{ + text-decoration:underline; +} + +#theIdentificationPage a:hover, +#theRegisterPage a:hover, +#thePasswordPage a:hover, +#theProfilePage a:hover{ + border-bottom:none; +} + +#separator{ + width:300px; + border-bottom:1px solid #D8D8D8; + display:block; + margin: 15px auto; +} + +#header-options{ + position:fixed; + width:100%; + display:flex; + justify-content: space-between; + font-size:15px; +} + +#header-options > *{ + padding:15px; +} + +#theIdentificationPage #header-options .toggle-mode, +#theProfilePage #header-options .toggle-mode{ + cursor:pointer; +} + +.gallery-icon-sun{ + display:none; +} + +.gallery-icon-eye{ + cursor:pointer; +} + +#language-switch{ + display:flex; + justify-content: flex-end; + font-size:15px; + position: fixed; + right: 0; + bottom: 0; +} + +#lang-select{ + width:fit-content; + position:relative; +} + +#selected-language-container{ + padding:15px; +} + +#lang-select #other-languages{ + display:none; + border-radius: 5px; + padding: 15px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); + margin-right:11px; + width:max-content; + position:absolute; + bottom:40px; + right:0; +} + +#lang-select #other-languages span{ + text-decoration:none; + display:block; + padding:5px 0; +} + +#lang-select #other-languages span:hover{ + color:#ff7700; + cursor:pointer; +} + +#lang-select:hover #other-languages{ + display:block; +} + +#lang-select .gallery-icon-left-chevron:before{ + rotate: 90deg; +} + +#selected-language{ + margin-left:5px; + text-decoration:underline; +} + +.success-message{ + width: calc(100% - 34px); + display: block; + padding: 15px; + margin-bottom:15px; +} + +.error-message{ + text-align: left; + position: absolute; + bottom: 5px; + left:0; + margin: 0; + display:none; +} + +#error_api_key_date.error-message { + bottom: -20px; +} + +.error-message i, +p.error-message, +.modal-secret { + color: #EB3223!important; +} + +.error_block_container { + position: absolute; + right: 15px; + max-width: 300px; + top:40px; +} + +.error_block { + display:flex; + background-color:#BE4949; + border-radius:5px; + color:#FFC8C8; + padding:15px; + position:relative; + align-items:center; +} + +.error_block p{ + margin:0; +} + +.error_block .gallery-icon-cancel:before{ + font-size:33px; + margin-left:0; + margin-right:15px; +} + +#password-form p.intro-paragraph{ + margin-bottom:15px; +} + +.profile-section { + width: 100%; + max-width: 700px; + margin-top: 0; + margin-bottom: 25px; +} + +#account-section { + margin-top: 25px; +} + +.profile-section .title { + position: relative; +} + +.profile-section .title p { + margin-bottom: 10px; +} + +.profile-section .form { + margin-top: 0; + max-height: 0; + overflow: hidden; + transition: max-height 0.6s ease; +} + +.profile-section .form.open { + overflow: hidden; +} + +.profile-section .reset { + display: flex; + justify-content: space-between; + /* gap: 15px; */ +} + +.profile-section .save, +.profile-section .new-apikey, +.profile-section .modal-input-keys { + display: flex; + gap: 15px; + justify-content: flex-end; + align-items: center; +} + +.profile-section .modal-input-keys { + position: absolute; + right: 30px; +} + +.profile-section .save .btn-main, +.profile-section .new-apikey .btn-main, +.profile-section .reset .btn-main { + padding: 10px 35px; +} + +.profile-section h1 { + margin-bottom: 10px; +} + +.gallery-icon-up-open:not( + .api-list .gallery-icon-up-open, + #api_key_list_expired .gallery-icon-up-open +) { + position: absolute; + top: 50%; + cursor: pointer; + right: 0; + transition: transform 0.5s ease; +} + +.gallery-icon-up-open.close, +.profile-section .icon-collapse.close { + position: relative; + transform: rotate(180deg); +} + +.profile-section .api-icon-collapse .icon-collapse.close { + top: 2px; + left: -0.2px; +} + +.profile-section .icon-collapse { + display: inline-block; + transition: transform 0.4s; + vertical-align: middle; +} + +.profile-section .username { + width: fit-content; + cursor: not-allowed; + background-color: transparent !important; + border: none !important; +} + +.profile-section .input-container.radio, +.profile-section .section-expiration, +.profile-section .api-icon-action { + gap: 10px; +} + +.profile-section .api-icon-action { + padding-right: 10px; + font-size: 14px; +} + +.profile-section .input-container.radio label { + display: flex; + align-items: center; + margin-bottom: 0; + padding: 5px 0; + gap: 5px; + cursor: pointer; +} + +.profile-section .preferences-options { + gap: 20px; + padding: 20px; + margin-top: 5px; +} + +.profile-section .preferences-options .option { + gap: 15px; +} + +.profile-section .api-tab { + display: grid; + grid-template-columns: 60px 2fr 1fr 1fr 1fr 0.5fr; + /* grid-template-columns: 60px 200px 100px 100px 100px 30px; */ + justify-items: start; + align-items: center; + max-height: 40px; +} + +.profile-section .api-list-head { + padding: 15px 0; + border-radius: 8px; + margin-top: 15px; +} + +.profile-section .api-expiration { + width: fit-content; + margin-bottom: 0; +} + +.profile-section .api-mail-infos { + position: relative; + font-size: 12px; + padding-top: 20px; + margin-bottom: 25px; + text-align: start; +} + +.profile-section .api-icon-collapse { + justify-self: center; +} + +.profile-section .api-icon-action i:hover, +.close-modal:hover, +.profile-section .icon-clone:hover { + color: #ff7700; +} + +.profile-section .api-tab-line, +.profile-section .api-tab-collapse { + padding: 10px 0; + border-radius: 8px; + white-space: nowrap; +} + +.profile-section .api-tab-line.open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.profile-section .api-tab-collapse.open { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.profile-section .api-tab-line i:not(#api_key_list_expired, .api_expiration .api-skull), +.profile-section .icon-clone { + cursor: pointer; +} + +.profile-section div.api-tab-line:nth-child(even) { + background-color: #303030; +} +.profile-section .api-tab-collapse .key { + gap: 10px; + padding: 5px 0; +} + +.profile-section .api-tab-collapse { + padding-bottom: 20px; + display: grid; + grid-template-columns: 60px auto; +} + +.profile-section .api_name, +.profile-section .api_creation, +.profile-section .api_expiration { + text-overflow: ellipsis; + max-width: 90%; + overflow: hidden; +} + +.profile-section #api_key_list_expired .api_expiration { + text-overflow: unset; + max-width: unset; +} + +.profile-section #api_key_list .border-line { + border: 1px solid transparent !important; +} + +.profile-section #api_key_list .edit { + border: 1px solid #ff7700 !important; +} + +.profile-section .api-list-head > p{ + text-align: start !important; +} + +.profile-section .api_last_use { + max-width: 98%; + overflow: hidden; + text-overflow: ellipsis; +} + +.profile-section .api_name_edit { + width: max-content; + background-color: transparent; + border: none; + border-radius: 3px; + width: 90%; +} + +.profile-section .new-apikey .btn-link{ + color: #9A9A9A !important; + font-weight: 700; +} + +.profile-section .api-copy { + padding: 1px 10px; + width: fit-content; + font-size: 12px; +} + +.profile-section #show_expired_list { + margin: 15px 0; +} + +.profile-section #api_key_list_expired { + max-height: 0; + transition: max-height 0.3s ease; + overflow: hidden; +} + +.profile-section .api-info { + font-size: 12px; +} + +.profile-section .api_key +.profile-section #api_id_key, +.profile-section #api_secret_key { + font-family: monospace !important; +} + +/* Modal */ + +.bg-modal { + display: none; + position: fixed; + z-index: 100; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0,0,0,0.7); +} + +.close-modal { + position: absolute; + right: -40px; + top: -40px; + font-size: 30px; + text-decoration: none !important; + cursor: pointer; +} + +.body-modal { + display: flex; + flex-direction: column; + position: absolute; + border-radius: 10px; + left: 50%; + top: 50%; + transform: translate(-50%, -48%); + text-align: left; + padding: 30px; + max-width: 600px; + width: 100%; +} + +#api_modal_revoke .body-modal { + max-width: 400px; +} + +.body-modal .btn-main { + margin-top: 0; +} + +.title-modal { + font-size: 24px !important; + font-weight: 600 !important; +} + +.subtitle-modal { + font-size: 16px !important; + font-weight: 400; +} + +.head-modal { + display: flex; + flex-direction: column; + align-items: start; + gap: 5px; +} + +#generate_keyapi .head-modal, +#api_modal_edit .head-modal, +#api_modal_revoke .head-modal { + margin-bottom: 25px; +} + +.input-modal-id { + margin-top: 25px; + margin-bottom: 5px; +} + +.input-modal-key { + margin-top: 30px; +} + +/* The switch */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 24px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +label.switch { + margin-bottom: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +input:checked + .slider:before, input:checked + .slider::after { + margin-left: 10px; +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +/* Tooltips */ +[data-tooltip]:hover::after { + position: absolute; + content: attr(data-tooltip); + animation: fadeIn 100ms cubic-bezier(0.42, 0, 0.62, 1.32) forwards; + animation-delay: 100ms; + border-radius: 5px; + max-width: 100%; + text-align: center; + font-size: 12px; + padding: 5px 10px; + box-shadow: 0px 10px 33px #3333332e; +} + +/* Light */ +#theIdentificationPage .light, +#theRegisterPage .light, +#thePasswordPage .light, +#theProfilePage .light{ + background: linear-gradient(75.69deg, #FFEACF 7.64%, #FFFAF3 77.87%); +} + +.light #login-form, +.light #register-form, +.light #password-form, +.light #lang-select #other-languages, +.light .profile-section, +.light .slider:before, +.light .body-modal, +.light [data-tooltip]:hover::after { + background-color:#ffffff; +} + +#theIdentificationPage .light a, +#theRegisterPage .light a, +#thePasswordPage .light a, +#theProfilePage .light a:not(.close-modal), +.light h1, +.light .input-container input, +.light .input-modal input, +.light .input-container select, +.light .input-container textarea, +.light .secondary-links, +.light .properties label, +.light .profile-section label, +.light .properties i, +.light .profile-section i, +.light #password-form p, +.light .profile-section p, +.light #lang-select #other-languages span, +.light .btn-cancel, +.light .btn-link { + color:#3C3C3C; +} + +#theIdentificationPage .light a:hover, +#theRegisterPage .light a:hover, +#thePasswordPage .light a:hover, +#theProfilePage .light a:hover{ + color:#ff7700; +} + +.light #header-options a, +.light #header-options .toggle-mode, +.light #selected-language-container{ + color:#ff7700; +} + +.light .input-container, +.light .input-modal, +.light .api-list-head { + background-color:#F0F0F0; + border:1px solid #F0F0F0; +} + +.light .input-container input:is(:-webkit-autofill, :autofill) +{ + box-shadow: 0 0 0 100px #F0F0F0 inset; + -webkit-box-shadow: 0 0 0px 1000px #F0F0F0 inset !important; + -webkit-text-fill-color: #3C3C3C; + color:#3C3C3C; +} + +.light #separator{ + border-bottom:1px solid #D8D8D8; +} + +.light .gallery-icon-eye{ + color:#898989; +} + +.light .success-message{ + background-color: #DBF6D7; + color: #6DCE5E !important; + border-left: 4px solid #6DCE5E; +} + +.light input:checked + .slider { + background-color: #FFA646; +} + +.light input:focus + .slider { + box-shadow: 0 0 1px #FFA646; +} + +.light .slider { + background-color: #CCCCCC; +} + +.light .api-list-body > div:nth-child(4n+1), +.light .api-list-body > div:nth-child(4n+2) { + background-color: #F8F8F8; +} + +.light .api-tab-line p, +.light .api-icon-action i, +.light .keys p:not(.api-copy), +.light .keys i, +.light #api_key_list_expired .api-skull { + color: #656565; +} + +.light .close-modal { + color: #ffffff; +} + +.light input[type="date"] { + color-scheme: light; +} + +.light input[type="date"]::-webkit-calendar-picker-indicator { + filter: invert(0); +} + +.light .btn-revoked { + background-color: #EB3223 !important; +} + +/* Dark */ +#theIdentificationPage .dark, +#theRegisterPage .dark, +#thePasswordPage .dark, +#theProfilePage .dark{ + background: linear-gradient(75.69deg, #1B1B1D 7.64%, #2F2F2F 77.87%); +} + +.dark #login-form, +.dark #register-form, +.dark #password-form, +.dark .profile-section, +.dark .body-modal { + background-color:#3C3C3C; +} + +#theIdentificationPage .dark a, +#theRegisterPage .dark a, +#thePasswordPage .dark a, +#theProfilePage .dark a:not(.close-modal), +.dark h1, +.dark .input-container input, +.dark .input-modal input, +.dark .input-container select, +.dark .input-container textarea, +.dark .secondary-links, +.dark .properties label, +.dark .profile-section label, +.dark .properties i, +.dark .profile-section i, +.dark #password-form p, +.dark .profile-section p, +.dark #lang-select #other-languages span, +.dark .btn-cancel, +.dark .btn-link { + color:#D6D6D6; +} + +#theIdentificationPage .dark a:hover, +#theRegisterPage .dark a:hover, +#thePasswordPage .dark a:hover, +#theProfilePage .dark a:hover, +.dark #lang-select #other-languages span{ + color:#ff7700; +} + +.dark #header-options a, +.dark #header-options .toggle-mode, +.dark #selected-language-container{ + color:#FFEBD0; +} + +.dark .input-container, +.dark .input-modal { + background-color:#303030; + border:1px solid #303030; +} + +.dark .input-container input:is(:-webkit-autofill, :autofill) +{ + box-shadow: 0 0 0 100px #303030 inset; + -webkit-box-shadow: 0 0 0px 1000px #303030 inset !important; + -webkit-text-fill-color: #D6D6D6; + color:#D6D6D6; +} + +.dark #separator{ + border-bottom:1px solid #303030; +} + +.dark .gallery-icon-eye{ + color:#898989; +} + +.dark #lang-select #other-languages, +.dark .slider, +.dark input:checked + .slider:before { + background-color: #3C3C3C; +} + +.dark .success-message{ + background-color: #4EA590; + color: #AAF6E4 !important; + border-left: 4px solid #AAF6E4; +} + +.dark .btn-secondary { + background-color: #202020!important; + color: white!important; +} + +.dark input:checked + .slider { + background-color: #FFA646; +} + +.dark .slider:before { + background-color: #777777; +} + +.dark .api-list-head, +.dark [data-tooltip]:hover::after{ + background-color: #2A2A2A; +} + +.dark .api-list-body > div:nth-child(4n+1), +.dark .api-list-body > div:nth-child(4n+2) { + background-color: #333333; +} + +.dark .icon-collapse { + color: white !important; +} + +.dark .close-modal { + color: #3C3C3C; +} + +.dark input[type="date"] { + color-scheme: dark; +} + +.dark input[type="date"]::-webkit-calendar-picker-indicator { + filter: invert(0); +} + +.dark .btn-revoked { + background-color: #BE4949 !important; +} + +/*Responsive display*/ +@media (max-width: 768px) { + #login-form, + #register-form, + #password-form{ + max-width:300px; + } +} diff --git a/tools/ws.htm b/tools/ws.htm index e6807cd0f..296f471c7 100644 --- a/tools/ws.htm +++ b/tools/ws.htm @@ -84,6 +84,21 @@