Compare commits

..

87 Commits

Author SHA1 Message Date
cube
71cc1d0357 Merge branch 'master' of https://tea.cubes.link/cube/myriad 2026-05-28 01:49:47 +01:00
cube
2fcce4cbbe we think we have export and import of pages and sections. there is definitely groups on member pages 2026-05-28 01:49:45 +01:00
1f3081471b Update README.md 2026-05-28 00:41:11 +00:00
82aee41da8 Update README.md 2026-05-27 23:34:41 +00:00
9607a7bf06 Update README.md 2026-05-26 11:55:36 +00:00
d2d8a8ff83 Update README.md 2026-05-26 11:48:38 +00:00
cube
09f2786754 Group pages plus some icon fetching changes 2026-05-23 14:28:00 +01:00
cube
133163986a handle invalid member id on member page 2026-05-20 17:24:08 +01:00
cube
2d3069f03a rm 2026-05-19 22:37:22 +01:00
cube
d6d17a2cf2 read me update 2026-05-19 22:36:49 +01:00
cube
222acacff5 fix #79 2026-05-19 22:31:12 +01:00
cube
c184a8d0ff fix #78 2026-05-19 21:29:44 +01:00
cube
062216e4c7 edit home page title -asters epic contribution 2026-05-10 12:54:09 +01:00
cube
ba7c316a64 fix list in edit blog post if member has html in their name 2026-05-10 12:51:04 +01:00
cube
ad978c57af somebody fucked up~ 2026-05-10 12:19:59 +01:00
cube
7f70bd8185 fix favicon changes 2026-05-10 00:47:48 +01:00
cube
1edaaf579b fix list if member has html in their name 2026-05-09 23:20:23 +01:00
cube
b13097862d favicon changes i think it works idk 2026-05-09 22:14:47 +01:00
cube
0ed498faff readme update 2026-05-09 21:49:22 +01:00
cube
3f0dc5ac26 fix #77 CHECK CONFIG 2026-05-09 20:19:40 +01:00
cube
4d2d76943a mobile nav style update 2026-05-09 19:44:49 +01:00
cube
f2dd764219 critical edit page update 2026-05-09 19:35:42 +01:00
cube
85c9f31592 custom pages in mobile nav bar 2026-05-09 19:03:54 +01:00
cube
bd7cc5a710 create folders from config if they dont exist so user doesnt have to 2026-05-09 14:33:38 +01:00
cube
8520ad6777 a bunch of updates CHECK CONFIG 2026-05-08 20:20:20 +01:00
cube
23b0819079 in front log 2026-05-01 19:59:00 +01:00
cube
9fcaf72457 images with same file name wont overwrite each other 2026-05-01 17:26:20 +01:00
cube
0df4bf926b remove potential html from title of member page 2026-05-01 17:09:05 +01:00
cube
fe26621221 remove potential html from title of member page 2026-05-01 17:08:18 +01:00
cube
b47fdac633 allow img tags to be used in member names 2026-05-01 17:03:23 +01:00
cube
b18c83a8ca image asset style update 2026-05-01 16:56:13 +01:00
cube
a21dc5f73f adjusted some styling 2026-05-01 16:11:39 +01:00
cube
0d4eec9c80 misc image uploads for use in custom sections, blog posts, or wherever you want!!!!! 2026-05-01 16:11:32 +01:00
cube
8db34a6d74 custom sections styling removal so custom style tags can be written 2026-05-01 01:47:28 +01:00
cube
a79dc7742b fix #5 2026-05-01 00:37:20 +01:00
cube
76b5ebb53d fix #54 2026-04-30 12:56:03 +01:00
cube
ab2d4b04ad forgot full list lol 2026-04-30 12:50:58 +01:00
cube
63895052f0 fix #45 2026-04-30 12:49:13 +01:00
cube
6fce468dc0 fix add to front location on member page 2026-04-30 12:29:18 +01:00
cube
7e6043891d fix #55 2026-04-30 12:22:32 +01:00
cube
c500214da0 maybe #72 2026-04-03 23:16:17 +01:00
cube
6ebc172aac rename sections 2026-04-03 22:49:23 +01:00
cube
2b8ce9c4c8 added section show fields and section title fields to export and import 2026-04-03 22:27:24 +01:00
cube
0fcb5e9b7b fix schema 2026-04-03 21:50:26 +01:00
cube
b40cb00d4d fix #15 REQUIRES DB INIT 2026-04-03 21:49:39 +01:00
cube
567f736d0d fix #68 2026-04-03 20:49:47 +01:00
cube
a2f47b4730 fix #70 2026-04-03 19:04:00 +01:00
cube
1df0feeea0 fix #69 2026-04-03 19:03:28 +01:00
cube
d76a02ed26 fix #53 2026-04-03 16:22:51 +01:00
cube
ab734d7ef1 no need to use br tags on blog posts 2026-04-03 01:34:35 +01:00
cube
8b78fa2480 remove need to manually use <br> tags in member bios 2026-04-02 15:27:19 +01:00
cube
fdcf2c3d96 fix #33 2026-04-02 15:26:30 +01:00
cube
86989a00fd readme 2026-03-31 23:20:50 +01:00
cube
19f6e3940e dark fix 2026-03-31 18:42:39 +01:00
cube
60a5d598fc dark theme update 2026-03-31 18:38:46 +01:00
cube
249ff7a5f3 fix #56 2026-03-31 17:01:07 +01:00
cube
931448bc05 scheme for sections and pages no need to init db because nothing is implemented 2026-03-31 16:54:54 +01:00
cube
03863b0250 readme 2026-03-31 14:54:17 +01:00
cube
f4fd5a6dac mobile nav plus additional nav options in admin panel for compactness 2026-03-31 14:36:52 +01:00
cube
17c3ca4a90 mobile view wip 2026-03-31 13:21:56 +01:00
cube
d0c38799e2 tweaks 2026-03-31 02:57:49 +01:00
cube
1d62847284 green 2026-03-31 02:38:39 +01:00
cube
d8708fe173 dark green 2026-03-31 02:26:12 +01:00
cube
5d8939bad3 reorder 2026-03-31 02:12:00 +01:00
cube
12c2a13c89 ugly christmas theme for certain specific individuals............... 2026-03-31 02:05:08 +01:00
cube
ca9d826386 reorder case insensitive 2026-03-31 01:58:59 +01:00
cube
6df8673ac1 reorder tweak 2026-03-31 01:51:59 +01:00
cube
4312e3e683 red 2026-03-31 01:50:00 +01:00
cube
174dfa3b52 rearrange 2026-03-31 01:47:37 +01:00
cube
e03ce3174a fix last updated 2026-03-31 01:43:20 +01:00
cube
8a6599354e green 2026-03-31 01:28:47 +01:00
cube
525d4ee431 green 2026-03-31 01:27:40 +01:00
cube
2c0f4e1fab blinkies size 2026-03-31 01:16:57 +01:00
cube
e55b0b7613 dark-blue theme 2026-03-31 01:10:45 +01:00
cube
f3124628cb readme and tweaks 2026-03-31 00:39:30 +01:00
cube
2de4f9af9c fixed latest issues with fresh database 2026-03-30 22:24:27 +01:00
cube
1ee6a96d58 front log and related datetime stuff 2026-03-30 22:20:16 +01:00
cube
db4a7cc46e tweaks 2026-03-30 16:48:04 +01:00
cube
9483193ebb full system zip export and import works. check readme 2026-03-30 16:18:29 +01:00
cube
f8efe51891 sqlite is just trying to embarrass me now 2026-03-30 15:15:24 +01:00
cube
e7cc0ed609 oops 2026-03-30 15:12:59 +01:00
cube
665c8e905d import and export entire system should fully work now but dont rely on anything here. keep your own personal backups elsewhere like a spreadsheet or something 2026-03-30 15:11:20 +01:00
cube
b907c1a546 working on imports and exports. everything in that regard is broken rn 2026-03-30 14:41:29 +01:00
cube
d20f558232 working on imports and exports. everything in that regard is broken rn 2026-03-30 14:40:02 +01:00
cube
96230b38ef working on imports and exports. everything in that regard is broken rn 2026-03-30 14:35:21 +01:00
cube
00171b10e5 working on imports and exports. everything in that regard is broken rn 2026-03-30 14:32:15 +01:00
cube
f7f3c6f100 working on imports and exports. everything in that regard is broken rn 2026-03-30 14:06:45 +01:00
39 changed files with 1508 additions and 368 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.venv/ .venv/
.vscode/
*.pyc *.pyc
__pycache__/ __pycache__/
@@ -16,3 +17,5 @@ build/
/myriad/static/blinkies /myriad/static/blinkies
/myriad/static/stamps /myriad/static/stamps
myriad/static/tmp myriad/static/tmp
myriad/static/misc
myriad/static/favicon.ico

173
README.md
View File

