nightly commit
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -10,4 +10,7 @@ src/__pycache__
|
|||||||
# ignore any image first
|
# ignore any image first
|
||||||
*.png
|
*.png
|
||||||
*.jpg
|
*.jpg
|
||||||
*.gif
|
*.gif
|
||||||
|
|
||||||
|
# elimate vscode
|
||||||
|
.vscode
|
||||||
@@ -105,6 +105,23 @@ class CalendarDatabase(object):
|
|||||||
(gotten_salt, gotten_password) = self.cursor.fetchone()
|
(gotten_salt, gotten_password) = self.cursor.fetchone()
|
||||||
|
|
||||||
if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt):
|
if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt):
|
||||||
|
token = utils.GenerateToken(username)
|
||||||
|
self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ?, [ccn_salt] = ? WHERE [ccn_name] = ?;', (
|
||||||
|
token,
|
||||||
|
utils.GetCurrentTimestamp() + 60 * 60 * 24 * 2, # add 2 day from now
|
||||||
|
utils.GenerateSalt(), # regenerate a new slat to prevent re-login try
|
||||||
|
username
|
||||||
|
))
|
||||||
|
return token
|
||||||
|
else:
|
||||||
|
# return empty string to indicate fail to login
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def common_webLogin(self, username, password):
|
||||||
|
self.cursor.execute('SELECT [ccn_name] FROM user WHERE [ccn_name] = ? AND [ccn_password] = ?;', (username, utils.ComputePasswordHash(password)))
|
||||||
|
|
||||||
|
if len(self.cursor.fetchall()) != 0:
|
||||||
token = utils.GenerateToken(username)
|
token = utils.GenerateToken(username)
|
||||||
self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ? WHERE [ccn_name] = ?;', (
|
self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ? WHERE [ccn_name] = ?;', (
|
||||||
token,
|
token,
|
||||||
@@ -118,15 +135,18 @@ class CalendarDatabase(object):
|
|||||||
|
|
||||||
@SafeDatabaseOperation
|
@SafeDatabaseOperation
|
||||||
def common_logout(self, token):
|
def common_logout(self, token):
|
||||||
username = self.get_username_from_token(cur, token)
|
username = self.get_username_from_token(token)
|
||||||
self.cursor.execute('UPDATE user SET [ccn_tokenExpireOn] = 0 WHERE [ccn_name] = ?;', (username, ))
|
self.cursor.execute('UPDATE user SET [ccn_tokenExpireOn] = 0 WHERE [ccn_name] = ?;', (username, ))
|
||||||
return None
|
return True
|
||||||
|
|
||||||
@SafeDatabaseOperation
|
@SafeDatabaseOperation
|
||||||
def common_tokenValid(self, token):
|
def common_tokenValid(self, token):
|
||||||
# get user name have check the validation, don't do anything more.
|
# get user name have check the validation, don't do anything more.
|
||||||
self.get_username_from_token(token)
|
try:
|
||||||
return result
|
self.get_username_from_token(token)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
@SafeDatabaseOperation
|
@SafeDatabaseOperation
|
||||||
def common_isAdmin(self, token):
|
def common_isAdmin(self, token):
|
||||||
@@ -142,7 +162,7 @@ class CalendarDatabase(object):
|
|||||||
newpassword,
|
newpassword,
|
||||||
username
|
username
|
||||||
))
|
))
|
||||||
return None
|
return True
|
||||||
|
|
||||||
# =============================== calendar
|
# =============================== calendar
|
||||||
|
|
||||||
@@ -151,8 +171,71 @@ class CalendarDatabase(object):
|
|||||||
|
|
||||||
|
|
||||||
# =============================== todo
|
# =============================== todo
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_getFull(self, token):
|
||||||
|
username = self.get_username_from_token(token)
|
||||||
|
self.cursor.execute('SELECT * FROM todo WHERE [ccn_belongTo] = ?;', (username, ))
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_getList(self, token):
|
||||||
|
username = self.get_username_from_token(token)
|
||||||
|
self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_belongTo] = ?;', (username, ))
|
||||||
|
return tuple(map(lambda x: x[0], self.cursor.fetchall()))
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_getDetail(self, token, uuid):
|
||||||
|
username = self.get_username_from_token(token)
|
||||||
|
self.cursor.execute('SELECT * FROM todo WHERE [ccn_belongTo] = ? AND [ccn_uuid] = ?;', (username, uuid))
|
||||||
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_add(self, token):
|
||||||
|
username = self.get_username_from_token(token)
|
||||||
|
newuuid = utils.GenerateUUID()
|
||||||
|
lastupdate = utils.GenerateUUID()
|
||||||
|
self.cursor.execute('INSERT INTO todo VALUES (?, ?, ?, ?);', (
|
||||||
|
newuuid,
|
||||||
|
username,
|
||||||
|
'',
|
||||||
|
lastupdate,
|
||||||
|
))
|
||||||
|
return newuuid
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_update(self, token, uuid, data, lastChange):
|
||||||
|
# check valid token
|
||||||
|
self.get_username_from_token(token)
|
||||||
|
# check sync conflict
|
||||||
|
self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_uuid] = ? AND [ccn_lastChange] = ?;', (
|
||||||
|
uuid,
|
||||||
|
lastChange
|
||||||
|
))
|
||||||
|
if len(self.cursor.fetchall()) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# update
|
||||||
|
self.cursor.execute('UPDATE todo SET [ccn_data] = ? WHERE [ccn_uuid] = ?;', (
|
||||||
|
data,
|
||||||
|
uuid
|
||||||
|
))
|
||||||
|
return True
|
||||||
|
|
||||||
|
@SafeDatabaseOperation
|
||||||
|
def todo_delete(self, token, uuid, lastChange):
|
||||||
|
# check valid token
|
||||||
|
self.get_username_from_token(token)
|
||||||
|
# check sync conflict
|
||||||
|
self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_uuid] = ? AND [ccn_lastChange] = ?;', (
|
||||||
|
uuid,
|
||||||
|
lastChange
|
||||||
|
))
|
||||||
|
if len(self.cursor.fetchall()) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# delete
|
||||||
|
self.cursor.execute('DELETE FROM todo WHERE [ccn_uuid] = ?;', (uuid, ))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# =============================== admin
|
# =============================== admin
|
||||||
|
|||||||
@@ -68,19 +68,54 @@ def web_loginHandle():
|
|||||||
|
|
||||||
@app.route('/api/common/salt', methods=['POST'])
|
@app.route('/api/common/salt', methods=['POST'])
|
||||||
def api_common_saltHandle():
|
def api_common_saltHandle():
|
||||||
pass
|
result = (False, None)
|
||||||
|
if (CheckParameter(('username', ))):
|
||||||
|
db = get_database()
|
||||||
|
result = db.common_salt(request.form['username'])
|
||||||
|
|
||||||
|
return ConstructResponseBody(result)
|
||||||
|
|
||||||
@app.route('/api/common/login', methods=['POST'])
|
@app.route('/api/common/login', methods=['POST'])
|
||||||
def api_common_loginHandle():
|
def api_common_loginHandle():
|
||||||
pass
|
result = (False, None)
|
||||||
|
if (CheckParameter(('username', 'password'))):
|
||||||
|
db = get_database()
|
||||||
|
result = db.common_login(
|
||||||
|
request.form['username'],
|
||||||
|
request.form['password']
|
||||||
|
)
|
||||||
|
|
||||||
|
return ConstructResponseBody(result)
|
||||||
|
|
||||||
|
@app.route('/api/common/webLogin', methods=['POST'])
|
||||||
|
def api_common_webLoginHandle():
|
||||||
|
result = (False, None)
|
||||||
|
if (CheckParameter(('username', 'password'))):
|
||||||
|
db = get_database()
|
||||||
|
result = db.common_webLogin(
|
||||||
|
request.form['username'],
|
||||||
|
request.form['password']
|
||||||
|
)
|
||||||
|
|
||||||
|
return ConstructResponseBody(result)
|
||||||
|
|
||||||
@app.route('/api/common/logout', methods=['POST'])
|
@app.route('/api/common/logout', methods=['POST'])
|
||||||
def api_common_logoutHandle():
|
def api_common_logoutHandle():
|
||||||
pass
|
result = (False, None)
|
||||||
|
if (CheckParameter(('token', ))):
|
||||||
|
db = get_database()
|
||||||
|
result = db.common_logout(request.form['token'])
|
||||||
|
|
||||||
|
return ConstructResponseBody(result)
|
||||||
|
|
||||||
@app.route('/api/common/tokenValid', methods=['POST'])
|
@app.route('/api/common/tokenValid', methods=['POST'])
|
||||||
def api_common_tokenValidHandle():
|
def api_common_tokenValidHandle():
|
||||||
pass
|
result = (False, None)
|
||||||
|
if (CheckParameter(('token', ))):
|
||||||
|
db = get_database()
|
||||||
|
result = db.common_tokenValid(request.form['token'])
|
||||||
|
|
||||||
|
return ConstructResponseBody(result)
|
||||||
|
|
||||||
@app.route('/api/common/isAdmin', methods=['POST'])
|
@app.route('/api/common/isAdmin', methods=['POST'])
|
||||||
def api_common_isAdminHandle():
|
def api_common_isAdminHandle():
|
||||||
@@ -217,6 +252,18 @@ def UpdateStaticResources():
|
|||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def CheckParameter(paramList):
|
||||||
|
gotten = set(request.form.keys())
|
||||||
|
paramSet = set(paramList)
|
||||||
|
return gotten.issubset(paramSet) and paramSet.issubset(gotten)
|
||||||
|
|
||||||
|
def ConstructResponseBody(returnedTuple):
|
||||||
|
return {
|
||||||
|
'success': returnedTuple[0],
|
||||||
|
'error': '',
|
||||||
|
'data': returnedTuple[1]
|
||||||
|
}
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
app.run(port=config.CustomConfig['web']['port'])
|
app.run(port=config.CustomConfig['web']['port'])
|
||||||
|
|
||||||
@@ -12,6 +12,9 @@ ccn-header-user-login=Login
|
|||||||
ccn-header-user-logout=Logout
|
ccn-header-user-logout=Logout
|
||||||
ccn-header-language=Language
|
ccn-header-language=Language
|
||||||
|
|
||||||
|
ccn-js-failToLogin=Fail to login. Please check your username or password.
|
||||||
|
ccn-js-failToLogout=Fail to logout due to unknow reason. Consider refreshing page to solve problem.
|
||||||
|
|
||||||
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>A light, self-host calendar system.</p><p>Originally, this app is served for yyc12345 personal use.</p><br /><p>Pull request / issue / translation are welcomed.</p><p>Submit them in our <a href="https://github.com/yyc12345/coconut-leaf">GitHub project</a>.</p><p>This project source code is licensed <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>.</p>
|
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>A light, self-host calendar system.</p><p>Originally, this app is served for yyc12345 personal use.</p><br /><p>Pull request / issue / translation are welcomed.</p><p>Submit them in our <a href="https://github.com/yyc12345/coconut-leaf">GitHub project</a>.</p><p>This project source code is licensed <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>.</p>
|
||||||
|
|
||||||
ccn-login-form-username=Username
|
ccn-login-form-username=Username
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ ccn-header-user-login=登录
|
|||||||
ccn-header-user-logout=登出
|
ccn-header-user-logout=登出
|
||||||
ccn-header-language=语言
|
ccn-header-language=语言
|
||||||
|
|
||||||
|
ccn-js-failToLogin=登陆失败,请检查您的用户名和密码。
|
||||||
|
ccn-js-failToLogout=由于未知原因,登出失败,请考虑刷新页面解决问题。
|
||||||
|
|
||||||
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>一个轻量的自建日历系统</p><p>原本是出于yyc12345的个人使用而制作的。</p><br /><p>欢迎提出Pull request / issue / 翻译</p><p>将他们提交到我们的<a href="https://github.com/yyc12345/coconut-leaf">GitHub项目</a>.</p><p>本工程代码使用<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>授权。</p>
|
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>一个轻量的自建日历系统</p><p>原本是出于yyc12345的个人使用而制作的。</p><br /><p>欢迎提出Pull request / issue / 翻译</p><p>将他们提交到我们的<a href="https://github.com/yyc12345/coconut-leaf">GitHub项目</a>.</p><p>本工程代码使用<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>授权。</p>
|
||||||
|
|
||||||
ccn-login-form-username=用户名
|
ccn-login-form-username=用户名
|
||||||
|
|||||||
@@ -1,3 +1,117 @@
|
|||||||
function cnn_api_tokenValid() {
|
// var cached_salt = undefined
|
||||||
return true;
|
|
||||||
|
/*
|
||||||
|
function cnn_api_common_salt(_username) {
|
||||||
|
// true or false
|
||||||
|
// gotten salt store in cached_salt.
|
||||||
|
var gotten_data = undefined;
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/common/salt',
|
||||||
|
type: "POST",
|
||||||
|
async: false,
|
||||||
|
data: {
|
||||||
|
username: _username
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
gotten_data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (IsResponseOK(gotten_data)) {
|
||||||
|
cached_salt = gotten_data['data'];
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cnn_api_common_login(_username, password) {
|
||||||
|
// return true or false, token is managed by this js file self.
|
||||||
|
// if cached_salt is undefined, return false directly
|
||||||
|
if (typeof(cached_salt) == undefined) return false;
|
||||||
|
|
||||||
|
var gotten_data = undefined;
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/common/login',
|
||||||
|
type: "POST",
|
||||||
|
async: false,
|
||||||
|
data: {
|
||||||
|
username: _username,
|
||||||
|
password: ComputPasswordWithSalt(password, cached_salt)
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
gotten_data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (IsResponseOK(gotten_data) && gotten_data['data'] != '') {
|
||||||
|
SetApiToken(gotten_data['data']);
|
||||||
|
cached_salt = undefined;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function cnn_api_common_webLogin(_username, password) {
|
||||||
|
var gotten_data = undefined;
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/common/webLogin',
|
||||||
|
type: "POST",
|
||||||
|
async: false,
|
||||||
|
data: {
|
||||||
|
username: _username,
|
||||||
|
password: password
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
gotten_data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (IsResponseOK(gotten_data) && gotten_data['data'] != '') {
|
||||||
|
SetApiToken(gotten_data['data']);
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cnn_api_common_logout() {
|
||||||
|
// return true or false
|
||||||
|
var gotten_data = undefined;
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/common/logout',
|
||||||
|
type: "POST",
|
||||||
|
async: false,
|
||||||
|
data: {
|
||||||
|
token: GetApiToken()
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
gotten_data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (IsResponseOK(gotten_data) && gotten_data['data']) {
|
||||||
|
SetApiToken('');
|
||||||
|
return true;
|
||||||
|
} return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cnn_api_common_tokenValid() {
|
||||||
|
// get from local database first, then judge it via post
|
||||||
|
// return true or false
|
||||||
|
var gotten_token = GetApiToken();
|
||||||
|
if (gotten_token == '') return false;
|
||||||
|
|
||||||
|
var gotten_data = undefined;
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/common/tokenValid',
|
||||||
|
type: "POST",
|
||||||
|
async: false,
|
||||||
|
data: {
|
||||||
|
token: GetApiToken()
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
gotten_data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (IsResponseOK(gotten_data) && gotten_data['data']) return true;
|
||||||
|
else {
|
||||||
|
SetApiToken('');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ function cnn_headerNav_Insert() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cnn_headerNav_LoggedRefresh() {
|
function cnn_headerNav_LoggedRefresh() {
|
||||||
if (cnn_api_tokenValid()) {
|
if (cnn_api_common_tokenValid()) {
|
||||||
// logged, show all nav button and logout button
|
// logged, show all nav button and logout button
|
||||||
$("#cnn-header-nav-home").show();
|
$("#cnn-header-nav-home").show();
|
||||||
$("#cnn-header-nav-calendar").show();
|
$("#cnn-header-nav-calendar").show();
|
||||||
@@ -41,8 +41,16 @@ function cnn_headerNav_BindEvents() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo: bind logout
|
// bind logout
|
||||||
|
$("#cnn-header-user-logout").click(function() {
|
||||||
|
if (cnn_api_common_logout()) {
|
||||||
|
// ok, logout
|
||||||
|
// jump into home page again
|
||||||
|
window.location.href = '/web/home';
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else alert($.i18n.prop("ccn-js-failToLogout"));
|
||||||
|
});
|
||||||
|
|
||||||
// bind burger menu
|
// bind burger menu
|
||||||
// copy from bulma website
|
// copy from bulma website
|
||||||
|
|||||||
@@ -5,6 +5,45 @@ $(document).ready(function() {
|
|||||||
cnn_headerNav_BindEvents();
|
cnn_headerNav_BindEvents();
|
||||||
cnn_headerNav_LoggedRefresh();
|
cnn_headerNav_LoggedRefresh();
|
||||||
|
|
||||||
|
// bind login event
|
||||||
|
$("#ccn-login-form-login").click(StartLogin);
|
||||||
|
|
||||||
// apply i18n
|
// apply i18n
|
||||||
ccn_i18n_ApplyLanguage();
|
ccn_i18n_ApplyLanguage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function StartLogin() {
|
||||||
|
// disable all ui first
|
||||||
|
$("#ccn-login-form-login").attr("disabled",true);
|
||||||
|
$("#ccn-login-form-username").attr("disabled",true);
|
||||||
|
$("#ccn-login-form-password").attr("disabled",true);
|
||||||
|
|
||||||
|
// get form data
|
||||||
|
username = $("#ccn-login-form-username").val();
|
||||||
|
password = $("#ccn-login-form-password").val();
|
||||||
|
|
||||||
|
/*
|
||||||
|
// try get salt
|
||||||
|
if (cnn_api_common_salt(username)) {
|
||||||
|
// continue login
|
||||||
|
if (cnn_api_common_login(username, password)) {
|
||||||
|
// ok, logged
|
||||||
|
// jump into home page again
|
||||||
|
window.location.href = '/web/home';
|
||||||
|
|
||||||
|
} else alert($.i18n.prop("ccn-js-failToLogin"));
|
||||||
|
} else alert($.i18n.prop("ccn-js-failToLogin"));
|
||||||
|
*/
|
||||||
|
if (cnn_api_common_webLogin(username, password)) {
|
||||||
|
// ok, logged
|
||||||
|
// jump into home page again
|
||||||
|
window.location.href = '/web/home';
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else alert($.i18n.prop("ccn-js-failToLogin"));
|
||||||
|
|
||||||
|
// retore ui
|
||||||
|
$("#ccn-login-form-login").removeAttr("disabled");
|
||||||
|
$("#ccn-login-form-username").removeAttr("disabled");
|
||||||
|
$("#ccn-login-form-password").removeAttr("disabled");
|
||||||
|
}
|
||||||
|
|||||||
36
src/static/js/utils.js
Normal file
36
src/static/js/utils.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
function ComputPasswordWithSalt(password, salt) {
|
||||||
|
return ComputeSHA256(ComputeSHA256(password) + salt.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function ComputeSHA256(strl) {
|
||||||
|
var tempstr = new TextEncoder().encode(strl);
|
||||||
|
var hashedStrl = undefined
|
||||||
|
var shitpromise = crypto.subtle.digest('SHA-256', tempstr);
|
||||||
|
Promise.all(shitpromise).then(function(result) {
|
||||||
|
hashedStrl = result;
|
||||||
|
});
|
||||||
|
var hashArray = Array.from(new Uint8Array(hashedStrl));
|
||||||
|
var hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
|
||||||
|
return hashHex.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsResponseOK(data) {
|
||||||
|
if (typeof(data) == 'undefined') {
|
||||||
|
console.log("Fail to execute an api!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!data['success']) {
|
||||||
|
console.log("Fail to execute an api! Reason:");
|
||||||
|
console.log(data['error']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetApiToken() {
|
||||||
|
return ccn_localstorageAssist_Get('ccn-token', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetApiToken(value) {
|
||||||
|
ccn_localstorageAssist_Set('ccn-token', value);
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/utils.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/api.js"></script>
|
<script type="text/javascript" src="/static/js/api.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/utils.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/api.js"></script>
|
<script type="text/javascript" src="/static/js/api.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/utils.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/api.js"></script>
|
<script type="text/javascript" src="/static/js/api.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/utils.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/api.js"></script>
|
<script type="text/javascript" src="/static/js/api.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label ccn-i18n" i18n-name="ccn-login-form-username"></label>
|
<label class="label ccn-i18n" i18n-name="ccn-login-form-username"></label>
|
||||||
<div class="control has-icons-left has-icons-right">
|
<div class="control has-icons-left has-icons-right">
|
||||||
<input class="input" type="text">
|
<input id="ccn-login-form-username" class="input" type="text">
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<i class="fas fa-user"></i>
|
<i class="fas fa-user"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label ccn-i18n" i18n-name="ccn-login-form-password"></label>
|
<label class="label ccn-i18n" i18n-name="ccn-login-form-password"></label>
|
||||||
<p class="control has-icons-left">
|
<p class="control has-icons-left">
|
||||||
<input class="input" type="password">
|
<input id="ccn-login-form-password" class="input" type="password">
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<i class="fas fa-lock"></i>
|
<i class="fas fa-lock"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -45,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-primary ccn-i18n" i18n-name="ccn-login-form-login"></button>
|
<button id="ccn-login-form-login" class="button is-primary ccn-i18n" i18n-name="ccn-login-form-login"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
<script type="text/javascript" src="/static/js/i18n.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/utils.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/api.js"></script>
|
<script type="text/javascript" src="/static/js/api.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
<script type="text/javascript" src="/static/js/headerNav.js"></script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user