1
0

nightly commit

This commit is contained in:
2021-02-03 16:08:40 +08:00
parent 0eeea13277
commit 809b32c051
20 changed files with 471 additions and 197 deletions

View File

@@ -108,7 +108,7 @@ class CalendarDatabase(object):
def tokenOper_is_admin(self, username): def tokenOper_is_admin(self, username):
self.cursor.execute('SELECT [ccn_isAdmin] FROM user WHERE [ccn_name] = ?;',(username, )) self.cursor.execute('SELECT [ccn_isAdmin] FROM user WHERE [ccn_name] = ?;',(username, ))
cache = self.cursor.fetchone()[0] cache = self.cursor.fetchone()[0]
return True if cache == 1 else False return cache == 1
def tokenOper_get_username(self, token): def tokenOper_get_username(self, token):
self.cursor.execute('SELECT [ccn_user] FROM token WHERE [ccn_token] = ? AND [ccn_tokenExpireOn] > ?;',( self.cursor.execute('SELECT [ccn_user] FROM token WHERE [ccn_token] = ? AND [ccn_tokenExpireOn] > ?;',(
@@ -239,35 +239,35 @@ class CalendarDatabase(object):
# analyse opt arg # analyse opt arg
reAnalyseLoop = False reAnalyseLoop = False
cache = optArgs.get('belongTo', default=None) cache = optArgs.get('belongTo', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_belongTo] = ?') sqlList.append('[ccn_belongTo] = ?')
argumentsList.append(cache) argumentsList.append(cache)
cache = optArgs.get('title', default=None) cache = optArgs.get('title', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_title] = ?') sqlList.append('[ccn_title] = ?')
argumentsList.append(cache) argumentsList.append(cache)
cache = optArgs.get('description', default=None) cache = optArgs.get('description', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_description] = ?') sqlList.append('[ccn_description] = ?')
argumentsList.append(cache) argumentsList.append(cache)
cache = optArgs.get('eventDateTimeStart', default=None) cache = optArgs.get('eventDateTimeStart', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_eventDateTimeStart] = ?') sqlList.append('[ccn_eventDateTimeStart] = ?')
argumentsList.append(cache) argumentsList.append(cache)
reAnalyseLoop = True reAnalyseLoop = True
analyseData[5] = cache analyseData[5] = cache
cache = optArgs.get('eventDateTimeEnd', default=None) cache = optArgs.get('eventDateTimeEnd', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_eventDateTimeEnd] = ?') sqlList.append('[ccn_eventDateTimeEnd] = ?')
argumentsList.append(cache) argumentsList.append(cache)
cache = optArgs.get('loopRules', default=None) cache = optArgs.get('loopRules', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_loopRules] = ?') sqlList.append('[ccn_loopRules] = ?')
argumentsList.append(cache) argumentsList.append(cache)
reAnalyseLoop = True reAnalyseLoop = True
analyseData[8] = cache analyseData[8] = cache
cache = optArgs.get('timezoneOffset', default=None) cache = optArgs.get('timezoneOffset', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_timezoneOffset] = ?') sqlList.append('[ccn_timezoneOffset] = ?')
argumentsList.append(cache) argumentsList.append(cache)
@@ -486,7 +486,7 @@ class CalendarDatabase(object):
@SafeDatabaseOperation @SafeDatabaseOperation
def admin_get(self, token): def admin_get(self, token):
username = self.tokenOper_get_username(token) username = self.tokenOper_get_username(token)
if not tokenOper_is_admin(username): if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.') raise Exception('Permission denied.')
self.cursor.execute('SELECT [ccn_name], [ccn_isAdmin] FROM user;') self.cursor.execute('SELECT [ccn_name], [ccn_isAdmin] FROM user;')
@@ -495,7 +495,7 @@ class CalendarDatabase(object):
@SafeDatabaseOperation @SafeDatabaseOperation
def admin_add(self, token, newname): def admin_add(self, token, newname):
username = self.tokenOper_get_username(token) username = self.tokenOper_get_username(token)
if not tokenOper_is_admin(username): if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.') raise Exception('Permission denied.')
newpassword = utils.ComputePasswordHash(utils.GenerateUUID()) newpassword = utils.ComputePasswordHash(utils.GenerateUUID())
@@ -508,9 +508,9 @@ class CalendarDatabase(object):
return (newname, False) return (newname, False)
@SafeDatabaseOperation @SafeDatabaseOperation
def admin_update(self, token, username, **optArgs): def admin_update(self, token, _username, **optArgs):
username = self.tokenOper_get_username(token) username = self.tokenOper_get_username(token)
if not tokenOper_is_admin(username): if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.') raise Exception('Permission denied.')
# construct data # construct data
@@ -518,19 +518,21 @@ class CalendarDatabase(object):
argumentsList = [] argumentsList = []
# analyse opt arg # analyse opt arg
cache = optArgs.get('password', default=None) cache = optArgs.get('password', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_password] = ?') sqlList.append('[ccn_password] = ?')
argumentsList.append(utils.ComputePasswordHash(cache)) argumentsList.append(utils.ComputePasswordHash(cache))
cache = optArgs.get('isAdmin', default=None) cache = optArgs.get('isAdmin', None)
if cache is not None: if cache is not None:
sqlList.append('[ccn_isAdmin] = ?') sqlList.append('[ccn_isAdmin] = ?')
argumentsList.append(1 if cache else 0) argumentsList.append(1 if cache else 0)
# execute # execute
argumentsList.append(username) argumentsList.append(_username)
self.cursor.execute('UPDATE user SET {} WHERE [ccn_name] = ?;'.format(', '.join(sqlList)), self.cursor.execute('UPDATE user SET {} WHERE [ccn_name] = ?;'.format(', '.join(sqlList)),
tuple(argumentsList)) tuple(argumentsList))
print(cache)
print(tuple(argumentsList))
if self.cursor.rowcount != 1: if self.cursor.rowcount != 1:
raise Exception('Fail to update due to no matched rows or too much rows.') raise Exception('Fail to update due to no matched rows or too much rows.')
return True return True
@@ -538,7 +540,7 @@ class CalendarDatabase(object):
@SafeDatabaseOperation @SafeDatabaseOperation
def admin_delete(self, token, username): def admin_delete(self, token, username):
_username = self.tokenOper_get_username(token) _username = self.tokenOper_get_username(token)
if not tokenOper_is_admin(_username): if not self.tokenOper_is_admin(_username):
raise Exception('Permission denied.') raise Exception('Permission denied.')
# delete # delete

View File

@@ -12,6 +12,7 @@ import os
import config import config
import database import database
import utils
app = Flask(__name__) app = Flask(__name__)
calendar_db = database.CalendarDatabase() calendar_db = database.CalendarDatabase()
@@ -289,7 +290,7 @@ def api_admin_updateHandle():
(('token', str, False), (('token', str, False),
('username', str, False), ('username', str, False),
('password', str, True), ('password', str, True),
('isAdmin', bool, True))) ('isAdmin', utils.Str2Bool, True)))
@app.route('/api/admin/delete', methods=['POST']) @app.route('/api/admin/delete', methods=['POST'])
def api_admin_deleteHandle(): def api_admin_deleteHandle():

View File

@@ -17,7 +17,11 @@ ccn-messagebox-title=Notification
ccn-js-fail-login=Fail to login. Please check your username or password. ccn-js-fail-login=Fail to login. Please check your username or password.
ccn-js-fail-logout=Fail to logout due to unknow reason. Consider refreshing page to solve problem. ccn-js-fail-logout=Fail to logout due to unknow reason. Consider refreshing page to solve problem.
ccn-js-fail-operate=An operation failed. It may caused by your input wrong data, or system error. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data. ccn-js-fail-get=A get operation failed. It may caused by server internal error or your limited permission. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.
ccn-js-fail-add=An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.
ccn-js-fail-update=An update operation failed. It may caused by wrong arguments or lost target. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.
ccn-js-fail-delete=A delete operation failed. It may caused by no matched item. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.
ccn-js-success=Operation OK.
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>
@@ -45,3 +49,10 @@ ccn-calendar-shared-list=Shared
ccn-calendar-sharing-ownedList=Owned ccn-calendar-sharing-ownedList=Owned
ccn-calendar-sharing-sharingTargetList=Sharing target ccn-calendar-sharing-sharingTargetList=Sharing target
ccn-calendar-sharing-sharingTargetEditing=Editing: ccn-calendar-sharing-sharingTargetEditing=Editing:
ccn-admin-tabcontrol-tabProfile=My Profile
ccn-admin-tabcontrol-tabUserList=Manager User
ccn-admin-changePassword=Change Password
ccn-admin-userList=User List
ccn-admin-userItem-newPassword=New Password
ccn-admin-userItem-isAdmin=Is Admin

View File

@@ -17,7 +17,11 @@ ccn-messagebox-title=通知
ccn-js-fail-login=登陆失败,请检查您的用户名和密码。 ccn-js-fail-login=登陆失败,请检查您的用户名和密码。
ccn-js-fail-logout=由于未知原因,登出失败,请考虑刷新页面解决问题。 ccn-js-fail-logout=由于未知原因,登出失败,请考虑刷新页面解决问题。
ccn-js-fail-operate=一个操作失败了,可能是您输入了错误的参数,又或者是系统错误。刷新页面可能会解决系统错误问题。请在刷新前备份好自己的数据。 ccn-js-fail-get=一个获取操作失败了,可能是系统错误或者您的权限不足。刷新页面可能会解决问题。请在刷新页面前备份好自己的数据。
ccn-js-fail-add=一个添加操作失败了,可能是您输入的参数有误。刷新页面可能会解决问题。请在刷新页面前备份好自己的数据。
ccn-js-fail-update=一个更新操作失败了,可能是没有找到匹配的条目或者您的参数输入错误。刷新页面可能会解决问题。请在刷新页面前备份好自己的数据。
ccn-js-fail-delete=一个删除操作失败了,可能是没有找到对应条目。刷新页面可能会解决问题。请在刷新页面前备份好自己的数据。
ccn-js-success=操作成功
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>
@@ -45,3 +49,11 @@ ccn-calendar-shared-list=被共享的集合
ccn-calendar-sharing-ownedList=我的集合 ccn-calendar-sharing-ownedList=我的集合
ccn-calendar-sharing-sharingTargetList=分享目标 ccn-calendar-sharing-sharingTargetList=分享目标
ccn-calendar-sharing-sharingTargetEditing=正在编辑集合: ccn-calendar-sharing-sharingTargetEditing=正在编辑集合:
ccn-admin-tabcontrol-tabProfile=我的资料
ccn-admin-tabcontrol-tabUserList=管理用户
ccn-admin-changePassword=更改密码
ccn-admin-userList=用户列表
ccn-admin-userItem-newPassword=新密码
ccn-admin-userItem-isAdmin=是管理员

View File

@@ -406,14 +406,18 @@ function ccn_api_admin_add(_username) {
} }
function ccn_api_admin_update(_username, _password, _isAdmin) { function ccn_api_admin_update(_username, _password, _isAdmin) {
return ccn_api_dataTemplate( var data = {};
if (typeof(_password) != 'undefined')
data.password = _password;
if (typeof(_isAdmin) != 'undefined')
data.isAdmin = _isAdmin;
if (Object.getOwnPropertyNames(data).length == 0) return false;
data.token = GetApiToken();
data.username = _username;
return ccn_api_boolTemplate(
'/api/admin/update', '/api/admin/update',
{ data
token: GetApiToken(),
username: _username,
password: _password,
isAdmin: _isAdmin
}
); );
} }

View File

@@ -29,6 +29,7 @@ function ccn_headerNav_BindEvents() {
$("#ccn-header-language > *").each(function(){ $("#ccn-header-language > *").each(function(){
$(this).click(function(){ $(this).click(function(){
ccn_i18n_ChangeLanguage($(this).attr("language")); ccn_i18n_ChangeLanguage($(this).attr("language"));
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage(); ccn_i18n_ApplyLanguage();
}); });
}); });

View File

@@ -23,16 +23,19 @@ function ccn_i18n_ChangeLanguage(newLang) {
return true; return true;
} }
function ccn_i18n_ApplyLanguage() { function ccn_i18n_LoadLanguage() {
$.i18n.properties({ $.i18n.properties({
name: 'strings_' + ccn_i18n_currentLanguage, name: 'strings_' + ccn_i18n_currentLanguage,
path: '/static/i18n/', path: '/static/i18n/',
encoding: 'utf-8', encoding: 'utf-8',
mode: 'map', mode: 'map',
async: true, async: false,
cache: false, cache: false,
language: ccn_i18n_currentLanguage, language: ccn_i18n_currentLanguage
callback: function() { });
}
function ccn_i18n_ApplyLanguage() {
//set usual block //set usual block
var cache = $("[i18n-name]"); var cache = $("[i18n-name]");
cache.each(function() { cache.each(function() {
@@ -59,5 +62,9 @@ function ccn_i18n_ApplyLanguage() {
break; break;
} }
} }
function ccn_i18n_ApplyLanguage2Content(ctx) {
ctx.find("[i18n-name]").each(function() {
$(this).html($.i18n.prop($(this).attr('i18n-name')));
}); });
} }

View File

@@ -1,3 +1,5 @@
var ccn_admin_userListCache = [];
$(document).ready(function() { $(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.admin; ccn_pages_currentPage = ccn_pages_enumPages.admin;
@@ -13,6 +15,191 @@ $(document).ready(function() {
ccn_messagebox_Insert(); ccn_messagebox_Insert();
ccn_messagebox_BindEvent(); ccn_messagebox_BindEvent();
// apply i18n // bind tab control switcher and set current tab
ccn_i18n_ApplyLanguage(); $("#tabcontrol-tab-1-1").click(function(){
ccn_tabcontrol_SwitchTab(1, 1);
}); });
$("#tabcontrol-tab-1-2").click(function(){
ccn_tabcontrol_SwitchTab(1, 2);
});
ccn_tabcontrol_SwitchTab(1, 1);
// load user tab according to admin status
if(!ccn_api_common_isAdmin())
$('#tabcontrol-tab-1-2').hide();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
// bind event
$('#ccn-admin-profile-btnChangePassword').click(ccn_admin_profile_ChangePassword);
$('#ccn-admin-userList-btnAdd').click(ccn_admin_userList_Add);
$('#ccn-admin-userList-btnRefresh').click(ccn_admin_userList_Refresh);
});
// ================== profile
function ccn_admin_profile_ChangePassword() {
var newpassword = $('#ccn-admin-profile-inputPassword').val();
if (newpassword == "") return;
var result = ccn_api_common_changePassword(newpassword);
if(result) {
ccn_messagebox_Show($.i18n.prop("ccn-js-success"));
$('#ccn-admin-profile-inputPassword').val('');
} else
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-update"));
}
// ================== user list
function ccn_admin_userList_RefreshCacheList() {
ccn_admin_userListCache = new Array();
var result = ccn_api_admin_get();
if(typeof(result) != 'undefined') {
for(var index in result) {
ccn_admin_userListCache[index] = result[index];
}
}
}
function ccn_admin_userList_RenderItem(item, index, listDOM, renderdata) {
var renderdata = {
uuid: index, // use index for uuid. there are no uuid for user
username: item[0]
}
// render
listDOM.append(ccn_template_userItem.render(renderdata));
// set mode
var uuid = index;
ccn_admin_userList_ChangeDisplayMode(uuid, false, item[1])
// bind event
$("#ccn-admin-userItem-btnEdit-" + uuid).click(ccn_admin_userList_ItemEdit);
$("#ccn-admin-userItem-btnDelete-" + uuid).click(ccn_admin_userList_ItemDelete);
$("#ccn-admin-userItem-btnUpdate-" + uuid).click(ccn_admin_userList_ItemUpdate);
$("#ccn-admin-userItem-btnCancelUpdate-" + uuid).click(ccn_admin_userList_ItemCancelUpdate);
}
function ccn_admin_userList_RenderCacheList() {
$('#ccn-admin-userList').empty();
var listDOM = $('#ccn-admin-userList');
for(var index in ccn_admin_userListCache) {
ccn_admin_userList_RenderItem(
ccn_admin_userListCache[index],
index,
listDOM
)
}
ccn_i18n_ApplyLanguage2Content(listDOM);
}
function ccn_admin_userList_ChangeDisplayMode(uuid, isEdit, isAdmin) {
if (typeof(isAdmin) != 'undefined') {
if (isAdmin)
$("#ccn-admin-userItem-iconIsAdmin-" + uuid).show();
else
$("#ccn-admin-userItem-iconIsAdmin-" + uuid).hide();
}
if (typeof(isEdit) != 'undefined') {
if (isEdit) {
$("#ccn-admin-userItem-btnEdit-" + uuid).hide();
$("#ccn-admin-userItem-btnDelete-" + uuid).hide();
$("#ccn-admin-userItem-btnUpdate-" + uuid).show();
$("#ccn-admin-userItem-btnCancelUpdate-" + uuid).show();
$("#ccn-admin-userItem-boxPassword-" + uuid).show();
$("#ccn-admin-userItem-boxIsAdmin-" + uuid).show();
} else {
$("#ccn-admin-userItem-btnEdit-" + uuid).show();
$("#ccn-admin-userItem-btnDelete-" + uuid).show();
$("#ccn-admin-userItem-btnUpdate-" + uuid).hide();
$("#ccn-admin-userItem-btnCancelUpdate-" + uuid).hide();
$("#ccn-admin-userItem-boxPassword-" + uuid).hide();
$("#ccn-admin-userItem-boxIsAdmin-" + uuid).hide();
}
}
}
function ccn_admin_userList_Refresh() {
// refresh and render once
ccn_admin_userList_RefreshCacheList();
ccn_admin_userList_RenderCacheList();
}
function ccn_admin_userList_Add() {
var username = $('#ccn-admin-userList-inputUsername').val();
if (username == "") return;
var result = ccn_api_admin_add(username);
if (typeof(result) == 'undefined') {
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-add"));
} else {
// render
var index = ccn_admin_userListCache.push(result) - 1;
var listDOM = $('#ccn-admin-userList');
ccn_admin_userList_RenderItem(result, index, listDOM);
ccn_i18n_ApplyLanguage2Content(listDOM);
}
}
function ccn_admin_userList_ItemEdit() {
var uuid = $(this).attr("uuid");
// copy isAdmin to checkbox and clean password box
$('#ccn-admin-userItem-inputIsAdmin-' + uuid).prop("checked", ccn_admin_userListCache[uuid][1]);
$('#ccn-admin-userItem-inputPassword-' + uuid).val('');
// switch to edit mode
ccn_admin_userList_ChangeDisplayMode(uuid, true, undefined);
}
function ccn_admin_userList_ItemDelete() {
var uuid = $(this).attr("uuid");
var result = ccn_api_admin_delete(ccn_admin_userListCache[uuid][0]);
if(!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-delete"));
} else {
// remove body
$("#ccn-admin-userItem-" + uuid).remove();
}
}
function ccn_admin_userList_ItemUpdate() {
var uuid = $(this).attr("uuid");
var newpassword = $('#ccn-admin-userItem-inputPassword-' + uuid).val();
var isAdmin = $('#ccn-admin-userItem-inputIsAdmin-' + uuid).prop("checked");
var result = ccn_api_admin_update(
ccn_admin_userListCache[uuid][0],
newpassword == "" ? undefined : newpassword,
isAdmin == ccn_admin_userListCache[uuid][1] ? undefined : isAdmin);
if (!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-update"));
} else {
// safely update data
ccn_admin_userListCache[uuid][1] = isAdmin
// switch to normal mode
ccn_admin_userList_ChangeDisplayMode(uuid, false, isAdmin);
}
}
function ccn_admin_userList_ItemCancelUpdate() {
var uuid = $(this).attr("uuid");
ccn_admin_userList_ChangeDisplayMode(uuid, false, undefined);
}

View File

@@ -1,3 +1,7 @@
var ccn_calendar_sharingListCache = [];
var ccn_calendar_sharingTargetListCache = [];
var ccn_calendar_sharedListCache = [];
$(document).ready(function() { $(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.calendar; ccn_pages_currentPage = ccn_pages_enumPages.calendar;
@@ -29,9 +33,54 @@ $(document).ready(function() {
ccn_tabcontrol_SwitchTab(1, 1); ccn_tabcontrol_SwitchTab(1, 1);
// apply i18n // apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage(); ccn_i18n_ApplyLanguage();
}); });
function ccn_calendar_LoadCalendarBody() { function ccn_calendar_LoadCalendarBody() {
$('#ccn-calendar-calendarBbody').append(ccn_template_calendarItem.render()); $('#ccn-calendar-calendarBbody').append(ccn_template_calendarItem.render());
} }
// ================== calendar
// ================== collection
function ccn_calendar_sharing_Refresh() {
ccn_calendar_sharingListCache = new Array();
var result = ccn_api_collection_getFullOwn();
if(typeof(result) != 'undefined') {
for(var index in result) {
ccn_calendar_sharingListCache[result[index][0]] = result[index];
}
}
// render
$('#ccn-admin-userList').empty();
var listDOM = $('#ccn-admin-userList');
for(var index in ccn_admin_userListCache) {
ccn_admin_userList_RenderItem(
ccn_admin_userListCache[index],
index,
listDOM
)
}
}
function ccn_calendar_sharing_RenderItem() {
}
function ccn_calendar_sharingTarget_Refresh() {
}
function ccn_calendar_sharingTarget_RenderItem() {
}
function ccn_calendar_shared_Refresh() {
}

View File

@@ -14,5 +14,6 @@ $(document).ready(function() {
ccn_messagebox_BindEvent(); ccn_messagebox_BindEvent();
// apply i18n // apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage(); ccn_i18n_ApplyLanguage();
}); });

View File

@@ -14,6 +14,7 @@ $(document).ready(function() {
ccn_messagebox_BindEvent(); ccn_messagebox_BindEvent();
// apply i18n // apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage(); ccn_i18n_ApplyLanguage();
// bind login event // bind login event

View File

@@ -16,6 +16,7 @@ $(document).ready(function() {
ccn_messagebox_BindEvent(); ccn_messagebox_BindEvent();
// apply i18n // apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage(); ccn_i18n_ApplyLanguage();
// refresh once // refresh once
@@ -103,7 +104,7 @@ function ccn_todo_Add() {
var result = ccn_api_todo_add(); var result = ccn_api_todo_add();
if (typeof(result) == 'undefined') { if (typeof(result) == 'undefined') {
// fail // fail
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-operate")); ccn_messagebox_Show($.i18n.prop("ccn-js-fail-add"));
} else { } else {
// add into cache // add into cache
ccn_todo_todoListCache[result[0]] = result; ccn_todo_todoListCache[result[0]] = result;
@@ -147,9 +148,9 @@ function ccn_todo_ItemDelete() {
ccn_todo_todoListCache[uuid][3] ccn_todo_todoListCache[uuid][3]
); );
if(typeof(result) == 'undefined') { if(!result) {
// fail // fail
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-operate")); ccn_messagebox_Show($.i18n.prop("ccn-js-fail-delete"));
} else { } else {
// remove body // remove body
$("#ccn-todo-todoItem-" + uuid).remove(); $("#ccn-todo-todoItem-" + uuid).remove();
@@ -168,7 +169,7 @@ function ccn_todo_ItemUpdate() {
if (typeof(result) == 'undefined') { if (typeof(result) == 'undefined') {
// fail // fail
ccn_messagebox_Show($.i18n.prop("ccn-js-fail-operate")); ccn_messagebox_Show($.i18n.prop("ccn-js-fail-update"));
} else { } else {
// safely update data & lastChanged and control // safely update data & lastChanged and control
ccn_todo_todoListCache[uuid][2] = newData; ccn_todo_todoListCache[uuid][2] = newData;

View File

@@ -2,6 +2,9 @@ var ccn_template_headerNav = undefined;
var ccn_template_messagebox = undefined; var ccn_template_messagebox = undefined;
var ccn_template_calendarItem = undefined; var ccn_template_calendarItem = undefined;
var ccn_template_scheduleItem = undefined; var ccn_template_scheduleItem = undefined;
var ccn_template_sharingItem = undefined;
var ccn_template_sharingTargetItem = undefined;
var ccn_template_sharedItem = undefined;
var ccn_template_userItem = undefined; var ccn_template_userItem = undefined;
var ccn_template_todoItem = undefined; var ccn_template_todoItem = undefined;
@@ -43,6 +46,30 @@ function ccn_template_Load() {
ccn_template_scheduleItem = $.templates(data); ccn_template_scheduleItem = $.templates(data);
} }
}); });
$.ajax({
url: $("#jsrender-tmpl-sharingItem").attr('src'),
type: "GET",
async: false,
success: function (data) {
ccn_template_sharingItem = $.templates(data);
}
});
$.ajax({
url: $("#jsrender-tmpl-sharingTargetItem").attr('src'),
type: "GET",
async: false,
success: function (data) {
ccn_template_scheduleItem = $.templates(data);
}
});
$.ajax({
url: $("#jsrender-tmpl-sharedItem").attr('src'),
type: "GET",
async: false,
success: function (data) {
ccn_template_scheduleItem = $.templates(data);
}
});
break; break;
case ccn_pages_enumPages.todo: case ccn_pages_enumPages.todo:
$.ajax({ $.ajax({

View File

@@ -0,0 +1,14 @@
<div class="collection-item card">
<div class="collection-item-words">
<b>this is a
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
<p><span>Shared by: </span><span>Diablo</span></p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye-slash"></i></span></a>
</div>
</div>

View File

@@ -0,0 +1,20 @@
<div class="collection-item card">
<div class="collection-item-words">
<p>this is a
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye-slash"></i></span></a>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<div class="collection-item card">
<div class="collection-item-words">
<p>boluo</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
</div>

View File

@@ -1,27 +1,35 @@
<div class="user-item card"> <div id="ccn-admin-userItem-{{:uuid}}" class="user-item card">
<div class="user-item-words"> <div class="user-item-words">
<p><b>this is a name</b></p> <div class="control" style="display: flex; flex-flow: row; align-items: center;">
<div id="ccn-admin-userItem-iconIsAdmin-{{:uuid}}" class="icon is-small" style="margin-right: 1rem;">
<i class="fas fa-wrench"></i>
</div>
<div id="ccn-admin-userItem-textName-{{:uuid}}">{{>username}}</div>
</div>
<div id="ccn-admin-userItem-boxPassword-{{:uuid}}" class="field">
<label class="label" i18n-name="ccn-admin-userItem-newPassword"></label>
<div class="control">
<input id="ccn-admin-userItem-inputPassword-{{:uuid}}" class="input" type="password"></input>
</div>
</div>
<div id="ccn-admin-userItem-boxIsAdmin-{{:uuid}}" class="field">
<label class="checkbox">
<input id="ccn-admin-userItem-inputIsAdmin-{{:uuid}}" type="checkbox"><span i18n-name="ccn-admin-userItem-isAdmin"></span>
</label>
</div>
</div> </div>
<div class="user-item-icon control"> <div id="ccn-admin-userItem-btnEdit-{{:uuid}}" uuid="{{:uuid}}" class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a> <a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div> </div>
<div class="user-item-icon control"> <div id="ccn-admin-userItem-btnDelete-{{:uuid}}" uuid="{{:uuid}}" class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a> <a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div> </div>
</div>
<div class="user-item card">
<div class="user-item-words">
<p><b>this is a name</b></p>
<div class="control">
<input class="input" type="password" placeholder="New password"></input>
</div>
</div>
<div class="user-item-icon control"> <div id="ccn-admin-userItem-btnUpdate-{{:uuid}}" uuid="{{:uuid}}" class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-check"></i></span></a> <a class="button"><span class="icon is-small"><i class="fas fa-check"></i></span></a>
</div> </div>
<div class="user-item-icon control"> <div id="ccn-admin-userItem-btnCancelUpdate-{{:uuid}}" uuid="{{:uuid}}" class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-times"></i></span></a> <a class="button"><span class="icon is-small"><i class="fas fa-times"></i></span></a>
</div> </div>
</div> </div>

View File

@@ -21,6 +21,7 @@
<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/template.js"></script> <script type="text/javascript" src="/static/js/template.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script> <script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="/static/js/tabcontrol.js"></script>
<script type="text/javascript" src="/static/js/messagebox.js"></script> <script type="text/javascript" src="/static/js/messagebox.js"></script>
<script type="text/javascript" src="/static/js/page/admin.js"></script> <script type="text/javascript" src="/static/js/page/admin.js"></script>
@@ -28,70 +29,55 @@
</head> </head>
<body> <body>
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<h1 class="title">My account</h1> <div class="container" style="margin-top: 20px;">
<div class="tabs">
<ul>
<li id="tabcontrol-tab-1-1" class="tabcontrol-tab-1"><a
i18n-name="ccn-admin-tabcontrol-tabProfile"></a></li>
<li id="tabcontrol-tab-1-2" class="tabcontrol-tab-1"><a
i18n-name="ccn-admin-tabcontrol-tabUserList"></a></li>
</ul>
</div>
</div>
<div id="tabcontrol-panel-1-1" class="container tabcontrol-panel-1" style="margin-top: 20px;">
<div class="field"> <div class="field">
<label class="label">Change password</label> <label class="label" i18n-name="ccn-admin-changePassword"></label>
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
<input class="input" type="password" placeholder="New password"> <input id="ccn-admin-profile-inputPassword" class="input" type="password">
</div> </div>
<div class="control"> <div class="control">
<a class="button is-primary"> <a id="ccn-admin-profile-btnChangePassword" class="button is-primary">
<span class="icon is-small"><i class="fas fa-key"></i></span> <span class="icon is-small"><i class="fas fa-key"></i></span>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div>
<h1 class="title">User list</h1> <div id="tabcontrol-panel-1-2" class="container tabcontrol-panel-1" style="margin-top: 20px;">
<h1 class="title" i18n-name="ccn-admin-userList"></h1>
<div class="control-list"> <div class="control-list">
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
<input class="input" type="text"> <input id="ccn-admin-userList-inputUsername" class="input" type="text">
</div> </div>
<div class="control"> <div id="ccn-admin-userList-btnAdd" class="control">
<a class="button is-primary"> <a class="button is-primary">
<span class="icon is-small"><i class="fas fa-plus"></i></span> <span class="icon is-small"><i class="fas fa-plus"></i></span>
</a> </a>
</div> </div>
</div> </div>
<div class="control"> <div id="ccn-admin-userList-btnRefresh" class="control">
<a class="button is-primary"> <a class="button is-primary">
<span class="icon is-small"><i class="fas fa-sync"></i></span> <span class="icon is-small"><i class="fas fa-sync"></i></span>
</a> </a>
</div> </div>
</div> </div>
<div style="display: flex; flex-flow: column; margin-top: 1.25rem;"> <div id="ccn-admin-userList" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<div class="user-item card">
<div class="user-item-words">
<p><b>this is a name</b></p>
</div>
<div class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div>
<div class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
</div>
<div class="user-item card">
<div class="user-item-words">
<p><b>this is a name</b></p>
<div class="control">
<input class="input" type="password" placeholder="New password"></input>
</div>
</div>
<div class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-check"></i></span></a>
</div>
<div class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-times"></i></span></a>
</div>
</div>
</div> </div>
</div> </div>

View File

@@ -16,6 +16,9 @@
<script type="text/x-jsrender" id="jsrender-tmpl-messagebox" src="/static/tmpl/messagebox.tmpl"></script> <script type="text/x-jsrender" id="jsrender-tmpl-messagebox" src="/static/tmpl/messagebox.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-calendarItem" src="/static/tmpl/calendarItem.tmpl"></script> <script type="text/x-jsrender" id="jsrender-tmpl-calendarItem" src="/static/tmpl/calendarItem.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-scheduleItem" src="/static/tmpl/scheduleItem.tmpl"></script> <script type="text/x-jsrender" id="jsrender-tmpl-scheduleItem" src="/static/tmpl/scheduleItem.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-sharingItem" src="/static/tmpl/sharingItem.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-sharingTargetItem" src="/static/tmpl/sharingTargetItem.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-sharedItem" src="/static/tmpl/sharedItem.tmpl"></script>
<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>
@@ -190,29 +193,7 @@
</div> </div>
</div> </div>
<div style="display: flex; flex-flow: column;"> <div id="ccn-calendar-sharedList" style="display: flex; flex-flow: column;">
<div class="collection-item card">
<div class="collection-item-words">
<b>this is a name</b>
<p><span>Shared by: </span><span>yyc12345</span></p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye"></i></span></a>
</div>
</div>
<div class="collection-item card">
<div class="collection-item-words">
<b>this is a
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
<p><span>Shared by: </span><span>Diablo</span></p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye-slash"></i></span></a>
</div>
</div>
</div> </div>
</div> </div>
@@ -241,40 +222,7 @@
</div> </div>
</div> </div>
<div style="display: flex; flex-flow: column; margin-top: 1.25rem;"> <div id="ccn-calendar-sharingList" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<div class="collection-item card">
<div class="collection-item-words">
<p>this is a name</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye"></i></span></a>
</div>
</div>
<div class="collection-item card">
<div class="collection-item-words">
<p>this is a
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></a>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-eye-slash"></i></span></a>
</div>
</div>
</div> </div>
</div> </div>
@@ -301,26 +249,7 @@
</div> </div>
</div> </div>
<div style="display: flex; flex-flow: column; margin-top: 1.25rem;"> <div id="ccn-calendar-sharingTargetList" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<div class="collection-item card">
<div class="collection-item-words">
<p>yyc12345</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
</div>
<div class="collection-item card">
<div class="collection-item-words">
<p>boluo</p>
</div>
<div class="collection-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
</div>
</div> </div>
</div> </div>

View File

@@ -39,3 +39,7 @@ def GetCurrentTimestamp():
def GetTokenExpireOn(): def GetTokenExpireOn():
return GetCurrentTimestamp() + 60 * 60 * 24 * 2 # add 2 day from now return GetCurrentTimestamp() + 60 * 60 * 24 * 2 # add 2 day from now
def Str2Bool(strl):
return strl.lower() == 'true'