This commit is contained in:
+10
-5
@@ -12,6 +12,8 @@ class Controller extends PhpObject
|
|||||||
'add_post',
|
'add_post',
|
||||||
'subscribe',
|
'subscribe',
|
||||||
'unsubscribe',
|
'unsubscribe',
|
||||||
|
'login',
|
||||||
|
'logout',
|
||||||
'update_project',
|
'update_project',
|
||||||
'upload',
|
'upload',
|
||||||
'add_comment',
|
'add_comment',
|
||||||
@@ -55,11 +57,15 @@ class Controller extends PhpObject
|
|||||||
$this->setReqVal('value', $asReq['value'] ?? '');
|
$this->setReqVal('value', $asReq['value'] ?? '');
|
||||||
$this->setReqVal('type', $asReq['type'] ?? '');
|
$this->setReqVal('type', $asReq['type'] ?? '');
|
||||||
$this->setReqVal('email', $asReq['email'] ?? '');
|
$this->setReqVal('email', $asReq['email'] ?? '');
|
||||||
|
$this->setReqVal('password', $asReq['password'] ?? '');
|
||||||
$this->setReqVal('latitude', $asReq['latitude'] ?? '');
|
$this->setReqVal('latitude', $asReq['latitude'] ?? '');
|
||||||
$this->setReqVal('longitude', $asReq['longitude'] ?? '');
|
$this->setReqVal('longitude', $asReq['longitude'] ?? '');
|
||||||
$this->setReqVal('timestamp', $asReq['timestamp'] ?? 0, 'positiveInt');
|
$this->setReqVal('timestamp', $asReq['timestamp'] ?? 0, 'positiveInt');
|
||||||
$this->setReqVal('csrf_token', $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ($_POST['csrf_token'] ?? ''));
|
$this->setReqVal('csrf_token', $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ($_POST['csrf_token'] ?? ''));
|
||||||
|
|
||||||
|
//Authentication and CSRF protection share the same server-side session.
|
||||||
|
$this->initCsrfToken();
|
||||||
|
|
||||||
//Create Spot Instance
|
//Create Spot Instance
|
||||||
$this->oSpot = new Spot($sProcessPage, $this->asReq['t']);
|
$this->oSpot = new Spot($sProcessPage, $this->asReq['t']);
|
||||||
$this->oSpot->setProjectId($this->asReq['id_project']);
|
$this->oSpot->setProjectId($this->asReq['id_project']);
|
||||||
@@ -72,6 +78,7 @@ class Controller extends PhpObject
|
|||||||
//Clean errors
|
//Clean errors
|
||||||
$sDebug = ob_get_clean();
|
$sDebug = ob_get_clean();
|
||||||
if($sDebug != '') $this->oSpot->addUncaughtError($sDebug);
|
if($sDebug != '') $this->oSpot->addUncaughtError($sDebug);
|
||||||
|
if(session_status() === PHP_SESSION_ACTIVE) session_write_close();
|
||||||
|
|
||||||
return $sResult;
|
return $sResult;
|
||||||
}
|
}
|
||||||
@@ -103,16 +110,13 @@ class Controller extends PhpObject
|
|||||||
{
|
{
|
||||||
if(PHP_SAPI === 'cli') return;
|
if(PHP_SAPI === 'cli') return;
|
||||||
|
|
||||||
$bCloseSession = false;
|
|
||||||
if(session_status() !== PHP_SESSION_ACTIVE) {
|
if(session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
$bSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
|
$bSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
|
||||||
session_set_cookie_params(array('httponly' => true, 'secure' => $bSecure, 'samesite' => 'Lax'));
|
session_set_cookie_params(array('httponly' => true, 'secure' => $bSecure, 'samesite' => 'Lax'));
|
||||||
session_start();
|
session_start();
|
||||||
$bCloseSession = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setCsrfToken();
|
$this->setCsrfToken();
|
||||||
if($bCloseSession) session_write_close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkCsrfToken(string $sClientToken): bool
|
private function checkCsrfToken(string $sClientToken): bool
|
||||||
@@ -130,9 +134,10 @@ class Controller extends PhpObject
|
|||||||
'next_feed' => $this->oSpot->getNextFeed($this->asReq['id']),
|
'next_feed' => $this->oSpot->getNextFeed($this->asReq['id']),
|
||||||
'new_feed' => $this->oSpot->getNewFeed($this->asReq['id']),
|
'new_feed' => $this->oSpot->getNewFeed($this->asReq['id']),
|
||||||
'add_post' => $this->oSpot->addPost($this->asReq['name'], $this->asReq['content']),
|
'add_post' => $this->oSpot->addPost($this->asReq['name'], $this->asReq['content']),
|
||||||
'subscribe' => $this->oSpot->subscribe($this->asReq['email'], $this->asReq['name']),
|
'subscribe' => $this->oSpot->subscribe(),
|
||||||
'unsubscribe' => $this->oSpot->unsubscribe(),
|
'unsubscribe' => $this->oSpot->unsubscribe(),
|
||||||
'unsubscribe_email' => $this->oSpot->unsubscribeFromEmail($this->asReq['id_entity']),
|
'login' => $this->oSpot->login($this->asReq['email'], $this->asReq['password'], $this->asReq['name']),
|
||||||
|
'logout' => $this->oSpot->logout(),
|
||||||
'update_project' => $this->oSpot->updateProject(),
|
'update_project' => $this->oSpot->updateProject(),
|
||||||
default => $this->dispatchAdmin($sAction)
|
default => $this->dispatchAdmin($sAction)
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-28
@@ -76,7 +76,7 @@ class Spot extends Main
|
|||||||
'email' => 'admin@admin.com',
|
'email' => 'admin@admin.com',
|
||||||
'language' => self::DEFAULT_LANG,
|
'language' => self::DEFAULT_LANG,
|
||||||
'timezone' => date_default_timezone_get(),
|
'timezone' => date_default_timezone_get(),
|
||||||
'active' => User::USER_ACTIVE,
|
'subscribed'=> User::USER_SUBSCRIBED,
|
||||||
'clearance' => User::CLEARANCE_ADMIN
|
'clearance' => User::CLEARANCE_ADMIN
|
||||||
));
|
));
|
||||||
$this->oUser->setUserId($iUserId);
|
$this->oUser->setUserId($iUserId);
|
||||||
@@ -94,13 +94,12 @@ class Spot extends Main
|
|||||||
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to'),
|
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to'),
|
||||||
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), Db::getId(User::USER_TABLE), 'name', 'content', 'site_time', 'timezone'),
|
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), Db::getId(User::USER_TABLE), 'name', 'content', 'site_time', 'timezone'),
|
||||||
Media::MEDIA_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'type', 'taken_on', 'posted_on', 'timezone', 'latitude', 'longitude', 'altitude', 'width', 'height', 'rotate', 'comment'),
|
Media::MEDIA_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'type', 'taken_on', 'posted_on', 'timezone', 'latitude', 'longitude', 'altitude', 'width', 'height', 'rotate', 'comment'),
|
||||||
User::USER_TABLE => array('name', 'email', 'gravatar', 'language', 'timezone', 'active', 'clearance'),
|
User::USER_TABLE => array('name', 'email', 'password', 'token', 'token_exp', 'gravatar', 'language', 'timezone', 'subscribed', 'clearance'),
|
||||||
Map::MAP_TABLE => array('codename', 'pattern', 'token', 'tile_size', 'min_zoom', 'max_zoom', 'attribution'),
|
Map::MAP_TABLE => array('codename', 'pattern', 'token', 'tile_size', 'min_zoom', 'max_zoom', 'attribution'),
|
||||||
Map::MAPPING_TABLE => array(Db::getId(Map::MAP_TABLE) , Db::getId(Project::PROJ_TABLE))
|
Map::MAPPING_TABLE => array(Db::getId(Map::MAP_TABLE) , Db::getId(Project::PROJ_TABLE))
|
||||||
),
|
),
|
||||||
'types' => array
|
'types' => array
|
||||||
(
|
(
|
||||||
'active' => "BOOLEAN DEFAULT ".User::USER_INACTIVE,
|
|
||||||
'clearance' => "TINYINT(1) DEFAULT ".User::CLEARANCE_USER,
|
'clearance' => "TINYINT(1) DEFAULT ".User::CLEARANCE_USER,
|
||||||
'active_from' => "TIMESTAMP DEFAULT 0",
|
'active_from' => "TIMESTAMP DEFAULT 0",
|
||||||
'active_to' => "TIMESTAMP DEFAULT 0",
|
'active_to' => "TIMESTAMP DEFAULT 0",
|
||||||
@@ -120,6 +119,7 @@ class Spot extends Main
|
|||||||
'model' => "VARCHAR(20)",
|
'model' => "VARCHAR(20)",
|
||||||
'name' => "VARCHAR(100)",
|
'name' => "VARCHAR(100)",
|
||||||
'pattern' => "VARCHAR(200) NOT NULL",
|
'pattern' => "VARCHAR(200) NOT NULL",
|
||||||
|
'password' => "VARCHAR(255) NOT NULL DEFAULT ''",
|
||||||
'posted_on' => "TIMESTAMP DEFAULT 0",
|
'posted_on' => "TIMESTAMP DEFAULT 0",
|
||||||
'ref_feed_id' => "VARCHAR(40)",
|
'ref_feed_id' => "VARCHAR(40)",
|
||||||
'ref_msg_id' => "VARCHAR(15)",
|
'ref_msg_id' => "VARCHAR(15)",
|
||||||
@@ -127,9 +127,11 @@ class Spot extends Main
|
|||||||
'rotate' => "SMALLINT",
|
'rotate' => "SMALLINT",
|
||||||
'site_time' => "TIMESTAMP DEFAULT 0", //DEFAULT 0 removes auto-set to current time
|
'site_time' => "TIMESTAMP DEFAULT 0", //DEFAULT 0 removes auto-set to current time
|
||||||
'status' => "VARCHAR(10)",
|
'status' => "VARCHAR(10)",
|
||||||
|
'subscribed' => "BOOLEAN DEFAULT ".User::USER_UNSUBSCRIBED,
|
||||||
'taken_on' => "TIMESTAMP DEFAULT 0",
|
'taken_on' => "TIMESTAMP DEFAULT 0",
|
||||||
'timezone' => "CHAR(64) NOT NULL", //see mysql.time_zone_name
|
'timezone' => "CHAR(64) NOT NULL", //see mysql.time_zone_name
|
||||||
'token' => "VARCHAR(4096)",
|
'token' => "VARCHAR(4096)",
|
||||||
|
'token_exp' => "TIMESTAMP DEFAULT 0",
|
||||||
'type' => "VARCHAR(20)",
|
'type' => "VARCHAR(20)",
|
||||||
'unix_time' => "INT",
|
'unix_time' => "INT",
|
||||||
'min_zoom' => "TINYINT UNSIGNED",
|
'min_zoom' => "TINYINT UNSIGNED",
|
||||||
@@ -282,7 +284,7 @@ class Spot extends Main
|
|||||||
|
|
||||||
private function sendEmail() {
|
private function sendEmail() {
|
||||||
$oEmail = new Email($this->asContext['serv_name'], 'email.update');
|
$oEmail = new Email($this->asContext['serv_name'], 'email.update');
|
||||||
$oEmail->setDestInfo($this->oUser->getActiveUsersInfo());
|
$oEmail->setDestInfo($this->oUser->getSubscribedUsersInfo());
|
||||||
|
|
||||||
//Add Position
|
//Add Position
|
||||||
$asSpotMessages = $this->getSpotMessages(array($this->oProject->getLastMessageId($this->getFeedConstraints(Feed::MSG_TABLE))));
|
$asSpotMessages = $this->getSpotMessages(array($this->oProject->getLastMessageId($this->getFeedConstraints(Feed::MSG_TABLE))));
|
||||||
@@ -377,32 +379,32 @@ class Spot extends Main
|
|||||||
return self::getJsonResult(true, '', $asLastUpdate);
|
return self::getJsonResult(true, '', $asLastUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscribe($sEmail, $sNickName) {
|
public function login($sEmail, $sPassword, $sNickName) {
|
||||||
$asResult = $this->oUser->addUser($sEmail, $this->oLang->getLanguage(), date_default_timezone_get(), $sNickName);
|
$asResult = $this->oUser->login($sEmail, $sPassword, $this->oLang->getLanguage(), date_default_timezone_get(), $sNickName);
|
||||||
$asUserInfo = $this->oUser->getUserInfo();
|
|
||||||
|
|
||||||
//Send Confirmation Email
|
if($asResult['result'] && $asResult['desc'] == 'subscribe_user') return $this->subscribe();
|
||||||
if($asResult['result'] && $asResult['desc']=='lang:newsletter.subscribed' && !Settings::DEBUG) {
|
else return self::getJsonResult($asResult['result'], $asResult['desc'], $this->oUser->getUserInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout() {
|
||||||
|
$asResult = $this->oUser->logout();
|
||||||
|
return self::getJsonResult($asResult['result'], $asResult['desc'], User::DEFAULT_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subscribe() {
|
||||||
|
$asResult = $this->oUser->setSubscription(true);
|
||||||
|
$asUserInfo = $this->oUser->getUserInfo();
|
||||||
|
if($asResult['result'] && !Settings::DEBUG) {
|
||||||
$oConfEmail = new Email($this->asContext['serv_name'], 'email.confirmation');
|
$oConfEmail = new Email($this->asContext['serv_name'], 'email.confirmation');
|
||||||
$oConfEmail->setDestInfo($asUserInfo);
|
$oConfEmail->setDestInfo($asUserInfo);
|
||||||
$oConfEmail->send();
|
$oConfEmail->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::getJsonResult($asResult['result'], $asResult['desc'], $asUserInfo);
|
return self::getJsonResult($asResult['result'], $asResult['desc'], $asUserInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unsubscribe() {
|
public function unsubscribe() {
|
||||||
$asResult = $this->oUser->removeUser();
|
$asResult = $this->oUser->setSubscription(false);
|
||||||
return self::getJsonResult($asResult['result'], $asResult['desc'], User::DEFAULT_USER);
|
return self::getJsonResult($asResult['result'], $asResult['desc'], $this->oUser->getUserInfo());
|
||||||
}
|
|
||||||
|
|
||||||
public function unsubscribeFromEmail($iUserId) {
|
|
||||||
$this->oUser->setUserId($iUserId);
|
|
||||||
$this->oLang->setLanguage($this->oUser->getLang(), self::DEFAULT_LANG);
|
|
||||||
$asResult = $this->oUser->removeUser();
|
|
||||||
|
|
||||||
$sDesc = explode(':', $asResult['desc'])[1];
|
|
||||||
return $this->oLang->getTranslation($sDesc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSpotMessages($asMsgIds=array())
|
private function getSpotMessages($asMsgIds=array())
|
||||||
@@ -726,7 +728,7 @@ class Spot extends Main
|
|||||||
'project' => $this->oProject->getProjects(),
|
'project' => $this->oProject->getProjects(),
|
||||||
'feed' => $oFeed->getFeeds(),
|
'feed' => $oFeed->getFeeds(),
|
||||||
'spot' => $oFeed->getSpots(),
|
'spot' => $oFeed->getSpots(),
|
||||||
'user' => $this->oUser->getActiveUsersInfo()
|
'user' => $this->oUser->getSubscribedUsersInfo()
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach($asData['project'] as &$asProject) {
|
foreach($asData['project'] as &$asProject) {
|
||||||
@@ -794,7 +796,7 @@ class Spot extends Main
|
|||||||
default:
|
default:
|
||||||
$sDesc = $this->oLang->getTranslation('error.unknown_field', $sField);
|
$sDesc = $this->oLang->getTranslation('error.unknown_field', $sField);
|
||||||
}
|
}
|
||||||
$asResult = $this->oUser->getActiveUserInfo($iId);
|
$asResult = $this->oUser->getUserById($iId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(!$bSuccess && $sDesc=='') $sDesc = Mask::LANG_PREFIX.'error.commit_db';
|
if(!$bSuccess && $sDesc=='') $sDesc = Mask::LANG_PREFIX.'error.commit_db';
|
||||||
@@ -852,11 +854,6 @@ class Spot extends Main
|
|||||||
$sDesc = $asResult['feed'][0]['desc'];
|
$sDesc = $asResult['feed'][0]['desc'];
|
||||||
$bSuccess = $asResult['feed'][0]['del'];
|
$bSuccess = $asResult['feed'][0]['del'];
|
||||||
break;
|
break;
|
||||||
case 'user':
|
|
||||||
$asResult = array('user' => array($this->oUser->removeUser($iId)));
|
|
||||||
$sDesc = $asResult['user'][0]['desc'];
|
|
||||||
$bSuccess = $asResult['user'][0]['result'];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::getJsonResult($bSuccess, $sDesc, $asResult);
|
return self::getJsonResult($bSuccess, $sDesc, $asResult);
|
||||||
|
|||||||
+197
-73
@@ -11,14 +11,17 @@ class User extends PhpObject {
|
|||||||
const USER_TABLE = 'users';
|
const USER_TABLE = 'users';
|
||||||
|
|
||||||
//Clearance Levels
|
//Clearance Levels
|
||||||
const USER_ACTIVE = 1;
|
|
||||||
const USER_INACTIVE = 0;
|
|
||||||
const CLEARANCE_USER = 0;
|
const CLEARANCE_USER = 0;
|
||||||
const CLEARANCE_ADMIN = 9;
|
const CLEARANCE_ADMIN = 9;
|
||||||
const CLEARANCES = array('user'=>self::CLEARANCE_USER, 'admin'=>self::CLEARANCE_ADMIN);
|
const CLEARANCES = array('user'=>self::CLEARANCE_USER, 'admin'=>self::CLEARANCE_ADMIN);
|
||||||
|
|
||||||
//Cookie
|
const USER_SUBSCRIBED = 1;
|
||||||
const COOKIE_ID_USER = 'subscriber';
|
const USER_UNSUBSCRIBED = 0;
|
||||||
|
|
||||||
|
//Session & Cookie
|
||||||
|
const SESSION_ID_USER = 'id_user';
|
||||||
|
const SESSION_ADMIN = 'admin_authenticated';
|
||||||
|
const COOKIE_TOKEN = 'login';
|
||||||
const COOKIE_DURATION = 60 * 60 * 24 * 365; //1 year
|
const COOKIE_DURATION = 60 * 60 * 24 * 365; //1 year
|
||||||
|
|
||||||
const DEFAULT_USER = array(
|
const DEFAULT_USER = array(
|
||||||
@@ -28,7 +31,7 @@ class User extends PhpObject {
|
|||||||
'email' => '',
|
'email' => '',
|
||||||
'language' => '',
|
'language' => '',
|
||||||
'timezone' => '',
|
'timezone' => '',
|
||||||
'active' => self::USER_INACTIVE,
|
'subscribed'=> self::USER_UNSUBSCRIBED,
|
||||||
'clearance' => self::CLEARANCE_USER
|
'clearance' => self::CLEARANCE_USER
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -46,8 +49,7 @@ class User extends PhpObject {
|
|||||||
parent::__construct(__CLASS__);
|
parent::__construct(__CLASS__);
|
||||||
$this->oDb = &$oDb;
|
$this->oDb = &$oDb;
|
||||||
$this->setUserId(0);
|
$this->setUserId(0);
|
||||||
$this->asUserInfo = self::DEFAULT_USER;
|
$this->checkSession();
|
||||||
$this->checkUserCookie();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserId() {
|
public function getUserId() {
|
||||||
@@ -56,9 +58,10 @@ class User extends PhpObject {
|
|||||||
|
|
||||||
public function setUserId($iUserId) {
|
public function setUserId($iUserId) {
|
||||||
$this->iUserId = 0;
|
$this->iUserId = 0;
|
||||||
|
$this->asUserInfo = self::DEFAULT_USER;
|
||||||
|
|
||||||
if($iUserId > 0) {
|
if($iUserId > 0) {
|
||||||
$asUser = $this->getActiveUserInfo($iUserId);
|
$asUser = $this->getUserById($iUserId);
|
||||||
if(!empty($asUser)) {
|
if(!empty($asUser)) {
|
||||||
$this->iUserId = $iUserId;
|
$this->iUserId = $iUserId;
|
||||||
$this->asUserInfo = $asUser;
|
$this->asUserInfo = $asUser;
|
||||||
@@ -70,27 +73,23 @@ class User extends PhpObject {
|
|||||||
return $this->asUserInfo;
|
return $this->asUserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActiveUserInfo($iUserId) {
|
public function getUserById($iUserId) {
|
||||||
$asUsersInfo = array();
|
$asUsersInfo = array();
|
||||||
if($iUserId > 0) $asUsersInfo = $this->getActiveUsersInfo($iUserId);
|
if($iUserId > 0) $asUsersInfo = $this->getUsersInfo($iUserId);
|
||||||
return empty($asUsersInfo)?array():array_shift($asUsersInfo);
|
return empty($asUsersInfo)?array():array_shift($asUsersInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActiveUsersInfo($iUserId=-1) {
|
public function getUsersInfo($iUserId=-1) {
|
||||||
|
|
||||||
//Mapping between user fields and DB fields
|
//Mapping between user fields and DB fields
|
||||||
$asSelect = array_keys($this->asUserInfo);
|
$asSelect = array_keys($this->asUserInfo);
|
||||||
$asSelect[array_search('id', $asSelect)] = Db::getId(self::USER_TABLE)." AS id";
|
$asSelect[array_search('id', $asSelect)] = Db::getId(self::USER_TABLE)." AS id";
|
||||||
|
|
||||||
//Non-admin cannot access clearance info
|
|
||||||
if(!$this->checkUserClearance(self::CLEARANCE_ADMIN)) unset($asSelect['clearance']);
|
|
||||||
|
|
||||||
$asInfo = array(
|
$asInfo = array(
|
||||||
'select' => $asSelect,
|
'select' => $asSelect,
|
||||||
'from' => self::USER_TABLE,
|
'from' => self::USER_TABLE
|
||||||
'constraint'=> array('active'=>self::USER_ACTIVE)
|
|
||||||
);
|
);
|
||||||
if($iUserId != -1) $asInfo['constraint'][Db::getId(self::USER_TABLE)] = $iUserId;
|
if($iUserId != -1) $asInfo['constraint'] = array(Db::getId(self::USER_TABLE) => $iUserId);
|
||||||
|
|
||||||
return $this->oDb->selectRows($asInfo);
|
return $this->oDb->selectRows($asInfo);
|
||||||
}
|
}
|
||||||
@@ -99,71 +98,116 @@ class User extends PhpObject {
|
|||||||
return $this->asUserInfo['language'];
|
return $this->asUserInfo['language'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addUser($sEmail, $sLang, $sTimezone, $sNickName='') {
|
private function addUser($sEmail, $sLang, $sTimezone, $sNickName='') {
|
||||||
$bSuccess = false;
|
$bSuccess = false;
|
||||||
$sDesc = '';
|
$sDesc = '';
|
||||||
$sEmail = trim($sEmail);
|
|
||||||
|
|
||||||
//Check Email availability
|
$iUserId = $this->oDb->insertRow(
|
||||||
$iUserId = $this->oDb->selectValue(self::USER_TABLE, Db::getId(self::USER_TABLE), array('email'=>$sEmail, 'active'=>self::USER_ACTIVE));
|
self::USER_TABLE,
|
||||||
|
array('email'=>$sEmail, 'language'=>$sLang, 'timezone'=>$sTimezone)
|
||||||
|
);
|
||||||
|
|
||||||
if($iUserId > 0) {
|
if($iUserId == 0) $sDesc = 'lang:error.commit_db';
|
||||||
//Just log user in
|
else $bSuccess = true;
|
||||||
$sDesc = 'lang:newsletter.email_exists';
|
|
||||||
$bSuccess = true;
|
//Extra optional values
|
||||||
|
if($bSuccess) {
|
||||||
|
$this->updateNickname($sNickName);
|
||||||
|
$this->updateGravatar($iUserId, $sEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Spot::getResult($bSuccess, $sDesc, [Db::getId(self::USER_TABLE) => $iUserId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubscription($bSubscribed) {
|
||||||
|
if($this->getUserId() > 0) {
|
||||||
|
$iSubscribed = $bSubscribed?1:0;
|
||||||
|
$iUserId = $this->oDb->updateRow(self::USER_TABLE, $this->getUserId(), array('subscribed'=>$iSubscribed));
|
||||||
|
if(!$iUserId) return Spot::getResult(false, 'lang:error.commit_db');
|
||||||
|
$this->asUserInfo['subscribed'] = $iSubscribed;
|
||||||
|
return Spot::getResult(true, $iSubscribed?'lang:account.subscribed':'lang:account.unsubscribed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscribedUsersInfo() {
|
||||||
|
$asSelect = array_keys($this->asUserInfo);
|
||||||
|
$asSelect[array_search('id', $asSelect)] = Db::getId(self::USER_TABLE).' AS id';
|
||||||
|
return $this->oDb->selectRows(array(
|
||||||
|
'select'=>$asSelect,
|
||||||
|
'from'=>self::USER_TABLE,
|
||||||
|
'constraint'=>array('subscribed'=>self::USER_SUBSCRIBED)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login($sEmail, $sPassword, $sLang, $sTimezone, $sNickName='') {
|
||||||
|
$bSuccess = false;
|
||||||
|
$sDesc = '';
|
||||||
|
$sEmail = strtolower(trim($sEmail));
|
||||||
|
|
||||||
|
//Check email value
|
||||||
|
if(!filter_var($sEmail, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$sDesc = 'lang:account.invalid_email';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//Add/Reactivate user
|
//Check Email presence in DB
|
||||||
$iUserId = $this->oDb->insertUpdateRow(
|
$asDBUser = $this->oDb->selectRow(
|
||||||
self::USER_TABLE,
|
self::USER_TABLE,
|
||||||
array('email'=>$sEmail, 'language'=>$sLang, 'timezone'=>$sTimezone, 'active'=>self::USER_ACTIVE),
|
array('email' => $sEmail),
|
||||||
array('email')
|
array(Db::getId(self::USER_TABLE), 'password', 'clearance')
|
||||||
);
|
);
|
||||||
|
$iUserId = $asDBUser[Db::getId(self::USER_TABLE)] ?? 0;
|
||||||
|
|
||||||
if($iUserId==0) $sDesc = 'lang:error.commit_db';
|
//User exists
|
||||||
|
if($iUserId > 0) {
|
||||||
|
//Is Admin
|
||||||
|
if($asDBUser['clearance'] >= self::CLEARANCE_ADMIN) {
|
||||||
|
//Request a password
|
||||||
|
if($sPassword === '') $sDesc = empty($asDBUser['password'])?'lang:account.set_password':'lang:account.password_required';
|
||||||
|
|
||||||
|
//Set password
|
||||||
|
elseif(empty($asDBUser['password'])) {
|
||||||
|
if(!$this->oDb->updateRow(self::USER_TABLE, $iUserId, array('password' => password_hash($sPassword, PASSWORD_DEFAULT)))) $sDesc = 'lang:error.commit_db';
|
||||||
|
else {
|
||||||
|
$sDesc = 'lang:account.password_set';
|
||||||
|
$bSuccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check password
|
||||||
|
elseif(password_verify($sPassword, $asDBUser['password'])) {
|
||||||
|
$bSuccess = true;
|
||||||
|
$sDesc = 'lang:account.logged_in';
|
||||||
|
}
|
||||||
|
else $sDesc = 'lang:account.invalid_credentials';
|
||||||
|
|
||||||
|
//die(print_r(['provided_pass'=>$sPassword, 'db_pass'=>$asDBUser['password']], true));
|
||||||
|
}
|
||||||
|
else $bSuccess = true;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$sDesc = 'lang:newsletter.subscribed';
|
//Unknown user, create it
|
||||||
$bSuccess = true;
|
$asAddResult = $this->addUser($sEmail, $sLang, $sTimezone, $sNickName);
|
||||||
|
$bSuccess = $asAddResult['result'];
|
||||||
|
$sDesc = $bSuccess?'subscribe_user':$asAddResult['desc'];
|
||||||
|
$iUserId = $asAddResult['data'][Db::getId(self::USER_TABLE)] ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($bSuccess) {
|
if($bSuccess) {
|
||||||
$this->setUserId($iUserId);
|
$this->setUserId($iUserId);
|
||||||
|
$this->setSession();
|
||||||
//Set Cookie (valid 1 year)
|
$this->setTokenCookie();
|
||||||
$this->updateCookie(self::COOKIE_DURATION);
|
|
||||||
|
|
||||||
//Update Nickname if user has already posted
|
|
||||||
$this->updateNickname($sNickName);
|
|
||||||
|
|
||||||
//Retrieve Gravatar image
|
|
||||||
$this->updateGravatar($iUserId, $sEmail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Spot::getResult($bSuccess, $sDesc);
|
return Spot::getResult($bSuccess, $sDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeUser($iUserId=0) {
|
public function logout() {
|
||||||
$iUserId = ($iUserId > 0)?$iUserId:$this->getUserId();
|
if($this->getUserId() > 0) $this->oDb->updateRow(self::USER_TABLE, $this->getUserId(), array('token' => '', 'token_exp' => '0000-00-00 00:00:00'));
|
||||||
$bSelf = ($iUserId == $this->getUserId());
|
$this->clearSession();
|
||||||
$bSuccess = false;
|
$this->clearCookie();
|
||||||
$sDesc = '';
|
$this->setUserId(0);
|
||||||
|
return Spot::getResult(true, 'lang:account.logged_out');
|
||||||
if($bSelf || $this->checkUserClearance(self::CLEARANCE_ADMIN)) {
|
|
||||||
if($this->getUserId() > 0) {
|
|
||||||
$iUserId = $this->oDb->updateRow(self::USER_TABLE, $iUserId, array('active' => self::USER_INACTIVE));
|
|
||||||
if($iUserId==0) $sDesc = 'lang:error.commit_db';
|
|
||||||
else {
|
|
||||||
$sDesc = 'lang:newsletter.unsubscribed';
|
|
||||||
if($bSelf) $this->updateCookie(-60 * 60); //Set Cookie in the past, deleting it
|
|
||||||
$bSuccess = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else $sDesc = 'lang:newsletter.unknown_email';
|
|
||||||
}
|
|
||||||
else $sDesc = 'lang:error.no_auth';
|
|
||||||
|
|
||||||
return Spot::getResult($bSuccess, $sDesc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateNickname($sNickname) {
|
public function updateNickname($sNickname) {
|
||||||
@@ -175,15 +219,6 @@ class User extends PhpObject {
|
|||||||
$this->oDb->updateRow(self::USER_TABLE, $iUserId, array('gravatar' => base64_encode($sImage)));
|
$this->oDb->updateRow(self::USER_TABLE, $iUserId, array('gravatar' => base64_encode($sImage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkUserCookie() {
|
|
||||||
if(isset($_COOKIE[self::COOKIE_ID_USER])){
|
|
||||||
$this->setUserId($_COOKIE[self::COOKIE_ID_USER]);
|
|
||||||
|
|
||||||
//Extend cookie life
|
|
||||||
if($this->getUserId() > 0) $this->updateCookie(self::COOKIE_DURATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkUserClearance($iClearance)
|
public function checkUserClearance($iClearance)
|
||||||
{
|
{
|
||||||
return ($this->asUserInfo['clearance'] >= $iClearance);
|
return ($this->asUserInfo['clearance'] >= $iClearance);
|
||||||
@@ -206,7 +241,96 @@ class User extends PhpObject {
|
|||||||
return Spot::getResult($bSuccess, $sDesc);
|
return Spot::getResult($bSuccess, $sDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateCookie($iDeltaTime) {
|
/* Session */
|
||||||
setcookie(self::COOKIE_ID_USER, ($iDeltaTime < 0)?'':$this->getUserId(), array('samesite' => 'Lax', 'expires' => time() + $iDeltaTime));
|
|
||||||
|
private function checkSession() {
|
||||||
|
$iUserId = (int) ($_SESSION[self::SESSION_ID_USER] ?? 0);
|
||||||
|
if($iUserId > 0) {
|
||||||
|
$this->setUserId($iUserId);
|
||||||
|
if($this->checkUserClearance(self::CLEARANCE_ADMIN) && empty($_SESSION[self::SESSION_ADMIN])) $this->logout();
|
||||||
|
}
|
||||||
|
else $this->checkTokenCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setSession() {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION[self::SESSION_ID_USER] = $this->getUserId();
|
||||||
|
$_SESSION[self::SESSION_ADMIN] = $this->checkUserClearance(self::CLEARANCE_ADMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clearSession() {
|
||||||
|
unset($_SESSION[self::SESSION_ID_USER], $_SESSION[self::SESSION_ADMIN]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cookie */
|
||||||
|
|
||||||
|
private function checkTokenCookie() {
|
||||||
|
$sCookieValue = $this->getCookie();
|
||||||
|
|
||||||
|
//Check cookie structure
|
||||||
|
$asToken = explode(':', $sCookieValue, 3);
|
||||||
|
$iUserId = $asToken[0];
|
||||||
|
if(count($asToken) !== 3 || !ctype_digit($iUserId) || !ctype_digit($asToken[1]) || !preg_match('/^[a-f0-9]{64}$/', $asToken[2])) {
|
||||||
|
if($sCookieValue !== '') $this->clearCookie();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get user info
|
||||||
|
$asUser = $this->oDb->selectRow(
|
||||||
|
self::USER_TABLE,
|
||||||
|
$iUserId,
|
||||||
|
array('clearance', 'token', 'token_exp')
|
||||||
|
);
|
||||||
|
|
||||||
|
//Check token value
|
||||||
|
if(!empty($asUser)
|
||||||
|
&& $asUser['clearance'] == $asToken[1]
|
||||||
|
&& time() <= strtotime($asUser['token_exp'])
|
||||||
|
&& hash_equals($asUser['token'], hash('sha256', $sCookieValue))
|
||||||
|
) {
|
||||||
|
$this->setUserId($iUserId);
|
||||||
|
$this->setSession();
|
||||||
|
}
|
||||||
|
else $this->clearCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setTokenCookie() {
|
||||||
|
$sToken = bin2hex(random_bytes(32));
|
||||||
|
$iClearance = $this->asUserInfo['clearance'];
|
||||||
|
$sCookieValue = implode(':', [$this->getUserId(), $iClearance, $sToken]);
|
||||||
|
|
||||||
|
$this->oDb->updateRow(
|
||||||
|
self::USER_TABLE,
|
||||||
|
$this->getUserId(),
|
||||||
|
array(
|
||||||
|
'token' => hash('sha256', $sCookieValue),
|
||||||
|
'token_exp' => date(Db::TIMESTAMP_FORMAT, time() + self::COOKIE_DURATION)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->setCookie($sCookieValue, time() + self::COOKIE_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCookie() {
|
||||||
|
return $_COOKIE[self::COOKIE_TOKEN] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCookie($sValue, $iExpires) {
|
||||||
|
setcookie(
|
||||||
|
self::COOKIE_TOKEN,
|
||||||
|
$sValue,
|
||||||
|
array(
|
||||||
|
'expires' => $iExpires,
|
||||||
|
'path' => '/',
|
||||||
|
'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'),
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clearCookie() {
|
||||||
|
$this->setCookie('', time() - 3600);
|
||||||
|
unset($_COOKIE[self::COOKIE_TOKEN]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+149
-241
@@ -43,12 +43,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
|
||||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.29.7"
|
"@babel/types": "^7.29.8"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
|
||||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.29.7",
|
"@babel/helper-string-parser": "^7.29.7",
|
||||||
@@ -70,40 +70,6 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/core": {
|
|
||||||
"version": "1.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
|
|
||||||
"integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@emnapi/wasi-threads": "1.2.2",
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/runtime": {
|
|
||||||
"version": "1.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
|
|
||||||
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.3.1.tgz",
|
||||||
@@ -239,29 +205,10 @@
|
|||||||
"pbf": "^5.1.0"
|
"pbf": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@tybys/wasm-util": "^0.10.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emnapi/core": "^1.7.1",
|
|
||||||
"@emnapi/runtime": "^1.7.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.139.0",
|
"version": "0.143.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz",
|
||||||
"integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
|
"integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -562,9 +509,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz",
|
||||||
"integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
|
"integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -579,9 +526,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz",
|
||||||
"integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
|
"integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -596,9 +543,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz",
|
||||||
"integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
|
"integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -613,9 +560,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz",
|
||||||
"integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
|
"integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -630,9 +577,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz",
|
||||||
"integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
|
"integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -647,9 +594,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz",
|
||||||
"integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
|
"integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -667,9 +614,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz",
|
||||||
"integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
|
"integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -687,9 +634,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz",
|
||||||
"integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
|
"integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -707,9 +654,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz",
|
||||||
"integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
|
"integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -727,9 +674,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz",
|
||||||
"integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
|
"integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -747,9 +694,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz",
|
||||||
"integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
|
"integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -767,9 +714,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz",
|
||||||
"integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
|
"integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -783,29 +730,10 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
|
|
||||||
"cpu": [
|
|
||||||
"wasm32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@emnapi/core": "1.11.1",
|
|
||||||
"@emnapi/runtime": "1.11.1",
|
|
||||||
"@napi-rs/wasm-runtime": "^1.1.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^20.19.0 || >=22.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz",
|
||||||
"integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
|
"integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -820,9 +748,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz",
|
||||||
"integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
|
"integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -849,17 +777,6 @@
|
|||||||
"integrity": "sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA==",
|
"integrity": "sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
|
||||||
"version": "0.10.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
|
|
||||||
"integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/geojson": {
|
"node_modules/@types/geojson": {
|
||||||
"version": "7946.0.16",
|
"version": "7946.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||||
@@ -949,39 +866,39 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz",
|
||||||
"integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==",
|
"integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.7",
|
"@babel/parser": "^7.29.8",
|
||||||
"@vue/shared": "3.5.40",
|
"@vue/shared": "3.5.41",
|
||||||
"entities": "^7.0.1",
|
"entities": "^7.0.1",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom": {
|
"node_modules/@vue/compiler-dom": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz",
|
||||||
"integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==",
|
"integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.40",
|
"@vue/compiler-core": "3.5.41",
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-sfc": {
|
"node_modules/@vue/compiler-sfc": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz",
|
||||||
"integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==",
|
"integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.7",
|
"@babel/parser": "^7.29.8",
|
||||||
"@vue/compiler-core": "3.5.40",
|
"@vue/compiler-core": "3.5.41",
|
||||||
"@vue/compiler-dom": "3.5.40",
|
"@vue/compiler-dom": "3.5.41",
|
||||||
"@vue/compiler-ssr": "3.5.40",
|
"@vue/compiler-ssr": "3.5.41",
|
||||||
"@vue/shared": "3.5.40",
|
"@vue/shared": "3.5.41",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"postcss": "^8.5.19",
|
"postcss": "^8.5.19",
|
||||||
@@ -989,61 +906,61 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr": {
|
"node_modules/@vue/compiler-ssr": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz",
|
||||||
"integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==",
|
"integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.40",
|
"@vue/compiler-dom": "3.5.41",
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz",
|
||||||
"integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==",
|
"integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core": {
|
"node_modules/@vue/runtime-core": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz",
|
||||||
"integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==",
|
"integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.40",
|
"@vue/reactivity": "3.5.41",
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-dom": {
|
"node_modules/@vue/runtime-dom": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz",
|
||||||
"integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==",
|
"integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.40",
|
"@vue/reactivity": "3.5.41",
|
||||||
"@vue/runtime-core": "3.5.40",
|
"@vue/runtime-core": "3.5.41",
|
||||||
"@vue/shared": "3.5.40",
|
"@vue/shared": "3.5.41",
|
||||||
"csstype": "^3.2.3"
|
"csstype": "^3.2.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/server-renderer": {
|
"node_modules/@vue/server-renderer": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz",
|
||||||
"integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==",
|
"integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.40",
|
"@vue/compiler-ssr": "3.5.41",
|
||||||
"@vue/runtime-dom": "3.5.40",
|
"@vue/runtime-dom": "3.5.41",
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
|
||||||
"integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==",
|
"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/autosize": {
|
"node_modules/autosize": {
|
||||||
@@ -1494,9 +1411,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/maplibre-gl": {
|
"node_modules/maplibre-gl": {
|
||||||
"version": "6.0.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.2.0.tgz",
|
||||||
"integrity": "sha512-1wBgEhzTZfA+cDpFdt0fM1mJA5mJ90fifORJ7D8JcKLJCvPT/iTx9bNxkP7DqEYde65DJd7gE7h83khwNRqdyg==",
|
"integrity": "sha512-PaNYtxWmYgIdDHshXsnU3Pho+H9IPme9H6dTjZbFWNazi+Q5QgKgIlu2GiSGVtOZ77/Fz3n5/1/kGM6ETzu0Lg==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapbox/point-geometry": "^1.1.0",
|
"@mapbox/point-geometry": "^1.1.0",
|
||||||
@@ -1504,7 +1421,7 @@
|
|||||||
"@mapbox/unitbezier": "^1.0.0",
|
"@mapbox/unitbezier": "^1.0.0",
|
||||||
"@mapbox/vector-tile": "^3.0.0",
|
"@mapbox/vector-tile": "^3.0.0",
|
||||||
"@maplibre/geojson-vt": "^6.1.1",
|
"@maplibre/geojson-vt": "^6.1.1",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^26.1.0",
|
"@maplibre/maplibre-gl-style-spec": "^26.2.1",
|
||||||
"@maplibre/mlt": "^1.1.12",
|
"@maplibre/mlt": "^1.1.12",
|
||||||
"@maplibre/vt-pbf": "^4.3.2",
|
"@maplibre/vt-pbf": "^4.3.2",
|
||||||
"@types/geojson": "^7946.0.16",
|
"@types/geojson": "^7946.0.16",
|
||||||
@@ -1629,9 +1546,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.23",
|
"version": "8.5.26",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
||||||
"integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
|
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1648,7 +1565,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.16",
|
"nanoid": "^3.3.17",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -1657,9 +1574,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss/node_modules/nanoid": {
|
"node_modules/postcss/node_modules/nanoid": {
|
||||||
"version": "3.3.16",
|
"version": "3.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -1681,9 +1598,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/preact": {
|
"node_modules/preact": {
|
||||||
"version": "10.29.7",
|
"version": "10.29.8",
|
||||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz",
|
||||||
"integrity": "sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==",
|
"integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1711,9 +1628,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "5.0.0",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
|
||||||
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
|
"integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 20.19.0"
|
"node": ">= 20.19.0"
|
||||||
@@ -1742,13 +1659,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz",
|
||||||
"integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
|
"integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.139.0",
|
"@oxc-project/types": "=0.143.0",
|
||||||
"@rolldown/pluginutils": "^1.0.0"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1758,21 +1675,20 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.1.5",
|
"@rolldown/binding-android-arm64": "1.2.3",
|
||||||
"@rolldown/binding-darwin-arm64": "1.1.5",
|
"@rolldown/binding-darwin-arm64": "1.2.3",
|
||||||
"@rolldown/binding-darwin-x64": "1.1.5",
|
"@rolldown/binding-darwin-x64": "1.2.3",
|
||||||
"@rolldown/binding-freebsd-x64": "1.1.5",
|
"@rolldown/binding-freebsd-x64": "1.2.3",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.2.3",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.1.5",
|
"@rolldown/binding-linux-arm64-gnu": "1.2.3",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.1.5",
|
"@rolldown/binding-linux-arm64-musl": "1.2.3",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.1.5",
|
"@rolldown/binding-linux-ppc64-gnu": "1.2.3",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.1.5",
|
"@rolldown/binding-linux-s390x-gnu": "1.2.3",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.1.5",
|
"@rolldown/binding-linux-x64-gnu": "1.2.3",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.1.5",
|
"@rolldown/binding-linux-x64-musl": "1.2.3",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.1.5",
|
"@rolldown/binding-openharmony-arm64": "1.2.3",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.1.5",
|
"@rolldown/binding-win32-arm64-msvc": "1.2.3",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.1.5",
|
"@rolldown/binding-win32-x64-msvc": "1.2.3"
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.1.5"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
@@ -1876,25 +1792,17 @@
|
|||||||
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
|
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "0BSD",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.1.5",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
|
||||||
"integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
|
"integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.33.0",
|
||||||
"picomatch": "^4.0.5",
|
"picomatch": "^4.0.5",
|
||||||
"postcss": "^8.5.17",
|
"postcss": "^8.5.25",
|
||||||
"rolldown": "~1.1.5",
|
"rolldown": "~1.2.1",
|
||||||
"tinyglobby": "^0.2.17"
|
"tinyglobby": "^0.2.17"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1911,7 +1819,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": "^20.19.0 || >=22.12.0",
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
"@vitejs/devtools": "^0.3.0",
|
"@vitejs/devtools": "^0.4.0",
|
||||||
"esbuild": "^0.27.0 || ^0.28.0",
|
"esbuild": "^0.27.0 || ^0.28.0",
|
||||||
"jiti": ">=1.21.0",
|
"jiti": ">=1.21.0",
|
||||||
"less": "^4.0.0",
|
"less": "^4.0.0",
|
||||||
@@ -1963,16 +1871,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.5.40",
|
"version": "3.5.41",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz",
|
||||||
"integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==",
|
"integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.40",
|
"@vue/compiler-dom": "3.5.41",
|
||||||
"@vue/compiler-sfc": "3.5.40",
|
"@vue/compiler-sfc": "3.5.41",
|
||||||
"@vue/runtime-dom": "3.5.40",
|
"@vue/runtime-dom": "3.5.41",
|
||||||
"@vue/server-renderer": "3.5.40",
|
"@vue/server-renderer": "3.5.41",
|
||||||
"@vue/shared": "3.5.40"
|
"@vue/shared": "3.5.41"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
|
|||||||
+27
-14
@@ -5,6 +5,31 @@
|
|||||||
"save": "Save",
|
"save": "Save",
|
||||||
"send": "Send"
|
"send": "Send"
|
||||||
},
|
},
|
||||||
|
"account": {
|
||||||
|
"login": "Log in",
|
||||||
|
"logout": "Log out",
|
||||||
|
"confirm_password": "Confirm password",
|
||||||
|
"email_placeholder": "my@email.com",
|
||||||
|
"invalid_credentials": "Incorrect password",
|
||||||
|
"invalid_email": "This doesn't look like a valid email address",
|
||||||
|
"logged_in": "Logged in",
|
||||||
|
"logged_out": "Logged out",
|
||||||
|
"newsletter": "Email me new updates",
|
||||||
|
"password": "Password",
|
||||||
|
"password_mismatch": "The passwords don't match",
|
||||||
|
"password_required": "Enter your password to continue",
|
||||||
|
"password_set": "Your password has been set successfully.",
|
||||||
|
"set_password": "Set a password for this administrator account",
|
||||||
|
"subscribed": "Thanks! You'll receive a confirmation email shortly.",
|
||||||
|
"unsubscribed": "Done. No more junk mail from me.",
|
||||||
|
"title": "Account",
|
||||||
|
"status": {
|
||||||
|
"logged_in": "You're logged in.",
|
||||||
|
"logged_in_named": "Hello $0, you're logged in.",
|
||||||
|
"logged_out": "Enter your email address below to receive my position as soon as I find it :)",
|
||||||
|
"subscribed": "You're all set. I'll send you updates!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"config": "Settings",
|
"config": "Settings",
|
||||||
"create_success": "Created",
|
"create_success": "Created",
|
||||||
@@ -85,19 +110,6 @@
|
|||||||
"locale": "en_NZ",
|
"locale": "en_NZ",
|
||||||
"page_og_desc": "Stay in touch while I'm away hiking."
|
"page_og_desc": "Stay in touch while I'm away hiking."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
|
||||||
"email_exists": "This email address is already subscribed. You can unsubscribe by clicking the button above.",
|
|
||||||
"email_placeholder": "my@email.com",
|
|
||||||
"invalid_email": "This doesn't look like a valid email address",
|
|
||||||
"subscribe": "Subscribe",
|
|
||||||
"subscribed": "Thanks! You'll receive a confirmation email shortly.",
|
|
||||||
"subscribed_desc": "You're all set. I'll send you updates!",
|
|
||||||
"title": "Keep in touch!",
|
|
||||||
"unknown_email": "Unknown email address",
|
|
||||||
"unsubscribe": "Unsubscribe",
|
|
||||||
"unsubscribed": "Done. No more junk mail from me.",
|
|
||||||
"unsubscribed_desc": "Enter your email address to receive my position as soon as I find it :)"
|
|
||||||
},
|
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copy direct link to clipboard",
|
"copy_to_clipboard": "Copy direct link to clipboard",
|
||||||
"link_copied": "Link copied!",
|
"link_copied": "Link copied!",
|
||||||
@@ -176,8 +188,9 @@
|
|||||||
"success": "$0 uploaded successfully"
|
"success": "$0 uploaded successfully"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Active users",
|
"subscribed": "Subscribed users",
|
||||||
"clearance": "Clearance",
|
"clearance": "Clearance",
|
||||||
|
"email": "Email",
|
||||||
"id": "User ID",
|
"id": "User ID",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"name": "User name"
|
"name": "User name"
|
||||||
|
|||||||
+27
-14
@@ -5,6 +5,31 @@
|
|||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"send": "Enviar"
|
"send": "Enviar"
|
||||||
},
|
},
|
||||||
|
"account": {
|
||||||
|
"login": "Iniciar sesión",
|
||||||
|
"logout": "Cerrar sesión",
|
||||||
|
"confirm_password": "Confirmar contraseña",
|
||||||
|
"email_placeholder": "nombre@email.com",
|
||||||
|
"invalid_credentials": "Contraseña incorrecta",
|
||||||
|
"invalid_email": "Esto no parece una dirección de correo electrónico",
|
||||||
|
"logged_in": "Sesión iniciada",
|
||||||
|
"logged_out": "Sesión cerrada",
|
||||||
|
"newsletter": "Enviarme nuevas actualizaciones por correo",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"password_mismatch": "Las contraseñas no coinciden",
|
||||||
|
"password_required": "Introduce tu contraseña para continuar",
|
||||||
|
"password_set": "Tu contraseña se ha establecido correctamente.",
|
||||||
|
"set_password": "Establece una contraseña para esta cuenta administradora",
|
||||||
|
"subscribed": "¡Gracias! Recibirás un correo electrónico de confirmación.",
|
||||||
|
"unsubscribed": "Listo. ¡No más spam!",
|
||||||
|
"title": "Cuenta",
|
||||||
|
"status": {
|
||||||
|
"logged_in": "Has iniciado sesión.",
|
||||||
|
"logged_in_named": "Hola $0, has iniciado sesión.",
|
||||||
|
"logged_out": "Añade tu dirección de correo electrónico y te enviaré mi posición actualizada tan pronto como la reciba :)",
|
||||||
|
"subscribed": "Todo está listo. Te enviaré noticias frescas en cuanto las reciba. Prometido..."
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"config": "Configuración",
|
"config": "Configuración",
|
||||||
"create_success": "Creado",
|
"create_success": "Creado",
|
||||||
@@ -85,19 +110,6 @@
|
|||||||
"locale": "es_ES",
|
"locale": "es_ES",
|
||||||
"page_og_desc": "Mantente en contacto conmigo durante mis aventuras en la montaña."
|
"page_og_desc": "Mantente en contacto conmigo durante mis aventuras en la montaña."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
|
||||||
"email_exists": "Esta dirección de correo electrónico ya está registrada. Puedes darte de baja haciendo clic en el botón de arriba.",
|
|
||||||
"email_placeholder": "nombre@email.com",
|
|
||||||
"invalid_email": "Esto no parece una dirección de correo electrónico",
|
|
||||||
"subscribe": "Suscribirse",
|
|
||||||
"subscribed": "¡Gracias! Recibirás un correo electrónico de confirmación.",
|
|
||||||
"subscribed_desc": "Todo está listo. Te enviaré noticias frescas en cuanto las reciba. Prometido...",
|
|
||||||
"title": "Mantente en contacto",
|
|
||||||
"unknown_email": "Dirección de correo electrónico desconocida",
|
|
||||||
"unsubscribe": "Darse de baja",
|
|
||||||
"unsubscribed": "Listo. ¡No más spam!",
|
|
||||||
"unsubscribed_desc": "Añade tu dirección de correo electrónico y te enviaré mi posición actualizada tan pronto como la reciba :)"
|
|
||||||
},
|
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copiar el enlace",
|
"copy_to_clipboard": "Copiar el enlace",
|
||||||
"link_copied": "¡Enlace copiado!",
|
"link_copied": "¡Enlace copiado!",
|
||||||
@@ -176,8 +188,9 @@
|
|||||||
"success": "$0 se ha subido correctamente."
|
"success": "$0 se ha subido correctamente."
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Usuarios activos",
|
"subscribed": "Usuarios suscritos",
|
||||||
"clearance": "Nivel de autorización",
|
"clearance": "Nivel de autorización",
|
||||||
|
"email": "Correo electrónico",
|
||||||
"id": "ID del usuario",
|
"id": "ID del usuario",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"name": "Nombre"
|
"name": "Nombre"
|
||||||
|
|||||||
+27
-14
@@ -5,6 +5,31 @@
|
|||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"send": "Envoyer"
|
"send": "Envoyer"
|
||||||
},
|
},
|
||||||
|
"account": {
|
||||||
|
"login": "Se connecter",
|
||||||
|
"logout": "Se déconnecter",
|
||||||
|
"confirm_password": "Confirmer le mot de passe",
|
||||||
|
"email_placeholder": "mon@email.com",
|
||||||
|
"invalid_credentials": "Mot de passe incorrect",
|
||||||
|
"invalid_email": "Ceci ne ressemble pas à une adresse e-mail",
|
||||||
|
"logged_in": "Connexion réussie",
|
||||||
|
"logged_out": "Déconnexion réussie",
|
||||||
|
"newsletter": "M'envoyer les nouvelles mises à jour par e-mail",
|
||||||
|
"password": "Mot de passe",
|
||||||
|
"password_mismatch": "Les mots de passe ne correspondent pas",
|
||||||
|
"password_required": "Saisis ton mot de passe pour continuer",
|
||||||
|
"password_set": "Ton mot de passe a bien été défini.",
|
||||||
|
"set_password": "Définis un mot de passe pour ce compte administrateur",
|
||||||
|
"subscribed": "Merci ! Tu vas recevoir un e-mail de confirmation très bientôt.",
|
||||||
|
"unsubscribed": "C'est fait. Fini le spam !",
|
||||||
|
"title": "Compte",
|
||||||
|
"status": {
|
||||||
|
"logged_in": "Tu es connecté.",
|
||||||
|
"logged_in_named": "Bonjour $0, tu es connecté.",
|
||||||
|
"logged_out": "Ajoute ton adresse e-mail et je t'enverrai ma nouvelle position dès que je la trouve :)",
|
||||||
|
"subscribed": "C'est tout bon. Je t'enverrai des nouvelles fraîches. Parole de scout."
|
||||||
|
}
|
||||||
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"config": "Paramètres",
|
"config": "Paramètres",
|
||||||
"create_success": "Créé",
|
"create_success": "Créé",
|
||||||
@@ -85,19 +110,6 @@
|
|||||||
"locale": "fr_CH",
|
"locale": "fr_CH",
|
||||||
"page_og_desc": "Garde le contact lorsque je suis sur les chemins."
|
"page_og_desc": "Garde le contact lorsque je suis sur les chemins."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
|
||||||
"email_exists": "Cette adresse e-mail est déjà enregistrée. Tu peux te désinscrire en cliquant sur le bouton ci-dessus.",
|
|
||||||
"email_placeholder": "mon@email.com",
|
|
||||||
"invalid_email": "Ceci ne ressemble pas à une adresse e-mail",
|
|
||||||
"subscribe": "S'abonner",
|
|
||||||
"subscribed": "Merci ! Tu vas recevoir un e-mail de confirmation très bientôt.",
|
|
||||||
"subscribed_desc": "C'est tout bon. Je t'enverrai des nouvelles fraîches. Parole de scout.",
|
|
||||||
"title": "Rester en contact",
|
|
||||||
"unknown_email": "Adresse e-mail inconnue",
|
|
||||||
"unsubscribe": "Se désinscrire",
|
|
||||||
"unsubscribed": "C'est fait. Fini le spam !",
|
|
||||||
"unsubscribed_desc": "Ajoute ton adresse e-mail et je t'enverrai ma nouvelle position dès que je la trouve :)"
|
|
||||||
},
|
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copier le lien dans le presse-papiers",
|
"copy_to_clipboard": "Copier le lien dans le presse-papiers",
|
||||||
"link_copied": "Lien copié !",
|
"link_copied": "Lien copié !",
|
||||||
@@ -176,8 +188,9 @@
|
|||||||
"success": "$0 a été téléversé"
|
"success": "$0 a été téléversé"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Utilisateurs actifs",
|
"subscribed": "Utilisateurs abonnés",
|
||||||
"clearance": "Niveau d'autorisation",
|
"clearance": "Niveau d'autorisation",
|
||||||
|
"email": "E-mail",
|
||||||
"id": "ID utilisateur",
|
"id": "ID utilisateur",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"name": "Nom"
|
"name": "Nom"
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
ALTER TABLE `users`
|
||||||
|
ADD COLUMN `password` VARCHAR(255) NOT NULL DEFAULT '' AFTER `email`,
|
||||||
|
ADD COLUMN `token` VARCHAR(4096) AFTER `password`,
|
||||||
|
ADD COLUMN `token_exp` TIMESTAMP DEFAULT 0 AFTER `token`;
|
||||||
|
|
||||||
|
ALTER TABLE `users`
|
||||||
|
CHANGE COLUMN `active` `subscribed` BOOLEAN DEFAULT 0;
|
||||||
@@ -195,27 +195,27 @@ export default {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ l('user.active') }}</h1>
|
<h1>{{ l('user.subscribed') }}</h1>
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ l('user.id') }}</th>
|
<th>{{ l('user.id') }}</th>
|
||||||
<th>{{ l('user.name') }}</th>
|
<th>{{ l('user.name') }}</th>
|
||||||
|
<th>{{ l('user.email') }}</th>
|
||||||
<th>{{ l('user.language') }}</th>
|
<th>{{ l('user.language') }}</th>
|
||||||
<th>{{ l('time.zone') }}</th>
|
<th>{{ l('time.zone') }}</th>
|
||||||
<th>{{ l('user.clearance') }}</th>
|
<th>{{ l('user.clearance') }}</th>
|
||||||
<th>{{ l('action.delete') }}</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="user in elems.user">
|
<tr v-for="user in elems.user">
|
||||||
<td>{{ user.id }}</td>
|
<td>{{ user.id }}</td>
|
||||||
<td>{{ user.name }}</td>
|
<td class="left">{{ user.name }}</td>
|
||||||
|
<td class="left">{{ user.email }}</td>
|
||||||
<td>{{ user.language }}</td>
|
<td>{{ user.language }}</td>
|
||||||
<td>{{ user.timezone }}</td>
|
<td>{{ user.timezone }}</td>
|
||||||
<td><AdminInput :type="'number'" :name="'clearance'" :elem="user" /></td>
|
<td><AdminInput :type="'number'" :name="'clearance'" :elem="user" /></td>
|
||||||
<td><SpotButton :icon="'close'" iconSize="lg" @click="deleteElem(user)" /></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<script>
|
||||||
|
import SpotButton from '@components/spotButton';
|
||||||
|
import SpotIcon from '@components/spotIcon';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SpotButton,
|
||||||
|
SpotIcon
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
feedbacks: [],
|
||||||
|
loginLoading: false,
|
||||||
|
newsletterLoading: false,
|
||||||
|
passwordMode: false,
|
||||||
|
settingPassword: false,
|
||||||
|
password: '',
|
||||||
|
passwordConfirmation: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
buttonClasses() {
|
||||||
|
return [
|
||||||
|
'manage',
|
||||||
|
this.action,
|
||||||
|
this.loginLoading?'loading':''
|
||||||
|
].filter(n => n).join(' ');
|
||||||
|
},
|
||||||
|
loggedIn() {
|
||||||
|
return this.user.id_user > 0;
|
||||||
|
},
|
||||||
|
action() {
|
||||||
|
return this.loggedIn?'logout':'login';
|
||||||
|
},
|
||||||
|
status() {
|
||||||
|
return this.loggedIn?(this.user.subscribed?'subscribed':(this.user.name?'logged_in_named':'logged_in')):'logged_out';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['api', 'lang', 'user'],
|
||||||
|
methods: {
|
||||||
|
manageSubscription() {
|
||||||
|
if(this.newsletterLoading) return;
|
||||||
|
this.newsletterLoading = true;
|
||||||
|
this.api.request(this.user.subscribed?'subscribe':'unsubscribe', {}, 'POST')
|
||||||
|
.then((asResponse) => {
|
||||||
|
this.user.setInfo(asResponse.data);
|
||||||
|
this.feedbacks.push({type:asResponse.result, msg:asResponse.desc});
|
||||||
|
})
|
||||||
|
.catch((sDesc) => {
|
||||||
|
this.user.subscribed = !this.user.subscribed;
|
||||||
|
this.feedbacks.push({type:'error', msg:sDesc?.message || sDesc});
|
||||||
|
})
|
||||||
|
.finally(() => {this.newsletterLoading = false;});
|
||||||
|
},
|
||||||
|
manageLogin() {
|
||||||
|
if(this.loginLoading) return;
|
||||||
|
|
||||||
|
var regexEmail = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
if(!regexEmail.test(this.user.email)) this.feedbacks.push({type:'error', 'msg':this.lang.get('account.invalid_email')});
|
||||||
|
else if(this.settingPassword && this.password !== this.passwordConfirmation) this.feedbacks.push({type:'error', 'msg':this.lang.get('account.password_mismatch')});
|
||||||
|
else {
|
||||||
|
const sAction = this.action;
|
||||||
|
this.loginLoading = true;
|
||||||
|
|
||||||
|
this.api.request(sAction, {'email': this.user.email, 'password': this.password, 'name': this.user.name}, 'POST')
|
||||||
|
.then((asResponse) => {
|
||||||
|
this.feedbacks.push({type: asResponse.result, msg: asResponse.desc});
|
||||||
|
this.user.setInfo(asResponse.data);
|
||||||
|
this.passwordMode = false;
|
||||||
|
this.settingPassword = false;
|
||||||
|
this.password = '';
|
||||||
|
this.passwordConfirmation = '';
|
||||||
|
})
|
||||||
|
.catch((oError) => {
|
||||||
|
switch(oError.descKey) {
|
||||||
|
case 'account.set_password':
|
||||||
|
this.passwordMode = true;
|
||||||
|
this.settingPassword = true;
|
||||||
|
this.feedbacks.push({type:'warning', msg:oError.message});
|
||||||
|
break;
|
||||||
|
case 'account.password_required':
|
||||||
|
this.passwordMode = true;
|
||||||
|
this.feedbacks.push({type:'success', msg:oError.message});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.feedbacks.push({type:'error', msg:oError.message});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {this.loginLoading = false;});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sub-section status">{{ lang.get('account.status.'+status, this.user.name) }}</div>
|
||||||
|
<div class="sub-section account-login">
|
||||||
|
<input type="email" name="email" id="email" :placeholder="lang.get('account.email_placeholder')" v-model="user.email" :disabled="loginLoading || loggedIn || passwordMode" @keyup.enter="manageLogin" />
|
||||||
|
<input v-if="passwordMode" type="password" name="password" id="password" :placeholder="lang.get('account.password')" v-model="password" :disabled="loginLoading" :autocomplete="settingPassword?'new-password':'current-password'" @keyup.enter="manageLogin" />
|
||||||
|
<input v-if="settingPassword" type="password" name="password_confirmation" id="password-confirmation" :placeholder="lang.get('account.confirm_password')" v-model="passwordConfirmation" :disabled="loginLoading" autocomplete="new-password" @keyup.enter="manageLogin" />
|
||||||
|
<SpotButton :classes="buttonClasses" :title="lang.get('account.'+action)" :icon="action" @click="manageLogin" />
|
||||||
|
</div>
|
||||||
|
<div class="sub-section newsletter radio" v-if="loggedIn">
|
||||||
|
<input type="checkbox" id="account-newsletter" v-model="user.subscribed" :true-value="1" :false-value="0" :disabled="newsletterLoading" @change="manageSubscription" />
|
||||||
|
<label for="account-newsletter">{{ lang.get('account.newsletter') }}</label>
|
||||||
|
</div>
|
||||||
|
<div v-if="feedbacks.length" class="sub-section feedback">
|
||||||
|
<p v-for="feedback in feedbacks" :key="feedback.type + '-' + feedback.msg" :class="feedback.type">
|
||||||
|
<SpotIcon :icon="feedback.type" :text="feedback.msg" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<script>
|
|
||||||
import SpotButton from '@components/spotButton';
|
|
||||||
import SpotIcon from '@components/spotIcon';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
SpotButton,
|
|
||||||
SpotIcon
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
feedbacks: [],
|
|
||||||
loading: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
buttonClasses() {
|
|
||||||
return [
|
|
||||||
'manage',
|
|
||||||
this.action,
|
|
||||||
this.loading?'loading':''
|
|
||||||
].filter(n => n).join(' ');
|
|
||||||
},
|
|
||||||
subscribed() {
|
|
||||||
return this.user.id_user > 0;
|
|
||||||
},
|
|
||||||
action() {
|
|
||||||
return this.subscribed?'unsubscribe':'subscribe';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inject: ['api', 'lang', 'user'],
|
|
||||||
methods: {
|
|
||||||
manage() {
|
|
||||||
if(this.loading) return;
|
|
||||||
|
|
||||||
var regexEmail = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
if(!regexEmail.test(this.user.email)) this.feedbacks.push({type:'error', 'msg':this.lang.get('newsletter.invalid_email')});
|
|
||||||
else {
|
|
||||||
const sAction = this.action;
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
this.api.request(sAction, {'email': this.user.email, 'name': this.user.name}, 'POST')
|
|
||||||
.then((asResponse) => {
|
|
||||||
this.feedbacks.push({type: asResponse.result, msg: asResponse.desc});
|
|
||||||
this.user.setInfo(asResponse.data);
|
|
||||||
})
|
|
||||||
.catch((sDesc) => {this.feedbacks.push({type:'error', msg:sDesc?.message || sDesc});})
|
|
||||||
.finally(() => {this.loading = false;});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="newsletter-form">
|
|
||||||
<input type="email" name="email" id="email" :placeholder="lang.get('newsletter.email_placeholder')" v-model="user.email" :disabled="loading || subscribed" />
|
|
||||||
<SpotButton :classes="buttonClasses" :title="lang.get('newsletter.'+action)" :icon="action" @click="manage" />
|
|
||||||
</div>
|
|
||||||
<div class="feedback">
|
|
||||||
<p v-for="feedback in feedbacks" :key="feedback.type + '-' + feedback.msg" :class="feedback.type">
|
|
||||||
<SpotIcon :icon="feedback.type" :text="feedback.msg" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{{ lang.get('newsletter.'+(subscribed?'subscribed':'unsubscribed')+'_desc') }}
|
|
||||||
</template>
|
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
import Simplebar from 'simplebar-vue';
|
import Simplebar from 'simplebar-vue';
|
||||||
|
|
||||||
import SpotIcon from '@components/spotIcon';
|
import SpotIcon from '@components/spotIcon';
|
||||||
import ProjectNewsletter from '@components/projectNewsletter';
|
import ProjectAccount from '@components/projectAccount';
|
||||||
import logoIconUrl from '@images/icons/favicon.svg';
|
import logoIconUrl from '@images/icons/favicon.svg';
|
||||||
import logoTitleUrl from '@images/logo_title.svg';
|
import logoTitleUrl from '@images/logo_title.svg';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SpotIcon,
|
SpotIcon,
|
||||||
ProjectNewsletter,
|
ProjectAccount,
|
||||||
Simplebar
|
Simplebar
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -125,10 +125,10 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-section settings-box newsletter">
|
<div class="settings-section settings-box account">
|
||||||
<h1><SpotIcon :icon="'newsletter'" width="fixed" :text="lang.get('newsletter.title')" /></h1>
|
<h1><SpotIcon :icon="'account'" width="fixed" :text="lang.get('account.title')" /></h1>
|
||||||
<div class="settings-section-body">
|
<div class="settings-section-body">
|
||||||
<ProjectNewsletter />
|
<ProjectAccount />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-section settings-box admin" v-if="user.hasClearance(consts.clearances.admin)">
|
<div class="settings-section settings-box admin" v-if="user.hasClearance(consts.clearances.admin)">
|
||||||
|
|||||||
+4
-1
@@ -49,10 +49,13 @@ export default class Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const oResponse = await oRequest.json();
|
const oResponse = await oRequest.json();
|
||||||
|
oResponse.descKey = this.lang.getLangKey(oResponse.desc);
|
||||||
oResponse.desc = this.lang.parse(oResponse.desc);
|
oResponse.desc = this.lang.parse(oResponse.desc);
|
||||||
|
|
||||||
if(oResponse.result == this.errorCode) {
|
if(oResponse.result == this.errorCode) {
|
||||||
throw oResponse.desc;
|
const oError = new Error(oResponse.desc);
|
||||||
|
oError.descKey = oResponse.descKey;
|
||||||
|
throw oError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return oResponse;
|
return oResponse;
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ import {
|
|||||||
faLayerGroup,
|
faLayerGroup,
|
||||||
faLink,
|
faLink,
|
||||||
faLocationPin,
|
faLocationPin,
|
||||||
|
faRightFromBracket,
|
||||||
|
faRightToBracket,
|
||||||
|
faUser,
|
||||||
faMagnifyingGlass,
|
faMagnifyingGlass,
|
||||||
faMagnifyingGlassLocation,
|
faMagnifyingGlassLocation,
|
||||||
faMapLocationDot,
|
faMapLocationDot,
|
||||||
@@ -47,7 +50,6 @@ import {
|
|||||||
faTemperatureThreeQuarters,
|
faTemperatureThreeQuarters,
|
||||||
faTriangleExclamation,
|
faTriangleExclamation,
|
||||||
faVideo,
|
faVideo,
|
||||||
faWifi,
|
|
||||||
faWind
|
faWind
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
@@ -100,8 +102,10 @@ const ICONS = {
|
|||||||
download: faFileArrowDown,
|
download: faFileArrowDown,
|
||||||
start: faCirclePlay,
|
start: faCirclePlay,
|
||||||
|
|
||||||
/* Admin */
|
/* Settings */
|
||||||
newsletter: faWifi,
|
account: faUser,
|
||||||
|
login: faRightToBracket,
|
||||||
|
logout: faRightFromBracket,
|
||||||
project: faPersonHiking,
|
project: faPersonHiking,
|
||||||
unsubscribe: faCircleXmark,
|
unsubscribe: faCircleXmark,
|
||||||
credits: faPaw,
|
credits: faPaw,
|
||||||
|
|||||||
+6
-4
@@ -23,9 +23,11 @@ export default class Lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse(message = '') {
|
parse(message = '') {
|
||||||
if(this.prefix && typeof message === 'string' && message.startsWith(this.prefix)) {
|
let sLangKey = this.getLangKey(message);
|
||||||
return this.get(message.slice(this.prefix.length));
|
return (sLangKey != '')?this.get(sLangKey):message;
|
||||||
}
|
}
|
||||||
return message;
|
|
||||||
|
getLangKey(message = '') {
|
||||||
|
return (this.prefix && typeof message === 'string' && message.startsWith(this.prefix))?message.slice(this.prefix.length):'';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,10 @@ h2 {
|
|||||||
|
|
||||||
.feedback {
|
.feedback {
|
||||||
p {
|
p {
|
||||||
margin: 0 0 var.$block-spacing 0;
|
margin: 0;
|
||||||
|
& + p {
|
||||||
|
margin-top: var.$block-spacing;
|
||||||
|
}
|
||||||
&.error {
|
&.error {
|
||||||
color: color.$error;
|
color: color.$error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@
|
|||||||
color: color.$default-hover;
|
color: color.$default-hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ $panel-actual-width: min($panel-width, #{$panel-width-max});
|
|||||||
color: color.$default-inv;
|
color: color.$default-inv;
|
||||||
|
|
||||||
&:hover, &:hover a, &:hover a:visited {
|
&:hover, &:hover a, &:hover a:visited {
|
||||||
background-color: color.$default-bg;
|
background-color: color.$default-bg-light;
|
||||||
color: color.$default;
|
color: color.$default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
margin-top: var.$block-spacing - var.$elem-spacing; //Gap counts for 1 $elem-spacing
|
margin-top: var.$block-spacing - var.$elem-spacing; //Gap counts for 1 $elem-spacing
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
color: color.$subtitle;
|
color: color.$subtitle;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
.find-me-spot {
|
.find-me-spot {
|
||||||
color: color.$spot;
|
color: color.$spot;
|
||||||
@@ -41,7 +42,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.settings-sections {
|
.settings-sections {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -97,16 +97,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sub-section + .sub-section {
|
||||||
|
margin-top: var.$block-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.newsletter {
|
&.account {
|
||||||
.newsletter-form {
|
.account-login {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var.$block-spacing;
|
gap: var.$block-spacing;
|
||||||
margin-bottom: var.$block-spacing;
|
|
||||||
|
|
||||||
input#email {
|
input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user