first commit
This commit is contained in:
54
src/coconut-leaf.py
Normal file
54
src/coconut-leaf.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
import server
|
||||
import config
|
||||
import utils
|
||||
import database
|
||||
|
||||
def GetUsernamePassword():
|
||||
print('What is the first username of this calendar system?')
|
||||
cache = input()
|
||||
while(not utils.IsValidUsername(cache)):
|
||||
print('Sorry, invalid data. Please try again.')
|
||||
cache = input()
|
||||
username = cache
|
||||
|
||||
print("Input this user password:")
|
||||
cache = input()
|
||||
while(not utils.IsValidPassword(cache)):
|
||||
print('Sorry, invalid data. Please try again.')
|
||||
cache = input()
|
||||
password = cache
|
||||
|
||||
return (username, password)
|
||||
|
||||
print('Coconut-leaf')
|
||||
print('A self-host, multi-account calendar system.')
|
||||
print('Project: https://github.com/yyc12345/coconut-leaf')
|
||||
print('===================')
|
||||
|
||||
# process args
|
||||
# preset init value
|
||||
need_init = False
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hi")
|
||||
except getopt.GetoptError:
|
||||
print('Wrong arguments!')
|
||||
print('python coconut-leaf.py [-i] [-h]')
|
||||
sys.exit(1)
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
print('python coconut-leaf.py [-i]')
|
||||
sys.exit(0)
|
||||
elif opt == '-i':
|
||||
need_init = True
|
||||
|
||||
if need_init:
|
||||
gotten_data = GetUsernamePassword()
|
||||
calendar = database.CalendarDatabase()
|
||||
calendar.init(*gotten_data)
|
||||
calendar.close()
|
||||
|
||||
print('Staring server...')
|
||||
server.run()
|
||||
14
src/config.cfg.template
Normal file
14
src/config.cfg.template
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"database-type": "sqlite",
|
||||
"database-config": {
|
||||
"user": "",
|
||||
"password": "",
|
||||
"db": "",
|
||||
"url": "",
|
||||
"port": 3306
|
||||
},
|
||||
"web": {
|
||||
"port": 8888
|
||||
},
|
||||
"debug": true
|
||||
}
|
||||
7
src/config.py
Normal file
7
src/config.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import json
|
||||
|
||||
CustomConfig = None
|
||||
|
||||
# read cfg
|
||||
with open('config.cfg', 'r', encoding='utf-8') as f:
|
||||
CustomConfig = json.load(f)
|
||||
51
src/database.py
Normal file
51
src/database.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import config
|
||||
import sqlite3
|
||||
import json
|
||||
|
||||
class CalendarDatabase(object):
|
||||
def __init__(self):
|
||||
self.db = None
|
||||
|
||||
def open(self):
|
||||
if (self.is_database_valid()):
|
||||
raise Exception('Databade is opened')
|
||||
|
||||
if config.CustomConfig['database-type'] == 'sqlite':
|
||||
self.db = sqlite3.connect(config.CustomConfig['database-config']['url'])
|
||||
elif config.CustomConfig['database-type'] == 'mysql':
|
||||
raise Exception('Not implemented database')
|
||||
else:
|
||||
raise Exception('Unknow database type')
|
||||
|
||||
def init(self, username, password):
|
||||
if (self.is_database_valid()):
|
||||
raise Exception('Databade is opened')
|
||||
|
||||
self.open()
|
||||
with open('sql/sqlite.sql', 'r', encoding='utf-8') as fsql:
|
||||
cursor = self.db.cursor()
|
||||
cursor.executescript(fsql.read())
|
||||
cursor.close()
|
||||
self.db.commit()
|
||||
|
||||
#todo: finish init
|
||||
|
||||
|
||||
def close(self):
|
||||
self.check_database()
|
||||
self.db.close()
|
||||
self.db = None
|
||||
|
||||
def check_database(self):
|
||||
if (not self.is_database_valid()):
|
||||
raise Exception('Databade is None')
|
||||
|
||||
def is_database_valid(self):
|
||||
return not (self.db == None)
|
||||
|
||||
# operation function
|
||||
def login_step1(self, username):
|
||||
self.check_database()
|
||||
|
||||
def login_step2(self, username, password):
|
||||
self.check_database()
|
||||
215
src/server.py
Normal file
215
src/server.py
Normal file
@@ -0,0 +1,215 @@
|
||||
from flask import Flask
|
||||
from flask import g
|
||||
from flask import render_template
|
||||
from flask import url_for
|
||||
from flask import request
|
||||
from flask import abort
|
||||
from flask import redirect
|
||||
|
||||
from functools import reduce
|
||||
import json
|
||||
import os
|
||||
|
||||
import config
|
||||
import database
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
render_static_resources = None
|
||||
|
||||
# =============================================database
|
||||
def get_database():
|
||||
db = getattr(g, '_database', None)
|
||||
if db is None:
|
||||
db = database.CalendarDatabase()
|
||||
db.open()
|
||||
return db
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_database(exception):
|
||||
db = getattr(g, '_database', None)
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
# ============================================= static page route
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def nospecHandle():
|
||||
return redirect(url_for('web_homeHandle'))
|
||||
|
||||
@app.route('/web/home', methods=['GET'])
|
||||
def web_homeHandle():
|
||||
UpdateStaticResources()
|
||||
return render_template("home.html", **render_static_resources)
|
||||
|
||||
@app.route('/web/calendar', methods=['GET'])
|
||||
def web_calendarHandle():
|
||||
UpdateStaticResources()
|
||||
return render_template("calendar.html", **render_static_resources)
|
||||
|
||||
@app.route('/web/todo', methods=['GET'])
|
||||
def web_todoHandle():
|
||||
UpdateStaticResources()
|
||||
return render_template("todo.html", **render_static_resources)
|
||||
|
||||
@app.route('/web/admin', methods=['GET'])
|
||||
def web_adminHandle():
|
||||
UpdateStaticResources()
|
||||
return render_template("admin.html", **render_static_resources)
|
||||
|
||||
# ============================================= query page route
|
||||
|
||||
# ================================ common
|
||||
|
||||
@app.route('/api/common/salt', methods=['POST'])
|
||||
def api_common_saltHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/common/login', methods=['POST'])
|
||||
def api_common_loginHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/common/logout', methods=['POST'])
|
||||
def api_common_logoutHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/common/tokenValid', methods=['POST'])
|
||||
def api_common_tokenValidHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/common/isAdmin', methods=['POST'])
|
||||
def api_common_isAdminHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/common/changePassword', methods=['POST'])
|
||||
def api_common_changePasswordHandle():
|
||||
pass
|
||||
|
||||
# ================================ calendar
|
||||
|
||||
@app.route('/api/calendar/getFull', methods=['POST'])
|
||||
def api_calendar_getFullHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/calendar/getList', methods=['POST'])
|
||||
def api_calendar_getListHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/calendar/getDetail', methods=['POST'])
|
||||
def api_calendar_getDetailHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/calendar/update', methods=['POST'])
|
||||
def api_calendar_updateHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/calendar/add', methods=['POST'])
|
||||
def api_calendar_addHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/calendar/delete', methods=['POST'])
|
||||
def api_calendar_deleteHandle():
|
||||
pass
|
||||
|
||||
# ================================ collection
|
||||
|
||||
@app.route('/api/collection/getOwn', methods=['POST'])
|
||||
def api_collection_getOwnHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/collection/addOwn', methods=['POST'])
|
||||
def api_collection_addOwnHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/collection/updateOwn', methods=['POST'])
|
||||
def api_collection_updateOwnHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/collection/deleteOwn', methods=['POST'])
|
||||
def api_collection_deleteOwnHandle():
|
||||
pass
|
||||
|
||||
|
||||
@app.route('/api/collection/getSharing', methods=['POST'])
|
||||
def api_collection_getSharingHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/collection/deleteSharing', methods=['POST'])
|
||||
def api_collection_deleteSharingHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/collection/addSharing', methods=['POST'])
|
||||
def api_collection_addSharingHandle():
|
||||
pass
|
||||
|
||||
|
||||
@app.route('/api/collection/getShared', methods=['POST'])
|
||||
def api_collection_getSharedHandle():
|
||||
pass
|
||||
|
||||
|
||||
# ================================ todo
|
||||
|
||||
@app.route('/api/todo/getFull', methods=['POST'])
|
||||
def api_todo_getFullHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/todo/getList', methods=['POST'])
|
||||
def api_todo_getListHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/todo/getDetail', methods=['POST'])
|
||||
def api_todo_getDetailHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/todo/add', methods=['POST'])
|
||||
def api_todo_addHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/todo/update', methods=['POST'])
|
||||
def api_todo_updateHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/todo/delete', methods=['POST'])
|
||||
def api_todo_deleteHandle():
|
||||
pass
|
||||
|
||||
# ================================ admin
|
||||
|
||||
@app.route('/api/admin/get', methods=['POST'])
|
||||
def api_admin_getHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/admin/add', methods=['POST'])
|
||||
def api_admin_addHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/admin/update', methods=['POST'])
|
||||
def api_admin_updateHandle():
|
||||
pass
|
||||
|
||||
@app.route('/api/admin/delete', methods=['POST'])
|
||||
def api_admin_deleteHandle():
|
||||
pass
|
||||
|
||||
# =============================================main run
|
||||
|
||||
def UpdateStaticResources():
|
||||
global render_static_resources
|
||||
if render_static_resources is not None:
|
||||
return
|
||||
|
||||
render_static_resources = {
|
||||
'url_js_localStorageAssist': url_for('static', filename='js/localStorageAssist.js'),
|
||||
'url_js_i18n': url_for('static', filename='js/i18n.js'),
|
||||
'url_js_api': url_for('static', filename='js/api.js'),
|
||||
'url_js_headerNav': url_for('static', filename='js/headerNav.js'),
|
||||
|
||||
'url_tmpl_headerNac': url_for('static', filename='tmpl/headerNav.tmpl'),
|
||||
|
||||
'url_js_pageHome': url_for('static', filename='js/page/home.js')
|
||||
}
|
||||
|
||||
def run():
|
||||
app.run(port=config.CustomConfig['web']['port'])
|
||||
|
||||
0
src/sql/mysql.sql
Normal file
0
src/sql/mysql.sql
Normal file
61
src/sql/sqlite.sql
Normal file
61
src/sql/sqlite.sql
Normal file
@@ -0,0 +1,61 @@
|
||||
PRAGMA encoding = "UTF-8";
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE user(
|
||||
[ccn_name] TEXT NOT NULL,
|
||||
[ccn_password] TEXT NOT NULL,
|
||||
[ccn_isAdmin] TINYINT NOT NULL CHECK(ccn_isAdmin = 1 OR ccn_isAdmin = 0),
|
||||
[ccn_salt] INTEGER NOT NULL,
|
||||
[ccn_token] TEXT UNIQUE NOT NULL,
|
||||
[ccn_tokenExpireOn] BIGINT NOT NULL,
|
||||
|
||||
PRIMARY KEY (ccn_name)
|
||||
);
|
||||
|
||||
CREATE TABLE collection(
|
||||
[ccn_uuid] TEXT NOT NULL,
|
||||
[ccn_name] TEXT NOT NULL,
|
||||
[ccn_user] TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (ccn_uuid),
|
||||
FOREIGN KEY (ccn_user) REFERENCES user(ccn_name) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE share(
|
||||
[ccn_uuid] TEXT NOT NULL,
|
||||
[ccn_target] TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (ccn_uuid) REFERENCES collection(ccn_uuid) ON DELETE CASCADE
|
||||
FOREIGN KEY (ccn_target) REFERENCES user(ccn_name) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE calendar(
|
||||
[ccn_uuid] TEXT NOT NULL,
|
||||
[ccn_belongTo] TEXT NOT NULL,
|
||||
|
||||
[ccn_title] TEXT NOT NULL,
|
||||
[ccn_description] TEXT NOT NULL,
|
||||
[ccn_lastChange] BIGINT NOT NULL,
|
||||
|
||||
[ccn_eventDateTimeType] TINYINT NOT NULL,
|
||||
[ccn_eventDateTimeStart] BIGINT NOT NULL,
|
||||
[ccn_eventDateTimeEnd] BIGINT NOT NULL,
|
||||
|
||||
[ccn_loopRules] TEXT NOT NULL,
|
||||
[ccn_loopDateTimeStart] BIGINT NOT NULL,
|
||||
[ccn_loopDateTimeEnd] BIGINT NOT NULL,
|
||||
|
||||
PRIMARY KEY (ccn_uuid),
|
||||
FOREIGN KEY (ccn_belongTo) REFERENCES collection(ccn_uuid) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE todo(
|
||||
[ccn_uuid] TEXT NOT NULL,
|
||||
[ccn_belongTo] TEXT NOT NULL,
|
||||
|
||||
[ccn_data] TEXT NOT NULL,
|
||||
[ccn_lastChange] BIGINT NOT NULL,
|
||||
|
||||
PRIMARY KEY (ccn_uuid),
|
||||
FOREIGN KEY (ccn_belongTo) REFERENCES user(ccn_name) ON DELETE CASCADE
|
||||
);
|
||||
37
src/static/css/admin.css
Normal file
37
src/static/css/admin.css
Normal file
@@ -0,0 +1,37 @@
|
||||
div.user-item {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
div.user-item-words {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
div.user-item-icon {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
div.control-list {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.control-list > * {
|
||||
margin-right: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
124
src/static/css/calendar.css
Normal file
124
src/static/css/calendar.css
Normal file
@@ -0,0 +1,124 @@
|
||||
#calendar-body div:nth-child(n+2) div {
|
||||
border-top: 0 solid black;
|
||||
border-left: 0 solid black;
|
||||
border-right: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
padding: 0.75em;
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#calendar-body div:nth-child(n+2) div:nth-child(1) {
|
||||
border-left: 1px solid black;
|
||||
}
|
||||
|
||||
#calendar-body div:nth-child(2) div {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
#calendar-body div div {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#calendar-body div {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
div.schedule-day {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
div.schedule-day-words {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
div.schedule-event-list {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
div.schedule-event {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
div.schedule-event-words {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
div.schedule-event-icon {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
#schedule-list div.schedule-day:nth-child(n+2) {
|
||||
border-top: 1px solid rgba(219,219,219,.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
div.collection-item {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
div.collection-item-words {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
div.collection-item-icon {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
div.control-list {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.control-list > * {
|
||||
margin-right: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
32
src/static/css/todo.css
Normal file
32
src/static/css/todo.css
Normal file
@@ -0,0 +1,32 @@
|
||||
div.todo-item {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
div.todo-item-words {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
div.todo-item-icon {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.control-list {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.control-list > * {
|
||||
margin-right: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
3
src/static/js/api.js
Normal file
3
src/static/js/api.js
Normal file
@@ -0,0 +1,3 @@
|
||||
function cnn_api_tokenValid() {
|
||||
return true;
|
||||
}
|
||||
11
src/static/js/headerNav.js
Normal file
11
src/static/js/headerNav.js
Normal file
@@ -0,0 +1,11 @@
|
||||
function cnn_headerNav_Insert() {
|
||||
$.ajax({
|
||||
url: $("#jsrender-tmpl-headerNav").attr('src'),
|
||||
type: "GET",
|
||||
success: function (data) {
|
||||
var tmpl = $.templates(data);
|
||||
$('body').prepend(tmpl.render());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
55
src/static/js/i18n.js
Normal file
55
src/static/js/i18n.js
Normal file
@@ -0,0 +1,55 @@
|
||||
var ccn_i18n_i18nSupported = ['en-US', 'zh-CN'];
|
||||
var ccn_i18n_currentLanguage = 'en-US';
|
||||
|
||||
// judge current language
|
||||
ccn_i18n_currentLanguage = ccn_localstorageAssist_Get('ccn-i18n', 'en-US');
|
||||
if (ccn_i18n_i18nSupported.indexOf(ccn_i18n_currentLanguage) == -1){
|
||||
ccn_localstorageAssist_Set('ccn-i18n', 'en-US');
|
||||
ccn_i18n_currentLanguage = 'en-US';
|
||||
}
|
||||
|
||||
function ccn_i18n_ChangeLanguage(newLang) {
|
||||
if (ccn_i18n_i18nSupported.indexOf(newLang) == -1) return false;
|
||||
ccn_i18n_currentLanguage = newLang;
|
||||
ccn_localstorageAssist_Set('ccn-i18n', newLang);
|
||||
return true;
|
||||
}
|
||||
|
||||
function ccn_i18n_ApplyLanguage() {
|
||||
$.i18n.properties({
|
||||
name: 'strings_' + ccn_i18n_currentLanguage,
|
||||
path: 'i18n/',
|
||||
mode: 'map',
|
||||
language: ccn_i18n_currentLanguage,
|
||||
callback: function() {
|
||||
//set usual block
|
||||
var cache = $(".ccn-i18n");
|
||||
cache.each(function() {
|
||||
$(this).html($.i18n.prop($(this).attr('name')));
|
||||
});
|
||||
|
||||
//set unusual block
|
||||
//set title
|
||||
switch(ccn_pages_currentPage) {
|
||||
case ccn_pages_enumPages.home:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-home'))
|
||||
break;
|
||||
case ccn_pages_enumPages.user:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-user'))
|
||||
break;
|
||||
case ccn_pages_enumPages.userinfo:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-userinfo'))
|
||||
break;
|
||||
case ccn_pages_enumPages.map:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-map'))
|
||||
break;
|
||||
case ccn_pages_enumPages.mapinfo:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-mapinfo'))
|
||||
break;
|
||||
case ccn_pages_enumPages.about:
|
||||
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-about'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
11
src/static/js/localStorageAssist.js
Normal file
11
src/static/js/localStorageAssist.js
Normal file
@@ -0,0 +1,11 @@
|
||||
function ccn_localstorageAssist_Get(index, defaultValue) {
|
||||
var cache = localStorage.getItem(index);
|
||||
if (cache == null) {
|
||||
ccn_localstorageAssist_Set(index, defaultValue);
|
||||
return defaultValue;
|
||||
} else return cache;
|
||||
}
|
||||
|
||||
function ccn_localstorageAssist_Set(index, value) {
|
||||
localStorage.setItem(index, value);
|
||||
}
|
||||
0
src/static/js/page/admin.js
Normal file
0
src/static/js/page/admin.js
Normal file
0
src/static/js/page/calendar.js
Normal file
0
src/static/js/page/calendar.js
Normal file
4
src/static/js/page/home.js
Normal file
4
src/static/js/page/home.js
Normal file
@@ -0,0 +1,4 @@
|
||||
$(document).ready(function() {
|
||||
// insert nav first
|
||||
cnn_headerNav_Insert();
|
||||
});
|
||||
0
src/static/js/page/login.js
Normal file
0
src/static/js/page/login.js
Normal file
0
src/static/js/page/todo.js
Normal file
0
src/static/js/page/todo.js
Normal file
41
src/static/tmpl/headerNav.tmpl
Normal file
41
src/static/tmpl/headerNav.tmpl
Normal file
@@ -0,0 +1,41 @@
|
||||
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="home">
|
||||
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="home">Home</a>
|
||||
<a class="navbar-item" href="calendar">Calendar</a>
|
||||
<a class="navbar-item" href="todo">Todo</a>
|
||||
<a class="navbar-item" href="admin">Admin</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<p class="control">
|
||||
<a class="button is-primary">Login</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-primary">Logout</a>
|
||||
</p>
|
||||
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Language</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a class="navbar-item">English</a>
|
||||
<a class="navbar-item">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
123
src/templates/admin.html
Normal file
123
src/templates/admin.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>coconut-leaf Admin</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="admin.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="home">
|
||||
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="home">Calendar</a>
|
||||
<a class="navbar-item" href="home">Todo</a>
|
||||
<a class="navbar-item" href="about">About</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<p class="control">
|
||||
<a class="button is-primary" href="javascript:void(0);">Logout</a>
|
||||
</p>
|
||||
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Language</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a href="javascript:void(0);" class="navbar-item">English</a>
|
||||
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
|
||||
<h1 class="title">My account</h1>
|
||||
<div class="field">
|
||||
<label class="label">Change password</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="password" placeholder="New password">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-key"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="title">User list</h1>
|
||||
<div class="control-list">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-plus"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-sync"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
597
src/templates/calendar.html
Normal file
597
src/templates/calendar.html
Normal file
@@ -0,0 +1,597 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>coconut-leaf Calendar</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="calendar.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="home">
|
||||
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="home">Calendar</a>
|
||||
<a class="navbar-item" href="home">Todo</a>
|
||||
<a class="navbar-item" href="about">About</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<p class="control">
|
||||
<a class="button is-primary" href="javascript:void(0);">Logout</a>
|
||||
</p>
|
||||
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Language</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a href="javascript:void(0);" class="navbar-item">English</a>
|
||||
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container" style="margin-top: 20px;">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li class="is-active"><a>Calendar</a></li>
|
||||
<li><a>Shared</a></li>
|
||||
<li><a>Sharing</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tabcontrol-1-1" class="container" style="margin-top: 20px; display: none;">
|
||||
<nav class="level">
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="date">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info">Jump</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="level-item control">
|
||||
<a class="button is-info">Today</a>
|
||||
</div>
|
||||
<div class="level-item control">
|
||||
<a class="button is-primary">Add...</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="calendar-body" class="card" style="padding: 1.25rem; display: flex; flex-flow: column;">
|
||||
<div style="margin: 0 0 0.75em 0;">
|
||||
<div><b>Monday</b></div>
|
||||
<div><b>Tuesday</b></div>
|
||||
<div><b>Wednesday</b></div>
|
||||
<div><b>Thursday</b></div>
|
||||
<div><b>Friday</b></div>
|
||||
<div><b style="color: red;">Saturday</b></div>
|
||||
<div><b style="color: red;">Sunday</b></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<p><b>1</b></p>
|
||||
<p><small>春分</small></p>
|
||||
<p><span class="icon is-small"><i class="fas fa-tasks"></i></span>114514</p>
|
||||
</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
<div>1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" style="padding: 1.25rem; display: flex; flex-flow: column; margin-top: 1.25rem;">
|
||||
<h1 class="title">Schedule</h1>
|
||||
|
||||
<div id="schedule-list">
|
||||
<div class="schedule-day container">
|
||||
<div class="schedule-day-words">
|
||||
<b>13</b>
|
||||
<b>Friday</b>
|
||||
</div>
|
||||
<div class="schedule-event-list">
|
||||
<div class="schedule-event card">
|
||||
<div class="schedule-event-words">
|
||||
<p class="level-item"><b>This is
|
||||
titleewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
|
||||
</p>
|
||||
<p class="level-item">this is
|
||||
subtitleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event-icon">
|
||||
<span class="icon is-small"><i class="fas fa-lock"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event card">
|
||||
<div class="schedule-event-words">
|
||||
<p class="level-item"><b>This is
|
||||
titleewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
|
||||
</p>
|
||||
<p class="level-item">this is
|
||||
subtitleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event-icon">
|
||||
<span class="icon is-small"><i class="fas fa-lock"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="schedule-day container">
|
||||
<div class="schedule-day-words">
|
||||
<b>13</b>
|
||||
<b>Friday</b>
|
||||
</div>
|
||||
<div class="schedule-event-list">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="schedule-day container">
|
||||
<div class="schedule-day-words">
|
||||
<b>13</b>
|
||||
<b>Friday</b>
|
||||
</div>
|
||||
<div class="schedule-event-list">
|
||||
<div class="schedule-event card">
|
||||
<div class="schedule-event-words">
|
||||
<p class="level-item"><b>This is
|
||||
titleewwwwwwwwwwwwwwwwww</b>
|
||||
</p>
|
||||
<p class="level-item">this is
|
||||
subtitleeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweeeeeeeeeeeeeeeeeeeeee
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event-icon">
|
||||
<span class="icon is-small"><i class="fas fa-lock"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event card">
|
||||
<div class="schedule-event-words">
|
||||
<p class="level-item"><b>This is
|
||||
titleeww</b>
|
||||
</p>
|
||||
<p class="level-item">this is
|
||||
subtitleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="schedule-event-icon">
|
||||
<span class="icon is-small"><i class="fas fa-lock"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="tabcontrol-1-2" class="container" style="margin-top: 20px; display: none;">
|
||||
|
||||
<div class="container" style="display: flex; flex-flow: column;">
|
||||
<h1 class="title">Shared</h1>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-sync"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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 id="tabcontrol-1-3" class="container" style="margin-top: 20px; display: none;">
|
||||
|
||||
<div class="container" style="display: flex; flex-flow: column;">
|
||||
<h1 class="title">Owned</h1>
|
||||
<div class="control-list">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-plus"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-sync"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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 class="container" style="display: flex; flex-flow: column;">
|
||||
<h1 class="title">Sharing target</h1>
|
||||
<label class="label"><span>Editing: </span><span>xxx</span></label>
|
||||
|
||||
<div class="control-list">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-plus"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-sync"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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>
|
||||
|
||||
<!-- add is-active in class to show this-->
|
||||
<div class="modal" style="float: left; position: fixed; top: 0; bottom: 0; left: 0; right: 0;">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Modal title</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<div class="modal-card-body" style="word-break: break-all;">
|
||||
<div class="field">
|
||||
<label class="label">Title</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Description</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Collection</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Select dropdown</option>
|
||||
<option>With options</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="label">Start date</label>
|
||||
<div class="control-list">
|
||||
<div class="control">
|
||||
<input class="input" type="date">
|
||||
</div>
|
||||
<div class="control">
|
||||
<input class="input" type="time">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="label">End date</label>
|
||||
<div class="control-list">
|
||||
<div class="control">
|
||||
<button class="button is-link">Spot</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link">Full day</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-list">
|
||||
<div class="control">
|
||||
<input class="input" type="date">
|
||||
</div>
|
||||
<div class="control">
|
||||
<input class="input" type="time">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="label">Loop</label>
|
||||
<label class="label">Loop method</label>
|
||||
<div class="control-list">
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-method">Never
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-method">Day
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-method">Week
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-method">Month
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-method">Year
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label class="label">Day loop span</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label class="label">Week loop span</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Week option</label>
|
||||
<div class="control-list">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Monday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Tuesday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Wednesdayday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Thursday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Firday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Saturday
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">Sunday
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label class="label">Month loop span</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Month option</label>
|
||||
<div class="control-list">
|
||||
<label class="radio">
|
||||
<input type="radio" name="month-loop-method">每月第X天
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="month-loop-method">每月倒数第X天
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="month-loop-method">每月第X个星期第Y天
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="month-loop-method">每月倒数第X个星期第Y天
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label class="label">Year loop span</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Loop end with</label>
|
||||
<div class="control-list">
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-end">Forever
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-end">A date
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="loop-end">Times
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="date">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success">Submit</button>
|
||||
<button class="button">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
src/templates/home.html
Normal file
38
src/templates/home.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>coconut-leaf Login</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
|
||||
|
||||
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="{{url_tmpl_headerNac}}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{url_js_localStorageAssist}}"></script>
|
||||
<script type="text/javascript" src="{{url_js_i18n}}"></script>
|
||||
<script type="text/javascript" src="{{url_js_api}}"></script>
|
||||
<script type="text/javascript" src="{{url_js_headerNav}}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{url_js_pageHome}}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container" style="margin-top: 1.25rem;">
|
||||
<article>
|
||||
<h1 class="title">coconut-leaf</h1>
|
||||
<p>A light, self-host calendar system.</p>
|
||||
<p>Originally, this app is served for yyc12345's 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>
|
||||
|
||||
</article>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
79
src/templates/login.html
Normal file
79
src/templates/login.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="height: 100%; overflow: hidden;">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>coconut-leaf Login</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body style="height: 100%; display: flex; flex-flow: column;">
|
||||
|
||||
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="home">
|
||||
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="home">Home</a>
|
||||
<a class="navbar-item" href="about">About</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Language</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a class="navbar-item">English</a>
|
||||
<a class="navbar-item">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div style="height: 100%; width: 100%; display: flex; justify-content: center; align-items: center;">
|
||||
<div class="card" style="padding: 1.25rem;">
|
||||
<div class="field">
|
||||
<label class="label">Username</label>
|
||||
<div class="control has-icons-left has-icons-right">
|
||||
<input class="input" type="text">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Password</label>
|
||||
<p class="control has-icons-left">
|
||||
<input class="input" type="password">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<button class="button is-primary">Login</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
113
src/templates/todo.html
Normal file
113
src/templates/todo.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>coconut-leaf Calendar</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="todo.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="home">
|
||||
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="home">Calendar</a>
|
||||
<a class="navbar-item" href="home">Todo</a>
|
||||
<a class="navbar-item" href="about">About</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<p class="control">
|
||||
<a class="button is-primary" href="javascript:void(0);">Logout</a>
|
||||
</p>
|
||||
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Language</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a href="javascript:void(0);" class="navbar-item">English</a>
|
||||
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
|
||||
<h1 class="title">Todo list</h1>
|
||||
<div class="control-list">
|
||||
<div class="control">
|
||||
<a class="button is-primary"><span class="icon is-small"><i class="fas fa-plus"></i></span></a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary">
|
||||
<span class="icon is-small"><i class="fas fa-sync"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-flow: column; margin-top: 1.25rem;">
|
||||
<div class="todo-item card">
|
||||
<div class="todo-item-words">
|
||||
<p>this is a name</p>
|
||||
</div>
|
||||
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></button>
|
||||
</div>
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-item card">
|
||||
<div class="todo-item-words">
|
||||
<p>this is a
|
||||
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></button>
|
||||
</div>
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-item card">
|
||||
<div class="todo-item-words control">
|
||||
<textarea class="textarea"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-check"></i></span></button>
|
||||
</div>
|
||||
<div class="todo-item-icon control">
|
||||
<button class="button"><span class="icon is-small"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
34
src/utils.py
Normal file
34
src/utils.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import hashlib
|
||||
import random
|
||||
import uuid
|
||||
|
||||
ValidUsername = set(map(lambda x:chr(x), range(48, 58, 1))) | set(map(lambda x:chr(x), range(65, 91, 1))) | set(map(lambda x:chr(x), range(97, 123, 1)))
|
||||
ValidPassword = set(map(lambda x:chr(x), range(33, 127, 1)))
|
||||
|
||||
def IsValidUsername(strl):
|
||||
return (len(set(strl) - ValidUsername) == 0)
|
||||
|
||||
def IsValidPassword(strl):
|
||||
return (len(set(strl) - ValidPassword) == 0)
|
||||
|
||||
def ComputePasswordHash(password):
|
||||
s = hashlib.sha256()
|
||||
s.update(password)
|
||||
return s.hexdigest()
|
||||
|
||||
def GenerateUUID():
|
||||
return str(uuid.uuid1())
|
||||
|
||||
def GenerateToken(username):
|
||||
s = hashlib.sha256()
|
||||
s.update(username)
|
||||
s.update(str(GenerateSalt()))
|
||||
return s.hexdigest()
|
||||
|
||||
def GenerateSalt():
|
||||
return random.randint(0, 6172748)
|
||||
|
||||
def ComputePasswordHashWithSalt(passwordHashed, salt):
|
||||
s = hashlib.sha256()
|
||||
s.update(passwordHashed + str(salt))
|
||||
return s.hexdigest()
|
||||
Reference in New Issue
Block a user