@@ -1,6 +1,12 @@
# myriad # myriad
flask app for plurals to publicly share member lists flask app for plural systems to publicly share member lists. the software is in basically a usable state right now, just be sure to keep regular backups. documentation is fairly limited but we're working on that. drop an email to `myriad [at] cubes [dot] link` if you wanna contact us about this project :)
# NOTICE
i am still actively developing myriad, there are plenty of things that aren't properly implemented and features im still working on so... if you decide to use it please do so at your own care - i.e. keep your own backups manually by simply copy and pasting the database and upload folders.
# more info
logged in users are presumed to all be admins with distinction only between being logged in and not logged in users are presumed to all be admins with distinction only between being logged in and not
@@ -10,140 +16,63 @@ the blinkies and stamps stuff is literally just because i want an easy way to up
make sure blinkies are actually blinkie-sized, and stamps are likewise stamp-sized. too much variation in size will make the layout weird. there's not validation because its designed just to make uploading the images not require ftp + manual code as i had been doing before. make sure blinkies are actually blinkie-sized, and stamps are likewise stamp-sized. too much variation in size will make the layout weird. there's not validation because its designed just to make uploading the images not require ftp + manual code as i had been doing before.
# dev set up (windows) # Config
- after cloning, run `py -3 -m venv .venv` in the root directory and then `.venv\Scripts\activate` this is how your config should look at the latest update
- then `pip install Flask` inside the virtual env
- you might also need to init a database, so use `flask --app myriad init-db`
- to start the site use `flask --app myriad run --debug`
do not deploy this way, the packaged flask server is not secure. production instructions will be provided when the project is ready
- you will need to run `.venv\Scripts\activate` from the folder every time you start working on it
- re-building the entire database with `flask --app myriad init-db` (losing all the data inside) will be necessary as development continues. DO NOT STORE ANYTHING IMPORTANT DURING DEVELOPMENT
- start the site with `flask --app myriad run --debug` as usual
# prod set up (linux)
it is recommended to run waitress in a tmux window for easier management
create new tmux window
`tmux new -s myriad`
detach from tmux window
`ctrl+b` -> `d`
attach to existing tmux window
`tmux attach -t myriad`
## installing
clone the repo and set up virtual env
`git clone https://tea.cubes.link/cube/myriad.git`
`cd myriad`
`python3 -m venv venv`
`source venv/bin/activate`
install necessary packages in the virtual env
`pip install flask`
`pip install waitress`
make sure all directories exist
`mkdir instance`
`mkdir myriad/static/icons`
`mkdir myriad/static/blinkies`
`mkdir myriad/static/stamps`
`mkdir myriad/static/tmp`
copy sample from readme and paste into the following
`sudo nano instance/config.py`
in a window where you can copy the output, (or in python itself) generate a secret key and copy it to the clipboard
`python -c 'import secrets; print(secrets.token_hex())'`
add a new line to config.py and paste in the key
`SECRET_KEY = '(paste generated key here)'`
initialize the database
`flask --app myriad init-db`
start running the site
`waitress-serve --port=80 --call 'myriad:create_app'`
after running and setting up first user stop waitress (CTRL+C), then edit the config to disable user registration for security
`sudo nano instance/config.py`
`REGISTRATION = False`
once you have disabled registration you will need to go to /auth/login in order to log in. removing the link in the sidebar just (slightly) obfuscates the possibilty from passing users
## updating
stop waitress (CTRL+C) and use `git pull` to pull changes
start waitress again to reload
if it is a database changing update, use backup features, re-init db, then re-import from backup
# config
- create `config.py` in the instance folder and customise the following settings to your needs
``` ```
REGISTRATION = True # Make sure to disable in production REGISTRATION = False # Make sure is disabled in production
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} # Can be anything you want STATIC_FOLDER = 'myriad/static'
ICON_UPLOAD_FOLDER = 'myriad/static/icons' # where member icons will be stored ICON_UPLOAD_FOLDER = 'myriad/static/icons' # where member icons will be stored
BLINKIES_UPLOAD_FOLDER = 'myriad/static/blinkies' # where site assets "blinkies" will be stored BLINKIES_UPLOAD_FOLDER = 'myriad/static/blinkies' # where blinkies will be stored
STAMPS_UPLOAD_FOLDER = 'myriad/static/stamps' # where site assets "stamps" will be stored STAMPS_UPLOAD_FOLDER = 'myriad/static/stamps' # where stamps will be stored
THEMES_FOLDER = 'myriad/static/themes' # where theme choices will be stored MISC_UPLOAD_FOLDER = 'myriad/static/misc' # where misc image uploads will be stored
TMP_FOLDER = 'myriad/static/tmp' # folder for creating export files THEMES_FOLDER = 'myriad/static/themes' # all the theme css files are here
TMP_FOLDER = 'myriad/static/tmp' # required for exports
SYSTEM_NAME = 'Myriad' # will be shown in the title bar
PAGES_NAME = 'Read More...' # will be shown in the nav bar if using custom pages
SECRET_KEY = "dev" # CHANGE THIS - see wiki for details
``` ```
# usage most likely if you get an error after updating, you need to make sure your config has all the fields as sampled above. we may have added something that you now need.
- the software here is free to use, and there's no requirement to link back # Commands
- edit the styles and functionality to suit your needs. i'm sure some of you out there are far better with CSS than I am
# dependencies (Re-)Initialize the database. **if a database already exists, this erases the entire thing!!! Use with caution**
- Flask ```
flask --app myriad init-db
```
# preview ---
- these screenshots are all from the public viewer's perspective Create an admin account. Functionally the same as the web Registration without the security issue of enable registration in the web browser. if not provided, will prompt for username and password.
- homepage with pinned members ```
![homepage with pinned members](https://i.ibb.co/mF52TSnM/Screenshot-2026-03-19-025454.png) flask --app myriad register-admin
```
- main blog post feed ```
![main blog post feed](https://i.ibb.co/tj5WCsx/Screenshot-2026-03-19-025836.png) flask --app myriad register-admin --username EXAMPLE_USER --password EXAMPLE_PASSWORD
```
- member page showing their uploaded icons (just about) # A note on data export/import
![member page](https://i.ibb.co/8LJBwVf2/Screenshot-2026-03-19-030004.png)
- groups view there are currently various ways to export and import data for use within myriad. individual member exports and imports deal only with the fields (though icons, stamps, and blinkies can be exported to a zip folder). they're designed to be used with an active database for whatever purpose the user requires.
![groups view](https://i.ibb.co/mF9JFR7x/Screenshot-2026-03-19-030108.png)
- a sample part of the administration the other option is to export the entire system to json or zip file. **importing an entire system is best used on a completely empty database, as it deals with inserting id fields**. if the database is not completely empty, there may be some weird importing errors. a full system import is useful for when an update is released that requires the database to be re-initialised, you can quickly restore information and images you had before.
![sample administration area](https://i.ibb.co/pr6qFywy/Screenshot-2026-03-19-030406.png)
**please don't rely entirely on our software, be sure to keep backups of your own data**.
# Development set up (Windows)
[Dev instructions are here](https://tea.cubes.link/cube/myriad/wiki/Development-setup-%28Windows%29)
# Deployment
[Deployment instructions are here](https://tea.cubes.link/cube/myriad/wiki/deployment-instructions)
# Usage
- The software here is free to use, and there's no requirement to link back
- Edit the styles and functionality to suit your needs. I'm sure some of you out there are far better with CSS than us

View File

@@ -1,20 +1,30 @@
import os import os, datetime
from flask import Flask from flask import Flask
from myriad.utilities import server_time, get_datetime_str, remove_html, get_pages
from myriad.db import get_db
def create_app(test_config=None): def create_app():
# create and configure the app # create and configure the app
app = Flask(__name__, instance_relative_config=True) app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('config.py') app.config.from_pyfile('config.py')
app.config.from_mapping(DATABASE=os.path.join(app.instance_path, 'database.sqlite'),) app.config.from_mapping(DATABASE=os.path.join(app.instance_path, 'database.sqlite'),)
# ensure the instance folder exists # ensure the necessary directories exist
os.makedirs(app.instance_path, exist_ok=True) os.makedirs(app.instance_path, exist_ok=True)
os.makedirs(app.config["ICON_UPLOAD_FOLDER"], exist_ok=True)
os.makedirs(app.config["BLINKIES_UPLOAD_FOLDER"], exist_ok=True)
os.makedirs(app.config["STAMPS_UPLOAD_FOLDER"], exist_ok=True)
os.makedirs(app.config["MISC_UPLOAD_FOLDER"], exist_ok=True)
os.makedirs(app.config["TMP_FOLDER"], exist_ok=True)
from . import db from . import db
db.init_app(app) db.init_app(app)
from . import commands
commands.init_commands(app)
from . import auth from . import auth
app.register_blueprint(auth.bp) app.register_blueprint(auth.bp)
@@ -33,6 +43,69 @@ def create_app(test_config=None):
def get_themes(): def get_themes():
themes = os.listdir(app.config["THEMES_FOLDER"]) themes = os.listdir(app.config["THEMES_FOLDER"])
return themes return themes
return dict(get_themes=get_themes)
def w_server_time():
return server_time()
def w_get_datetime_str(dt_obj):
return get_datetime_str(dt_obj)
def get_member(mid):
db = get_db()
member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone()
return member
def get_member_icon(mid):
db = get_db()
member = get_member(mid)
try:
icon_id = member[6]
icon_name = db.execute("SELECT * FROM icons WHERE id=(?)",(icon_id,)).fetchone()
except TypeError:
return 'any.jpg'
return icon_name[2]
def get_member_icons(mid):
db = get_db()
icons = db.execute("SELECT * FROM icons WHERE member_id=(?)",(mid,)).fetchall()
return icons
def get_group(gid):
db = get_db()
group = db.execute("SELECT * FROM groups WHERE id=(?)",(gid,)).fetchone()
return group
def get_system_name():
return app.config["SYSTEM_NAME"]
def get_pages_name():
return app.config["PAGES_NAME"]
def check_favicon():
if os.path.isfile(os.path.join(app.config["STATIC_FOLDER"], "favicon.ico")):
return True
return False
def get_pins_public():
db = get_db()
pins = db.execute("SELECT * FROM member WHERE homepage=(?) AND public=(?) ORDER BY member_name",(1, 1)).fetchall()
return pins
def get_pins_all():
db = get_db()
pins = db.execute("SELECT * FROM member WHERE homepage=(?) ORDER BY member_name",(1,)).fetchall()
return pins
return dict(get_themes=get_themes,
server_time=w_server_time,
get_datetime_str=w_get_datetime_str,
get_member=get_member,
get_member_icon=get_member_icon,
get_member_icons=get_member_icons,
get_group=get_group,
remove_html=remove_html,
get_pages=get_pages,
get_system_name=get_system_name,
get_pages_name=get_pages_name,
check_favicon=check_favicon,
get_pins_public=get_pins_public,
get_pins_all=get_pins_all)
return app return app

View File

@@ -14,9 +14,7 @@ def login_required(view):
def wrapped_view(**kwargs): def wrapped_view(**kwargs):
if g.user is None: if g.user is None:
return redirect(url_for('auth.login')) return redirect(url_for('auth.login'))
return view(**kwargs) return view(**kwargs)
return wrapped_view return wrapped_view
@@ -27,9 +25,7 @@ def load_logged_in_user():
if user_id is None: if user_id is None:
g.user = None g.user = None
else: else:
g.user = get_db().execute( g.user = get_db().execute('SELECT * FROM user WHERE id = ?', (user_id,)).fetchone()
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
@@ -50,10 +46,7 @@ def register():
if error is None: if error is None:
try: try:
db.execute( db.execute("INSERT INTO user (username, password) VALUES (?, ?)",(username, generate_password_hash(password)),)
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit() db.commit()
except db.IntegrityError: except db.IntegrityError:
error = f"User {username} is already registered." error = f"User {username} is already registered."
@@ -71,9 +64,7 @@ def login():
password = request.form['password'] password = request.form['password']
db = get_db() db = get_db()
error = None error = None
user = db.execute( user = db.execute('SELECT * FROM user WHERE username = ?', (username,)).fetchone()
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None: if user is None:
error = 'Incorrect username.' error = 'Incorrect username.'

View File

@@ -3,6 +3,7 @@ from flask import (
) )
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import os, uuid import os, uuid
from myriad.utilities import server_time, get_datetime_obj, get_datetime_str
from myriad.auth import login_required from myriad.auth import login_required
from myriad.db import get_db from myriad.db import get_db
@@ -29,15 +30,16 @@ def blog():
@login_required @login_required
def new(): def new():
db = get_db() db = get_db()
members = db.execute("SELECT id,member_name FROM member").fetchall() members = db.execute("SELECT id,member_name FROM member ORDER BY member_name COLLATE NOCASE").fetchall()
if request.method == 'POST': if request.method == 'POST':
title = request.form['title'] title = request.form['title']
content = request.form['content'] content = request.form['content']
mid = request.form["mid"] mid = request.form["mid"]
privacy = request.form["privacy"] privacy = request.form["privacy"]
created = get_datetime_obj(server_time())
db.execute("INSERT INTO blog (member_id, title, content, public) VALUES (?, ?, ?, ?)",(mid, title, content, privacy)) db.execute("INSERT INTO blog (member_id, title, content, public, created) VALUES (?, ?, ?, ?, ?)",(mid, title, content, privacy, created))
db.commit() db.commit()
return redirect(url_for('blog.blog')) return redirect(url_for('blog.blog'))

36
myriad/commands.py Normal file
View File

@@ -0,0 +1,36 @@
import click
from myriad.db import get_db
from werkzeug.security import generate_password_hash
@click.command('register-admin')
@click.option("--username", prompt="Username")
@click.option("--password", prompt="Password")
def register_admin_command(username, password):
"""Register an admin account."""
db = get_db()
valid = True
if not username.strip():
click.echo("Must enter a username")
valid = False
if not password.strip():
click.echo("Must enter a password")
valid = False
if not valid:
return
try:
db.execute("INSERT INTO user (username, password) VALUES (?, ?)",(username, generate_password_hash(password)),)
db.commit()
except db.IntegrityError:
click.echo(f"User {username} is already registered.")
return
click.echo('Admin account registered')
def init_commands(app):
app.cli.add_command(register_admin_command)

View File

@@ -5,6 +5,7 @@ from werkzeug.exceptions import abort
from myriad.auth import login_required from myriad.auth import login_required
from myriad.db import get_db from myriad.db import get_db
from myriad.utilities import get_pages
bp = Blueprint('home', __name__) bp = Blueprint('home', __name__)
@@ -12,48 +13,55 @@ bp = Blueprint('home', __name__)
def index(): def index():
db = get_db() db = get_db()
fronters = db.execute("SELECT * FROM member WHERE front=(?) ORDER BY member_name",(1,)).fetchall() fronters = db.execute("SELECT * FROM member WHERE front=(?) ORDER BY member_name",(1,)).fetchall()
homepage = db.execute("SELECT * FROM member WHERE homepage=(?) ORDER BY member_name",(1,)).fetchall() homepage = db.execute("SELECT * FROM member WHERE homepage=(?) ORDER BY member_name COLLATE NOCASE",(1,)).fetchall()
icons={} latest_start = db.execute("SELECT start_time FROM front_log ORDER BY start_time DESC").fetchone()
for member in homepage: latest_end = db.execute("SELECT end_time FROM front_log ORDER BY end_time DESC").fetchone()
icon_id = member[6]
if icon_id: latest = None
icon = db.execute("SELECT icon_location FROM icons WHERE id=(?)",(icon_id,)).fetchone() if latest_start and latest_end:
if icon: start = latest_start[0]
icons[member[0]] = icon[0] end = latest_end[0]
if end:
if start > end:
latest = start
else: else:
icons[member[0]] = None latest = end
else:
latest = start
elif latest_start and not latest_end:
latest = latest_start[0]
return render_template('index.html', front_list=fronters, home_pins=homepage, icons=icons) return render_template('index.html', front_list=fronters, home_pins=homepage, last_updated=latest)
@bp.route('/full') @bp.route('/full')
def full_list(): def full_list():
db = get_db() db = get_db()
members = db.execute('SELECT * FROM member ORDER BY member_name').fetchall() members = db.execute('SELECT * FROM member ORDER BY member_name COLLATE NOCASE').fetchall()
icons={} return render_template('full.html', memberlist=members)
for member in members:
icon_id = member[6]
if icon_id:
icon = db.execute("SELECT icon_location FROM icons WHERE id=(?)",(icon_id,)).fetchone()
if icon:
icons[member[0]] = icon[0]
else:
icons[member[0]] = None
return render_template('full.html', memberlist=members, icons=icons)
@bp.route('/member/<mid>') @bp.route('/member/<mid>')
def page(mid): def page(mid):
db = get_db() db = get_db()
try:
member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone() member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone()
blog = db.execute("SELECT * FROM blog WHERE member_id=(?) ORDER BY created DESC",(mid,)).fetchall() blog = db.execute("SELECT * FROM blog WHERE member_id=(?) ORDER BY created DESC",(mid,)).fetchall()
icon = db.execute("SELECT icon_location FROM icons WHERE id=(?)",(member[6],)).fetchone() icon = db.execute("SELECT icon_location FROM icons WHERE id=(?)",(member[6],)).fetchone()
all_icons = db.execute("SELECT icon_location FROM icons WHERE member_id=(?)",(mid,)).fetchall() all_icons = db.execute("SELECT icon_location FROM icons WHERE member_id=(?)",(mid,)).fetchall()
blinkies = db.execute("SELECT blinkie_location FROM blinkies WHERE member_id=(?)",(mid,)).fetchall() blinkies = db.execute("SELECT blinkie_location FROM blinkies WHERE member_id=(?)",(mid,)).fetchall()
stamps = db.execute("SELECT stamp_location FROM stamps WHERE member_id=(?)",(mid,)).fetchall() stamps = db.execute("SELECT stamp_location FROM stamps WHERE member_id=(?)",(mid,)).fetchall()
blog_public = db.execute("SELECT * FROM blog WHERE member_id=(?) AND public=(?)",(mid,1)).fetchall()
sections = db.execute("SELECT * FROM sections WHERE member_id=(?) ORDER BY position ASC",(mid,)).fetchall()
groups = db.execute("SELECT * FROM group_members WHERE member_id=(?)",(mid,)).fetchall()
except TypeError:
return "Not Found <br> <a href='/'>Go Home</a>", 404
return render_template('page.html', member=member, blog=blog, icon=icon, all_icons=all_icons, blinkies=blinkies, stamps=stamps) blog_public_show = False
if len(blog_public) > 0:
blog_public_show = True
return render_template('page.html', member=member, blog=blog, icon=icon, all_icons=all_icons, blinkies=blinkies, stamps=stamps, blog_public_show=blog_public_show, sections=sections, groups=groups)
@bp.route("/groups") @bp.route("/groups")
def groups(): def groups():
@@ -71,3 +79,31 @@ def groups():
group_members[gid]=[member] group_members[gid]=[member]
return render_template("groups.html", groups=groups, group_members=group_members) return render_template("groups.html", groups=groups, group_members=group_members)
@bp.route("/group/<gid>")
def group_page(gid):
db = get_db()
try:
group = db.execute("SELECT * FROM groups WHERE id=(?)",(gid,)).fetchone()
group_members = db.execute("SELECT * FROM group_members WHERE group_id=(?)",(gid,)).fetchall()
members = []
for member in group_members:
m = db.execute("SELECT * FROM member WHERE id=(?)",(member[2],)).fetchone()
members.append(m)
except TypeError:
return "Not Found <br> <a href='/'>Go Home</a>", 404
return render_template('group_page.html', group=group, members=members)
@bp.route("/page/<pid>")
def custom_page(pid):
db = get_db()
try:
page = db.execute("SELECT * FROM pages WHERE id=(?)",(pid,)).fetchone()
except TypeError:
return "Not Found <br> <a href='/'>Go Home</a>", 404
return render_template('custom_page.html', page=page)
@bp.route("/page")
def pages():
pages = get_pages()
return render_template('pages.html', pages=pages)

View File

@@ -1,10 +1,12 @@
from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for, current_app, send_file, send_from_directory from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for, current_app, send_file, send_from_directory
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import os, uuid, json, zipfile, datetime import os, uuid, json, zipfile, datetime, sqlite3
from myriad.utilities import server_time, get_datetime_obj, server_time_obj
from myriad.auth import login_required from myriad.auth import login_required
from myriad.db import get_db from myriad.db import get_db
from myriad.utilities import get_datetime_str
bp = Blueprint('manage', __name__, url_prefix='/manage') bp = Blueprint('manage', __name__, url_prefix='/manage')
@@ -16,11 +18,18 @@ def new():
subtitle = request.form["subtitle"] subtitle = request.form["subtitle"]
bio = request.form['bio'] bio = request.form['bio']
user_id = g.user[0] user_id = g.user[0]
date_created = get_datetime_obj(server_time())
privacy = request.form["privacy"]
db = get_db() db = get_db()
db.execute("INSERT INTO member (user_id, member_name, bio, subtitle) VALUES (?, ?, ?, ?)",(user_id, name, bio, subtitle)) db.execute("INSERT INTO member (user_id, member_name, bio, subtitle, created, public) VALUES (?, ?, ?, ?, ?, ?)",(user_id, name, bio, subtitle, date_created, privacy))
db.commit() db.commit()
return redirect(url_for('home.full_list'))
last = db.execute('SELECT last_insert_rowid()').fetchone()
new_mid = last[0]
return redirect(url_for('manage.edit', mid=new_mid))
return render_template('manage/new.html') return render_template('manage/new.html')
@@ -29,7 +38,6 @@ def new():
def import_member_route(): def import_member_route():
db = get_db() db = get_db()
response="" response=""
data=None
mid=None mid=None
if request.method=="POST": if request.method=="POST":
@@ -72,7 +80,31 @@ def edit(mid):
db.execute("UPDATE member SET member_name=(?), bio=(?), subtitle=(?), public=(?), theme=(?) WHERE id=(?)",(name, bio, subtitle, privacy, theme, mid)) db.execute("UPDATE member SET member_name=(?), bio=(?), subtitle=(?), public=(?), theme=(?) WHERE id=(?)",(name, bio, subtitle, privacy, theme, mid))
db.commit() db.commit()
edit_location="details" edit_location = "details"
if "page_settings" in request.form:
show_groups = "show_groups" in request.form
show_blog = "show_blog" in request.form
show_icons = "show_icons" in request.form
show_blinkies = "show_blinkies" in request.form
show_stamps = "show_stamps" in request.form
db.execute("UPDATE member SET show_groups=(?), show_blog=(?), show_icons=(?), show_blinkies=(?), show_stamps=(?) WHERE id=(?)",(show_groups, show_blog, show_icons, show_blinkies, show_stamps, mid))
db.commit()
edit_location = "page_settings"
if "section_titles" in request.form:
groups_title = request.form["groups_title"]
blog_title = request.form["blog_title"]
icons_title = request.form["icons_title"]
blinkies_title = request.form["blinkies_title"]
stamps_title = request.form["stamps_title"]
db.execute("UPDATE member SET groups_title=(?), blog_title=(?), icons_title=(?), blinkies_title=(?), stamps_title=(?) WHERE id=(?)",(groups_title, blog_title, icons_title, blinkies_title, stamps_title, mid))
db.commit()
edit_location = "page_settings"
if "file" in request.files: if "file" in request.files:
# here we are just saving the uploaded file to the icons folder. # here we are just saving the uploaded file to the icons folder.
@@ -84,21 +116,21 @@ def edit(mid):
db.execute("INSERT INTO icons (member_id, icon_location) VALUES (?, ?)", (mid, filename)) db.execute("INSERT INTO icons (member_id, icon_location) VALUES (?, ?)", (mid, filename))
db.commit() db.commit()
edit_location="icons" edit_location = "icons"
if "gid_add" in request.form: if "gid_add" in request.form:
gid = request.form["gid_add"] gid = request.form["gid_add"]
db.execute("INSERT INTO group_members (group_id,member_id) VALUES (?,?)",(gid,mid)) db.execute("INSERT INTO group_members (group_id,member_id) VALUES (?,?)",(gid,mid))
db.commit() db.commit()
edit_location="groups" edit_location = "groups"
elif "gid_remove" in request.form: elif "gid_remove" in request.form:
gid = request.form["gid_remove"] gid = request.form["gid_remove"]
db.execute("DELETE FROM group_members WHERE group_id=(?) AND member_id=(?)",(gid,mid)) db.execute("DELETE FROM group_members WHERE group_id=(?) AND member_id=(?)",(gid,mid))
db.commit() db.commit()
edit_location="groups" edit_location = "groups"
if "blinkie" in request.files: if "blinkie" in request.files:
file = request.files["blinkie"] file = request.files["blinkie"]
@@ -107,7 +139,7 @@ def edit(mid):
db.execute("INSERT INTO blinkies (member_id, blinkie_location) VALUES (?, ?)", (mid, filename)) db.execute("INSERT INTO blinkies (member_id, blinkie_location) VALUES (?, ?)", (mid, filename))
db.commit() db.commit()
edit_location="blinkies" edit_location = "blinkies"
if "stamp" in request.files: if "stamp" in request.files:
file = request.files["stamp"] file = request.files["stamp"]
@@ -116,13 +148,34 @@ def edit(mid):
db.execute("INSERT INTO stamps (member_id, stamp_location) VALUES (?, ?)", (mid, filename)) db.execute("INSERT INTO stamps (member_id, stamp_location) VALUES (?, ?)", (mid, filename))
db.commit() db.commit()
edit_location="stamps" edit_location = "stamps"
if "new_section" in request.form:
section_title = request.form["section_title"]
section_content = request.form["section_content"]
db.execute("INSERT INTO sections (member_id, title, content) VALUES (?, ?, ?)", (mid, section_title, section_content))
db.commit()
edit_location = "sections"
if "update_section" in request.form:
section_id = request.form["section_id"]
section_title = request.form["section_title"]
section_content = request.form["section_content"]
section_position = request.form["section_pos"]
db.execute("UPDATE sections SET title=(?), content=(?), position=(?) WHERE id=(?)",(section_title, section_content, section_position, section_id))
db.commit()
edit_location = "sections"
member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone() member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone()
icons = db.execute("SELECT * FROM icons WHERE member_id=(?)",(mid,)).fetchall() icons = db.execute("SELECT * FROM icons WHERE member_id=(?)",(mid,)).fetchall()
blinkies = db.execute("SELECT * FROM blinkies WHERE member_id=(?)",(mid,)).fetchall() blinkies = db.execute("SELECT * FROM blinkies WHERE member_id=(?)",(mid,)).fetchall()
stamps = db.execute("SELECT * FROM stamps WHERE member_id=(?)",(mid,)).fetchall() stamps = db.execute("SELECT * FROM stamps WHERE member_id=(?)",(mid,)).fetchall()
sections = db.execute("SELECT * FROM sections WHERE member_id=(?)",(mid,)).fetchall()
groups = db.execute("SELECT * FROM groups").fetchall() groups = db.execute("SELECT * FROM groups").fetchall()
member_groups = db.execute("SELECT * FROM group_members WHERE member_id=(?)",(mid,)).fetchall() member_groups = db.execute("SELECT * FROM group_members WHERE member_id=(?)",(mid,)).fetchall()
@@ -142,7 +195,18 @@ def edit(mid):
themes = os.listdir(current_app.config["THEMES_FOLDER"]) themes = os.listdir(current_app.config["THEMES_FOLDER"])
return render_template("manage/edit.html", member=member, icons=icons, unjoined_groups=unjoined_groups, joined_groups=joined_groups, themes=themes, edit_location=edit_location, blinkies=blinkies, stamps=stamps) return render_template("manage/edit.html", member=member, icons=icons, unjoined_groups=unjoined_groups, joined_groups=joined_groups, themes=themes, edit_location=edit_location, blinkies=blinkies, stamps=stamps, sections=sections)
@bp.route("/delete_section/<sid>")
@login_required
def delete_section(sid):
db = get_db()
mid = db.execute("SELECT member_id FROM sections WHERE id=(?)",(sid,)).fetchone()[0]
db.execute("DELETE FROM sections WHERE id=(?)",(sid,))
db.commit()
return redirect(url_for("manage.edit", mid=mid))
@bp.route("/set_main_icon/<mid>/<icon_id>") @bp.route("/set_main_icon/<mid>/<icon_id>")
@login_required @login_required
@@ -194,8 +258,13 @@ def add_to_front(mid,location):
db.execute("UPDATE member SET front=(?) WHERE id=(?)",(1, mid)) db.execute("UPDATE member SET front=(?) WHERE id=(?)",(1, mid))
db.commit() db.commit()
db.execute("INSERT INTO front_log (member_id, start_time) VALUES (?, ?)",(mid, server_time_obj()))
db.commit()
if location == "home": if location == "home":
return redirect(url_for('index')) return redirect(url_for('index'))
elif location == "mid":
return redirect(url_for('home.page', mid=mid))
else: else:
return redirect(url_for('home.full_list')) return redirect(url_for('home.full_list'))
@@ -206,11 +275,27 @@ def remove_front(mid, location):
db.execute("UPDATE member SET front=(?) WHERE id=(?)",(0, mid)) db.execute("UPDATE member SET front=(?) WHERE id=(?)",(0, mid))
db.commit() db.commit()
current_entry = db.execute("SELECT id FROM front_log WHERE member_id=(?) AND end_time IS NULL",(mid,)).fetchone()
current_entry_id = current_entry[0]
db.execute("UPDATE front_log SET end_time=(?) WHERE id=(?)",(server_time_obj(), current_entry_id))
db.commit()
if location == "home": if location == "home":
return redirect(url_for('index')) return redirect(url_for('index'))
elif location == "mid":
return redirect(url_for('home.page', mid=mid))
else: else:
return redirect(url_for('home.full_list')) return redirect(url_for('home.full_list'))
@bp.route("/delete_front_log/<fid>")
@login_required
def delete_front_log(fid):
db = get_db()
db.execute("DELETE FROM front_log WHERE id=(?)",(fid,))
db.commit()
return redirect(url_for('manage.admin'))
@bp.route("/add_to_home/<mid>/<location>") @bp.route("/add_to_home/<mid>/<location>")
@login_required @login_required
@@ -221,6 +306,8 @@ def add_to_home(mid, location):
if location == "home": if location == "home":
return redirect(url_for('index')) return redirect(url_for('index'))
elif location == "mid":
return redirect(url_for('home.page', mid=mid))
else: else:
return redirect(url_for('home.full_list')) return redirect(url_for('home.full_list'))
@@ -233,6 +320,8 @@ def remove_home(mid,location):
if location == "home": if location == "home":
return redirect(url_for('index')) return redirect(url_for('index'))
elif location == "mid":
return redirect(url_for('home.page', mid=mid))
else: else:
return redirect(url_for('home.full_list')) return redirect(url_for('home.full_list'))
@@ -249,18 +338,50 @@ def import_groups(groups):
else: else:
privacy = 0 privacy = 0
db.execute("INSERT INTO groups (id, group_name, group_description, public) VALUES (?, ?, ?, ?)", (gid, name, description, privacy)) try:
db.execute("INSERT INTO groups (id, group_name, group_description, public) VALUES (?, ?, ?, ?)",
(gid, name, description, privacy))
db.commit() db.commit()
except sqlite3.IntegrityError:
pass
def import_sections(sections):
db = get_db()
for section in sections:
sid = section["id"]
mid = section["member_id"]
title = section["title"]
content = section["content"]
position = section["position"]
try:
db.execute("INSERT INTO sections (id, member_id, title, content, position) VALUES (?, ?, ?, ?, ?)",
(sid, mid, title, content, position))
db.commit()
except sqlite3.IntegrityError:
pass
def import_pages(pages):
db = get_db()
for page in pages:
pid = page["id"]
title = page["title"]
content = page["content"]
position = page["position"]
try:
db.execute("INSERT INTO pages (id, title, content, position) VALUES (?, ?, ?, ?)",
(pid, title, content, position))
db.commit()
except sqlite3.IntegrityError:
pass
def import_member(member): def import_member(member):
db = get_db() db = get_db()
date_raw = member["date-created"].split(",") mid = member["id"]
date = date_raw[0]
day,month,year = date.split("/") date_created_obj = get_datetime_obj(member["date-created"])
time = date_raw[1]
hour,minute,second = time.split(":")
date_created = datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
name = member["name"] name = member["name"]
subtitle = member["subtitle"] subtitle = member["subtitle"]
@@ -272,9 +393,93 @@ def import_member(member):
privacy = 0 privacy = 0
theme = member["theme"] theme = member["theme"]
homepage = member["homepage-pin"]
user_id = 0
main_icon_id = ""
groups = member["groups"]
for group in groups:
db.execute("INSERT INTO group_members (group_id, member_id) VALUES (?, ?)",(group, mid))
db.commit()
blog = member["blog"]
for post in blog:
post_date_created = get_datetime_obj(post["date-created"])
title = post["title"]
content = post["content"]
if post["privacy"] == "public":
privacy = 1
else:
privacy = 0
db.execute("INSERT INTO blog (member_id, created, title, content, public) VALUES (?, ?, ?, ?, ?)",(mid, post_date_created, title, content, privacy))
db.commit()
icons = member["icons"]
for icon in icons:
db.execute("INSERT INTO icons (member_id, icon_location) VALUES (?, ?)",(mid, icon))
db.commit()
if icon == member["main-icon"]:
last = db.execute('SELECT last_insert_rowid()').fetchone()
main_icon_id = last[0]
blinkies = member["blinkies"]
for blinkie in blinkies:
db.execute("INSERT INTO blinkies (member_id, blinkie_location) VALUES (?, ?)",(mid, blinkie))
db.commit()
stamps = member["stamps"]
for stamp in stamps:
db.execute("INSERT INTO stamps (member_id, stamp_location) VALUES (?, ?)",(mid, stamp))
db.commit()
show_blog = member["show-blog"]
show_icons = member["show-icons"]
show_blinkies = member["show-blinkies"]
show_stamps = member["show-stamps"]
show_groups = member["show-groups"]
blog_title = member["blog-title"]
icons_title = member["icons-title"]
blinkies_title = member["blinkies-title"]
stamps_title = member["stamps-title"]
groups_title = member["groups-title"]
try:
db.execute("""
INSERT INTO member
(id,created,user_id, member_name,subtitle,
bio,public,theme,homepage,main_icon,show_blog,
show_icons,show_blinkies,show_stamps,show_groups,
blog_title,icons_title,blinkies_title,stamps_title,groups_title)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(mid,
date_created_obj,
user_id,
name,
subtitle,
description,
privacy,
theme,
homepage,
main_icon_id,
show_blog,
show_icons,
show_blinkies,
show_stamps,
show_groups,
blog_title,
icons_title,
blinkies_title,
stamps_title,
groups_title))
db.commit()
except sqlite3.IntegrityError:
pass
#db.execute("INSERT INTO member (created,user_id, member_name, bio,public) VALUES (?, ?, ?, ?, ?)",(data[0], data[1], data[2], data[3], data[4]))
#db.commit()
@bp.route("/admin", methods=("GET", "POST")) @bp.route("/admin", methods=("GET", "POST"))
@@ -295,15 +500,54 @@ def admin():
for member in members: for member in members:
import_member(member) import_member(member)
return "<a href='/'>go home</a>" return "<a href='/'>go home</a>"
elif "zip" in request.files: elif "zip" in request.files:
return "upload zip" file = request.files["zip"]
with zipfile.ZipFile(file, "r") as zipf:
for f in zipf.namelist():
dirs = f.split("/")
content_type = dirs[2]
filename = dirs[3]
if content_type == "tmp":
system_content = zipf.read(f)
system_json = json.loads(system_content.decode())
groups_i = system_json["groups"]
sections = system_json["sections"]
pages_i = system_json["pages"]
import_groups(groups_i)
import_sections(sections)
import_pages(pages_i)
for m in system_json["members"]:
import_member(m)
else:
zipf.extract(f)
return "<a href='/'>go home</a>"
elif "new_page" in request.form:
page_title = request.form["page_title"]
page_content = request.form["page_content"]
db.execute("INSERT INTO pages (title, content) VALUES (?, ?)", (page_title, page_content))
db.commit()
elif "favicon" in request.files:
file = request.files["favicon"]
filename = "favicon.ico"
file.save(os.path.join(current_app.config["STATIC_FOLDER"], filename))
users = db.execute("SELECT * FROM user").fetchall() users = db.execute("SELECT * FROM user").fetchall()
front_log = db.execute("SELECT * FROM front_log ORDER BY start_time DESC").fetchall()
pages = db.execute("SELECT * FROM pages").fetchall()
return render_template("manage/admin.html", users=users) return render_template("manage/admin.html", users=users, front_log=front_log, pages=pages)
@@ -318,20 +562,25 @@ def generate_json(mid):
member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone() member = db.execute("SELECT * FROM member WHERE id=(?)",(mid,)).fetchone()
groups_r = db.execute("SELECT group_id FROM group_members WHERE member_id=(?)",(mid,)).fetchall() groups_r = db.execute("SELECT group_id FROM group_members WHERE member_id=(?)",(mid,)).fetchall()
blog_r = db.execute("SELECT * FROM blog WHERE member_id=(?)",(mid,)).fetchall() blog_r = db.execute("SELECT * FROM blog WHERE member_id=(?)",(mid,)).fetchall()
icons_r = db.execute("SELECT icon_location FROM icons WHERE member_id=(?)",(mid,)).fetchall()
blinkies_r = db.execute("SELECT blinkie_location FROM blinkies WHERE member_id=(?)",(mid,)).fetchall()
stamps_r = db.execute("SELECT stamp_location FROM stamps WHERE member_id=(?)",(mid,)).fetchall()
if member[9] == 1: if member[9] == 1:
privacy = "public" member_privacy = "public"
else: else:
privacy = "private" member_privacy = "private"
date_created = member[2].strftime("%d/%m/%Y, %H:%M:%S") date_created = get_datetime_str(member[2])
homepage = member[7]
groups = [] groups = []
for group in groups_r: for group in groups_r:
groups.append(group[0]) groups.append(group[0])
blog = [] blog = []
for post in blog_r: for post in blog_r:
d_c = post[2].strftime("%d/%m/%Y, %H:%M:%S") d_c = get_datetime_str(post[2])
if post[5] == 1: if post[5] == 1:
priv = "public" priv = "public"
@@ -350,6 +599,21 @@ def generate_json(mid):
blog.append(p) blog.append(p)
icons = []
for icon in icons_r:
icons.append(icon[0])
main_icon_id = member[6]
main_icon = db.execute("SELECT icon_location FROM icons WHERE id=(?)",(main_icon_id,)).fetchone()
blinkies = []
for blinkie in blinkies_r:
blinkies.append(blinkie[0])
stamps = []
for stamp in stamps_r:
stamps.append(stamp[0])
data = { data = {
"id":mid, "id":mid,
@@ -358,11 +622,29 @@ def generate_json(mid):
"name":member[3], "name":member[3],
"subtitle":member[4], "subtitle":member[4],
"description":member[5], "description":member[5],
"privacy":privacy, "privacy":member_privacy,
"theme":member[10], "theme":member[10],
"groups":groups, "groups":groups,
"blog":blog "blog":blog,
"icons":icons,
"blinkies":blinkies,
"stamps":stamps,
"homepage-pin":homepage,
"show-blog":member[11],
"show-icons":member[12],
"show-blinkies":member[13],
"show-stamps":member[14],
"show-groups":member[15],
"blog-title":member[16],
"icons-title":member[17],
"blinkies-title":member[18],
"stamps-title":member[19],
"groups-title":member[20]
} }
if main_icon:
data["main-icon"] = main_icon[0]
else:
data["main-icon"] = ""
return data return data
@@ -387,6 +669,64 @@ def generate_json_groups():
groups.append(g) groups.append(g)
return groups return groups
def generate_json_frontlog():
db = get_db()
front_log_r = db.execute("SELECT * FROM front_log ORDER BY start_time DESC").fetchall()
front_log = []
for front in front_log_r:
start_d_c = get_datetime_str(front[2])
if front[3]:
end_d_c = get_datetime_str(front[3])
else:
end_d_c = "(no end time)"
f = {
"id":front[0],
"member_id":front[1],
"start_time":start_d_c,
"end_time":end_d_c
}
front_log.append(f)
return front_log
def generate_json_sections():
db = get_db()
sections_r = db.execute("SELECT * FROM sections").fetchall()
sections = []
for section in sections_r:
s = {
"id":section[0],
"member_id":section[1],
"title":section[2],
"content":section[3],
"position":section[4]
}
sections.append(s)
return sections
def generate_json_pages():
db=get_db()
pages_r = db.execute("SELECT * FROM pages").fetchall()
pages=[]
for page in pages_r:
p = {
"id":page[0],
"title":page[1],
"content":page[2],
"position":page[3]
}
pages.append(p)
return pages
@bp.route("/export_fields/<mid>") @bp.route("/export_fields/<mid>")
@login_required @login_required
def export_fields(mid): def export_fields(mid):
@@ -493,7 +833,6 @@ def export_member(mid):
def export_system(): def export_system():
db = get_db() db = get_db()
members = db.execute("SELECT * FROM member").fetchall() members = db.execute("SELECT * FROM member").fetchall()
groups_r = db.execute("SELECT * FROM groups").fetchall()
data = {} data = {}
data["members"] = [] data["members"] = []
@@ -504,6 +843,15 @@ def export_system():
groups = generate_json_groups() groups = generate_json_groups()
data["groups"] = groups data["groups"] = groups
front_log = generate_json_frontlog()
data["front_log"] = front_log
sections = generate_json_sections()
data["sections"] = sections
pages = generate_json_pages()
data["pages"] = pages
filename = "myriad_system_textonly.json" filename = "myriad_system_textonly.json"
file_full_path = current_app.config["TMP_FOLDER"] + "/" + filename file_full_path = current_app.config["TMP_FOLDER"] + "/" + filename
with open(file_full_path, 'w') as f: with open(file_full_path, 'w') as f:
@@ -519,6 +867,7 @@ def export_system_full():
icons = db.execute("SELECT icon_location FROM icons").fetchall() icons = db.execute("SELECT icon_location FROM icons").fetchall()
blinkies = db.execute("SELECT blinkie_location FROM blinkies").fetchall() blinkies = db.execute("SELECT blinkie_location FROM blinkies").fetchall()
stamps = db.execute("SELECT stamp_location FROM stamps").fetchall() stamps = db.execute("SELECT stamp_location FROM stamps").fetchall()
misc = os.listdir(current_app.config["MISC_UPLOAD_FOLDER"])
data = {} data = {}
data["members"] = [] data["members"] = []
@@ -529,6 +878,15 @@ def export_system_full():
groups = generate_json_groups() groups = generate_json_groups()
data["groups"] = groups data["groups"] = groups
front_log = generate_json_frontlog()
data["front_log"] = front_log
sections = generate_json_sections()
data["sections"] = sections
pages = generate_json_pages()
data["pages"] = pages
filename = "myriad_system.json" filename = "myriad_system.json"
file_full_path = current_app.config["TMP_FOLDER"] + "/" + filename file_full_path = current_app.config["TMP_FOLDER"] + "/" + filename
with open(file_full_path, 'w') as f: with open(file_full_path, 'w') as f:
@@ -547,6 +905,8 @@ def export_system_full():
for stamp in stamps: for stamp in stamps:
sname = stamp[0] sname = stamp[0]
zipf.write(current_app.config["STAMPS_UPLOAD_FOLDER"] + "/" + sname) zipf.write(current_app.config["STAMPS_UPLOAD_FOLDER"] + "/" + sname)
#for misc_file in misc:
#zipf.write(current_app.config["MISC_UPLOAD_FOLDER"] + "/" + misc_file)
return send_file("static/tmp/"+zip_name, as_attachment=True) return send_file("static/tmp/"+zip_name, as_attachment=True)
@@ -571,6 +931,22 @@ def assets():
filename = file.filename filename = file.filename
file.save(os.path.join(current_app.config["STAMPS_UPLOAD_FOLDER"], filename)) file.save(os.path.join(current_app.config["STAMPS_UPLOAD_FOLDER"], filename))
if "image" in request.files:
file = request.files["image"]
filename = file.filename
fname, ftype = filename.split(".")
i = 0
if os.path.exists(os.path.join(current_app.config["MISC_UPLOAD_FOLDER"], filename)):
i = 2
while os.path.exists(os.path.join(current_app.config["MISC_UPLOAD_FOLDER"], fname+str(i)+"."+ftype)):
i += 1
if i == 0:
file.save(os.path.join(current_app.config["MISC_UPLOAD_FOLDER"], filename))
else:
file.save(os.path.join(current_app.config["MISC_UPLOAD_FOLDER"], fname+str(i)+"."+ftype))
icons = db.execute("SELECT * FROM icons").fetchall() icons = db.execute("SELECT * FROM icons").fetchall()
icon_storage = os.listdir(current_app.config["ICON_UPLOAD_FOLDER"]) icon_storage = os.listdir(current_app.config["ICON_UPLOAD_FOLDER"])
@@ -593,8 +969,9 @@ def assets():
blinkies = os.listdir(current_app.config["BLINKIES_UPLOAD_FOLDER"]) blinkies = os.listdir(current_app.config["BLINKIES_UPLOAD_FOLDER"])
stamps = os.listdir(current_app.config["STAMPS_UPLOAD_FOLDER"]) stamps = os.listdir(current_app.config["STAMPS_UPLOAD_FOLDER"])
images = os.listdir(current_app.config["MISC_UPLOAD_FOLDER"])
return render_template("manage/assets.html", icons=unlinked_icons, icon_storage=i_storage, blinkies=blinkies, stamps=stamps) return render_template("manage/assets.html", icons=unlinked_icons, icon_storage=i_storage, blinkies=blinkies, stamps=stamps, images=images)
@bp.route("/delete_idb") @bp.route("/delete_idb")
@login_required @login_required
@@ -675,7 +1052,30 @@ def group_delete(gid):
return redirect(url_for("manage.groups")) return redirect(url_for("manage.groups"))
@bp.route("/edit_page/<pid>", methods=("GET", "POST"))
@login_required
def edit_page(pid):
db = get_db()
if request.method == "POST":
content = request.form["content"]
title = request.form["title"]
db.execute("UPDATE pages SET content=(?), title=(?) WHERE id=(?)",(content, title, pid))
db.commit()
page = db.execute("SELECT * FROM pages WHERE id=(?)",(pid,)).fetchone()
return render_template("manage/edit_custom_page.html", page=page)
@bp.route("/delete_page/<pid>")
@login_required
def delete_page(pid):
db = get_db()
db.execute("DELETE FROM pages WHERE id=(?)",(pid,))
db.commit()
return redirect(url_for("manage.admin"))

View File

@@ -6,7 +6,10 @@ DROP TABLE IF EXISTS group_members;
DROP TABLE IF EXISTS blog; DROP TABLE IF EXISTS blog;
DROP TABLE IF EXISTS blinkies; DROP TABLE IF EXISTS blinkies;
DROP TABLE IF EXISTS stamps; DROP TABLE IF EXISTS stamps;
DROP TABLE IF EXISTS front_log;
DROP TABLE IF EXISTS sections; DROP TABLE IF EXISTS sections;
DROP TABLE IF EXISTS pages;
DROP TABLE IF EXISTS custom_urls;
CREATE TABLE user ( CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -17,7 +20,7 @@ CREATE TABLE user (
CREATE TABLE member ( CREATE TABLE member (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created TIMESTAMP,
member_name TEXT NOT NULL, member_name TEXT NOT NULL,
subtitle TEXT DEFAULT "", subtitle TEXT DEFAULT "",
bio TEXT, bio TEXT,
@@ -26,6 +29,16 @@ CREATE TABLE member (
front BOOLEAN NOT NULL DEFAULT 0, front BOOLEAN NOT NULL DEFAULT 0,
public BOOLEAN NOT NULL DEFAULT 1, public BOOLEAN NOT NULL DEFAULT 1,
theme TEXT NOT NULL DEFAULT 'default', theme TEXT NOT NULL DEFAULT 'default',
show_blog BOOLEAN NOT NULL DEFAULT 1,
show_icons BOOLEAN NOT NULL DEFAULT 1,
show_blinkies BOOLEAN NOT NULL DEFAULT 1,
show_stamps BOOLEAN NOT NULL DEFAULT 1,
show_groups BOOLEAN NOT NULL DEFAULT 1,
blog_title TEXT DEFAULT "Blog",
icons_title TEXT DEFAULT "Icons",
blinkies_title TEXT DEFAULT "Blinkies",
stamps_title TEXT DEFAULT "Stamps",
groups_title TEXT DEFAULT "Groups",
FOREIGN KEY (user_id) REFERENCES user (id), FOREIGN KEY (user_id) REFERENCES user (id),
FOREIGN KEY (main_icon) REFERENCES icons (id) FOREIGN KEY (main_icon) REFERENCES icons (id)
); );
@@ -55,7 +68,7 @@ CREATE TABLE group_members (
CREATE TABLE blog ( CREATE TABLE blog (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL, member_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created TIMESTAMP,
title TEXT, title TEXT,
content TEXT, content TEXT,
public BOOLEAN NOT NULL DEFAULT 1, public BOOLEAN NOT NULL DEFAULT 1,
@@ -76,10 +89,33 @@ CREATE TABLE stamps (
FOREIGN KEY (member_id) REFERENCES member (id) FOREIGN KEY (member_id) REFERENCES member (id)
); );
CREATE TABLE front_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
start_time TIMESTAMP,
end_time TIMESTAMP,
FOREIGN KEY (member_id) REFERENCES member (id)
);
CREATE TABLE sections ( CREATE TABLE sections (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL, member_id INTEGER NOT NULL,
title TEXT, title TEXT,
content TEXT, content TEXT,
position INTEGER,
FOREIGN KEY (member_id) REFERENCES member (id)
);
CREATE TABLE pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT,
position INTEGER
);
CREATE TABLE custom_urls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
custom_url TEXT,
FOREIGN KEY (member_id) REFERENCES member (id) FOREIGN KEY (member_id) REFERENCES member (id)
); );

BIN
myriad/static/default.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
myriad/static/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

View File

@@ -25,6 +25,7 @@ a:hover{
form{ form{
margin:20px; margin:20px;
max-width:800px;
} }
label,input{ label,input{
margin:10px; margin:10px;
@@ -41,37 +42,6 @@ form textarea{
} }
#main{
background-color: rgb(255, 255, 255, 0);
border-radius: 5px;
width: 60%;
margin:auto;
padding:20px;
display:flex;
margin-top:20px;
}
#nav{
margin-right:20px;
flex:20%;
height:fit-content;
position:sticky;
position: -webkit-sticky;
}
.navitem{
display:block;
}
.container{
background-color:#e6f7ff;
border-color:#99dfff;
border-style:solid;
border-width:2px;
flex: 80%;
padding: 10px;
border-radius:5px;
}
.profile{ .profile{
margin:15px; margin:15px;
border-style:solid; border-style:solid;
@@ -124,6 +94,33 @@ form textarea{
height:100px; height:100px;
float:left; float:left;
} }
.manage_images_3{
display:block;
width:180px;
height:100px;
float:left;
}
.blinkie
{
height:20px;
width:auto;
}
.stamp
{
height:56px;
width:auto;
}
.mng_img
{
height:56px;
width:auto;
object-fit:scale-down;
max-width:180px;
overflow:hidden;
}
@@ -164,9 +161,9 @@ form textarea{
.title{ .title{
font-size:20px; font-size:20px;
display:block;
margin-top:20px; margin-top:20px;
margin-bottom:20px; margin-bottom:20px;
display:block;
} }
.heading.big{ .heading.big{
font-size:20px; font-size:20px;
@@ -182,6 +179,17 @@ form textarea{
display:block; display:block;
} }
.lock{
float:left;
margin-right:10px;
}
.minilock{
width:16px;
height:auto;
float:left;
margin-right:10px;
}
#blog{ #blog{
max-height:300px; max-height:300px;
overflow-y:scroll; overflow-y:scroll;
@@ -225,16 +233,102 @@ form textarea{
font-size:10px; font-size:10px;
} }
.log{
max-height:300px;
overflow-y:scroll;
}
.image-sections{
margin-top:20px;
margin-bottom:20px;
}
#mobile-nav{
display:none;
background-color:#e6f7ff;
border-color:#99dfff;
border-style:solid;
border-width:2px;
border-radius:5px;
top:0;
position:absolute;
padding:10px;
margin-top:20px;
}
#main{
background-color: rgb(255, 255, 255, 0);
border-radius: 5px;
width: 60%;
margin:auto;
padding:20px;
display:flex;
margin-top:40px;
}
#nav{
margin-left:-20px;
margin-right:20px;
flex:25%;
height:fit-content;
position:sticky;
position: -webkit-sticky;
}
.navitem{
display:block;
}
.container{
background-color:#e6f7ff;
border-color:#99dfff;
border-style:solid;
border-width:2px;
border-radius:5px;
flex: 80%;
padding: 10px;
}
@media (max-width: 1000px) {
.mobile{
display:none;
}
@media (max-width: 1000px)
{
#main{ #main{
width:90%; width:100%;
margin-left:-20px;
}
.profile{
width:100%;
margin:auto;
margin-top:20px;
} }
#nav{ #nav{
display:none; display:none;
} }
.mobile{
display:inline;
}
#mobile-nav
{
display:inline-block;
}
.container{
margin-top:20px;
margin-left:auto;
margin-right:auto;
width:300px;
}
.icon{
width:80px;
height:80px;
}
.manage_icons_3, .manage_blinkies_3, .manage_stamps_3{
clear:both;
}
} }

View File

@@ -0,0 +1,43 @@
.christmas{
background-color:#fac9c9;
border-color:#da1414;
color:black;
}
.christmas .heading
{
background-color:#c4efd2;
border-color:#4e9e81;
color:black;
}
.christmas .heading b
{
color:black;
}
.christmas a{
color: #329d76;
}
.christmas a:hover{
color: #22694f;
}
.christmas ::selection {
background:#da1414;
color: white;
text-shadow: none;
}
.christmas ::-moz-selection {
background: #da1414;
color: #EEE;
text-shadow: none;
}
.christmas .icon{
border-color:#da1414;
}
body.christmas{
background: linear-gradient(90deg, #5abf72 0%, #da1414 50%, #bf8282 100%);
scrollbar-color:#da1414 #f49191;
}
.christmas .container, .christmas #mobile-nav{
background-color:#fac9c9;
border-color:#da1414;
}

View File

@@ -0,0 +1,43 @@
.dark-blue{
background-color:#c9cbfa;
border-color:#1438da;
color:black;
}
.dark-blue .heading
{
background-color:#9691f4;
border-color:#142eda;
color:black;
}
.dark-blue .heading b
{
color:black;
}
.dark-blue a{
color: #141bda;
}
.dark-blue a:hover{
color: #0e1798;
}
.dark-blue ::selection {
background:#144fda;
color: white;
text-shadow: none;
}
.dark-blue ::-moz-selection {
background: #144fda;
color: #EEE;
text-shadow: none;
}
.dark-blue .icon{
border-color:#1438da;
}
body.dark-blue{
background: linear-gradient(90deg, #4685fb 0%, #142bda 50%, #0e1598 100%);
scrollbar-color:#a414da #d991f4;
}
.dark-blue .container, .dark-blue #mobile-nav{
background-color:#c9cffa;
border-color:#142bda;
}

View File

@@ -0,0 +1,43 @@
.dark-green{
background-color:#92dea3;
border-color:rgb(60, 138, 105);
color:black;
}
.dark-green .heading
{
background-color:#75d78b;
border-color:#4c9576;
color:black;
}
.dark-green .heading b
{
color:black;
}
.dark-green a{
color: #379162;
}
.dark-green a:hover{
color: #19533b;
}
.dark-green ::selection {
background:#44b889;
color: white;
text-shadow: none;
}
.dark-green ::-moz-selection {
background: #44b889;
color: #EEE;
text-shadow: none;
}
.dark-green .icon{
border-color:#0e985e;
}
body.dark-green{
background: linear-gradient(90deg, #6dad9e 0%, #41da86 50%, #36b78c 100%);
scrollbar-color:#5ed3a2 #a8d0c5;
}
.dark-green .container, .dark-green #mobile-nav{
background-color:#c9fae8;
border-color:#59d7af;
}

View File

@@ -1,5 +1,5 @@
.dark{ .dark{
background-color:#1B1F23; background-color:#2c3339;
border-color:#000000; border-color:#000000;
color:#D0D5DA; color:#D0D5DA;
} }
@@ -8,7 +8,7 @@
} }
.dark .heading .dark .heading
{ {
background-color:#292F35; background-color:#1B1F23;
border-color:#000000; border-color:#000000;
color:#D0D5DA; color:#D0D5DA;
} }
@@ -23,12 +23,12 @@
color: #D0D5DA; color: #D0D5DA;
} }
.dark ::selection { .dark ::selection {
background:#D0D5DA; background:#1B1F23;
color: white; color: white;
text-shadow: none; text-shadow: none;
} }
.dark ::-moz-selection { .dark ::-moz-selection {
background: #D0D5DA; background: #1B1F23;
color: #EEE; color: #EEE;
text-shadow: none; text-shadow: none;
} }
@@ -40,7 +40,15 @@ body.dark{
background: linear-gradient(90deg, #1B1F23 0%, #1B1F23 50%, #1B1F23 100%); background: linear-gradient(90deg, #1B1F23 0%, #1B1F23 50%, #1B1F23 100%);
scrollbar-color:#292F35 #373b3e; scrollbar-color:#292F35 #373b3e;
} }
.dark .container{ .dark .container, .dark #mobile-nav{
background-color:#1B1F23; background-color:#2c3339;
border-color:black; border-color:black;
} }
.dark .post{
border-color:black;
background-color:#1B1F23 ;
color:white;
}
.dark #blog .timestamp{
color:#959595;
}

View File

@@ -38,3 +38,7 @@
background: linear-gradient(90deg, #00b7ff 0%, #57c785 50%, #eddd53 100%); background: linear-gradient(90deg, #00b7ff 0%, #57c785 50%, #eddd53 100%);
scrollbar-color:#008bcc #b3e7ff; scrollbar-color:#008bcc #b3e7ff;
} }
.default .container, .default #mobile-nav{
background-color:#e6f7ff;
border-color:#99dfff;
}

View File

@@ -0,0 +1,43 @@
.green{
background-color:#c9facf;
border-color:#14da84;
color:black;
}
.green .heading
{
background-color:#91f4b7;
border-color:#14da88;
color:black;
}
.green .heading b
{
color:black;
}
.green a{
color: #11c668;
}
.green a:hover{
color: #0e985e;
}
.green ::selection {
background:#14da8b;
color: white;
text-shadow: none;
}
.green ::-moz-selection {
background: #14da8b;
color: #EEE;
text-shadow: none;
}
.green .icon{
border-color:#14da88;
}
body.green{
background: linear-gradient(90deg, #67e2ab 0%, #14da8b 50%, #0e986a 100%);
scrollbar-color:#14da88 #91f4d8;
}
.green .container, .green #mobile-nav{
background-color:#c9fae8;
border-color:#14da9b;
}

View File

@@ -38,7 +38,7 @@ body.pink-lighter{
background: linear-gradient(90deg, #f5c2ea 0%, #e89dfb 50%, #f6959c 100%); background: linear-gradient(90deg, #f5c2ea 0%, #e89dfb 50%, #f6959c 100%);
scrollbar-color:#a414da #d991f4; scrollbar-color:#a414da #d991f4;
} }
.pink-lighter .container{ .pink-lighter .container, .pink-lighter #mobile-nav{
background-color:#fac9f6; background-color:#fac9f6;
border-color:#da14c6; border-color:#da14c6;
} }

View File

@@ -34,10 +34,10 @@
} }
body.pink{ body.pink{
background: linear-gradient(90deg, #f8a1e5 0%, #d55ff3 50%, #f5c35e 100%); background: linear-gradient(90deg, rgba(235, 202, 202, 1) 0%, rgba(201, 77, 255, 1) 50%, rgba(242, 234, 124, 1) 100%);
scrollbar-color:#a414da #d991f4; scrollbar-color:#a414da #d991f4;
} }
.pink .container{ .pink .container, .pink #mobile-nav{
background-color:#fac9f6; background-color:#fac9f6;
border-color:#da14c6; border-color:#da14c6;
} }

View File

@@ -38,7 +38,7 @@ body.purple{
background: linear-gradient(90deg, #fb46c0 0%, #a414da 50%, #720e98 100%); background: linear-gradient(90deg, #fb46c0 0%, #a414da 50%, #720e98 100%);
scrollbar-color:#a414da #d991f4; scrollbar-color:#a414da #d991f4;
} }
.purple .container{ .purple .container, .purple #mobile-nav{
background-color:#ecc9fa; background-color:#ecc9fa;
border-color:#a414da; border-color:#a414da;
} }

View File

@@ -0,0 +1,43 @@
.red{
background-color:#fac9c9;
border-color:#da1414;
color:black;
}
.red .heading
{
background-color:#f49191;
border-color:#da1414;
color:black;
}
.red .heading b
{
color:black;
}
.red a{
color: #c61111;
}
.red a:hover{
color: #980e0e;
}
.red ::selection {
background:#da1414;
color: white;
text-shadow: none;
}
.red ::-moz-selection {
background: #da1414;
color: #EEE;
text-shadow: none;
}
.red .icon{
border-color:#da1414;
}
body.red{
background: linear-gradient(90deg, #fb4646 0%, #da1414 50%, #980e0e 100%);
scrollbar-color:#da1414 #f49191;
}
.red .container, .red #mobile-nav{
background-color:#fac9c9;
border-color:#da1414;
}

View File

@@ -1,6 +1,13 @@
<!doctype html> <!doctype html>
<title>{% block title %}{% endblock %} - Myriad</title> <title>{% block title %}{% endblock %} - {{ get_system_name() }}</title>
{% if check_favicon() %}
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
{% else %}
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='default.ico') }}">
{% endif %}
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
{% set themes = get_themes() %} {% set themes = get_themes() %}
@@ -8,11 +15,15 @@
<link rel="stylesheet" href="{{ url_for('static', filename='themes/'+theme) }}"> <link rel="stylesheet" href="{{ url_for('static', filename='themes/'+theme) }}">
{% endfor %} {% endfor %}
<meta name="viewport" content="width=device-width">
<div id="main"> <div id="main">
<div class="container" id="nav"> <div class="container" id="nav">
<div class="heading">Myriad</div> <div class="navitem">{{ server_time() }}</div>
<div class="heading">{{ get_system_name() }}</div>
<div class="navitem">> <a href="{{ url_for('home.index') }}">Home</a></div> <div class="navitem">> <a href="{{ url_for('home.index') }}">Home</a></div>
<div class="navitem">> <a href="{{ url_for('home.full_list') }}">Full List</a></div> <div class="navitem">> <a href="{{ url_for('home.full_list') }}">Full List</a></div>
<div class="navitem">> <a href="{{ url_for('home.groups') }}">Groups</a></div> <div class="navitem">> <a href="{{ url_for('home.groups') }}">Groups</a></div>
@@ -20,12 +31,34 @@
{% if g.user %} {% if g.user %}
<div class="heading">Manage</div> <div class="heading">Manage</div>
<div class="navitem">> <a href="{{ url_for('manage.new') }}">Add New Member</a></div> <div class="navitem">> <a href="{{ url_for('manage.new') }}">Add New Member</a></div>
<div class="navitem">> <a href="{{ url_for('manage.import_member') }}">Import New Member</a></div> <div class="navitem">> <a href="{{ url_for('manage.import_member_route') }}">Import New Member</a></div>
<div class="navitem">> <a href="{{ url_for('manage.groups') }}">Member groups</a></div> <div class="navitem">> <a href="{{ url_for('manage.groups') }}">Manage groups</a></div>
<div class="navitem">> <a href="{{ url_for('manage.assets') }}">Site Assets</a></div> <div class="navitem">> <a href="{{ url_for('manage.assets') }}">Site Assets</a></div>
<div class="navitem">> <a href="{{ url_for('manage.admin') }}">Site Administration</a></div> <div class="navitem">> <a href="{{ url_for('manage.admin') }}">Site Administration</a></div>
{% endif %} {% endif %}
{% if g.user %}
{% set pins = get_pins_all() %}
{% else %}
{% set pins = get_pins_public() %}
{% endif %}
{% if pins %}
<div class="heading">Pinned</div>
{% for pin in pins %}
<div class="navitem">> <a href="{{ url_for('home.page', mid=pin[0]) }}">{{pin[3]|safe}}</a></div>
{% endfor %}
{% endif %}
{% set pages = get_pages() %}
{% if pages %}
<div class="heading">{{ get_pages_name() }}</div>
{% endif %}
{% for page in pages %}
<div class="navitem">> <a href="{{ url_for('home.custom_page', pid=page[0]) }}">{{page[1]|safe}}</a></div>
{% endfor %}
<div class="heading">Blog</div> <div class="heading">Blog</div>
{% if g.user %}<div class="navitem">> <a href="{{ url_for('blog.new') }}">New Post</a></div>{% endif %} {% if g.user %}<div class="navitem">> <a href="{{ url_for('blog.new') }}">New Post</a></div>{% endif %}
<div class="navitem">> <a href="{{ url_for('blog.blog') }}">View All Posts</a></div> <div class="navitem">> <a href="{{ url_for('blog.blog') }}">View All Posts</a></div>
@@ -47,6 +80,15 @@
</div> </div>
<div id="mobile-nav">
<a href="{{ url_for('home.index') }}">Home</a> |
<a href="{{ url_for('home.full_list') }}">Full List</a> |
<a href="{{ url_for('home.groups') }}">Groups</a> |
<a href="{{ url_for('blog.blog') }}">Blog</a> |
{% set pages = get_pages() %}{% if pages %}<a href="{{ url_for('home.pages') }}">{{ get_pages_name() }}</a> | {% endif %}
{% if g.user %}<a class="mobile" href="{{ url_for('manage.admin') }}">Admin</a>{% endif %}
</div>
{% block content %}{% endblock %} {% block content %}{% endblock %}

View File

@@ -7,6 +7,8 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
{% if g.user %}<a class="mobile" href="{{ url_for('blog.new') }}">New post</a>{% endif %}
{% for post in blog %} {% for post in blog %}
{% set op = member_ids[post[1]] %} {% set op = member_ids[post[1]] %}
{% if not g.user and op[9]==0 %} {% if not g.user and op[9]==0 %}
@@ -19,9 +21,9 @@
<img src="{{ url_for('static', filename='any.jpg') }}" class="icon"> <img src="{{ url_for('static', filename='any.jpg') }}" class="icon">
{% endif %} {% endif %}
<div class="title">{{post[3]|safe}}</div> <div class="title">{{post[3]|safe}}</div>
<div class="timestamp">{{post[2]}} - <a href="{{ url_for('home.page', mid=post[1]) }}">{{op[3]}}</a> {% if g.user %}{% if op[9]==0 %}(Private){% else %}(Public)</b>{% endif %}{% endif %}</div> <div class="timestamp">{{ get_datetime_str(post[2]) }} - <a href="{{ url_for('home.page', mid=post[1]) }}">{{op[3]|safe}}</a> {% if g.user %}{% if op[9]==0 %}(Private){% else %}(Public)</b>{% endif %}{% endif %}</div>
<div class="content"> <div class="content">
{{post[4]|safe}} {{post[4].replace('\n', '<br>')|safe}}
</div> </div>
<br class="clear" /> <br class="clear" />
{% if g.user %} {% if g.user %}

View File

@@ -15,7 +15,7 @@
<label for="mid">Choose a member:</label> <label for="mid">Choose a member:</label>
<select name="mid" id="mid"> <select name="mid" id="mid">
{% for member in members %} {% for member in members %}
<option value="{{member[0]}}" {% if member[0]==post[1] %}selected{% endif %}>{{member[1]}}</option> <option value="{{member[0]}}" {% if member[0]==post[1] %}selected{% endif %}>{{ remove_html(member[1]) }}</option>
{% endfor %} {% endfor %}
</select> </select>
<br> <br>

View File

@@ -15,7 +15,7 @@
<label for="mid">Choose a member:</label> <label for="mid">Choose a member:</label>
<select name="mid" id="mid"> <select name="mid" id="mid">
{% for member in members %} {% for member in members %}
<option value="{{member[0]}}">{{member[1]}}</option> <option value="{{member[0]}}">{{ remove_html(member[1]) }}</option>
{% endfor %} {% endfor %}
</select> </select>
<br> <br>

View File

@@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% block title %}{{ remove_html(page[1]) }}{% endblock %}
{% block content %}
<div class="container">
{% if g.user %}<p><a href="{{ url_for('manage.edit_page', pid=page[0]) }}">Edit Page</a></p>{% endif %}
{{ page[2]|safe }}
</div>
{% endblock %}

View File

@@ -13,7 +13,7 @@
{% for member in memberlist %} {% for member in memberlist %}
{% if not g.user and member[9]==0 %} {% if not g.user and member[9]==0 %}
{% else %} {% else %}
<a href="#m{{ member[0] }}">{{ member[3] }}</a> | <a href="#m{{ member[0] }}">{{ member[3]|safe }}</a> |
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
@@ -25,14 +25,11 @@
{% else %} {% else %}
<div class="profile {{member[10]}}" id="m{{ member[0] }}"> <div class="profile {{member[10]}}" id="m{{ member[0] }}">
<div class="heading"><b>{{ member[3]|safe }}</b> {{ member[4]|safe }}</div> <div class="heading">{% if member[9]==0 %}<img class="minilock" src="{{ url_for('static', filename='lock.png') }}">{% endif %}<b>{{ member[3]|safe }}</b> {{ member[4]|safe }}</div>
{% if icons[member[0]] %} {% set icon = get_member_icon(member[0]) %}
<img src="{{ url_for('static', filename='icons/'+icons[member[0]]) }}" class="icon"> <img src="{{ url_for('static', filename='icons/'+icon) }}" class="icon">
{% else %}
<img src="{{ url_for('static', filename='any.jpg') }}" class="icon">
{% endif %}
<div class="bio"> <div class="bio">
{{ member[5]|safe }} {{ member[5].replace('\n', '<br>')|safe }}
</div> </div>
<br class="clear" /> <br class="clear" />
<div class="heading links"><a href="{{ url_for('home.page', mid=member[0]) }}">View Page</a>{% if g.user %} &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='full') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='full') }}">Remove from Front</a>{% endif %} &#9734 <a href="{{ url_for('manage.edit', mid=member[0]) }}">Edit</a> &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='full') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='full') }}">Unpin from Homepage</a>{% endif %}{% endif %}</div> <div class="heading links"><a href="{{ url_for('home.page', mid=member[0]) }}">View Page</a>{% if g.user %} &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='full') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='full') }}">Remove from Front</a>{% endif %} &#9734 <a href="{{ url_for('manage.edit', mid=member[0]) }}">Edit</a> &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='full') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='full') }}">Unpin from Homepage</a>{% endif %}{% endif %}</div>

View File

@@ -0,0 +1,40 @@
{% extends 'base.html' %}
{% block title %}{{ remove_html(group[1]) }}{% endblock %}
{% block content %}
<div class="container">
<div class="heading big">{{group[1]}}</div>
{% if not g.user and group[3]==0 %}
permission denied
{% else %}
<p>{{ group[2] }}</p>
{% for member in members %}
{% if not g.user and member[9]==0 %}
{% else %}
<div class="profile {{member[10]}}" id="m{{ member[0] }}">
<div class="heading">{% if member[9]==0 %}<img class="minilock" src="{{ url_for('static', filename='lock.png') }}">{% endif %}<b>{{ member[3]|safe }}</b> {{ member[4]|safe }}</div>
{% set icon = get_member_icon(member[0]) %}
<img src="{{ url_for('static', filename='icons/'+icon) }}" class="icon">
<div class="bio">
{{ member[5].replace('\n', '<br>')|safe }}
</div>
<br class="clear" />
<div class="heading links"><a href="{{ url_for('home.page', mid=member[0]) }}">View Page</a>{% if g.user %} &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='home') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='home') }}">Remove from Front</a>{% endif %} &#9734 <a href="{{ url_for('manage.edit', mid=member[0]) }}">Edit</a> &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='home') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='home') }}">Unpin from Homepage</a>{% endif %}{% endif %}</div>
<br class="clear" />
</div>
{% endif %}
{% endfor %}
{% endif %}
</div>
{% endblock %}

View File

@@ -7,12 +7,15 @@
{% for group in groups %} {% for group in groups %}
{% if not g.user and group[3]==0 %} {% if not g.user and group[3]==0 %}
{% else %} {% else %}
<div class="heading big">{{group[1]}}</div> <div class="heading big"><a href="{{ url_for('home.group_page', gid=group[0]) }}">{{group[1]}}</a></div>
<div class="maintext"> <div class="maintext">
{{group[2]}}<br><br> {{group[2]}}<br><br>
{% if group[0] in group_members %} {% if group[0] in group_members %}
{% for member in group_members[group[0]] %} {% for member in group_members[group[0]] %}
&#10032; <a href="{{ url_for('home.page', mid=member[0]) }}">{{ member[3] }}</a> <br> {% if not g.user and member[9]==0 %}
{% else %}
&#10032; <a href="{{ url_for('home.page', mid=member[0]) }}">{{ member[3]|safe }}</a> <br>
{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</div> </div>

View File

@@ -1,16 +1,17 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Welcome{% endblock %} {% block title %}Home{% endblock %}
{% block content %} {% block content %}
<div class="container"> <div class="container">
{% if front_list|length > 0 %}
<div id="frontbanner" class="heading"> <div id="frontbanner" class="heading">
<b>currently fronting: </b> {% for member in front_list %}{% if not g.user and member[9]==0 %}{% else %}<a href="{{ url_for('home.page', mid=member[0]) }}">{{ member[3] }}</a> {% if front_list.index(member) != front_list|length -1 %}&{% endif %}{% endif %} {% endfor %} {% if front_list|length > 0 %}
</div> <b>currently fronting: </b> {% for member in front_list %}{% if not g.user and member[9]==0 %}{% else %}<a href="{{ url_for('home.page', mid=member[0]) }}">{{ member[3]|safe }}</a> {% if front_list.index(member) != front_list|length -1 %}&{% endif %}{% endif %} {% endfor %}
{% else %} {% else %}
<p>There are currently no members listed as fronting</p> <i><sub>There are currently no members listed as fronting</sub></i>
{% endif %} {% endif %}
{% if last_updated %}<br><i><sub>last updated: {{ get_datetime_str(last_updated) }}</sub></i>{% endif %}
</div>
{% for member in home_pins %} {% for member in home_pins %}
@@ -19,14 +20,11 @@
<div class="profile {{member[10]}}" id="m{{ member[0] }}"> <div class="profile {{member[10]}}" id="m{{ member[0] }}">
<div class="heading"><b>{{ member[3]|safe }}</b> {{ member[4]|safe }}</div> <div class="heading">{% if member[9]==0 %}<img class="minilock" src="{{ url_for('static', filename='lock.png') }}">{% endif %}<b>{{ member[3]|safe }}</b> {{ member[4]|safe }}</div>
{% if icons[member[0]] %} {% set icon = get_member_icon(member[0]) %}
<img src="{{ url_for('static', filename='icons/'+icons[member[0]]) }}" class="icon"> <img src="{{ url_for('static', filename='icons/'+icon) }}" class="icon">
{% else %}
<img src="{{ url_for('static', filename='any.jpg') }}" class="icon">
{% endif %}
<div class="bio"> <div class="bio">
{{ member[5]|safe }} {{ member[5].replace('\n', '<br>')|safe }}
</div> </div>
<br class="clear" /> <br class="clear" />
<div class="heading links"><a href="{{ url_for('home.page', mid=member[0]) }}">View Page</a>{% if g.user %} &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='home') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='home') }}">Remove from Front</a>{% endif %} &#9734 <a href="{{ url_for('manage.edit', mid=member[0]) }}">Edit</a> &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='home') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='home') }}">Unpin from Homepage</a>{% endif %}{% endif %}</div> <div class="heading links"><a href="{{ url_for('home.page', mid=member[0]) }}">View Page</a>{% if g.user %} &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='home') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='home') }}">Remove from Front</a>{% endif %} &#9734 <a href="{{ url_for('manage.edit', mid=member[0]) }}">Edit</a> &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='home') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='home') }}">Unpin from Homepage</a>{% endif %}{% endif %}</div>

View File

@@ -6,23 +6,53 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<div class="heading">Backup</div> <div class="mobile">
<a href="{{ url_for('manage.export_system') }}">Export entire system TEXT ONLY</a><br> <div class="heading">Manage Members</div>
<a href="{{ url_for('manage.export_system_full') }}">Export entire system WITH ATTACHMENTS</a> <a href="{{ url_for('manage.new') }}">Add new member</a><br><br>
<a href="{{ url_for('manage.groups') }}">Manage groups</a><br><br>
<a href="{{ url_for('manage.assets') }}">Site assets</a>
</div>
<hr> <div class="heading">Front Log</div>
<div class="maintext">Front change history</div>
<div class="log">
{% for front in front_log %}
<p><b>{{ get_datetime_str(front[2]) }}</b> - {{ get_member(front[1])[3]|safe }}{% if front[3] %} <i>(ended: {{ get_datetime_str(front[3]) }})</i> | <a class="danger" href="{{ url_for('manage.delete_front_log', fid=front[0]) }}">delete</a>{% endif %}</p>
{% endfor %}
</div>
<div class="heading">Import system from JSON</div> <div class="heading">Custom Pages</div>
<p>You must only run full system imports on a fresh database</p> <form method="post" id="pages">
<form method="post" enctype="multipart/form-data" id="json"> <label for="page_title">Page Title</label>
<input type="file" name="json"> <input name="page_title" id="page_title"><br>
<input type="submit" value="Upload JSON"> <label for="page_content">Page Content</label>
<textarea name="page_content" id="page_content">Hello World!</textarea><br><br>
<input type="submit" name="new_page" value="Create New Page">
</form> </form>
<hr> <hr>
<div class="heading">Import system from ZIP</div> {% for page in pages %}
<p>You must only run full system imports on a fresh database</p> <p><a href="{{ url_for('home.custom_page', pid=page[0]) }}">{{page[1]}}</a> - <a href="{{ url_for('manage.edit_page', pid=page[0]) }}">Edit Page</a> &#9734 <a href="{{ url_for('manage.delete_page', pid=page[0]) }}">Delete Page</a></p>
{% endfor %}
<div class="heading">Site Favicon</div>
<p>This will replace the old favicon, without the ability to restore it if you change your mind</p>
<p>For best results, use a 32x32 favicon. Icons8 is a good resource, as is <a href="https://realfavicongenerator.net">this png converter</a></p>
<p>Changes will take some time to be reflected - or try a hard/force refresh on the browser</p>
<form method="post" enctype="multipart/form-data">
<input type="file" name="favicon">
<input type="submit" value="Upload Favicon">
</form>
<hr>
<div class="heading">Export & Import</div>
<a href="{{ url_for('manage.export_system_full') }}">Export entire system as ZIP</a>
<hr>
<p><b>Import system from previous Myriad export</b></p>
<p class="danger">This will replace existing data</p>
<p>Be sure to upload the .zip folder as it was downloaded without unzipping it</p>
<form method="post" enctype="multipart/form-data" id="zip"> <form method="post" enctype="multipart/form-data" id="zip">
<input type="file" name="zip"> <input type="file" name="zip">
<input type="submit" value="Upload ZIP"> <input type="submit" value="Upload ZIP">
@@ -30,35 +60,5 @@
<hr> <hr>
<a class="danger">Clear database</a> WARNING: this action is permanent and irreversible<br>
<br><br>
<div class="heading big pink">WIP ZONE</div>
<div class="heading">Site Users</div>
<div class="maintext">These are the usernames that can log in to the site</div>
{% for user in users %}
<p><b>{{ user[1] }}</b>{% if user[0]!=g.user[0] %} | <a>Delete user</a>{% endif %}</p>
{% endfor %}
<p><a>Add New User</a></p>
<div class="heading">Admin Log</div>
<div class="maintext">Actions taken by registered users</div>
<div class="heading">Front Log</div>
<div class="maintext">Front change history</div>
<div class="heading">Site Theme</div>
<div class="maintext">Choose the overall theme for the site here</div>
<form>
<select name="theme" id="theme">
{% for theme in get_themes() %}
{% set theme_name = theme.split(".")[0] %}
<option value="{{theme_name}}">{{theme_name}}</option>
{% endfor %}
</select>
<input type="submit" value="Submit">
</form>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -36,9 +36,44 @@
{% endif %} {% endif %}
<div class="heading big">Blinkies</div> <div class="heading big">Blinkies</div>
<p>Not implemented yet (sorry!)</p>
<div class="heading big">Stamps</div> <div class="heading big">Stamps</div>
<p>Not implemented yet (sorry!)</p>
<hr>
<div class="heading big">Upload Images</div>
<p>Upload images for use in pages and custom sections.
Just right click, copy image URL, then use it in the HTML
however you like. You can use style tags to customize how the
images look/behave on your page, just be careful not to use class or
id selectors that already exist (or do, if you are wanting to overwrite
their behaviour!). We will make a list of in-use class and ids here, soon.
</p>
<form method="post" enctype="multipart/form-data" id="image">
<input type="file" name="image" class="mobile-edit">
<input type="submit" value="Upload to Site">
</form>
<div class="manage_images">
<div class="manage_images_2">
{% for image in images %}
<div class="manage_images_3">
<img class="mng_img" src="{{ url_for('static', filename='misc/'+image) }}">
<br class="clear" />
{% set imgurl = url_for('static', filename='misc/'+image) %}
<a href="" onclick="copyURL('{{imgurl}}')">Copy Image URL</a> &#9734 <a href="">Delete</a>
</div>
{% endfor %}
</div>
</div>
<script>
function copyURL(a) {
navigator.clipboard.writeText(a);
}
</script>
</div> </div>

View File

@@ -1,13 +1,13 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block header %} {% block header %}
<div class="title">{% block title %}Edit {{ member[3] }}{% endblock %}</div> <div class="title">{% block title %}Edit {{ remove_html(member[3]) }}{% endblock %}</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container"> <div class="container">
<a href="{{url_for('home.page', mid=member[0])}}">View {{member[3]}}'s page</a> <a href="{{url_for('home.page', mid=member[0])}}">View {{member[3]|safe}}'s page</a>
<br class="clear" /> <br class="clear" />
<div class="heading">Edit Details</div> <div class="heading">Edit Details</div>
@@ -33,9 +33,40 @@
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
<a href="{{ url_for('manage.delete', mid=member[0]) }}" class="danger">Delete member</a> - WARNING: this is permanent and cannot be undone! <div class="heading">Public Page Settings</div>
<p>Select which sections to show on this member's public page</p>
<form method="post" id="page_settings">
<input type="checkbox" id="show_groups" name="show_groups" {% if member[15] %}checked{% endif %}>
<label for="show_groups">Groups</label><br>
<input type="checkbox" id="show_blog" name="show_blog" {% if member[11] %}checked{% endif %}>
<label for="show_blog">Blog</label><br>
<input type="checkbox" id="show_icons" name="show_icons" {% if member[12] %}checked{% endif %}>
<label for="show_icons">Icons</label><br>
<input type="checkbox" id="show_blinkies" name="show_blinkies" {% if member[13] %}checked{% endif %}>
<label for="show_blinkies">Blinkies</label><br>
<input type="checkbox" id="show_stamps" name="show_stamps" {% if member[14] %}checked{% endif %}>
<label for="show_stamps">Stamps</label><br>
<input type="submit" name="page_settings" value="Submit">
</form>
<hr>
<p>Customize section titles. Leave blank to hide the heading bar for that section altogether.</p>
<form method="post">
<label for="groups_title">Groups</label>
<input name="groups_title" id="groups_title" value="{{ member[20] }}"><br>
<label for="blog_title">Blog</label>
<input name="blog_title" id="blog_title" value="{{ member[16] }}"><br>
<label for="icons_title">Icons</label>
<input name="icons_title" id="icons_title" value="{{ member[17] }}"><br>
<label for="blinkies_title">Blinkies</label>
<input name="blinkies_title" id="blinkies_title" value="{{ member[18] }}"><br>
<label for="stamps_title">Stamps</label>
<input name="stamps_title" id="stamps_title" value="{{ member[19] }}"><br>
<input type="submit" name="section_titles" value="Submit">
</form>
{% if unjoined_groups or joined_groups %}
<div class="heading">Manage groups</div> <div class="heading">Manage groups</div>
<form method="post" id="groups"> <form method="post" id="groups">
@@ -55,11 +86,12 @@
</select> </select>
<input type="submit" value="Remove from group"> <input type="submit" value="Remove from group">
</form> </form>
{% endif %}
<div class="heading">Manage Icons</div> <div class="heading">Manage Icons</div>
<form method="post" enctype="multipart/form-data" id="icons"> <form method="post" enctype="multipart/form-data" id="icons">
<input type="file" name="file"> <input type="file" name="file" class="mobile-edit">
<input type="submit" value="Upload New Icon"> <input type="submit" value="Upload New Icon">
</form> </form>
@@ -80,7 +112,7 @@
<div class="heading">Manage Blinkies</div> <div class="heading">Manage Blinkies</div>
<form method="post" enctype="multipart/form-data" id="blinkies"> <form method="post" enctype="multipart/form-data" id="blinkies">
<input type="file" name="blinkie"> <input type="file" name="blinkie" class="mobile-edit">
<input type="submit" value="Upload to Blinkies"> <input type="submit" value="Upload to Blinkies">
</form> </form>
@@ -88,7 +120,7 @@
<div class="manage_images_2"> <div class="manage_images_2">
{% for blinkie in blinkies %} {% for blinkie in blinkies %}
<div class="manage_blinkies_3"> <div class="manage_blinkies_3">
<img src="{{ url_for('static', filename='blinkies/'+blinkie[2]) }}"> <img class="blinkie" src="{{ url_for('static', filename='blinkies/'+blinkie[2]) }}">
<br class="clear" /> <br class="clear" />
<a href="{{ url_for('manage.delete_blinkie', mid=member[0], blinkie_id=blinkie[0]) }}">Delete</a> <a href="{{ url_for('manage.delete_blinkie', mid=member[0], blinkie_id=blinkie[0]) }}">Delete</a>
</div> </div>
@@ -99,7 +131,7 @@
<div class="heading">Manage Stamps</div> <div class="heading">Manage Stamps</div>
<form method="post" enctype="multipart/form-data" id="stamps"> <form method="post" enctype="multipart/form-data" id="stamps">
<input type="file" name="stamp"> <input type="file" name="stamp" class="mobile-edit">
<input type="submit" value="Upload to Stamps"> <input type="submit" value="Upload to Stamps">
</form> </form>
@@ -107,7 +139,7 @@
<div class="manage_images_2"> <div class="manage_images_2">
{% for stamp in stamps %} {% for stamp in stamps %}
<div class="manage_stamps_3"> <div class="manage_stamps_3">
<img src="{{ url_for('static', filename='stamps/'+stamp[2]) }}"> <img class="stamp" src="{{ url_for('static', filename='stamps/'+stamp[2]) }}">
<br class="clear" /> <br class="clear" />
<a href="{{ url_for('manage.delete_stamp', mid=member[0], stamp_id=stamp[0]) }}">Delete</a> <a href="{{ url_for('manage.delete_stamp', mid=member[0], stamp_id=stamp[0]) }}">Delete</a>
</div> </div>
@@ -115,6 +147,36 @@
</div> </div>
</div> </div>
<hr>
<div class="heading">Custom Page Sections</div>
<p>Here you can make sections for your page, embed whatever you like</p>
<form method="post" id="sections">
<label for="section_title">Section Title</label>
<input name="section_title" id="section_title"><br>
<label for="section_content">Section Content</label>
<textarea name="section_content" id="section_content">Hello World!</textarea><br><br>
<input type="submit" name="new_section" value="Create New Section">
</form>
<hr>
{% for section in sections %}
<form method="post">
<label for="section_pos">Position</label>
<input name="section_pos" id="section_pos" value="{{section[4]}}"><br>
<label for="section_title">Section Title</label>
<input name="section_title" id="section_title" value="{{section[2]}}"><br>
<label for="section_content">Section Content</label>
<textarea name="section_content" id="section_content">{{section[3]}}</textarea><br><br>
<input type="hidden" id="section_id" name="section_id" value="{{section[0]}}">
<input type="submit" name="update_section" value="Update Section">
</form>
<a href="{{ url_for('manage.delete_section', sid=section[0]) }}">Delete section</a>
{% endfor %}
<div class="heading">Manage Member Data</div> <div class="heading">Manage Member Data</div>
<a href="{{ url_for('manage.export_fields', mid=member[0]) }}">Export Fields</a><br> <a href="{{ url_for('manage.export_fields', mid=member[0]) }}">Export Fields</a><br>
@@ -123,6 +185,9 @@
{% if stamps %}<a href="{{ url_for('manage.export_stamps', mid=member[0]) }}">Export stamps</a><br>{% endif %} {% if stamps %}<a href="{{ url_for('manage.export_stamps', mid=member[0]) }}">Export stamps</a><br>{% endif %}
{% if icons or blinkies or stamps %}<a href="{{ url_for('manage.export_member', mid=member[0]) }}">Export all member data</a><br>{% endif %} {% if icons or blinkies or stamps %}<a href="{{ url_for('manage.export_member', mid=member[0]) }}">Export all member data</a><br>{% endif %}
<hr>
<div class="heading danger">Danger zone</div>
<a href="{{ url_for('manage.delete', mid=member[0]) }}" class="danger">Delete member</a> - WARNING: this is permanent and cannot be undone!
{% if edit_location %} {% if edit_location %}

View File

@@ -0,0 +1,30 @@
<head>
<!--1. Import highlighter-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30/themes/prism.min.css">
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30/components/prism-core.min.js" data-manual></script><!--Remove data-manual if also using Prism normally-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.30/plugins/autoloader/prism-autoloader.min.js"></script>
<!--2. Import code-input-js-->
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.8/code-input.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.8/code-input.min.css">
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.8/plugins/indent.min.js"></script>
<!--3. Join code-input-js to highlighter-->
<script>codeInput.registerTemplate("syntax-highlighted", new codeInput.templates.Prism(Prism, [new codeInput.plugins.Indent()]));</script>
</head>
{% extends 'base.html' %}
{% block title %}{{ remove_html(page[1]) }}{% endblock %}
{% block content %}
<div class="container">
<form method="post">
<label for="title">Page Title</label>
<input name="title" id="title" value="{{page[1]}}"><br>
<code-input language="HTML"><textarea data-code-input-fallback name="content">{{ page[2] }}</textarea></code-input>
<input type="submit" name="update_page" value="Save Page">
</form>
</div>
{% endblock %}

View File

@@ -13,6 +13,10 @@
<input name="subtitle" id="subtitle"><br> <input name="subtitle" id="subtitle"><br>
<label for="bio">Description</label> <label for="bio">Description</label>
<textarea name="bio" id="bio"></textarea><br> <textarea name="bio" id="bio"></textarea><br>
<input type="radio" id="public" name="privacy" value=1 checked>
<label for="public">Public</label><br>
<input type="radio" id="private" name="privacy" value=0>
<label for="private">Private</label><br>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>

View File

@@ -1,5 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}{{ member[3] }}{% endblock %} {% block title %}{{ remove_html(member[3]) }}{% endblock %}
{% block content %} {% block content %}
@@ -12,23 +12,29 @@
{% else %} {% else %}
{% if g.user %} {% if g.user %}
<a href="{{url_for('manage.edit', mid=member[0])}}">Edit member</a> <a href="{{url_for('manage.edit', mid=member[0])}}">Edit member</a> &#9734 {% if member[8]==0 %}<a href="{{ url_for('manage.add_to_front', mid=member[0],location='mid') }}">Add to Front</a>{% else %}<a href="{{ url_for('manage.remove_front', mid=member[0],location='mid') }}">Remove from Front</a>{% endif %} &#9734 {% if member[7]==0 %}<a href="{{ url_for('manage.add_to_home', mid=member[0],location='mid') }}">Pin to Homepage</a>{% else %}<a href="{{ url_for('manage.remove_home', mid=member[0],location='mid') }}">Unpin from Homepage</a>{% endif %}
<br class="clear" /> <br class="clear" />
{% endif %} {% endif %}
{% if member[8]==1 %}<div class="heading"><i>Currently Fronting</i></div>{% endif %}
{% if icon %} {% if icon %}
<img class="icon" src="{{ url_for('static', filename='icons/'+icon[0]) }}"> <img class="icon" src="{{ url_for('static', filename='icons/'+icon[0]) }}">
{% else %} {% else %}
<img class="icon" src="{{ url_for('static', filename='any.jpg') }}"> <img class="icon" src="{{ url_for('static', filename='any.jpg') }}">
{% endif %} {% endif %}
<div class="title">{{member[3]}}</div> <div class="title">{% if member[9]==0 %}<img class="lock" src="{{ url_for('static', filename='lock.png') }}">{% endif %}{{member[3]|safe}}</div>
<div class="maintext"> <div class="maintext">
{{member[5]|safe}} {{member[5].replace('\n', '<br>')|safe}}
</div> </div>
<br class="clear" /> <br class="clear" />
{% if member[11] %}
{% if blog|length > 0 %} {% if blog|length > 0 %}
<div class="heading big">{{member[3]}}'s blog</div> {% if not g.user and not blog_public_show %}
{% else %}
{% if member[16] %}<div class="heading big">{{ member[16] }}</div>{% endif %}
{% endif %}
<div id="blog"> <div id="blog">
{% for post in blog %} {% for post in blog %}
{% if not g.user and post[5]==0 %} {% if not g.user and post[5]==0 %}
@@ -37,7 +43,7 @@
<div class="title">{{post[3]|safe}}</div> <div class="title">{{post[3]|safe}}</div>
<div class="timestamp">{{post[2]}} {% if g.user %}{% if post[5]==0 %}(Private){% else %}(Public){% endif %}{% endif %}</div> <div class="timestamp">{{post[2]}} {% if g.user %}{% if post[5]==0 %}(Private){% else %}(Public){% endif %}{% endif %}</div>
<div class="content"> <div class="content">
{{post[4]|safe}} {{post[4].replace('\n', '<br>')|safe}}
</div> </div>
{% if g.user %}<a href="{{url_for('blog.toggle',pid=post[0], location=member[0])}}">Toggle privacy</a> | <a href="{{url_for('blog.edit',pid=post[0],location=member[0])}}">Edit Post</a> | <a href="{{url_for('blog.delete', pid=post[0], location=member[0])}}" class="danger">Delete post</a>{% endif %} {% if g.user %}<a href="{{url_for('blog.toggle',pid=post[0], location=member[0])}}">Toggle privacy</a> | <a href="{{url_for('blog.edit',pid=post[0],location=member[0])}}">Edit Post</a> | <a href="{{url_for('blog.delete', pid=post[0], location=member[0])}}" class="danger">Delete post</a>{% endif %}
</div> </div>
@@ -45,28 +51,57 @@
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% endif %}
{% for section in sections %}
{% if section[2] %}<div class="heading big">{{ section[2] }}</div>{% endif %}
{{ section[3]|safe }}
{% endfor %}
{% if member[13] %}
{% if blinkies|length > 0 %}
{% if member[18] %}<div class="heading big">{{ member[18] }}</div>{% endif %}
<div class="image-section">
{% for blinkie in blinkies %}
<img class="blinkie" src="{{ url_for('static', filename='blinkies/'+blinkie[0]) }}">
{% endfor %}
</div>
{% endif %}
{% endif %}
<br class="clear" />
{% if member[14] %}
{% if stamps|length > 0 %}
{% if member[19] %}<div class="heading big">{{ member[19] }}</div>{% endif %}
<div class="image-section">
{% for stamp in stamps %}
<img class="stamp" src="{{ url_for('static', filename='stamps/'+stamp[0]) }}">
{% endfor %}
</div>
{% endif %}
{% endif %}
<br class="clear" />
{% if member[12] %}
{% if all_icons|length > 1 %} {% if all_icons|length > 1 %}
<div class="heading big">Icons</div> {% if member[17] %}<div class="heading big">{{ member[17] }}</div>{% endif %}
<div class="image-section">
{% for i in all_icons %} {% for i in all_icons %}
<img class="icon" src="{{ url_for('static', filename='icons/'+i[0]) }}"> <img class="icon" src="{{ url_for('static', filename='icons/'+i[0]) }}">
{% endfor %} {% endfor %}
</div>
{% endif %}
{% endif %} {% endif %}
<br class="clear" /> <br class="clear" />
{% if blinkies|length > 0 %} {% if member[15] %}
<div class="heading big">Blinkies</div> {% if groups|length > 0 %}
{% for blinkie in blinkies %} <div class="heading big">{{ member[20] }}</div>
<img src="{{ url_for('static', filename='blinkies/'+blinkie[0]) }}"> {% for gr in groups %}
{% set group = get_group(gr[1]) %}
>> <a href="{{ url_for('home.group_page', gid=group[0]) }}">{{ group[1] }}</a><br>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<br class="clear" />
{% if stamps|length > 0 %}
<div class="heading big">Stamps</div>
{% for stamp in stamps %}
<img src="{{ url_for('static', filename='stamps/'+stamp[0]) }}">
{% endfor %}
{% endif %} {% endif %}

View File

@@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% block title %}{{ get_pages_name() }}{% endblock %}
{% block content %}
<div class="container">
{% if not pages %}
<i>There are no pages to show</i>
{% endif %}
{% for page in pages %}
<p><a href="{{ url_for('home.custom_page', pid=page[0]) }}">{{ page[1]|safe }}</a></p>
{% endfor %}
</div>
{% endblock %}

31
myriad/utilities.py Normal file
View File

@@ -0,0 +1,31 @@
import datetime, re
from myriad.db import get_db
def server_time():
raw = datetime.datetime.now()
real = raw.strftime("%d/%m/%Y, %H:%M:%S")
return real
def server_time_obj():
return datetime.datetime.now()
def get_datetime_obj(dt_string):
date_raw = dt_string.split(",")
date = date_raw[0]
day,month,year = date.split("/")
time = date_raw[1]
hour,minute,second = time.split(":")
dt_obj = datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
return dt_obj
def get_datetime_str(dt_obj):
return dt_obj.strftime("%d/%m/%Y, %H:%M:%S")
def remove_html(mystring):
newstring = re.sub('<[^<]+?>', '', mystring)
return newstring
def get_pages():
db = get_db()
pages = db.execute("SELECT * FROM pages ORDER BY position").fetchall()
return pages