mirror of
https://github.com/Piwigo/Piwigo.git
synced 2026-04-30 10:34:58 +02:00
Merge branch 'master' into issue-2362-add-filters-options-in-admin
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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']),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
BIN
admin/themes/default/images/promote-newsletter.png
Normal file
BIN
admin/themes/default/images/promote-newsletter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
@@ -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(`
|
||||
<div class="promote-newsletter">
|
||||
<div class="promote-content">
|
||||
|
||||
<img class="promote-image" src="admin/themes/default/images/promote-newsletter.png">
|
||||
|
||||
<div class="promote-newsletter-content">
|
||||
<span class="promote-newsletter-title">{"Subscribe to our newsletter and stay updated!"|@translate|escape:javascript}</span>
|
||||
<div class="promote-content subscribe-newsletter">
|
||||
<input type="text" id="newsletterSubscribeInput" value="{$EMAIL}" class="left-side">
|
||||
<a href="{$SUBSCRIBE_BASE_URL}{$EMAIL}" id="newsletterSubscribeLink" class="right-side go-to-porg icon-thumbs-up newsletter-hide">{"Sign up to the newsletter"|@translate|escape:javascript}</a>
|
||||
</div>
|
||||
<a href="{$OLD_NEWSLETTERS_URL}" class="promote-link">{"See previous newsletters"|@translate|escape:javascript}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a href="#" class="dont-show-again icon-cancel tiptip newsletter-hide" title="{'Understood, do not show again'|translate|escape:javascript}"></a>
|
||||
</div>`);
|
||||
|
||||
{/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)}
|
||||
<br><span class="newsletter-subscription"><a href="{$SUBSCRIBE_BASE_URL}{$EMAIL}" id="newsletterSubscribe" class="externalLink cluetip icon-mail-alt" title="{'Piwigo Announcements Newsletter'|@translate}|{'Keep in touch with Piwigo project, subscribe to Piwigo Announcement Newsletter. You will receive emails when a new release is available (sometimes including a security bug fix, it\'s important to know and upgrade) and when major events happen to the project. Only a few emails a year.'|@translate|@htmlspecialchars|@nl2br}">{'Subscribe %s to Piwigo Announcements Newsletter'|@translate:$EMAIL}</a> <a href="#" class="newsletter-hide">{'... or hide this link'|translate}</a></span>
|
||||
{/if}
|
||||
</p>
|
||||
@@ -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('<div class="jGrowl-popup-icon icon-ok"></div>')
|
||||
}
|
||||
};
|
||||
|
||||
if ((jQuery(popup[i])).hasClass("error")){
|
||||
if (! ((jQuery(popup[i]).children(":first")).hasClass("jGrowl-popup-icon icon-cancel"))){
|
||||
jQuery(popup[i]).prepend('<div class="jGrowl-popup-icon icon-cancel"></div>')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(callback);
|
||||
observer.observe(targetNode, config);
|
||||
|
||||
function ignoreExtension(type, id) {
|
||||
queuedManager.add({
|
||||
type: 'GET',
|
||||
|
||||
@@ -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;
|
||||
|
||||
2
i.php
2
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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 |
|
||||
// +-----------------------------------------------------------------------+
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
?>
|
||||
|
||||
?>
|
||||
@@ -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;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
36
install/db/176-database.php
Normal file
36
install/db/176-database.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | This file is part of Piwigo. |
|
||||
// | |
|
||||
// | For copyright and license information, please view the COPYING.txt |
|
||||
// | file that was distributed with this source code. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
|
||||
if (!defined('PHPWG_ROOT_PATH'))
|
||||
{
|
||||
die('Hacking attempt!');
|
||||
}
|
||||
|
||||
$upgrade_description = 'Modification to the user_auth_key table to match the api keys';
|
||||
|
||||
// we are modifying the "auth_key" table structure to support the new API key system.
|
||||
// the existing structure was too limited for our needs, this update ensures better
|
||||
// flexibility and security for managing API access tokens in the future.
|
||||
pwg_query(
|
||||
'ALTER TABLE `'.PREFIX_TABLE.'user_auth_keys`
|
||||
ADD COLUMN `apikey_secret` VARCHAR(255) DEFAULT NULL AFTER auth_key,
|
||||
ADD COLUMN `apikey_name` VARCHAR(100) DEFAULT NULL,
|
||||
ADD COLUMN `key_type` VARCHAR(40) DEFAULT NULL,
|
||||
ADD COLUMN `revoked_on` datetime DEFAULT NULL,
|
||||
ADD COLUMN `last_used_on` datetime DEFAULT NULL
|
||||
;');
|
||||
|
||||
// For rows that already exist in the table, we add a key_type
|
||||
pwg_query(
|
||||
'UPDATE `'.PREFIX_TABLE.'user_auth_keys`
|
||||
SET `key_type` = \'auth_key\'
|
||||
WHERE `key_type` IS NULL
|
||||
;');
|
||||
|
||||
echo "\n".$upgrade_description."\n";
|
||||
?>
|
||||
33
install/db/177-database.php
Normal file
33
install/db/177-database.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | This file is part of Piwigo. |
|
||||
// | |
|
||||
// | For copyright and license information, please view the COPYING.txt |
|
||||
// | file that was distributed with this source code. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
|
||||
if (!defined('PHPWG_ROOT_PATH'))
|
||||
{
|
||||
die('Hacking attempt!');
|
||||
}
|
||||
|
||||
global $conf;
|
||||
load_conf_from_db();
|
||||
|
||||
$upgrade_description = 'Add 3XL and 4XL sizes';
|
||||
|
||||
//Get predefined image sizes
|
||||
$derivatives = unserialize($conf['derivatives']);
|
||||
|
||||
//get default sizes from derivative_std_params
|
||||
$default_sizes = ImageStdParams::get_default_sizes();
|
||||
|
||||
//Get 3XL and 4XL from default values
|
||||
$derivatives['d'][IMG_3XLARGE] = $default_sizes['3xlarge'];
|
||||
$derivatives['d'][IMG_4XLARGE] = $default_sizes['4xlarge'];
|
||||
|
||||
// Save derivative new settings
|
||||
ImageStdParams::set_and_save($derivatives['d']);
|
||||
|
||||
echo "\n".$upgrade_description."\n";
|
||||
?>
|
||||
@@ -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';
|
||||
@@ -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 <em>%s</em> will be used to notify you when your API key is about to expire.'] = 'L\'email <em>%s</em> 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';
|
||||
|
||||
108
profile.php
108
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 <em>%s</em> 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 );
|
||||
|
||||
|
||||
@@ -84,6 +84,17 @@
|
||||
</fieldset>
|
||||
{/if}
|
||||
|
||||
{if isset($PLUGINS_PROFILE)}
|
||||
{foreach from=$PLUGINS_PROFILE item=plugin_block}
|
||||
<fieldset>
|
||||
<legend>{$plugin_block.name}</legend>
|
||||
<div class="plugins fields">
|
||||
{include file=$plugin_block.template}
|
||||
</div>
|
||||
</fieldset>
|
||||
{/foreach}
|
||||
{/if}
|
||||
|
||||
<p class="bottomButtons">
|
||||
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN}">
|
||||
<input class="submit" type="submit" name="validate" value="{'Submit'|@translate}">
|
||||
|
||||
28
themes/default/vendor/fontello/config.json
vendored
28
themes/default/vendor/fontello/config.json
vendored
@@ -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",
|
||||
|
||||
@@ -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'; } /* '' */
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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 = ' '); }
|
||||
|
||||
@@ -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 = ' '); }
|
||||
|
||||
@@ -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'; } /* '' */
|
||||
|
||||
20
themes/default/vendor/fontello/demo.html
vendored
20
themes/default/vendor/fontello/demo.html
vendored
@@ -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 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xe943">
|
||||
<i class="demo-icon gallery-icon-hash"></i> <span class="i-name">gallery-icon-hash</span><span class="i-code">0xe943</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xe944">
|
||||
<i class="demo-icon gallery-icon-skull"></i> <span class="i-name">gallery-icon-skull</span><span class="i-code">0xe944</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf0a0">
|
||||
<i class="demo-icon gallery-icon-hdd"></i> <span class="i-name">gallery-icon-hdd</span><span class="i-code">0xf0a0</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf123">
|
||||
<i class="demo-icon gallery-icon-star-half-alt"></i> <span class="i-name">gallery-icon-star-half-alt</span><span class="i-code">0xf123</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf125">
|
||||
<i class="demo-icon gallery-icon-crop"></i> <span class="i-name">gallery-icon-crop</span><span class="i-code">0xf125</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf177">
|
||||
<i class="demo-icon gallery-icon-left"></i> <span class="i-name">gallery-icon-left</span><span class="i-code">0xf177</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf1c5">
|
||||
<i class="demo-icon gallery-icon-file-image"></i> <span class="i-name">gallery-icon-file-image</span><span class="i-code">0xf1c5</span>
|
||||
</div>
|
||||
|
||||
BIN
themes/default/vendor/fontello/font/gallery-icon.eot
vendored
BIN
themes/default/vendor/fontello/font/gallery-icon.eot
vendored
Binary file not shown.
@@ -70,6 +70,10 @@
|
||||
|
||||
<glyph glyph-name="search" unicode="" d="M643 386q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
||||
|
||||
<glyph glyph-name="hash" unicode="" d="M748 406c-9 0-18-7-19-16l-15-75c-2-12 7-24 20-24h133c11 0 20-9 20-20v-134c0-11-9-20-20-20h-175c-9 0-18-6-20-16l-47-235c-2-9-10-16-20-16h-154c-13 0-22 11-20 24l44 220c3 12-7 23-20 23h-81c-10 0-18-6-20-16l-45-235c-2-9-10-16-20-16h-153c-13 0-22 11-20 24l43 220c3 12-7 23-19 23h-120c-11 0-20 9-20 20v134c0 11 9 20 20 20h163c10 0 18 7 20 16l14 75c3 13-7 24-19 24h-129c-11 0-20 9-20 20v138c0 11 9 20 20 20h172c10 0 18 7 20 16l46 234c2 9 10 16 20 16h154c12 0 22-12 19-24l-44-218c-2-12 7-24 20-24h80c9 0 18 7 19 16l47 234c1 9 10 16 19 16h154c13 0 23-11 20-24l-44-218c-2-12 7-24 20-24h122c11 0 20-9 20-20v-138c0-11-9-20-20-20h-165z m-350-91c-2-12 8-24 20-24h80c9 0 17 7 19 16l15 75c2 13-7 24-20 24h-80c-9 0-18-7-19-16l-15-75z" horiz-adv-x="933" />
|
||||
|
||||
<glyph glyph-name="skull" unicode="" d="M813 71c114 80 187 203 187 342 0 241-224 437-500 437-276 0-500-196-500-437 0-139 73-262 188-342 0-1 0-1 0-2v-125c0-52 42-94 93-94h94v94c0 17 14 31 31 31 17 0 32-14 32-31v-94h125v94c0 17 14 31 31 31 17 0 31-14 31-31v-94h94c52 0 94 42 94 94v125c0 1 0 1 0 2z m-625 279c0 33 13 65 36 88 24 24 55 37 89 37 33 0 64-13 88-37 23-23 37-55 37-88 0-33-14-65-37-88-24-24-55-37-88-37-34 0-65 13-89 37-23 23-36 55-36 88z m500 125c33 0 64-13 88-37 23-23 37-55 37-88 0-33-14-65-37-88-24-24-55-37-88-37-34 0-65 13-89 37-23 23-36 55-36 88 0 33 13 65 36 88 24 24 55 37 89 37z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="hdd" unicode="" d="M580 171q0-18-13-31t-31-13-32 13-13 31 13 32 32 13 31-13 13-32z m143 0q0-18-13-31t-31-13-32 13-13 31 13 32 32 13 31-13 13-32z m63-89v179q0 7-6 12t-12 6h-679q-7 0-12-6t-6-12v-179q0-7 6-12t12-6h679q7 0 12 6t6 12z m-687 268h659l-88 269q-2 7-9 12t-14 5h-437q-7 0-14-5t-9-12z m758-89v-179q0-37-26-63t-63-26h-679q-36 0-63 26t-26 63v179q0 14 9 42l110 338q9 29 35 48t56 18h437q31 0 56-18t35-48l110-338q9-28 9-42z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="star-half-alt" unicode="" d="M662 316l143 140-198 29-37 5-17 34-89 179v-537l33-17 178-94-34 198-6 37z m252 146l-202-197 48-279q2-19-4-29t-19-11q-9 0-22 7l-251 132-250-132q-13-7-23-7-12 0-19 11t-3 29l48 279-203 197q-18 18-13 33t30 20l280 40 126 254q11 23 27 23 16 0 28-23l125-254 280-40q25-4 31-20t-14-33z" horiz-adv-x="928.6" />
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
BIN
themes/default/vendor/fontello/font/gallery-icon.ttf
vendored
BIN
themes/default/vendor/fontello/font/gallery-icon.ttf
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
themes/standard_pages/fonts/OpenSans-VariableFont_wdth,wght.ttf
Normal file
BIN
themes/standard_pages/fonts/OpenSans-VariableFont_wdth,wght.ttf
Normal file
Binary file not shown.
560
themes/standard_pages/js/profile.js
Normal file
560
themes/standard_pages/js/profile.js
Normal file
@@ -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(`<i class="gallery-icon-skull api-skull"></i> <span data-tooltip="${line.expired_on_format}">${line.expired_on_since}</span>`);
|
||||
} else {
|
||||
api_line.find('.api_expiration').html(`<i class="gallery-icon-skull api-skull"></i> <span>${/\d/.test(line.revoked_on_since) ? line.revoked_on_since : no_time_elapsed}</span> <i data-tooltip="${line.revoked_on_message}" class="icon-info-circled-1 api-info"></i>`);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
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');
|
||||
}
|
||||
32
themes/standard_pages/js/toaster.js
Normal file
32
themes/standard_pages/js/toaster.js
Normal file
@@ -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);
|
||||
}
|
||||
472
themes/standard_pages/template/profile.tpl
Normal file
472
themes/standard_pages/template/profile.tpl
Normal file
@@ -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}
|
||||
|
||||
<script>
|
||||
var selected_language = `{$language_options[$current_language]}`;
|
||||
var url_logo_light = `{$ROOT_URL}themes/standard_pages/images/piwigo_logo.svg`;
|
||||
var url_logo_dark = `{$ROOT_URL}themes/standard_pages/images/piwigo_logo_dark.svg`;
|
||||
</script>
|
||||
{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}
|
||||
|
||||
<container id="mode" class="light">
|
||||
<section id="header-options">
|
||||
<div>
|
||||
<i class="gallery-icon-moon toggle-mode" id="toggle_mode_light" onclick="toggle_mode('dark')"></i>
|
||||
<i class="gallery-icon-sun toggle-mode" id="toggle_mode_dark" onclick="toggle_mode('light')"></i>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{$HELP_LINK}" target="_blank">{'Help'|translate}</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="logo-section">
|
||||
<img id="piwigo-logo" src="{$ROOT_URL}themes/standard_pages/images/piwigo_logo.svg">
|
||||
</section>
|
||||
|
||||
<a href="{$U_HOME}" id="return-to-gallery"><i class="gallery-icon-left"></i> {'Return to the gallery'|translate}</a>
|
||||
|
||||
{* ACCOUNT *}
|
||||
<section id="account-section" class="profile-section">
|
||||
<div class="title">
|
||||
<div class="column-flex">
|
||||
<h1>{'Account'|translate}</h1>
|
||||
<p>{'Manage your account'|translate}</p>
|
||||
</div>
|
||||
<i class="gallery-icon-up-open display-btn close" data-display="account-display"></i>
|
||||
</div>
|
||||
<div class="form" id="account-display">
|
||||
<div class="column-flex first">
|
||||
<label>{'Username'|translate}</label>
|
||||
<div class="row-flex input-container username">
|
||||
<i class="gallery-icon-user"></i>
|
||||
<p>{$USERNAME}</p>
|
||||
<input id="pwg_token" type="hidden" value="{$PWG_TOKEN}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="column-flex">
|
||||
<label for="email">{'Email address'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-mail-alt"></i>
|
||||
<input type="email" name="mail_address" id="email" value="{$EMAIL}" />
|
||||
</div>
|
||||
<p id="email_error" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'must not be empty'|translate}</p>
|
||||
</div>
|
||||
<div class="save">
|
||||
<button class="btn btn-main" id="save_account">{'Submit'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{* PREFERENCES *}
|
||||
{if $ALLOW_USER_CUSTOMIZATION}
|
||||
<section id="preferences-section" class="profile-section">
|
||||
<div class="title">
|
||||
<div class="column-flex">
|
||||
<h1>{'Preferences'|translate}</h1>
|
||||
<p>{'Choose how you want to see your gallery'|translate}</p>
|
||||
</div>
|
||||
<i class="gallery-icon-up-open display-btn close" data-display="preferences-display"></i>
|
||||
</div>
|
||||
<div class="form" id="preferences-display">
|
||||
<div class="column-flex first">
|
||||
<label for="nb_image_page">{'Number of photos per page'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-picture"></i>
|
||||
<input type="number" size="4" maxlength="3" name="nb_image_page" id="nb_image_page"
|
||||
value="{$NB_IMAGE_PAGE}" />
|
||||
</div>
|
||||
<p id="error_nb_image" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex">
|
||||
<label>{'Theme'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-brush"></i>
|
||||
{html_options name=theme options=$template_options selected=$template_selection}
|
||||
</div>
|
||||
<p class="error-message"><i class="gallery-icon-attention-circled"></i> {'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex">
|
||||
<label>{'Language'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-language"></i>
|
||||
{html_options name=language options=$language_options selected=$language_selection}
|
||||
</div>
|
||||
<p class="error-message"><i class="gallery-icon-attention-circled"></i> {'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex">
|
||||
<label for="recent_period">{'Recent period'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-calendar"></i>
|
||||
<input type="number" size="3" maxlength="2" name="recent_period" id="recent_period"
|
||||
value="{$RECENT_PERIOD}" />
|
||||
</div>
|
||||
<p id="error_period" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
{* OPTIONS *}
|
||||
<label class="options-title">{'Options'|translate}</label>
|
||||
<div class="column-flex input-container preferences-options">
|
||||
<div class="row-flex option">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="opt_album" {if "true" === $EXPAND}checked{/if}>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<p>{'Expand all albums'|@translate}</p>
|
||||
</div>
|
||||
|
||||
{if $ACTIVATE_COMMENTS}
|
||||
<div class="row-flex option">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="opt_comment" {if "true" === $NB_COMMENTS}checked{/if}>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<p>{'Show number of comments'|@translate}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="row-flex option">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="opt_hits" {if "true" === $NB_HITS}checked{/if}>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<p>{'Show number of hits'|@translate}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="reset">
|
||||
<button class="btn btn-main btn-secondary"
|
||||
id="default_preferences">{'Reset to default values'|translate}</button>
|
||||
<div class="save">
|
||||
<button class="btn btn-main btn-secondary" id="reset_preferences">{'Reset'|translate}</button>
|
||||
<button class="btn btn-main" id="save_preferences">{'Submit'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{* PASSWORD *}
|
||||
{if not $SPECIAL_USER}
|
||||
<section id="password-section" class="profile-section">
|
||||
<div class="title">
|
||||
<div class="column-flex">
|
||||
<h1>{'Password'|translate}</h1>
|
||||
<p>{'Change your password'|translate}</p>
|
||||
</div>
|
||||
<i class="gallery-icon-up-open display-btn close" data-display="password-display"></i>
|
||||
</div>
|
||||
<div class="form" id="password-display">
|
||||
<div class="column-flex">
|
||||
<label for="password">{'Password'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="gallery-icon-lock"></i>
|
||||
<input type="password" class="" name="password" id="password" size="25" />
|
||||
<i class="gallery-icon-eye togglePassword"></i>
|
||||
</div>
|
||||
<p class="error-message"><i class="gallery-icon-attention-circled"></i> {'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex">
|
||||
<label for="password_new">{'New password'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="gallery-icon-lock"></i>
|
||||
<input type="password" class="" name="new-password" id="password_new" size="25" />
|
||||
<i class="gallery-icon-eye togglePassword"></i>
|
||||
</div>
|
||||
<p class="error-message"><i class="gallery-icon-attention-circled"></i> {'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex">
|
||||
<label for="password_conf">{'Confirm my new password'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="gallery-icon-lock"></i>
|
||||
<input type="password" class="" name="password_conf" id="password_conf" size="25" />
|
||||
<i class="gallery-icon-eye togglePassword"></i>
|
||||
</div>
|
||||
<p class="error-message"><i class="gallery-icon-attention-circled"></i> {'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="save">
|
||||
<button class="btn btn-main" id="save_password">{'Submit'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{* API KEY *}
|
||||
<section id="apikey-section" class="profile-section">
|
||||
<div class="title">
|
||||
<div class="column-flex">
|
||||
<h1>{'API Keys'|translate}</h1>
|
||||
<p>{'Create API Keys to secure your acount'|translate}</p>
|
||||
</div>
|
||||
<i class="gallery-icon-up-open display-btn close" data-display="apikey-display"></i>
|
||||
</div>
|
||||
|
||||
<div class="form" id="apikey-display">
|
||||
<div class="api-cant-manage" id="cant_manage_api">
|
||||
<p>{'To manage your API keys, please log in with your username/password.'|translate|escape:html}</p>
|
||||
</div>
|
||||
|
||||
<div class="new-apikey can-manage">
|
||||
<button class="btn btn-main" id="new_apikey">{'New API Key'|translate}</button>
|
||||
</div>
|
||||
<div class="api-list can-manage">
|
||||
<div class="api-list-head api-tab">
|
||||
<div aria-hidden="true"></div>
|
||||
<p>{'API Key name'|translate}</p>
|
||||
<p>{'Created at'|translate}</p>
|
||||
<p>{'Last use'|translate}</p>
|
||||
<p id="api_expires_in">{'Expires in'|translate}</p>
|
||||
<div aria-hidden="true"></div>
|
||||
</div>
|
||||
<div class="api-list-body" id="api_key_list">
|
||||
|
||||
<div class="api-tab-line border-line template-api" id="api_line">
|
||||
<div class="api-icon-collapse">
|
||||
<i class="gallery-icon-up-open icon-collapse close" data-api=""></i>
|
||||
</div>
|
||||
<p class="api_name"></p>
|
||||
<p class="api_creation"></p>
|
||||
<p class="api_last_use"></p>
|
||||
<p class="api_expiration"></p>
|
||||
<div class="api-icon-action row-flex" data-api="" data-pkid="">
|
||||
<i class="icon-pencil edit-mode"></i>
|
||||
<i class="icon-trash-1 delete-mode"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="api-tab-collapse border-line template-api" style="display: none;" id="api_collapse">
|
||||
<div aria-hidden="true"></div>
|
||||
<div class="keys">
|
||||
<div class="row-flex key">
|
||||
<i class="gallery-icon-hash"></i>
|
||||
<p class="api_key"></p>
|
||||
<i class="icon-clone" data-copy="" data-success=""></i>
|
||||
<p id="" class="api-copy api-hide success-message">{"Public key copied."|translate|escape:html}</p>
|
||||
</div>
|
||||
<div class="row-flex key">
|
||||
<i class="icon-key"></i>
|
||||
<p>{"The secret key can no longer be displayed."|translate}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="new-apikey">
|
||||
<button class="btn btn-link" id="show_expired_list" data-show="false">{'Show expired keys'|translate}</button>
|
||||
</div>
|
||||
<div class="api-list-body" id="api_key_list_expired">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* API KEY MODAL *}
|
||||
<div class="bg-modal" id="api_modal">
|
||||
<div class="body-modal">
|
||||
<a class="icon-cancel close-modal" id="close_api_modal"></a>
|
||||
|
||||
<div id="generate_keyapi">
|
||||
<div class="head-modal">
|
||||
<p class="title-modal">{'Generate API Key'|translate}</p>
|
||||
<p class="subtitle-modal">{'Create a new API key to secure your account.'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="column-flex first">
|
||||
<label for="api_key_name">{'API Key name'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-key"></i>
|
||||
<input type="text" id="api_key_name" />
|
||||
</div>
|
||||
<p id="error_api_key_name" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="row-flex section-expiration">
|
||||
<div class="column-flex">
|
||||
<label>{'Duration'|translate}</label>
|
||||
<div class="row-flex input-container api-expiration">
|
||||
<i class="gallery-icon-calendar"></i>
|
||||
{html_options name=api_expiration options=$API_EXPIRATION}
|
||||
</div>
|
||||
<p id="error_api_key_date" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'you must choose a date'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex" id="api_custom_date">
|
||||
<label for="api_expiration_date">{'Custom date'|translate}</label>
|
||||
<div class="row-flex input-container api-expiration">
|
||||
<input type="date" id="api_expiration_date" name="api_expiration_custom" min="{$API_CURRENT_DATE}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="api-mail-infos">{$API_EMAIL_INFOS}</p>
|
||||
|
||||
<div class="save">
|
||||
<button class="btn btn-cancel" id="cancel_apikey">{'Cancel'|translate}</button>
|
||||
<button class="btn btn-main" id="save_apikey">{'Generate key'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="retrieves_keyapi">
|
||||
<div class="head-modal">
|
||||
<p class="title-modal">{'Generate API Key'|translate}</p>
|
||||
<p class="subtitle-modal">{'Save your secret Key and ID'|translate}</p>
|
||||
<p class="modal-secret">{'This will not be displayed again. You must copy it to continue.'|translate}
|
||||
<p>
|
||||
</div>
|
||||
|
||||
<div class="modal-input-keys">
|
||||
<p id="api_id_copy_success" class="api-copy api-hide success-message">
|
||||
{"Public key copied."|translate|escape:html}</p>
|
||||
</div>
|
||||
<div class="input-modal input-modal-id row-flex">
|
||||
<i class="gallery-icon-hash"></i>
|
||||
<input type="text" id="api_id_key" />
|
||||
<i class="icon-clone" id="api_id_copy"></i>
|
||||
</div>
|
||||
|
||||
<div class="modal-input-keys">
|
||||
<p id="api_key_copy_success" class="modal-input-key api-copy api-hide success-message">
|
||||
{"Secret key copied. Keep it in a safe place."|translate|escape:html}</p>
|
||||
</div>
|
||||
<div class="input-modal input-modal-key row-flex">
|
||||
<i class="icon-key"></i>
|
||||
<input type="text" id="api_secret_key" />
|
||||
<i class="icon-clone" id="api_secret_copy"></i>
|
||||
</div>
|
||||
|
||||
<div class="save">
|
||||
<button class="btn btn-main" id="done_apikey" disabled>{'Done'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* API KEY MODAL EDIT *}
|
||||
<div class="bg-modal" id="api_modal_edit">
|
||||
<div class="body-modal">
|
||||
<a class="icon-cancel close-modal" id="close_api_modal_edit"></a>
|
||||
|
||||
<div>
|
||||
<div class="head-modal">
|
||||
<p class="title-modal">{'Edit API Key'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="column-flex first">
|
||||
<label for="api_key_edit">{'API Key name'|translate}</label>
|
||||
<div class="row-flex input-container">
|
||||
<i class="icon-key"></i>
|
||||
<input type="text" id="api_key_edit" />
|
||||
</div>
|
||||
<p id="error_api_key_edit" class="error-message"><i class="gallery-icon-attention-circled"></i>
|
||||
{'must not be empty'|translate}</p>
|
||||
</div>
|
||||
|
||||
<div class="save">
|
||||
<button class="btn btn-main" id="save_api_edit">{'Save'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{* API KEY MODAL REVOKE *}
|
||||
<div class="bg-modal" id="api_modal_revoke">
|
||||
<div class="body-modal">
|
||||
<a class="icon-cancel close-modal" id="close_api_modal_revoke"></a>
|
||||
|
||||
<div>
|
||||
<div class="head-modal">
|
||||
<p class="title-modal" id="api_modal_revoke_title"></p>
|
||||
</div>
|
||||
|
||||
<div class="save">
|
||||
<button class="btn btn-cancel" id="cancel_api_revoke">{'Cancel'|translate}</button>
|
||||
<button class="btn btn-main btn-revoked" id="revoke_api_key">{'Revoke'|translate}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{if isset($PLUGINS_PROFILE)}
|
||||
{foreach from=$PLUGINS_PROFILE item=plugin_block key=k_block}
|
||||
<section id="{$k_block}-section" class="profile-section">
|
||||
<div class="title">
|
||||
<div class="column-flex">
|
||||
<h1>{$plugin_block.name}</h1>
|
||||
<p>{$plugin_block.desc}</p>
|
||||
</div>
|
||||
<i class="gallery-icon-up-open display-btn close" data-display="{$k_block}-display"></i>
|
||||
</div>
|
||||
<div class="form plugins" id="{$k_block}-display">
|
||||
{include file=$plugin_block.template}
|
||||
{if $plugin_block.standard_show_save}
|
||||
<div class="save">
|
||||
<button class="btn btn-main" id="save_{$k_block}">{'Submit'|translate}</button>
|
||||
</div>
|
||||
{footer_script}
|
||||
standardSaveSelector.push('#save_{$k_block}');
|
||||
{/footer_script}
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
{/foreach}
|
||||
{/if}
|
||||
|
||||
{if count($language_options) > 1}
|
||||
<section id="language-switch">
|
||||
<div id="lang-select">
|
||||
<span id="other-languages">
|
||||
{foreach from=$language_options key=$code item=$lang}
|
||||
<span id="lang={$code}" onclick="setCookie('lang','{$code}',30)">{$lang}</span>
|
||||
{/foreach}
|
||||
</span>
|
||||
<div id="selected-language-container">
|
||||
<i class="gallery-icon-left-chevron"></i><span
|
||||
id="selected-language">{$language_options[$current_language]}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
{include file='toaster.tpl'}
|
||||
</container>
|
||||
59
themes/standard_pages/template/toaster.tpl
Normal file
59
themes/standard_pages/template/toaster.tpl
Normal file
@@ -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}
|
||||
<div class="toaster" id="pwg_toaster">
|
||||
<div class="toast template" id="toast_template">
|
||||
<i class="toast_icon"></i>
|
||||
<p class="toast_text"></p>
|
||||
</div>
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load Diff
19
tools/ws.htm
19
tools/ws.htm
@@ -84,6 +84,21 @@
|
||||
|
||||
|
||||
<form id="methodWrapper" style="display:none;">
|
||||
<div class="card">
|
||||
<h3 class="card-title"><i class="icon-cog-alt"></i>Authenticate with API Key (Header)</h3>
|
||||
<div class="card-content">
|
||||
<p class="header-description"> Introduced in Piwigo 16, you can now use an API key in the HTTP header
|
||||
to perform authenticated requests without a user session.
|
||||
For more details, check out our <a href="https://github.com/Piwigo/Piwigo/wiki/Piwigo-Web-API#api-key-authentication" target="_blank">documentation</a>.
|
||||
</p>
|
||||
<p class="header-warning">Doesn't work when you use "INVOKE (new window)"</p>
|
||||
<div class="header-setting">
|
||||
<p>Authorization:</p>
|
||||
<input type="text" id="apiKey" placeholder="pkid-xxxxxxxx-xxxxxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- methodHeader -->
|
||||
|
||||
<div class="card" id="methodDescription" style="display:none;">
|
||||
<h3 class="card-title"><i class="icon-book"></i>Description</h3>
|
||||
<blockquote>
|
||||
@@ -134,7 +149,7 @@
|
||||
<div class="select">
|
||||
<select id="responseFormat">
|
||||
<option value="json" selected>JSON</option>
|
||||
<option value="rest" selected>REST (xml)</option>
|
||||
<option value="rest">REST (xml)</option>
|
||||
<option value="php">PHP serial</option>
|
||||
<option value="xmlrpc">XML RPC</option>
|
||||
</select>
|
||||
@@ -187,7 +202,7 @@
|
||||
</div> <!-- the_container -->
|
||||
|
||||
<div id="the_footer">
|
||||
Copyright © 2002-2021 <a href="http://piwigo.org">Piwigo Team</a>
|
||||
Copyright © 2002-2025 <a href="http://piwigo.org">Piwigo Team</a>
|
||||
</div> <!-- the_footer -->
|
||||
</div>
|
||||
|
||||
|
||||
@@ -349,6 +349,29 @@ input[type="text"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-setting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-setting input {
|
||||
max-width: 580px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-description {
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-warning {
|
||||
margin: 0;
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* #requestResultDisplay {
|
||||
background: white;
|
||||
} */
|
||||
|
||||
103
tools/ws/ws.js
103
tools/ws/ws.js
@@ -93,7 +93,8 @@ $(() => {
|
||||
url: ws_url,
|
||||
data: { format: "json", method: "reflection.getMethodList" }
|
||||
}).done(function (result) {
|
||||
console.log(result);
|
||||
// for debug
|
||||
//console.log(result);
|
||||
result = parsePwgJSON(result);
|
||||
|
||||
if (result != null) {
|
||||
@@ -328,6 +329,8 @@ $(() => {
|
||||
// invoke method
|
||||
function invokeMethod(methodName, newWindow) {
|
||||
|
||||
$('#json-viewer').jsonViewer({});
|
||||
|
||||
$('#requestURLDisplay').show();
|
||||
$('#requestResultDisplay').show();
|
||||
|
||||
@@ -335,6 +338,18 @@ $(() => {
|
||||
|
||||
let reqUrl = ws_url + "?format=" + $("#responseFormat").val();
|
||||
|
||||
const isJson = $("#responseFormat").val() === 'json';
|
||||
const authorization = $('#apiKey').val();
|
||||
const useCookie = '' === authorization;
|
||||
|
||||
let fetchOption = {};
|
||||
if (!useCookie) {
|
||||
fetchOption.credentials = 'omit';
|
||||
fetchOption.headers = {
|
||||
Authorization: authorization
|
||||
}
|
||||
}
|
||||
|
||||
// GET
|
||||
if ($("#requestFormat").val() == 'get') {
|
||||
reqUrl += "&method=" + methodName;
|
||||
@@ -361,19 +376,11 @@ $(() => {
|
||||
window.open(reqUrl);
|
||||
}
|
||||
else {
|
||||
if ($("#responseFormat").val() === 'json') {
|
||||
$("#invokeFrame").hide();
|
||||
$('#json-viewer').show();
|
||||
fetch(reqUrl)
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
$('#json-viewer').jsonViewer(json);
|
||||
})
|
||||
} else {
|
||||
$("#invokeFrame").show();
|
||||
$('#json-viewer').hide();
|
||||
$("#invokeFrame").attr('src', reqUrl);
|
||||
}
|
||||
fetch(reqUrl, fetchOption)
|
||||
.then(data => data.text())
|
||||
.then(data => {
|
||||
showResponseData(data);
|
||||
})
|
||||
}
|
||||
|
||||
$('#requestURLDisplay').find('.url').html(reqUrl).end()
|
||||
@@ -411,22 +418,30 @@ $(() => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!newWindow && $("#responseFormat").val() === 'json') {
|
||||
if (!newWindow) {
|
||||
$("#invokeFrame").hide();
|
||||
$('#json-viewer').show();
|
||||
jQuery.ajax({
|
||||
url: reqUrl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
"method": methodName,
|
||||
...params
|
||||
},
|
||||
success : function(data) {
|
||||
$('#json-viewer').jsonViewer(data);
|
||||
}
|
||||
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('method', methodName);
|
||||
for (const key in params) {
|
||||
formData.append(key, params[key]);
|
||||
}
|
||||
|
||||
fetchOption.headers ??= {};
|
||||
fetchOption.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
|
||||
fetch(reqUrl, {
|
||||
...fetchOption,
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(data => {
|
||||
return data.text();
|
||||
})
|
||||
.then(data => {
|
||||
showResponseData(data);
|
||||
});
|
||||
} else {
|
||||
$("#invokeFrame").show();
|
||||
$('#json-viewer').hide();
|
||||
@@ -490,4 +505,36 @@ $(() => {
|
||||
$('#the_body').addClass('dark-mode');
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function showResponseData(data) {
|
||||
const isJson = $("#responseFormat").val() === 'json';
|
||||
if (isJson) {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
$('#json-viewer').jsonViewer(json);
|
||||
$("#invokeFrame").hide();
|
||||
$('#json-viewer').show();
|
||||
} catch (error) {
|
||||
const iframe = $('#invokeFrame');
|
||||
const iframeDoc = iframe[0].contentDocument || iframe[0].contentWindow.document;
|
||||
|
||||
iframeDoc.open();
|
||||
iframeDoc.write(`<pre>${data}</pre>`);
|
||||
iframeDoc.close();
|
||||
$("#invokeFrame").show();
|
||||
$('#json-viewer').hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const iframe = $('#invokeFrame');
|
||||
const iframeDoc = iframe[0].contentDocument || iframe[0].contentWindow.document;
|
||||
|
||||
iframeDoc.open();
|
||||
iframeDoc.write(data);
|
||||
iframeDoc.close();
|
||||
|
||||
$("#invokeFrame").show();
|
||||
$('#json-viewer').hide();
|
||||
|
||||
}
|
||||
81
ws.php
81
ws.php
@@ -173,7 +173,7 @@ function ws_addDefaultMethods( $arr )
|
||||
array_merge(array(
|
||||
'types' => array('default'=>null,
|
||||
'flags'=>WS_PARAM_FORCE_ARRAY,
|
||||
'info'=>'square, thumb, 2small, xsmall, small, medium, large, xlarge, xxlarge'),
|
||||
'info'=>'square, thumb, 2small, xsmall, small, medium, large, xlarge, xxlarge, 3xlarge, 4xlarge'),
|
||||
'ids' => array('default'=>null,
|
||||
'flags'=>WS_PARAM_FORCE_ARRAY,
|
||||
'type'=>WS_TYPE_ID),
|
||||
@@ -1238,6 +1238,33 @@ enabled_high, registration_date, registration_date_string, registration_date_sin
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>true, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.users.setMyInfo',
|
||||
'ws_users_setMyInfo',
|
||||
array(
|
||||
'email' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'nb_image_page' => array('flags'=>WS_PARAM_OPTIONAL,
|
||||
'type'=>WS_TYPE_INT|WS_TYPE_POSITIVE|WS_TYPE_NOTNULL),
|
||||
'theme' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'language' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'recent_period' => array('flags'=>WS_PARAM_OPTIONAL,
|
||||
'type'=>WS_TYPE_INT|WS_TYPE_POSITIVE),
|
||||
'expand' => array('flags'=>WS_PARAM_OPTIONAL,
|
||||
'type'=>WS_TYPE_BOOL),
|
||||
'show_nb_comments' => array('flags'=>WS_PARAM_OPTIONAL,
|
||||
'type'=>WS_TYPE_BOOL),
|
||||
'show_nb_hits' => array('flags'=>WS_PARAM_OPTIONAL,
|
||||
'type'=>WS_TYPE_BOOL),
|
||||
'password' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'new_password' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'conf_new_password' => array('flags'=>WS_PARAM_OPTIONAL),
|
||||
'pwg_token' => array(),
|
||||
),
|
||||
'',
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>false, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.permissions.getList',
|
||||
@@ -1540,6 +1567,58 @@ enabled_high, registration_date, registration_date_string, registration_date_sin
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>true, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.users.api_key.create',
|
||||
'ws_create_api_key',
|
||||
array(
|
||||
'key_name' => array(),
|
||||
'duration' => array(
|
||||
'type' => WS_TYPE_INT|WS_TYPE_POSITIVE,
|
||||
'info' => 'Number of days',
|
||||
),
|
||||
'pwg_token' => array(),
|
||||
),
|
||||
'Create a new api key for the user in the current session',
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>false, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.users.api_key.revoke',
|
||||
'ws_revoke_api_key',
|
||||
array(
|
||||
'pkid' => array(),
|
||||
'pwg_token' => array(),
|
||||
),
|
||||
'Revoke a api key for the user in the current session',
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>false, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.users.api_key.edit',
|
||||
'ws_edit_api_key',
|
||||
array(
|
||||
'key_name' => array(),
|
||||
'pkid' => array(),
|
||||
'pwg_token' => array(),
|
||||
),
|
||||
'Edit a api key for the user in the current session',
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>false, 'post_only'=>true)
|
||||
);
|
||||
|
||||
$service->addMethod(
|
||||
'pwg.users.api_key.get',
|
||||
'ws_get_api_key',
|
||||
array(
|
||||
'pwg_token' => array(),
|
||||
),
|
||||
'Get all api key for the user in the current session',
|
||||
$ws_functions_root . 'pwg.users.php',
|
||||
array('admin_only'=>false, 'post_only'=>true)
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user