Compare commits
No commits in common. "main" and "current_wdka_release" have entirely different histories.
main
...
current_wd
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,12 +3,10 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.eggs/
|
.eggs/
|
||||||
.ruff_cache
|
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
pip-wheel-metadata/
|
pip-wheel-metadata/
|
||||||
|
|
||||||
verse/tmpupload/*
|
verse/tmpupload/*
|
||||||
verse/stash/*
|
verse/stash/*
|
||||||
verse/search/searchdata/*
|
|
||||||
*.db
|
*.db
|
||||||
|
@ -8,15 +8,10 @@ The contribution consisted of setting up distribusi. ruruhuis.nl (distribusi is
|
|||||||
|
|
||||||
This particular work in progress project is an attempt to make distribusi into a webinterface that can be operated remotely without any knowlegde of CLI. Trying to somehow combine the ideas of distribusi with the ideas of a [tildeverse](https://tildeverse.org/) or [Tilde club ](https://tilde.club/), but also be neither of these ideas.
|
This particular work in progress project is an attempt to make distribusi into a webinterface that can be operated remotely without any knowlegde of CLI. Trying to somehow combine the ideas of distribusi with the ideas of a [tildeverse](https://tildeverse.org/) or [Tilde club ](https://tilde.club/), but also be neither of these ideas.
|
||||||
|
|
||||||
This project was made for Autonomous Practices at the WDKA in Rotterdam.
|
This project is made for Autonomous Practices at the WDKA in Rotterdam.
|
||||||
|
|
||||||
The second stage of this project might be the archiving part of the [Toolsheds fellowship.](https://nieuweinstituut.nl/en/articles/call-for-fellows-tool-sheds)
|
|
||||||
|
|
||||||
|
|
||||||
## Work in progress
|
## Work in progress
|
||||||
|
|
||||||
Currently this repo is transforming the academic based search filters into adjustable
|
Amazingly helpful testers, currently I am writing some database upgrades and new functionalities, and they don't work, so have some patience with testing the in development material.
|
||||||
small Autonomous space filters.
|
|
||||||
|
|
||||||
## Start your engines!
|
## Start your engines!
|
||||||
|
|
||||||
|
34
notes.md
Normal file
34
notes.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# these are some notes
|
||||||
|
|
||||||
|
from distribusi.cli import build_argparser
|
||||||
|
# from distribusi.distribusi import distribusify
|
||||||
|
Works!
|
||||||
|
|
||||||
|
# Shit! We need entire CRUD functionality.
|
||||||
|
Done!
|
||||||
|
## Create:
|
||||||
|
Done
|
||||||
|
|
||||||
|
### Uploading
|
||||||
|
Done
|
||||||
|
|
||||||
|
### CSS editing.
|
||||||
|
a user can edit CSS of a file in the folder called $distribusiname
|
||||||
|
Todo: render the placeholder for html editor better.
|
||||||
|
|
||||||
|
### Theme selection
|
||||||
|
a user can select a CSS file from a radio menu
|
||||||
|
|
||||||
|
### Distribusi
|
||||||
|
A flag in de DB is set to true and distribusi is run on the folder of the users
|
||||||
|
called $distribusiname
|
||||||
|
|
||||||
|
## Read:
|
||||||
|
Based on flags set in the user DB the distribusi folders are set to visible.
|
||||||
|
|
||||||
|
|
||||||
|
## Update:
|
||||||
|
Done
|
||||||
|
|
||||||
|
## Delete:
|
||||||
|
Done
|
@ -1,49 +1,25 @@
|
|||||||
[tool.ruff]
|
[tool.black]
|
||||||
line-length = 79
|
line-length = 79
|
||||||
target-version = "py311"
|
target-version = ['py37', 'py38', 'py39']
|
||||||
#include = '\.pyi?$'
|
include = '\.pyi?$'
|
||||||
exclude = [
|
exclude = '''
|
||||||
".bzr",
|
/(
|
||||||
".direnv",
|
\.eggs
|
||||||
".eggs",
|
| \.git
|
||||||
".git",
|
| \.hg
|
||||||
".git-rewrite",
|
| \.mypy_cache
|
||||||
".hg",
|
| \.tox
|
||||||
".ipynb_checkpoints",
|
| \.venv
|
||||||
".mypy_cache",
|
| _build
|
||||||
".nox",
|
| buck-out
|
||||||
".pants.d",
|
| build
|
||||||
".pyenv",
|
| dist
|
||||||
".pytest_cache",
|
|
||||||
".pytype",
|
# The following are specific to Black, you probably don't want those.
|
||||||
".ruff_cache",
|
| blib2to3
|
||||||
".svn",
|
| tests/data
|
||||||
".tox",
|
| profiling
|
||||||
".venv",
|
)/
|
||||||
".vscode",
|
'''
|
||||||
"__pypackages__",
|
|
||||||
"_build",
|
|
||||||
"buck-out",
|
|
||||||
"build",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"site-packages",
|
|
||||||
"venv",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
||||||
select = ["E4", "E7", "E9", "F"]
|
|
||||||
ignore = []
|
|
||||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
||||||
fixable = ["ALL"]
|
|
||||||
unfixable = []
|
|
||||||
# Allow unused variables when underscore-prefixed.
|
|
||||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
||||||
|
|
||||||
[tool.ruff.format]
|
|
||||||
quote-style = "double"
|
|
||||||
indent-style = "space"
|
|
||||||
docstring-code-format = true
|
|
||||||
line-ending = "auto"
|
|
||||||
skip-magic-trailing-comma = false
|
|
||||||
|
@ -1,57 +1,48 @@
|
|||||||
alembic==1.7.5
|
alembic==1.7.5
|
||||||
APScheduler==3.10.4
|
|
||||||
Babel==2.9.1
|
Babel==2.9.1
|
||||||
bcrypt==3.2.0
|
bcrypt==3.2.0
|
||||||
black==24.4.0
|
black==21.11b1
|
||||||
bleach==4.1.0
|
bleach==4.1.0
|
||||||
bleach-allowlist==1.0.3
|
bleach-allowlist==1.0.3
|
||||||
blinker==1.7.0
|
blinker==1.4
|
||||||
cffi
|
cffi==1.15.0
|
||||||
click==8.1.7
|
click==8.0.3
|
||||||
distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi@3eefd6e5ca7048555d441df8c6fbf4f2e255acac
|
|
||||||
dnspython==2.1.0
|
dnspython==2.1.0
|
||||||
email-validator==1.1.3
|
email-validator==1.1.3
|
||||||
Flask==3.0.3
|
Flask==2.0.2
|
||||||
Flask-APScheduler==1.13.1
|
|
||||||
Flask-BabelEx==0.9.4
|
Flask-BabelEx==0.9.4
|
||||||
Flask-Bcrypt==1.0.1
|
Flask-Bcrypt==0.7.1
|
||||||
Flask-Login==0.6.3
|
Flask-Login==0.5.0
|
||||||
Flask-Mail==0.9.1
|
Flask-Mail==0.9.1
|
||||||
Flask-Migrate==3.1.0
|
Flask-Migrate==3.1.0
|
||||||
Flask-Principal==0.4.0
|
Flask-Principal==0.4.0
|
||||||
Flask-Security==3.0.0
|
Flask-Security==3.0.0
|
||||||
Flask-Security-Too==4.1.3
|
Flask-Security-Too==4.1.3
|
||||||
Flask-SQLAlchemy==3.1.1
|
Flask-SQLAlchemy==2.5.1
|
||||||
Flask-WTF==1.2.1
|
Flask-WTF==1.0.0
|
||||||
greenlet==3.0.3
|
greenlet==1.1.2
|
||||||
idna==3.3
|
idna==3.3
|
||||||
isort==5.13.2
|
itsdangerous==2.0.1
|
||||||
itsdangerous==2.2.0
|
Jinja2==3.0.3
|
||||||
Jinja2==3.1.3
|
|
||||||
Mako==1.1.6
|
Mako==1.1.6
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.0.1
|
||||||
msgpack==1.0.8
|
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
neovim==0.3.1
|
packaging==21.3
|
||||||
packaging==24.0
|
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
pathspec==0.9.0
|
pathspec==0.9.0
|
||||||
Pillow
|
Pillow==8.3.2
|
||||||
platformdirs==2.4.0
|
platformdirs==2.4.0
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
pynvim==0.5.0
|
|
||||||
pyparsing==3.0.7
|
pyparsing==3.0.7
|
||||||
python-dateutil==2.9.0.post0
|
|
||||||
python-magic==0.4.24
|
python-magic==0.4.24
|
||||||
pytz==2021.3
|
pytz==2021.3
|
||||||
regex
|
regex==2021.11.10
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
speaklater==1.3
|
speaklater==1.3
|
||||||
SQLAlchemy==2.0.29
|
SQLAlchemy==1.4.27
|
||||||
tomli==1.2.2
|
tomli==1.2.2
|
||||||
typing_extensions==4.11.0
|
typing_extensions==4.0.1
|
||||||
tzlocal==5.2
|
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
Werkzeug==3.0.2
|
Werkzeug==2.0.2
|
||||||
Whoosh==2.7.4
|
|
||||||
WTForms==3.0.0
|
WTForms==3.0.0
|
||||||
|
distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi
|
||||||
|
4
setup.py
4
setup.py
@ -1,3 +1,3 @@
|
|||||||
from setuptools import find_packages, setup
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
setup(name="library", version="1.0", packages=find_packages())
|
setup(name='library', version='1.0', packages=find_packages())
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
from flask_login import current_user
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
|
|
||||||
def is_adminuser():
|
|
||||||
if not current_user.is_authenticated:
|
|
||||||
return False
|
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
|
||||||
return user.admin
|
|
@ -1,44 +1,36 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
from flask import render_template, Blueprint
|
|
||||||
from flask_login import current_user, login_required
|
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from admin import is_adminuser
|
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
|
||||||
from admin_page.forms.admindistribusiform import AdminDistribusiForm
|
|
||||||
from admin_page.forms.adminuserform import AdminUserForm
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
admin_page = Blueprint(
|
from usermodel import User
|
||||||
"admin",
|
from distribusimodel import Distribusis
|
||||||
__name__,
|
from distribusisinfo import DistribusisInfo
|
||||||
template_folder="templates/describe_files",
|
|
||||||
static_folder="static",
|
from forms.adminuserform import AdminUserForm
|
||||||
)
|
from forms.admindistribusiform import AdminDistribusiForm
|
||||||
|
|
||||||
|
|
||||||
@admin_page.route("/admin", methods=["GET", "POST"])
|
def AdminPage():
|
||||||
@login_required
|
adminuserform = AddUsersToForm(AdminUserForm())
|
||||||
def admin():
|
admindistribusiform = AddDistribusisToForm(AdminDistribusiForm())
|
||||||
if not is_adminuser():
|
|
||||||
return redirect(url_for("index"))
|
|
||||||
adminuserform = add_users_to_form(AdminUserForm())
|
|
||||||
admindistribusiform = add_distribusis_to_form(AdminDistribusiForm())
|
|
||||||
if admindistribusiform.validate_on_submit():
|
if admindistribusiform.validate_on_submit():
|
||||||
delete_distribusis(admindistribusiform)
|
DeleteDistribusis(admindistribusiform)
|
||||||
|
|
||||||
if adminuserform.validate_on_submit():
|
if adminuserform.validate_on_submit():
|
||||||
if adminuserform.delete.data:
|
if adminuserform.delete.data:
|
||||||
delete_users(adminuserform)
|
DeleteUsers(adminuserform)
|
||||||
|
if adminuserform.tutors.data:
|
||||||
|
ToggleUsersAsTutors(adminuserform, True)
|
||||||
|
if adminuserform.nottutors.data:
|
||||||
|
ToggleUsersAsTutors(adminuserform, False)
|
||||||
|
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"admin.html",
|
"admin.html",
|
||||||
@ -48,18 +40,36 @@ def admin():
|
|||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def delete_users(adminuserform):
|
def DeleteUsers(adminuserform):
|
||||||
for userform in adminuserform:
|
for userform in adminuserform:
|
||||||
if "user" in userform.id:
|
if "user" in userform.id:
|
||||||
if userform.data:
|
if userform.data:
|
||||||
useremail = userform.label.text
|
useremail = userform.label.text
|
||||||
user = User.query.filter_by(email=useremail).first()
|
user = User.query.filter_by(email=useremail).first()
|
||||||
delete_User_distribusis(user)
|
DeleteUserDistribusis(user)
|
||||||
delete_user_from_db(user)
|
DeleteUserFromDb(user)
|
||||||
userform.errors.append(f"User {useremail} deleted!")
|
userform.errors.append(f"User {useremail} deleted!")
|
||||||
|
|
||||||
|
|
||||||
def delete_user_from_db(user):
|
def ToggleUsersAsTutors(adminuserform, is_tutor):
|
||||||
|
for userform in adminuserform:
|
||||||
|
if "user" in userform.id:
|
||||||
|
if userform.data:
|
||||||
|
useremail = userform.label.text
|
||||||
|
user = User.query.filter_by(email=useremail).first()
|
||||||
|
ToggleUserTutorinDb(user, is_tutor)
|
||||||
|
userform.errors.append(f"Is User {useremail} tutor {is_tutor}")
|
||||||
|
|
||||||
|
|
||||||
|
def ToggleUserTutorinDb(user, is_tutor):
|
||||||
|
try:
|
||||||
|
user.tutor = is_tutor
|
||||||
|
db.session.commit()
|
||||||
|
except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
|
||||||
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
|
def DeleteUserFromDb(user):
|
||||||
try:
|
try:
|
||||||
db.session.delete(user)
|
db.session.delete(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -67,14 +77,14 @@ def delete_user_from_db(user):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
def delete_User_distribusis(user):
|
def DeleteUserDistribusis(user):
|
||||||
distribusis = DistribusisInfo.get_user_distribusis(user.email)
|
distribusis = DistribusisInfo.getuserdistribusis(user.email)
|
||||||
for distribusi in distribusis:
|
for distribusi in distribusis:
|
||||||
delete_distribusi_files(distribusi.distribusiname)
|
DeleteDistribusiFiles(distribusi.distribusiname)
|
||||||
delete_distribusi_from_db(distribusi)
|
DeleteDistribusiFromDb(distribusi)
|
||||||
|
|
||||||
|
|
||||||
def delete_distribusis(admindistribusiform):
|
def DeleteDistribusis(admindistribusiform):
|
||||||
for distribusiform in admindistribusiform:
|
for distribusiform in admindistribusiform:
|
||||||
if "distribusi" in distribusiform.id:
|
if "distribusi" in distribusiform.id:
|
||||||
if distribusiform.data:
|
if distribusiform.data:
|
||||||
@ -82,12 +92,12 @@ def delete_distribusis(admindistribusiform):
|
|||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=distribusiname
|
distribusiname=distribusiname
|
||||||
).first()
|
).first()
|
||||||
delete_distribusi_from_db(distribusi)
|
DeleteDistribusiFromDb(distribusi)
|
||||||
delete_distribusi_files(distribusiname)
|
DeleteDistribusiFiles(distribusiname)
|
||||||
distribusiform.errors.append("Deleted distribusi")
|
distribusiform.errors.append("Deleted distribusi")
|
||||||
|
|
||||||
|
|
||||||
def delete_distribusi_from_db(distribusi):
|
def DeleteDistribusiFromDb(distribusi):
|
||||||
try:
|
try:
|
||||||
db.session.delete(distribusi)
|
db.session.delete(distribusi)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -95,7 +105,7 @@ def delete_distribusi_from_db(distribusi):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
def delete_distribusi_files(distribusiname):
|
def DeleteDistribusiFiles(distribusiname):
|
||||||
userfolder = os.path.join("stash", distribusiname)
|
userfolder = os.path.join("stash", distribusiname)
|
||||||
if os.path.exists(userfolder):
|
if os.path.exists(userfolder):
|
||||||
shutil.rmtree(userfolder)
|
shutil.rmtree(userfolder)
|
||||||
@ -104,15 +114,15 @@ def delete_distribusi_files(distribusiname):
|
|||||||
shutil.rmtree(cssfolder)
|
shutil.rmtree(cssfolder)
|
||||||
|
|
||||||
|
|
||||||
def add_distribusis_to_form(admindistribusiform):
|
def AddDistribusisToForm(admindistribusiform):
|
||||||
distribusis = DistribusisInfo.visible_distribusis()
|
distribusis = DistribusisInfo.visibledistribusis()
|
||||||
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
|
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
|
||||||
distribusis
|
distribusis
|
||||||
)
|
)
|
||||||
return admindistribusiform
|
return admindistribusiform
|
||||||
|
|
||||||
|
|
||||||
def add_users_to_form(adminuserform):
|
def AddUsersToForm(adminuserform):
|
||||||
users = User.query.all()
|
users = User.query.all()
|
||||||
adminuserform = AdminUserForm.user_list_form_builder(users)
|
adminuserform = AdminUserForm.user_list_form_builder(users)
|
||||||
return adminuserform
|
return adminuserform
|
@ -1,15 +1,13 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from sqlalchemy.exc import (
|
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
InterfaceError,
|
|
||||||
InvalidRequestError,
|
|
||||||
)
|
|
||||||
|
|
||||||
from app import create_app, db
|
from app import create_app, db
|
||||||
from models.distribusi_model import Distribusis # noqa: F401
|
from sqlalchemy.exc import (
|
||||||
from models.user_model import User # noqa: F401
|
InvalidRequestError,
|
||||||
|
InterfaceError,
|
||||||
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
|
)
|
||||||
|
from usermodel import User # noqa: F401
|
||||||
|
from distribusimodel import Distribusis # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
def admintool():
|
def admintool():
|
||||||
|
49
verse/app.py
49
verse/app.py
@ -1,15 +1,13 @@
|
|||||||
import os
|
import os
|
||||||
import tomllib
|
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bcrypt import Bcrypt
|
|
||||||
from flask_login import LoginManager
|
|
||||||
from flask_migrate import Migrate
|
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_bcrypt import Bcrypt
|
||||||
|
from flask_migrate import Migrate
|
||||||
from flask_wtf.csrf import CSRFProtect
|
from flask_wtf.csrf import CSRFProtect
|
||||||
|
from flask_login import (
|
||||||
|
LoginManager,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
APP = Flask(__name__, static_folder="static")
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
migrate = Migrate()
|
migrate = Migrate()
|
||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
@ -17,16 +15,18 @@ login_manager = LoginManager()
|
|||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
APP.secret_key = os.urandom(24)
|
APP = Flask(__name__, static_folder="static")
|
||||||
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db"
|
|
||||||
APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
|
APP.secret_key = "secret-key"
|
||||||
|
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data/distribusiverse.db"
|
||||||
|
APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
|
||||||
|
APP.config["MAX_CONTENT_LENGTH"] = 150 * 1024 * 1024
|
||||||
|
|
||||||
APP.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024
|
|
||||||
APP.config["MAIL_SERVER"] = "mail.autonomic.zone"
|
APP.config["MAIL_SERVER"] = "mail.autonomic.zone"
|
||||||
APP.config["MAIL_PORT"] = 587
|
APP.config["MAIL_PORT"] = 587
|
||||||
APP.config["MAIL_USE_SSL"] = False
|
APP.config["MAIL_USE_SSL"] = False
|
||||||
APP.config["MAIL_USE_TLS"] = True
|
APP.config["MAIL_USE_TLS"] = True
|
||||||
APP.config["MAIL_USERNAME"] = "noreply@vvvvvvaria.org"
|
APP.config['MAIL_USERNAME'] = "noreply@vvvvvvaria.org"
|
||||||
|
|
||||||
login_manager.session_protection = "strong"
|
login_manager.session_protection = "strong"
|
||||||
login_manager.login_view = "index"
|
login_manager.login_view = "index"
|
||||||
@ -38,35 +38,10 @@ def create_app():
|
|||||||
APP.config["UPLOAD_FOLDER"] = "tmpupload"
|
APP.config["UPLOAD_FOLDER"] = "tmpupload"
|
||||||
APP.config["PUBLIC_THEMES"] = "themes/publicthemes"
|
APP.config["PUBLIC_THEMES"] = "themes/publicthemes"
|
||||||
|
|
||||||
# user settings_file
|
|
||||||
settings()
|
|
||||||
|
|
||||||
csrf.init_app(APP)
|
csrf.init_app(APP)
|
||||||
login_manager.init_app(APP)
|
login_manager.init_app(APP)
|
||||||
db.init_app(APP)
|
db.init_app(APP)
|
||||||
migrate.init_app(APP, db, render_as_batch=True)
|
migrate.init_app(APP, db, render_as_batch=True)
|
||||||
bcrypt.init_app(APP)
|
bcrypt.init_app(APP)
|
||||||
|
|
||||||
@APP.context_processor
|
|
||||||
def inject_title():
|
|
||||||
return dict(title=APP.config["title"])
|
|
||||||
|
|
||||||
return APP
|
return APP
|
||||||
|
|
||||||
|
|
||||||
def settings():
|
|
||||||
settings = settings_from_file()
|
|
||||||
APP.config.update(settings)
|
|
||||||
return APP
|
|
||||||
|
|
||||||
|
|
||||||
def get_app():
|
|
||||||
return APP
|
|
||||||
|
|
||||||
|
|
||||||
def settings_from_file():
|
|
||||||
if os.path.isfile("settings_development.toml"):
|
|
||||||
with open("settings_development.toml", "rb") as settings_file:
|
|
||||||
return tomllib.load(settings_file)
|
|
||||||
with open("settings.toml", "rb") as settings_file:
|
|
||||||
return tomllib.load(settings_file)
|
|
||||||
|
3
verse/data/.gitignore
vendored
Normal file
3
verse/data/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
*/
|
||||||
|
!.gitignore
|
@ -1,13 +1,11 @@
|
|||||||
def deploy():
|
def deploy():
|
||||||
"""Run deployment of database."""
|
"""Run deployment of database."""
|
||||||
from flask_migrate import init, migrate, stamp, upgrade
|
|
||||||
|
|
||||||
from app import create_app, db
|
from app import create_app, db
|
||||||
from models.distribusi_model import Distribusis # noqa: F401
|
from flask_migrate import upgrade, migrate, init, stamp
|
||||||
from models.distribusi_file_model import DistribusiFiles # noqa: F401
|
|
||||||
|
|
||||||
# This model is required for flask_migrate to make the table
|
# This model is required for flask_migrate to make the table
|
||||||
from models.user_model import User # noqa: F401
|
from usermodel import User # noqa: F401
|
||||||
|
from distribusimodel import Distribusis # noqa: F401
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
app.app_context().push()
|
app.app_context().push()
|
||||||
|
@ -1,160 +0,0 @@
|
|||||||
import os
|
|
||||||
from flask import (
|
|
||||||
Blueprint,
|
|
||||||
render_template,
|
|
||||||
redirect,
|
|
||||||
url_for,
|
|
||||||
send_from_directory,
|
|
||||||
flash,
|
|
||||||
)
|
|
||||||
from flask_login import current_user, login_required
|
|
||||||
from sqlalchemy.exc import (
|
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
IntegrityError,
|
|
||||||
InterfaceError,
|
|
||||||
InvalidRequestError,
|
|
||||||
)
|
|
||||||
import piexif
|
|
||||||
from PIL import Image
|
|
||||||
from app import db
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from models.distribusi_file_model import DistribusiFiles
|
|
||||||
from describer.forms.describe_files_form import DescribeFilesForm
|
|
||||||
from describer.forms.redistribusi_form import ReDistribusiForm
|
|
||||||
from distribusikan.distribusi_workflow import run_distribusi, get_css_file
|
|
||||||
|
|
||||||
describer = Blueprint(
|
|
||||||
"describer",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/describe_files",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@describer.route("/<string:distribusiname>")
|
|
||||||
@login_required
|
|
||||||
def show_distribusi_files(distribusiname):
|
|
||||||
if not current_user.is_authenticated:
|
|
||||||
return redirect(url_for("index"))
|
|
||||||
distribusi = Distribusis.query.filter_by(
|
|
||||||
distribusiname=distribusiname
|
|
||||||
).first()
|
|
||||||
redistribusi_form = ReDistribusiForm()
|
|
||||||
distribusi_file_forms = get_distribusi_file_forms(distribusi.id)
|
|
||||||
return render_template(
|
|
||||||
"describe.html",
|
|
||||||
distribusiname=distribusiname,
|
|
||||||
redistribusi_form=redistribusi_form,
|
|
||||||
distribusi_file_forms=distribusi_file_forms,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@describer.route("/redistribusi/<string:distribusiname>", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def re_distribusi_files(distribusiname):
|
|
||||||
distribusi = Distribusis.query.filter_by(
|
|
||||||
distribusiname=distribusiname
|
|
||||||
).first()
|
|
||||||
redistribusi_form = ReDistribusiForm()
|
|
||||||
if redistribusi_form.validate_on_submit():
|
|
||||||
userfolder = os.path.join("stash", distribusi.distribusiname)
|
|
||||||
cssfile = get_css_file(distribusi)
|
|
||||||
run_distribusi(userfolder, cssfile)
|
|
||||||
return redirect(
|
|
||||||
url_for(
|
|
||||||
"describer.show_distribusi_files",
|
|
||||||
distribusiname=distribusi.distribusiname,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@describer.route("/describe_file/<int:file_id>", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def describe_file(file_id):
|
|
||||||
distribusi_file = DistribusiFiles.query.filter_by(id=file_id).first()
|
|
||||||
describe_form = DescribeFilesForm(
|
|
||||||
distribusi_file.id, distribusi_file.path, distribusi_file.type
|
|
||||||
)
|
|
||||||
save_described_file_to_db(describe_form, distribusi_file)
|
|
||||||
add_alttext_to_file(describe_form, distribusi_file)
|
|
||||||
add_description_to_file(describe_form, distribusi_file)
|
|
||||||
distribusi = Distribusis.query.filter_by(
|
|
||||||
id=distribusi_file.distribusi
|
|
||||||
).first()
|
|
||||||
return redirect(
|
|
||||||
url_for(
|
|
||||||
"describer.show_distribusi_files",
|
|
||||||
distribusiname=distribusi.distribusiname,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@describer.route("/stash/<path:path>")
|
|
||||||
def send_stash_file(path):
|
|
||||||
return send_from_directory("stash", path)
|
|
||||||
|
|
||||||
|
|
||||||
def get_distribusi_file_forms(distribusi_id):
|
|
||||||
distribusi_file_forms = {}
|
|
||||||
distribusi_files = DistribusiFiles.query.filter_by(
|
|
||||||
distribusi=distribusi_id
|
|
||||||
).all()
|
|
||||||
for distribusi_file in distribusi_files:
|
|
||||||
describe_form = DescribeFilesForm(
|
|
||||||
distribusi_file.id, distribusi_file.path, distribusi_file.type
|
|
||||||
)
|
|
||||||
describe_form.description.data = distribusi_file.description
|
|
||||||
describe_form.alttext.data = distribusi_file.alttext
|
|
||||||
describe_form.searchtags.data = distribusi_file.tags
|
|
||||||
distribusi_file_forms[distribusi_file.id] = describe_form
|
|
||||||
return distribusi_file_forms
|
|
||||||
|
|
||||||
|
|
||||||
def save_described_file_to_db(describe_form, distribusi_file):
|
|
||||||
try:
|
|
||||||
if describe_form.description.data:
|
|
||||||
print(distribusi_file.id)
|
|
||||||
distribusi_file.description = describe_form.description.data
|
|
||||||
if describe_form.alttext.data:
|
|
||||||
distribusi_file.alttext = describe_form.alttext.data
|
|
||||||
if describe_form.searchtags.data:
|
|
||||||
distribusi_file.tags = describe_form.searchtags.data
|
|
||||||
db.session.add(distribusi_file)
|
|
||||||
db.session.commit()
|
|
||||||
except (InvalidRequestError, IntegrityError):
|
|
||||||
db.session.rollback()
|
|
||||||
describe_form.save.errors.append("Something went wrong!")
|
|
||||||
flash("Something went wrong!", "danger")
|
|
||||||
except DataError:
|
|
||||||
db.session.rollback()
|
|
||||||
describe_form.save.errors.append("Invalid Entry")
|
|
||||||
flash("Invalid Entry", "warning")
|
|
||||||
except InterfaceError:
|
|
||||||
db.session.rollback()
|
|
||||||
describe_form.save.errors.append("Error connecting to the database")
|
|
||||||
flash("Error connecting to the database", "danger")
|
|
||||||
except DatabaseError:
|
|
||||||
db.session.rollback()
|
|
||||||
describe_form.save.errors.append("Error connecting to the database")
|
|
||||||
flash("Error connecting to the database", "danger")
|
|
||||||
|
|
||||||
|
|
||||||
def add_alttext_to_file(describe_form, distribusi_file):
|
|
||||||
if not describe_form.alttext.data:
|
|
||||||
return
|
|
||||||
filename_no_ext = os.path.splitext(distribusi_file.path)[0]
|
|
||||||
with open(f"{filename_no_ext}_alttext.txt", "w") as alttext_file:
|
|
||||||
alttext_file.write(describe_form.alttext.data)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def add_description_to_file(describe_form, distribusi_file):
|
|
||||||
if not describe_form.description.data:
|
|
||||||
return
|
|
||||||
filename_no_ext = os.path.splitext(distribusi_file.path)[0]
|
|
||||||
with open(
|
|
||||||
f"{filename_no_ext}_dv_description.txt", "w"
|
|
||||||
) as description_file:
|
|
||||||
description_file.write(describe_form.description.data)
|
|
||||||
return
|
|
@ -1,48 +0,0 @@
|
|||||||
"""Describe File Form to describe files in the distribusi archive"""
|
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import StringField, SubmitField, validators
|
|
||||||
from wtforms.validators import Length
|
|
||||||
from wtforms.widgets import TextArea
|
|
||||||
|
|
||||||
|
|
||||||
class DescribeFilesForm(FlaskForm):
|
|
||||||
"""DescribeFileForm selection form."""
|
|
||||||
|
|
||||||
alttext = StringField(
|
|
||||||
"Alt-text for this file:",
|
|
||||||
validators=[
|
|
||||||
Length(3, 255),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
searchtags = StringField(
|
|
||||||
"Add search tags, seperated by commas. No need for the '#' sign:",
|
|
||||||
validators=[
|
|
||||||
Length(3, 500),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
description = StringField(
|
|
||||||
"Description of this file:",
|
|
||||||
validators=[
|
|
||||||
Length(3, 4096),
|
|
||||||
],
|
|
||||||
widget=TextArea(),
|
|
||||||
)
|
|
||||||
save = SubmitField("Save")
|
|
||||||
|
|
||||||
def __init__(self, id, file_path=None, type=None):
|
|
||||||
super(DescribeFilesForm, self).__init__()
|
|
||||||
self.id = id
|
|
||||||
self.file_path = file_path
|
|
||||||
self.type = type
|
|
||||||
self.alttext.id = f"alttext-{id}"
|
|
||||||
self.searchtags.id = f"searchtags-{id}"
|
|
||||||
self.description.id = f"description-{id}"
|
|
||||||
self.save.id = f"save-{id}"
|
|
||||||
|
|
||||||
|
|
||||||
# def StringFieldWithId(form_name, **kwargs):
|
|
||||||
# name = kwargs.get('name', 'Bar')
|
|
||||||
# return wtf.StringField(name, render_kw={
|
|
||||||
# 'id': f'{}{}'.format(form_name, name.lower(),
|
|
||||||
# **kwargs.get('render_kw', {})})
|
|
@ -1,8 +0,0 @@
|
|||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import SubmitField
|
|
||||||
|
|
||||||
|
|
||||||
class ReDistribusiForm(FlaskForm):
|
|
||||||
"""Re-Distribusi form class to re-distrusi with desribed files"""
|
|
||||||
|
|
||||||
submit = SubmitField("Re-Distribusi!")
|
|
@ -1,32 +0,0 @@
|
|||||||
#describe_workflow {
|
|
||||||
display: flex;
|
|
||||||
width: 60em;
|
|
||||||
margin:0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.redistribusi {
|
|
||||||
position: sticky;
|
|
||||||
top: 2em;
|
|
||||||
margin-top: 2em;
|
|
||||||
margin-left: 1em;
|
|
||||||
width: 15em;
|
|
||||||
height: 10em;
|
|
||||||
border-style: outset;
|
|
||||||
float: right;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.distribusi_file {
|
|
||||||
margin-top: 2em;
|
|
||||||
padding: 0.5em;
|
|
||||||
width: 42em;
|
|
||||||
background-color:#fdfdfd;
|
|
||||||
text-decoration: none;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
border-style: outset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.distribusi_file > img, video {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
{% extends "base/base.html" %}
|
|
||||||
{% block main %}
|
|
||||||
<div id="buttons">
|
|
||||||
{% if current_user.is_authenticated %}
|
|
||||||
<div class="distribusi">
|
|
||||||
<a href="{{ url_for('distribusikan.distribusi') }}">
|
|
||||||
<input type="button" name="button" value="Distribusi"></input>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% if adminuser %}
|
|
||||||
<div class="admin">
|
|
||||||
<a href="/admin">
|
|
||||||
<input type="button" name="button" value="Admin"></input>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="logout">
|
|
||||||
<a href="/logout">
|
|
||||||
<input type="button" name="button" value="Logout"></input>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="help">
|
|
||||||
<a href="/help">
|
|
||||||
<input type="button" name="button" value="Help"></input>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="describe_workflow">
|
|
||||||
<div class="distribusi_files">
|
|
||||||
{% for id, describe_form in distribusi_file_forms.items() %}
|
|
||||||
<div class="distribusi_file">
|
|
||||||
{% if describe_form.type == "image" %}
|
|
||||||
<img src="{{describe_form.file_path}}" alt="" data-src="{{describe_form.file_path}}" loading="lazy"/>
|
|
||||||
{% elif describe_form.type == "video" %}
|
|
||||||
<video src="{{describe_form.file_path}}" preload="auto" alt="" controls loading="lazy"></video>
|
|
||||||
{% elif describe_form.type == "audio" %}
|
|
||||||
<audio src="{{describe_form.file_path}}" preload="auto" alt="" controls loading="lazy"> </audio>
|
|
||||||
{% else %}
|
|
||||||
<a href="{{describe_form.file_path}}">file: {{describe_form.file_path}}</a>
|
|
||||||
{% endif %}
|
|
||||||
<form id={{id}} method="POST" enctype="multipart/form-data" action="{{ url_for('describer.describe_file', file_id=id) }}">
|
|
||||||
{{ describe_form.csrf_token }}
|
|
||||||
<p>File path: {{describe_form.file_path}}</p>
|
|
||||||
<fieldset class="description">
|
|
||||||
{{ describe_form.description.label }}
|
|
||||||
{{ describe_form.description }}
|
|
||||||
{% for message in describe_form.description.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="">
|
|
||||||
{{ describe_form.searchtags.label }}
|
|
||||||
{{ describe_form.searchtags }}
|
|
||||||
{% for message in describe_form.searchtags.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="">
|
|
||||||
{{ describe_form.alttext.label }}
|
|
||||||
{{ describe_form.alttext }}
|
|
||||||
{% for message in describe_form.alttext.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="button">
|
|
||||||
{{ describe_form.save }}
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endfor%}
|
|
||||||
</div>
|
|
||||||
{% block redistribusi %}
|
|
||||||
{% include "redistribusi.html" %}
|
|
||||||
{% endblock redistribusi%}
|
|
||||||
</div>
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('describer.static', filename='css/describer.css')}}">
|
|
||||||
{% endblock %}
|
|
@ -1,13 +0,0 @@
|
|||||||
<div class="redistribusi">
|
|
||||||
<p class="tooltip">Run distribusi again after describing your files.<span class="tooltiptext">Distribusi will run again and add your alttext and descriptions.
|
|
||||||
</span></p>
|
|
||||||
<form id={{distribusiname}} method="POST" enctype="multipart/form-data" action="{{ url_for('describer.re_distribusi_files', distribusiname=distribusiname) }}">
|
|
||||||
{{ redistribusi_form.csrf_token }}
|
|
||||||
<fieldset class="">
|
|
||||||
{{ redistribusi_form.submit }}
|
|
||||||
{% for message in redistribusi_form.submit.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,82 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import magic
|
|
||||||
from distribusi.mappings import FILE_TYPES
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from models.distribusi_file_model import DistribusiFiles
|
|
||||||
from app import create_app, get_app, db
|
|
||||||
from sqlalchemy.exc import (
|
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
IntegrityError,
|
|
||||||
InterfaceError,
|
|
||||||
InvalidRequestError,
|
|
||||||
)
|
|
||||||
|
|
||||||
MIME_TYPE = magic.Magic(mime=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _distribusi_file_with_type(distribusi, full_path):
|
|
||||||
mime = MIME_TYPE.from_file(full_path)
|
|
||||||
type_, subtype = mime.split("/")
|
|
||||||
if type_ in FILE_TYPES:
|
|
||||||
_add_distribusi_file_to_db(distribusi, full_path, type_)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_distribusi_from_path(path):
|
|
||||||
distribusi = Distribusis.query.filter_by(distribusiname=path).first()
|
|
||||||
return distribusi
|
|
||||||
|
|
||||||
|
|
||||||
def _add_distribusi_file_to_db(distribusi, full_path, type):
|
|
||||||
app = get_app()
|
|
||||||
app.logger.info(f"adding file to database: {full_path} type: {type}")
|
|
||||||
distribusi_file = DistribusiFiles.query.filter_by(path=full_path).first()
|
|
||||||
if distribusi_file is not None:
|
|
||||||
app.logger.error(f"File already in database: {full_path}")
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
new_distribusi_file = DistribusiFiles(
|
|
||||||
path=full_path,
|
|
||||||
type=type,
|
|
||||||
distribusi=distribusi.id,
|
|
||||||
)
|
|
||||||
db.session.add(new_distribusi_file)
|
|
||||||
db.session.commit()
|
|
||||||
return
|
|
||||||
except InvalidRequestError:
|
|
||||||
db.session.rollback()
|
|
||||||
app.logger.error("Something went wrong!")
|
|
||||||
except IntegrityError:
|
|
||||||
db.session.rollback()
|
|
||||||
app.logger.error("File %s already exists!", full_path)
|
|
||||||
except DataError:
|
|
||||||
db.session.rollback()
|
|
||||||
app.logger.error("%s Invalid Entry", full_path)
|
|
||||||
except InterfaceError:
|
|
||||||
db.session.rollback()
|
|
||||||
app.logger.error("Error connecting to the database")
|
|
||||||
except DatabaseError:
|
|
||||||
db.session.rollback()
|
|
||||||
app.logger.error("Error connecting to the database")
|
|
||||||
|
|
||||||
|
|
||||||
def add_distribusi_files_to_db(path):
|
|
||||||
distribusi = _get_distribusi_from_path(path)
|
|
||||||
path = os.path.join("stash", path)
|
|
||||||
for root, dirs, files in os.walk(path, topdown=True):
|
|
||||||
files = list(filter(lambda f: not f.startswith("."), files))
|
|
||||||
files = list(filter(lambda f: not f.endswith(".html"), files))
|
|
||||||
files = list(filter(lambda f: not f.endswith("_thumbnail.jpg"), files))
|
|
||||||
files = list(filter(lambda f: not f.endswith("_alttext.txt"), files))
|
|
||||||
files = list(
|
|
||||||
filter(lambda f: not f.endswith("_dv_description.txt"), files)
|
|
||||||
)
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
full_path = os.path.join(root, file)
|
|
||||||
distribusi_file = DistribusiFiles.query.filter_by(
|
|
||||||
path=full_path
|
|
||||||
).first()
|
|
||||||
if distribusi_file is None:
|
|
||||||
_distribusi_file_with_type(distribusi, full_path)
|
|
@ -1,47 +0,0 @@
|
|||||||
from flask import Blueprint
|
|
||||||
from flask_login import login_required
|
|
||||||
|
|
||||||
from distribusikan.distribusi_selector import distribusi_selector
|
|
||||||
|
|
||||||
# Distribusi Information
|
|
||||||
from distribusikan.distribusi_workflow import distribusi_workflow
|
|
||||||
from distribusikan.css_editor_page import css_editor_page
|
|
||||||
from distribusikan.theme_selector import theme_selector
|
|
||||||
from distribusikan.upload_page import upload_page
|
|
||||||
|
|
||||||
distribusikan = Blueprint(
|
|
||||||
"distribusikan",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/distribusikan",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@distribusikan.route("/distribusi", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def distribusi():
|
|
||||||
return distribusi_workflow()
|
|
||||||
|
|
||||||
|
|
||||||
@distribusikan.route("/upload", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def upload():
|
|
||||||
return upload_page()
|
|
||||||
|
|
||||||
|
|
||||||
@distribusikan.route("/theme", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def theme():
|
|
||||||
return theme_selector()
|
|
||||||
|
|
||||||
|
|
||||||
@distribusikan.route("/editor", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def editor():
|
|
||||||
return css_editor_page()
|
|
||||||
|
|
||||||
|
|
||||||
@distribusikan.route("/selector", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def selector():
|
|
||||||
return distribusi_selector()
|
|
@ -1,94 +0,0 @@
|
|||||||
from flask_wtf import FlaskForm
|
|
||||||
from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize
|
|
||||||
from wtforms import (
|
|
||||||
SelectField,
|
|
||||||
StringField,
|
|
||||||
SubmitField,
|
|
||||||
TextAreaField,
|
|
||||||
validators,
|
|
||||||
)
|
|
||||||
from wtforms.validators import (
|
|
||||||
DataRequired,
|
|
||||||
Length,
|
|
||||||
ValidationError,
|
|
||||||
)
|
|
||||||
from wtforms.widgets import TextArea
|
|
||||||
|
|
||||||
from app import settings
|
|
||||||
|
|
||||||
|
|
||||||
class UploadForm(FlaskForm):
|
|
||||||
"""File upload class for a new site in distribusi-verse"""
|
|
||||||
|
|
||||||
def distribusiname(form, field):
|
|
||||||
if field.data.lower() == "new":
|
|
||||||
raise ValidationError("Name has to be unique and not just new.")
|
|
||||||
|
|
||||||
def category_choices():
|
|
||||||
APP = settings()
|
|
||||||
config_categories = APP.config["categories"]
|
|
||||||
categories = []
|
|
||||||
for config_category in config_categories:
|
|
||||||
categories.append((config_category, config_category))
|
|
||||||
return categories
|
|
||||||
|
|
||||||
def year_choices():
|
|
||||||
APP = settings()
|
|
||||||
start_time = APP.config["start_time"]
|
|
||||||
end_time = APP.config["end_time"]
|
|
||||||
year_range = range(start_time.year, end_time.year, 1)
|
|
||||||
year_choices = []
|
|
||||||
for year in year_range:
|
|
||||||
year_choices.append((str(year), str(year)))
|
|
||||||
return year_choices
|
|
||||||
|
|
||||||
sitename = StringField(
|
|
||||||
"Name of your archive section:",
|
|
||||||
validators=[
|
|
||||||
validators.InputRequired(),
|
|
||||||
Length(2, 100),
|
|
||||||
distribusiname,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
description = StringField(
|
|
||||||
"Description of this distribusi:",
|
|
||||||
validators=[
|
|
||||||
Length(10, 32000),
|
|
||||||
],
|
|
||||||
widget=TextArea(),
|
|
||||||
)
|
|
||||||
year = SelectField(
|
|
||||||
"Year:",
|
|
||||||
validate_choice=True,
|
|
||||||
coerce=str,
|
|
||||||
choices=year_choices,
|
|
||||||
option_widget=None,
|
|
||||||
validators=[DataRequired()],
|
|
||||||
)
|
|
||||||
category = SelectField(
|
|
||||||
"Category:",
|
|
||||||
validate_choice=True,
|
|
||||||
coerce=str,
|
|
||||||
choices=category_choices,
|
|
||||||
option_widget=None,
|
|
||||||
validators=[DataRequired()],
|
|
||||||
)
|
|
||||||
|
|
||||||
tags = StringField(
|
|
||||||
"Add search tags, seperated by commas. No need for the '#' sign:",
|
|
||||||
validators=[validators.InputRequired(), Length(3, 500)],
|
|
||||||
)
|
|
||||||
|
|
||||||
zipfile = FileField(
|
|
||||||
"Upload your zip file with content here:",
|
|
||||||
validators=[
|
|
||||||
FileAllowed(["zip"], "Zip archives only!"),
|
|
||||||
FileRequired(),
|
|
||||||
FileSize(
|
|
||||||
max_size=1073741824,
|
|
||||||
message="Zipfile size must be smaller than 1024MB",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
submit = SubmitField("Upload")
|
|
@ -1,32 +0,0 @@
|
|||||||
#publicthemes > ul {
|
|
||||||
max-height: 20em;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#publicthemes > ul > li{
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow{
|
|
||||||
margin-top: 1em;
|
|
||||||
padding: 0.5em;
|
|
||||||
background-color:#fdfdfd;
|
|
||||||
text-decoration: none;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
border-style: outset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow > p {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow > h2 {
|
|
||||||
padding-left: 0.4em;;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow input{
|
|
||||||
max-width: 20em;
|
|
||||||
}
|
|
||||||
.new_divider {
|
|
||||||
height: 5em;
|
|
||||||
}
|
|
@ -9,11 +9,14 @@ class Distribusis(db.Model):
|
|||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
distribusiname = db.Column(db.String(300), nullable=False, unique=True)
|
distribusiname = db.Column(db.String(300), nullable=False, unique=True)
|
||||||
userid = db.Column(db.Integer, db.ForeignKey("users.id"))
|
userid = db.Column(db.Integer, db.ForeignKey("users.id"))
|
||||||
category = db.Column(db.String(500), nullable=True, unique=False)
|
term = db.Column(db.String(5), nullable=False, unique=False)
|
||||||
|
course = db.Column(db.String(500), nullable=True, unique=False)
|
||||||
|
|
||||||
|
# Academic year eg:2020-2021, so no need for a Datetime object
|
||||||
year = db.Column(db.String(9), nullable=True, unique=False)
|
year = db.Column(db.String(9), nullable=True, unique=False)
|
||||||
description = db.Column(db.String(32000), nullable=True, unique=False)
|
|
||||||
tags = db.Column(db.String(500), nullable=True, unique=False)
|
tags = db.Column(db.String(500), nullable=True, unique=False)
|
||||||
publictheme = db.Column(db.String(300), unique=True, nullable=True)
|
publictheme = db.Column(db.String(300), unique=True, nullable=True)
|
||||||
|
|
||||||
visible = db.Column(db.Boolean, default=False)
|
visible = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
@ -1,89 +1,77 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from flask import flash, render_template
|
||||||
from flask import flash, render_template, redirect, url_for
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from app import db
|
from usermodel import User
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
from distribusimodel import Distribusis
|
||||||
from distribusikan.forms.distribusiform import DistribusiForm
|
from distribusisinfo import DistribusisInfo
|
||||||
from distribusikan.forms.publicthemeform import PublicThemeForm
|
|
||||||
from distribusikan.forms.selectorform import SelectorForm
|
from forms.selectorform import SelectorForm
|
||||||
from distribusikan.forms.themeform import ThemeForm
|
from forms.uploadform import UploadForm
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
from forms.distribusiform import DistribusiForm
|
||||||
from models.distribusi_model import Distribusis
|
from forms.themeform import ThemeForm
|
||||||
from models.user_model import User
|
from forms.publicthemeform import PublicThemeForm
|
||||||
|
|
||||||
# UserPengguna
|
# UserPengguna
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
|
||||||
def distribusi_selector():
|
|
||||||
|
def DistribusiSelector():
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
if selectorform.validate_on_submit():
|
if selectorform.validate_on_submit():
|
||||||
if selectorform.new.data:
|
if selectorform.new.data:
|
||||||
select_new_distribusi()
|
SelectNewDistribusi()
|
||||||
if selectorform.describe.data:
|
|
||||||
return select_describe_distribusi(selectorform.distribusis.data)
|
|
||||||
if selectorform.delete.data:
|
if selectorform.delete.data:
|
||||||
selectorform = delete_distribusi(selectorform.distribusis.data)
|
selectorform = DeleteDistribusi(selectorform.distribusis.data)
|
||||||
selectorform.distribusis.choices = (
|
selectorform.distribusis.choices = (
|
||||||
DistribusisInfo.user_distribusinames()
|
DistribusisInfo.userdistribusinames()
|
||||||
)
|
)
|
||||||
if selectorform.update.data:
|
if selectorform.update.data:
|
||||||
select_update_distribusi(selectorform.distribusis.data)
|
SelectUpdateDistribusi(selectorform.distribusis.data)
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
uploadform = auto_fill_in_upload_form(
|
uploadform = AutoFillInUploadForm(uploadform, current_distribusi)
|
||||||
uploadform, current_distribusi
|
|
||||||
)
|
|
||||||
|
|
||||||
return render_distribusi_template(
|
return RenderDistribusiTemplate(
|
||||||
selectorform, uploadform, current_distribusi
|
selectorform, uploadform, current_distribusi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def auto_fill_in_upload_form(uploadform, current_distribusi):
|
def AutoFillInUploadForm(uploadform, current_distribusi):
|
||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
).first()
|
).first()
|
||||||
uploadform.sitename.data = distribusi.distribusiname
|
uploadform.sitename.data = distribusi.distribusiname
|
||||||
uploadform.sitename.render_kw = {"readonly": True}
|
uploadform.sitename.render_kw = {"readonly": True}
|
||||||
uploadform.description.data = distribusi.description
|
uploadform.term.data = distribusi.term
|
||||||
uploadform.category.data = distribusi.category
|
uploadform.course.data = distribusi.course
|
||||||
uploadform.year.data = distribusi.year
|
uploadform.academicyear.data = distribusi.year
|
||||||
uploadform.tags.data = distribusi.tags
|
uploadform.tags.data = distribusi.tags
|
||||||
return uploadform
|
return uploadform
|
||||||
|
|
||||||
|
|
||||||
def select_new_distribusi():
|
def SelectNewDistribusi():
|
||||||
print("make a new distribusi")
|
print("make a new distribusi")
|
||||||
select_current_distribusi("new")
|
SelectCurrentDistribusi("new")
|
||||||
|
|
||||||
|
|
||||||
def select_describe_distribusi(distribusiname):
|
def SelectUpdateDistribusi(distribusiname):
|
||||||
return redirect(
|
|
||||||
url_for(
|
|
||||||
"describer.show_distribusi_files",
|
|
||||||
distribusiname=distribusiname,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def select_update_distribusi(distribusiname):
|
|
||||||
print(f"Update this distribusi {distribusiname}")
|
print(f"Update this distribusi {distribusiname}")
|
||||||
select_current_distribusi(distribusiname)
|
SelectCurrentDistribusi(distribusiname)
|
||||||
|
|
||||||
|
|
||||||
def delete_distribusi(distribusiname):
|
def DeleteDistribusi(distribusiname):
|
||||||
print(f"delete this distribusi {distribusiname}")
|
print(f"delete this distribusi {distribusiname}")
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
try:
|
try:
|
||||||
@ -117,7 +105,7 @@ def delete_distribusi(distribusiname):
|
|||||||
return selectorform
|
return selectorform
|
||||||
|
|
||||||
|
|
||||||
def select_current_distribusi(distribusiname):
|
def SelectCurrentDistribusi(distribusiname):
|
||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
return
|
return
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
@ -129,35 +117,35 @@ def select_current_distribusi(distribusiname):
|
|||||||
flash("An error occured !", "danger")
|
flash("An error occured !", "danger")
|
||||||
|
|
||||||
|
|
||||||
def distribusi_selected():
|
def DistribusiSelected():
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
if user.currentdistribusi is None:
|
if user.currentdistribusi is None:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def selector_visible():
|
def SelectorVisible():
|
||||||
has_distribusi = UserHelper.has_distribusi()
|
has_distribusi = UserHelper.has_distribusi()
|
||||||
is_distribusi_selected = distribusi_selected()
|
distribusi_selected = DistribusiSelected()
|
||||||
if is_distribusi_selected:
|
if distribusi_selected:
|
||||||
return False
|
return False
|
||||||
if not has_distribusi:
|
if not has_distribusi:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def render_distribusi_template(selectorform, uploadform, current_distribusi):
|
def RenderDistribusiTemplate(selectorform, uploadform, current_distribusi):
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
||||||
|
|
||||||
# because the user has chosen to update his distribusi, we assume
|
# because the user has chosen to update his distribusi, we assume
|
||||||
# no selected css.
|
# no selected css.
|
||||||
css_selected = False
|
css_selected = False
|
||||||
selectorvisible = selector_visible()
|
selectorvisible = SelectorVisible()
|
||||||
limit_reached = UserHelper.distribusi_limit_reached()
|
limit_reached = UserHelper.distribusi_limit_reached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
@ -1,18 +1,18 @@
|
|||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from models.distribusi_model import Distribusis
|
from usermodel import User
|
||||||
from models.user_model import User
|
from distribusimodel import Distribusis
|
||||||
|
|
||||||
|
|
||||||
class DistribusisInfo:
|
class DistribusisInfo:
|
||||||
def user_distribusinames():
|
def userdistribusinames():
|
||||||
distribusinames = []
|
distribusinames = []
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
for distribusi in Distribusis.query.filter_by(userid=user.id).all():
|
for distribusi in Distribusis.query.filter_by(userid=user.id).all():
|
||||||
distribusinames.append(distribusi.distribusiname)
|
distribusinames.append(distribusi.distribusiname)
|
||||||
return distribusinames
|
return distribusinames
|
||||||
|
|
||||||
def public_themes():
|
def publicthemes():
|
||||||
publicthemes = []
|
publicthemes = []
|
||||||
distribusis = Distribusis.query.filter(
|
distribusis = Distribusis.query.filter(
|
||||||
Distribusis.publictheme.isnot(None)
|
Distribusis.publictheme.isnot(None)
|
||||||
@ -27,12 +27,12 @@ made by {user.username}""",
|
|||||||
publicthemes.append(publictheme)
|
publicthemes.append(publictheme)
|
||||||
return publicthemes
|
return publicthemes
|
||||||
|
|
||||||
def visible_distribusis():
|
def visibledistribusis():
|
||||||
distribusis = Distribusis.query.filter(
|
distribusis = Distribusis.query.filter(
|
||||||
Distribusis.visible.isnot(False)
|
Distribusis.visible.isnot(False)
|
||||||
).all()
|
).all()
|
||||||
return distribusis
|
return distribusis
|
||||||
|
|
||||||
def get_user_distribusis(useremail):
|
def getuserdistribusis(useremail):
|
||||||
user = User.query.filter_by(email=useremail).first()
|
user = User.query.filter_by(email=useremail).first()
|
||||||
return Distribusis.query.filter_by(userid=user.id).all()
|
return Distribusis.query.filter_by(userid=user.id).all()
|
@ -1,38 +1,43 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from flask_login import current_user
|
||||||
|
from flask import (
|
||||||
|
render_template,
|
||||||
|
redirect,
|
||||||
|
url_for,
|
||||||
|
flash,
|
||||||
|
)
|
||||||
|
from sqlalchemy.exc import (
|
||||||
|
InvalidRequestError,
|
||||||
|
DataError,
|
||||||
|
InterfaceError,
|
||||||
|
DatabaseError,
|
||||||
|
)
|
||||||
|
from app import db
|
||||||
|
|
||||||
|
from usermodel import User
|
||||||
|
from distribusimodel import Distribusis
|
||||||
|
|
||||||
|
# UserPengguna
|
||||||
|
from statuspengguna.helper import UserHelper
|
||||||
|
from distribusiselector import SelectorVisible
|
||||||
|
|
||||||
|
# Forms!
|
||||||
|
from forms.uploadform import UploadForm
|
||||||
|
from forms.distribusiform import DistribusiForm
|
||||||
|
from forms.themeform import ThemeForm
|
||||||
|
from forms.publicthemeform import PublicThemeForm
|
||||||
|
from forms.selectorform import SelectorForm
|
||||||
|
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
# Tada!
|
# Tada!
|
||||||
from distribusi.cli import build_argparser
|
from distribusi.cli import build_argparser
|
||||||
from distribusi.distribusi import distribusify
|
from distribusi.distribusi import distribusify
|
||||||
from flask import flash, redirect, render_template, url_for
|
|
||||||
from flask_login import current_user
|
|
||||||
from sqlalchemy.exc import (
|
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
InterfaceError,
|
|
||||||
InvalidRequestError,
|
|
||||||
)
|
|
||||||
|
|
||||||
from app import db
|
|
||||||
from distribusikan.add_files_to_describer import add_distribusi_files_to_db
|
|
||||||
from distribusikan.distribusi_selector import selector_visible
|
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
|
||||||
from distribusikan.forms.distribusiform import DistribusiForm
|
|
||||||
from distribusikan.forms.publicthemeform import PublicThemeForm
|
|
||||||
from distribusikan.forms.selectorform import SelectorForm
|
|
||||||
from distribusikan.forms.themeform import ThemeForm
|
|
||||||
|
|
||||||
# Forms!
|
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
# UserPengguna
|
|
||||||
from statuspengguna.helper import UserHelper
|
|
||||||
|
|
||||||
|
|
||||||
def distribusi_workflow():
|
def DistribusiWorkflow():
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
@ -42,19 +47,18 @@ def distribusi_workflow():
|
|||||||
|
|
||||||
if distribusiform.validate_on_submit():
|
if distribusiform.validate_on_submit():
|
||||||
userfolder = os.path.join("stash", distribusi.distribusiname)
|
userfolder = os.path.join("stash", distribusi.distribusiname)
|
||||||
cssfile = get_css_file(distribusi)
|
cssfile = GetCssFile(distribusi)
|
||||||
unzip_distribusi_files(distribusi, userfolder)
|
UnzipDistribusiFiles(distribusi, userfolder)
|
||||||
clean_up_distribusi_files(userfolder)
|
CleanUpDistribusiFiles(userfolder)
|
||||||
add_distribusi_files_to_db(distribusi.distribusiname)
|
RunDistribusi(userfolder, cssfile)
|
||||||
run_distribusi(userfolder, cssfile)
|
SetDistribusiToVisible(distribusi, user)
|
||||||
set_distribusi_to_visible(distribusi, user)
|
DeleteCssFile(cssfile)
|
||||||
delete_css_file(cssfile)
|
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
return render_distribusi_template(distribusiform, current_distribusi)
|
return RenderDistribusiTemplate(distribusiform, current_distribusi)
|
||||||
|
|
||||||
|
|
||||||
def unzip_distribusi_files(distribusi, userfolder):
|
def UnzipDistribusiFiles(distribusi, userfolder):
|
||||||
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
||||||
unzipfile = os.path.join(userfolder, zipfilename)
|
unzipfile = os.path.join(userfolder, zipfilename)
|
||||||
|
|
||||||
@ -66,15 +70,15 @@ def unzip_distribusi_files(distribusi, userfolder):
|
|||||||
os.remove(os.path.join(userfolder, zipfilename))
|
os.remove(os.path.join(userfolder, zipfilename))
|
||||||
|
|
||||||
|
|
||||||
def clean_up_distribusi_files(userfolder):
|
def CleanUpDistribusiFiles(userfolder):
|
||||||
if os.path.exists(userfolder):
|
if os.path.exists(userfolder):
|
||||||
remove_mac_folders(userfolder)
|
RemoveMacFolders(userfolder)
|
||||||
|
|
||||||
|
|
||||||
def remove_mac_folders(path):
|
def RemoveMacFolders(path):
|
||||||
for filename in os.listdir(path):
|
for filename in os.listdir(path):
|
||||||
fullpath = os.path.join(path, filename)
|
fullpath = os.path.join(path, filename)
|
||||||
if filename.startswith("."):
|
if filename.startswith('.'):
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
shutil.rmtree(fullpath)
|
shutil.rmtree(fullpath)
|
||||||
else:
|
else:
|
||||||
@ -82,10 +86,10 @@ def remove_mac_folders(path):
|
|||||||
if filename == "__MACOSX":
|
if filename == "__MACOSX":
|
||||||
shutil.rmtree(fullpath)
|
shutil.rmtree(fullpath)
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
remove_mac_folders(fullpath)
|
RemoveMacFolders(fullpath)
|
||||||
|
|
||||||
|
|
||||||
def get_css_file(distribusi):
|
def GetCssFile(distribusi):
|
||||||
cssfile = ""
|
cssfile = ""
|
||||||
cssfolder = os.path.join("themes/userthemes", distribusi.distribusiname)
|
cssfolder = os.path.join("themes/userthemes", distribusi.distribusiname)
|
||||||
if os.path.exists(cssfolder):
|
if os.path.exists(cssfolder):
|
||||||
@ -95,14 +99,13 @@ def get_css_file(distribusi):
|
|||||||
return cssfile
|
return cssfile
|
||||||
|
|
||||||
|
|
||||||
def run_distribusi(userfolder, cssfile):
|
def RunDistribusi(userfolder, cssfile):
|
||||||
print(f"Run distribusi on this folder: {userfolder} with css:{cssfile}")
|
|
||||||
parser = build_argparser()
|
parser = build_argparser()
|
||||||
args = parser.parse_args(["-t", "-a", "--menu-with-index", "-s", cssfile])
|
args = parser.parse_args(["--menu-with-index", "-s", cssfile])
|
||||||
distribusify(args, userfolder)
|
distribusify(args, userfolder)
|
||||||
|
|
||||||
|
|
||||||
def set_distribusi_to_visible(distribusi, user):
|
def SetDistribusiToVisible(distribusi, user):
|
||||||
try:
|
try:
|
||||||
distribusi.visible = True
|
distribusi.visible = True
|
||||||
user.currentdistribusi = None
|
user.currentdistribusi = None
|
||||||
@ -112,19 +115,19 @@ def set_distribusi_to_visible(distribusi, user):
|
|||||||
flash("Unknown error occured!")
|
flash("Unknown error occured!")
|
||||||
|
|
||||||
|
|
||||||
def delete_css_file(cssfile):
|
def DeleteCssFile(cssfile):
|
||||||
if os.path.exists(cssfile):
|
if os.path.exists(cssfile):
|
||||||
os.remove(cssfile)
|
os.remove(cssfile)
|
||||||
|
|
||||||
|
|
||||||
def render_distribusi_template(distribusiform, current_distribusi):
|
def RenderDistribusiTemplate(distribusiform, current_distribusi):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
selectorvisible = selector_visible()
|
selectorvisible = SelectorVisible()
|
||||||
|
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
@ -1,40 +1,41 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
from bleach_allowlist import all_styles
|
from bleach_allowlist import all_styles
|
||||||
|
import shutil
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
from werkzeug.utils import secure_filename
|
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
from distribusimodel import Distribusis
|
||||||
from distribusikan.forms.distribusiform import DistribusiForm
|
|
||||||
from distribusikan.forms.editorform import EditorForm
|
|
||||||
from distribusikan.forms.publicthemeform import PublicThemeForm
|
|
||||||
from distribusikan.forms.selectorform import SelectorForm
|
|
||||||
from distribusikan.forms.themeform import ThemeForm
|
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
|
from forms.uploadform import UploadForm
|
||||||
|
from forms.distribusiform import DistribusiForm
|
||||||
|
from forms.themeform import ThemeForm
|
||||||
|
from forms.publicthemeform import PublicThemeForm
|
||||||
|
from forms.editorform import EditorForm
|
||||||
|
from forms.selectorform import SelectorForm
|
||||||
|
|
||||||
|
|
||||||
def css_editor_page():
|
def Editor():
|
||||||
editorform = EditorForm()
|
editorform = EditorForm()
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
if editorform.validate_on_submit():
|
if editorform.validate_on_submit():
|
||||||
validate_edit_css_form(editorform, current_distribusi)
|
ValidateEditCssForm(editorform, current_distribusi)
|
||||||
return render_distribusi_template(current_distribusi)
|
return RenderDistribusiTemplate(current_distribusi)
|
||||||
|
|
||||||
return render_editor_template(editorform, current_distribusi)
|
return RenderEditorTemplate(editorform, current_distribusi)
|
||||||
|
|
||||||
|
|
||||||
def validate_edit_css_form(editorform, current_distribusi):
|
def ValidateEditCssForm(editorform, current_distribusi):
|
||||||
newcssfolder = os.path.join("themes/userthemes", current_distribusi)
|
newcssfolder = os.path.join("themes/userthemes", current_distribusi)
|
||||||
if os.path.exists(newcssfolder):
|
if os.path.exists(newcssfolder):
|
||||||
shutil.rmtree(newcssfolder)
|
shutil.rmtree(newcssfolder)
|
||||||
@ -44,22 +45,22 @@ def validate_edit_css_form(editorform, current_distribusi):
|
|||||||
shutil.rmtree(publicfolder)
|
shutil.rmtree(publicfolder)
|
||||||
|
|
||||||
if editorform.public.data:
|
if editorform.public.data:
|
||||||
make_public_theme(editorform, current_distribusi)
|
MakePublicTheme(editorform, current_distribusi)
|
||||||
if editorform.cssfile.data:
|
if editorform.cssfile.data:
|
||||||
save_upload_css_file(editorform, publicfolder)
|
SaveUploadCssFile(editorform, publicfolder)
|
||||||
copy_public_to_user_folder(editorform, publicfolder, newcssfolder)
|
CopyPublicToUserFolder(editorform, publicfolder, newcssfolder)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
write_css_to_file(editorform, publicfolder)
|
WriteCssToFile(editorform, publicfolder)
|
||||||
|
|
||||||
if editorform.cssfile.data:
|
if editorform.cssfile.data:
|
||||||
save_upload_css_file(editorform, newcssfolder)
|
SaveUploadCssFile(editorform, newcssfolder)
|
||||||
return
|
return
|
||||||
if editorform.cssname.data:
|
if editorform.cssname.data:
|
||||||
write_css_to_file(editorform, newcssfolder)
|
WriteCssToFile(editorform, newcssfolder)
|
||||||
|
|
||||||
|
|
||||||
def save_upload_css_file(editorform, newcssfolder):
|
def SaveUploadCssFile(editorform, newcssfolder):
|
||||||
if not os.path.exists(newcssfolder):
|
if not os.path.exists(newcssfolder):
|
||||||
os.mkdir(newcssfolder)
|
os.mkdir(newcssfolder)
|
||||||
cssfile = editorform.cssfile.data
|
cssfile = editorform.cssfile.data
|
||||||
@ -67,37 +68,38 @@ def save_upload_css_file(editorform, newcssfolder):
|
|||||||
cssfile.save(os.path.join(newcssfolder, cssfilename))
|
cssfile.save(os.path.join(newcssfolder, cssfilename))
|
||||||
openfile = open(os.path.join(newcssfolder, cssfilename), "r")
|
openfile = open(os.path.join(newcssfolder, cssfilename), "r")
|
||||||
cleancss = bleach.clean(openfile.read(), all_styles)
|
cleancss = bleach.clean(openfile.read(), all_styles)
|
||||||
cleancss = cleancss.replace(">", ">")
|
cleancss = cleancss.replace('>', '>')
|
||||||
openfile.close()
|
openfile.close()
|
||||||
cleanfile = open(os.path.join(newcssfolder, cssfilename), "w")
|
cleanfile = open(os.path.join(newcssfolder, cssfilename), "w")
|
||||||
cleanfile.write(cleancss)
|
cleanfile.write(cleancss)
|
||||||
cleanfile.close()
|
cleanfile.close()
|
||||||
|
|
||||||
|
|
||||||
def write_css_to_file(editorform, newcssfolder):
|
def WriteCssToFile(editorform, newcssfolder):
|
||||||
if not os.path.exists(newcssfolder):
|
if not os.path.exists(newcssfolder):
|
||||||
os.mkdir(newcssfolder)
|
os.mkdir(newcssfolder)
|
||||||
|
|
||||||
cssfilename = f"{secure_filename(editorform.cssname.data)}.css"
|
cssfilename = f"{secure_filename(editorform.cssname.data)}.css"
|
||||||
cleancss = bleach.clean(editorform.css.data, all_styles)
|
cleancss = bleach.clean(editorform.css.data, all_styles)
|
||||||
cleancss = cleancss.replace(">", ">")
|
cleancss = cleancss.replace('>', '>')
|
||||||
with open(os.path.join(newcssfolder, cssfilename), "w") as cssfile:
|
with open(os.path.join(newcssfolder, cssfilename), "w") as cssfile:
|
||||||
cssfile.write(cleancss)
|
cssfile.write(cleancss)
|
||||||
cssfile.close
|
cssfile.close
|
||||||
|
|
||||||
|
|
||||||
def copy_public_to_user_folder(editorform, publicfolder, newcssfolder):
|
def CopyPublicToUserFolder(editorform, publicfolder, newcssfolder):
|
||||||
if not os.path.exists(newcssfolder):
|
if not os.path.exists(newcssfolder):
|
||||||
os.mkdir(newcssfolder)
|
os.mkdir(newcssfolder)
|
||||||
copycssfile = os.path.join(
|
copycssfile = os.path.join(
|
||||||
publicfolder, f"{secure_filename(editorform.cssname.data)}.css"
|
publicfolder,
|
||||||
|
f"{secure_filename(editorform.cssname.data)}.css"
|
||||||
)
|
)
|
||||||
print(f"copying file: {copycssfile}")
|
print(f"copying file: {copycssfile}")
|
||||||
print(f"to folder: {newcssfolder}")
|
print(f"to folder: {newcssfolder}")
|
||||||
shutil.copy(copycssfile, newcssfolder)
|
shutil.copy(copycssfile, newcssfolder)
|
||||||
|
|
||||||
|
|
||||||
def make_public_theme(editorform, current_distribusi):
|
def MakePublicTheme(editorform, current_distribusi):
|
||||||
try:
|
try:
|
||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
@ -119,12 +121,12 @@ def make_public_theme(editorform, current_distribusi):
|
|||||||
editorform.public.errors.append("Error connecting to the database")
|
editorform.public.errors.append("Error connecting to the database")
|
||||||
|
|
||||||
|
|
||||||
def render_distribusi_template(current_distribusi):
|
def RenderDistribusiTemplate(current_distribusi):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
|
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||||
@ -148,11 +150,11 @@ def render_distribusi_template(current_distribusi):
|
|||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def render_editor_template(editorform, current_distribusi):
|
def RenderEditorTemplate(editorform, current_distribusi):
|
||||||
html_placeholder = get_html_placeholder()
|
htmlplaceholder = HtmlPlaceholder()
|
||||||
|
|
||||||
css_placeholder = get_css_placeholder(current_distribusi)
|
cssplaceholder = CssPlaceholder(current_distribusi)
|
||||||
editorform.css.data = css_placeholder
|
editorform.css.data = cssplaceholder
|
||||||
|
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
||||||
@ -161,33 +163,33 @@ def render_editor_template(editorform, current_distribusi):
|
|||||||
files_uploaded=files_uploaded,
|
files_uploaded=files_uploaded,
|
||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
editorform=editorform,
|
editorform=editorform,
|
||||||
html_placeholder=html_placeholder,
|
htmlplaceholder=htmlplaceholder,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def get_css_placeholder(current_distribusi):
|
def CssPlaceholder(current_distribusi):
|
||||||
css_placeholder = "Try out your CSS here"
|
cssplaceholder = "Try out your CSS here"
|
||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
).first()
|
).first()
|
||||||
if distribusi is not None and distribusi.publictheme is not None:
|
if distribusi is not None and distribusi.publictheme is not None:
|
||||||
css_placeholder = get_public_css_file(distribusi)
|
cssplaceholder = GetPublicCssFile(distribusi)
|
||||||
else:
|
else:
|
||||||
with open("themes/editor/placeholder.css") as f:
|
with open("themes/editor/placeholder.css") as f:
|
||||||
css_placeholder = f.read()
|
cssplaceholder = f.read()
|
||||||
return css_placeholder
|
return cssplaceholder
|
||||||
|
|
||||||
|
|
||||||
def get_html_placeholder():
|
def HtmlPlaceholder():
|
||||||
html_placeholder = "Write some test HTML here"
|
htmlplaceholder = "Write some test HTML here"
|
||||||
with open("themes/editor/placeholder.html") as f:
|
with open("themes/editor/placeholder.html") as f:
|
||||||
html_placeholder = f.read()
|
htmlplaceholder = f.read()
|
||||||
return html_placeholder
|
return htmlplaceholder
|
||||||
|
|
||||||
|
|
||||||
def get_public_css_file(distribusi):
|
def GetPublicCssFile(distribusi):
|
||||||
css_placeholder = ""
|
cssplaceholder = ""
|
||||||
publicthemefolder = os.path.join(
|
publicthemefolder = os.path.join(
|
||||||
"themes/publicthemes", distribusi.distribusiname
|
"themes/publicthemes", distribusi.distribusiname
|
||||||
)
|
)
|
||||||
@ -195,5 +197,5 @@ def get_public_css_file(distribusi):
|
|||||||
if filename.endswith(".css"):
|
if filename.endswith(".css"):
|
||||||
cssfile = os.path.join(publicthemefolder, filename)
|
cssfile = os.path.join(publicthemefolder, filename)
|
||||||
with open(cssfile) as f:
|
with open(cssfile) as f:
|
||||||
css_placeholder = f.read()
|
cssplaceholder = f.read()
|
||||||
return css_placeholder
|
return cssplaceholder
|
@ -1,7 +1,9 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import BooleanField, SubmitField
|
from wtforms import (
|
||||||
|
BooleanField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AdminDistribusiForm(FlaskForm):
|
class AdminDistribusiForm(FlaskForm):
|
||||||
@ -13,7 +15,7 @@ class AdminDistribusiForm(FlaskForm):
|
|||||||
class DistribusiListForm(AdminDistribusiForm):
|
class DistribusiListForm(AdminDistribusiForm):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for i, distribusi in enumerate(distribusis):
|
for (i, distribusi) in enumerate(distribusis):
|
||||||
setattr(
|
setattr(
|
||||||
DistribusiListForm,
|
DistribusiListForm,
|
||||||
f"distribusi_{i}",
|
f"distribusi_{i}",
|
@ -1,7 +1,9 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import BooleanField, SubmitField
|
from wtforms import (
|
||||||
|
SubmitField,
|
||||||
|
BooleanField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AdminUserForm(FlaskForm):
|
class AdminUserForm(FlaskForm):
|
||||||
@ -11,7 +13,7 @@ class AdminUserForm(FlaskForm):
|
|||||||
class UserListForm(AdminUserForm):
|
class UserListForm(AdminUserForm):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for i, user in enumerate(users):
|
for (i, user) in enumerate(users):
|
||||||
setattr(
|
setattr(
|
||||||
UserListForm,
|
UserListForm,
|
||||||
f"user_{i}",
|
f"user_{i}",
|
||||||
@ -20,4 +22,8 @@ class AdminUserForm(FlaskForm):
|
|||||||
|
|
||||||
return UserListForm()
|
return UserListForm()
|
||||||
|
|
||||||
|
tutors = SubmitField("Are tutors")
|
||||||
|
|
||||||
|
nottutors = SubmitField("Are not tutors")
|
||||||
|
|
||||||
delete = SubmitField("Delete")
|
delete = SubmitField("Delete")
|
@ -1,15 +1,15 @@
|
|||||||
"""Form to save your CSS editor work."""
|
"""Form to save your CSS editor work."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from flask_wtf.file import FileAllowed, FileField, FileSize
|
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
BooleanField,
|
|
||||||
StringField,
|
StringField,
|
||||||
SubmitField,
|
|
||||||
TextAreaField,
|
TextAreaField,
|
||||||
validators,
|
BooleanField,
|
||||||
|
SubmitField,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
from wtforms.validators import Length
|
from wtforms.validators import Length
|
||||||
|
from flask_wtf.file import FileField, FileAllowed, FileSize
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
|
|
||||||
class EditorForm(FlaskForm):
|
class EditorForm(FlaskForm):
|
@ -1,8 +1,12 @@
|
|||||||
"""Forgotten password form to help user."""
|
"""Forgotten password form to help user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, SubmitField, validators
|
|
||||||
from wtforms.validators import Email, Length
|
|
||||||
|
|
||||||
|
|
||||||
class ForgotPasswordForm(FlaskForm):
|
class ForgotPasswordForm(FlaskForm):
|
@ -1,8 +1,13 @@
|
|||||||
"""Login form to validate user."""
|
"""Login form to validate user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
PasswordField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import PasswordField, StringField, SubmitField, validators
|
|
||||||
from wtforms.validators import Email, Length
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(FlaskForm):
|
class LoginForm(FlaskForm):
|
@ -1,7 +1,9 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import RadioField, SubmitField
|
from wtforms import (
|
||||||
|
RadioField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PublicThemeForm(FlaskForm):
|
class PublicThemeForm(FlaskForm):
|
@ -1,13 +1,22 @@
|
|||||||
"""Register form to make a new user."""
|
"""Register form to make a new user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
PasswordField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email, EqualTo, ValidationError
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import PasswordField, StringField, SubmitField, validators
|
|
||||||
from wtforms.validators import Email, EqualTo, Length
|
|
||||||
|
|
||||||
|
|
||||||
class RegisterForm(FlaskForm):
|
class RegisterForm(FlaskForm):
|
||||||
"""Register for distribusi-verse form class"""
|
"""Register for distribusi-verse form class"""
|
||||||
|
|
||||||
|
def hremail(form, field):
|
||||||
|
if not field.data.endswith("@hr.nl"):
|
||||||
|
raise ValidationError("Only HRO accounts are allowed.")
|
||||||
|
|
||||||
username = StringField(
|
username = StringField(
|
||||||
"Username:",
|
"Username:",
|
||||||
validators=[validators.InputRequired(), Length(3, 150)],
|
validators=[validators.InputRequired(), Length(3, 150)],
|
||||||
@ -18,8 +27,9 @@ class RegisterForm(FlaskForm):
|
|||||||
validators=[
|
validators=[
|
||||||
validators.InputRequired(),
|
validators.InputRequired(),
|
||||||
Email(),
|
Email(),
|
||||||
Length(6, 128),
|
Length(6, 64),
|
||||||
],
|
hremail,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
password = PasswordField(
|
password = PasswordField(
|
@ -1,8 +1,12 @@
|
|||||||
"""Reset Password Form form to reset a users PasswordField."""
|
"""Reset Password Form form to reset a users PasswordField."""
|
||||||
|
from wtforms import (
|
||||||
|
SubmitField,
|
||||||
|
PasswordField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, EqualTo
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import PasswordField, SubmitField, validators
|
|
||||||
from wtforms.validators import EqualTo, Length
|
|
||||||
|
|
||||||
|
|
||||||
class ResetPasswordForm(FlaskForm):
|
class ResetPasswordForm(FlaskForm):
|
@ -1,5 +1,5 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import SelectField, SubmitField
|
from wtforms import SubmitField, SelectField
|
||||||
|
|
||||||
|
|
||||||
class SelectorForm(FlaskForm):
|
class SelectorForm(FlaskForm):
|
||||||
@ -9,6 +9,4 @@ class SelectorForm(FlaskForm):
|
|||||||
|
|
||||||
update = SubmitField("update")
|
update = SubmitField("update")
|
||||||
|
|
||||||
describe = SubmitField("describe")
|
|
||||||
|
|
||||||
delete = SubmitField("delete")
|
delete = SubmitField("delete")
|
@ -1,7 +1,9 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import RadioField, SubmitField
|
from wtforms import (
|
||||||
|
RadioField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ThemeForm(FlaskForm):
|
class ThemeForm(FlaskForm):
|
88
verse/forms/uploadform.py
Normal file
88
verse/forms/uploadform.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask_wtf.file import FileField, FileAllowed, FileRequired, FileSize
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import (
|
||||||
|
Length,
|
||||||
|
NumberRange,
|
||||||
|
DataRequired,
|
||||||
|
ValidationError,
|
||||||
|
)
|
||||||
|
from wtforms import (
|
||||||
|
SubmitField,
|
||||||
|
StringField,
|
||||||
|
IntegerField,
|
||||||
|
SelectField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UploadForm(FlaskForm):
|
||||||
|
"""File upload class for a new site in distribusi-verse"""
|
||||||
|
|
||||||
|
def distribusiname(form, field):
|
||||||
|
if field.data.lower() == "new":
|
||||||
|
raise ValidationError("Name has to be unique and not just new.")
|
||||||
|
|
||||||
|
sitename = StringField(
|
||||||
|
"Name of your website:",
|
||||||
|
validators=[validators.InputRequired(), Length(2, 100), distribusiname],
|
||||||
|
)
|
||||||
|
academicyear = SelectField(
|
||||||
|
"Academic year:",
|
||||||
|
validate_choice=True,
|
||||||
|
coerce=str,
|
||||||
|
choices=[
|
||||||
|
(u'2021-2022', u'2021-2022'),
|
||||||
|
(u'2022-2023', u'2022-2023'),
|
||||||
|
(u'2023-2024', u'2023-2024'),
|
||||||
|
(u'2024-2025', u'2024-2025'),
|
||||||
|
(u'2020-2021', u'past: 2020-2021'),
|
||||||
|
],
|
||||||
|
option_widget=None,
|
||||||
|
validators=[DataRequired()]
|
||||||
|
)
|
||||||
|
term = SelectField(
|
||||||
|
"Term:",
|
||||||
|
validate_choice=True,
|
||||||
|
coerce=str,
|
||||||
|
choices=[
|
||||||
|
(u'1.2', u'1.2'),
|
||||||
|
(u'2.3', u'2.3'),
|
||||||
|
(u'3.1', u'3.1'),
|
||||||
|
(u'4.1', u'4.1'),
|
||||||
|
(u'4.2', u'4.2'),
|
||||||
|
],
|
||||||
|
option_widget=None,
|
||||||
|
validators=[DataRequired()]
|
||||||
|
)
|
||||||
|
tags = StringField(
|
||||||
|
"Add tags, seperated by commas. No need for the '#' sign:",
|
||||||
|
validators=[validators.InputRequired(), Length(2, 500)],
|
||||||
|
)
|
||||||
|
|
||||||
|
course = SelectField(
|
||||||
|
u'Course:',
|
||||||
|
validate_choice=True,
|
||||||
|
coerce=str,
|
||||||
|
choices=[
|
||||||
|
('hacking', u'Autonomous - Hacking'),
|
||||||
|
('digitalcraft', u'Autonomous - Digital Craft'),
|
||||||
|
('criticalstudies', u'Autonomous - Critical Studies'),
|
||||||
|
('publicprivate', u'Autonomous - Public&Private'),
|
||||||
|
],
|
||||||
|
option_widget=None,
|
||||||
|
validators=[DataRequired()]
|
||||||
|
)
|
||||||
|
|
||||||
|
zipfile = FileField(
|
||||||
|
"Upload your zip file with content here:",
|
||||||
|
validators=[
|
||||||
|
FileAllowed(["zip"], "Zip archives only!"),
|
||||||
|
FileRequired(),
|
||||||
|
FileSize(
|
||||||
|
max_size=104857600,
|
||||||
|
message="Zipfile size must be smaller than 100MB",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
submit = SubmitField("Upload")
|
@ -14,19 +14,17 @@ config = context.config
|
|||||||
# Interpret the config file for Python logging.
|
# Interpret the config file for Python logging.
|
||||||
# This line sets up loggers basically.
|
# This line sets up loggers basically.
|
||||||
fileConfig(config.config_file_name)
|
fileConfig(config.config_file_name)
|
||||||
logger = logging.getLogger("alembic.env")
|
logger = logging.getLogger('alembic.env')
|
||||||
|
|
||||||
# add your model's MetaData object here
|
# add your model's MetaData object here
|
||||||
# for 'autogenerate' support
|
# for 'autogenerate' support
|
||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
config.set_main_option(
|
config.set_main_option(
|
||||||
"sqlalchemy.url",
|
'sqlalchemy.url',
|
||||||
str(current_app.extensions["migrate"].db.get_engine().url).replace(
|
str(current_app.extensions['migrate'].db.get_engine().url).replace(
|
||||||
"%", "%%"
|
'%', '%%'))
|
||||||
),
|
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||||
)
|
|
||||||
target_metadata = current_app.extensions["migrate"].db.metadata
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
@ -67,20 +65,20 @@ def run_migrations_online():
|
|||||||
# when there are no changes to the schema
|
# when there are no changes to the schema
|
||||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||||
def process_revision_directives(context, revision, directives):
|
def process_revision_directives(context, revision, directives):
|
||||||
if getattr(config.cmd_opts, "autogenerate", False):
|
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||||
script = directives[0]
|
script = directives[0]
|
||||||
if script.upgrade_ops.is_empty():
|
if script.upgrade_ops.is_empty():
|
||||||
directives[:] = []
|
directives[:] = []
|
||||||
logger.info("No changes in schema detected.")
|
logger.info('No changes in schema detected.')
|
||||||
|
|
||||||
connectable = current_app.extensions["migrate"].db.get_engine()
|
connectable = current_app.extensions['migrate'].db.get_engine()
|
||||||
|
|
||||||
with connectable.connect() as connection:
|
with connectable.connect() as connection:
|
||||||
context.configure(
|
context.configure(
|
||||||
connection=connection,
|
connection=connection,
|
||||||
target_metadata=target_metadata,
|
target_metadata=target_metadata,
|
||||||
process_revision_directives=process_revision_directives,
|
process_revision_directives=process_revision_directives,
|
||||||
**current_app.extensions["migrate"].configure_args,
|
**current_app.extensions['migrate'].configure_args
|
||||||
)
|
)
|
||||||
|
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
from app import db
|
|
||||||
|
|
||||||
|
|
||||||
class DistribusiFiles(db.Model):
|
|
||||||
"""Distribusi file model class for a single file in a distribusi"""
|
|
||||||
|
|
||||||
__tablename__ = "distribusi_files"
|
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
type = db.Column(db.String(100), nullable=True, unique=False)
|
|
||||||
distribusi = db.Column(db.Integer, db.ForeignKey("distribusis.id"))
|
|
||||||
path = db.Column(db.String(4096), nullable=True, unique=True)
|
|
||||||
alttext = db.Column(db.String(255), nullable=True, unique=False)
|
|
||||||
tags = db.Column(db.String(500), nullable=True, unique=False)
|
|
||||||
description = db.Column(db.String(4096), nullable=True, unique=False)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Distribusi_File %r>" % self.path
|
|
@ -1,11 +0,0 @@
|
|||||||
"""SearchForm to search files and distribusis in the distribusi archive"""
|
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import StringField, SubmitField, validators
|
|
||||||
from wtforms.validators import Length
|
|
||||||
from wtforms.widgets import TextArea
|
|
||||||
|
|
||||||
|
|
||||||
class SearchForm(FlaskForm):
|
|
||||||
searchfield = StringField("Search distribusi-verse archive")
|
|
||||||
submit = SubmitField("Search")
|
|
@ -1,42 +0,0 @@
|
|||||||
import os
|
|
||||||
from flask import Blueprint, render_template
|
|
||||||
from whoosh.fields import *
|
|
||||||
from whoosh.index import open_dir
|
|
||||||
from whoosh.qparser import QueryParser
|
|
||||||
from search.forms.searchform import SearchForm
|
|
||||||
|
|
||||||
searchpages = Blueprint(
|
|
||||||
"search",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/search",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
SCRIPT_DIR = os.path.dirname(__file__)
|
|
||||||
SEARCH_DATA_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "searchdata"))
|
|
||||||
|
|
||||||
|
|
||||||
@searchpages.route("/", methods=["GET", "POST"])
|
|
||||||
def searchpage():
|
|
||||||
searchform = SearchForm()
|
|
||||||
found_distribusis = []
|
|
||||||
if searchform.validate_on_submit():
|
|
||||||
found_distribusis = search(searchform.searchfield.data)
|
|
||||||
template = render_template(
|
|
||||||
"search.html",
|
|
||||||
searchform=searchform,
|
|
||||||
found_distribusis=found_distribusis,
|
|
||||||
)
|
|
||||||
return template
|
|
||||||
|
|
||||||
|
|
||||||
def search(searchinput):
|
|
||||||
"""search and get search result titles and return them as distribusi ids"""
|
|
||||||
ix = open_dir(SEARCH_DATA_DIR)
|
|
||||||
with ix.searcher() as searcher:
|
|
||||||
query = QueryParser("content", ix.schema).parse(searchinput)
|
|
||||||
search_results = searcher.search(query)
|
|
||||||
found_distribusis = []
|
|
||||||
for result in search_results:
|
|
||||||
found_distribusis.append(result["title"])
|
|
||||||
return found_distribusis
|
|
@ -1,52 +0,0 @@
|
|||||||
import os
|
|
||||||
from whoosh.fields import *
|
|
||||||
from whoosh.index import create_in
|
|
||||||
from whoosh.qparser import QueryParser
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
import flask_apscheduler
|
|
||||||
|
|
||||||
|
|
||||||
SCRIPT_DIR = os.path.dirname(__file__)
|
|
||||||
SEARCH_DATA_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "searchdata"))
|
|
||||||
|
|
||||||
|
|
||||||
def init_search_index(APP):
|
|
||||||
scheduler = flask_apscheduler.APScheduler()
|
|
||||||
scheduler.api_enabled = False
|
|
||||||
scheduler.init_app(APP)
|
|
||||||
scheduler.start()
|
|
||||||
index_distribusis(APP)
|
|
||||||
index_distribusi_files(APP)
|
|
||||||
|
|
||||||
@scheduler.task("interval", id="update", minutes=60)
|
|
||||||
def update_search_index():
|
|
||||||
index_distribusis(APP)
|
|
||||||
index_distribusi_files(APP)
|
|
||||||
|
|
||||||
|
|
||||||
def index_distribusis(APP):
|
|
||||||
schema = Schema(
|
|
||||||
title=TEXT(stored=True), path=ID(stored=True), content=TEXT
|
|
||||||
)
|
|
||||||
ix = create_in(SEARCH_DATA_DIR, schema)
|
|
||||||
writer = ix.writer()
|
|
||||||
distribusis = _visible_distribusis(APP)
|
|
||||||
for distribusi in distribusis:
|
|
||||||
writer.add_document(
|
|
||||||
title=distribusi.distribusiname,
|
|
||||||
path="/a",
|
|
||||||
content=distribusi.description,
|
|
||||||
)
|
|
||||||
writer.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def index_distribusi_files(APP):
|
|
||||||
APP.logger.info("searching distribusi files not implemented yet.")
|
|
||||||
|
|
||||||
|
|
||||||
def _visible_distribusis(APP):
|
|
||||||
with APP.app_context():
|
|
||||||
distribusis = Distribusis.query.filter(
|
|
||||||
Distribusis.visible.isnot(False)
|
|
||||||
).all()
|
|
||||||
return distribusis
|
|
@ -1,23 +0,0 @@
|
|||||||
{% extends "base/base.html" %}
|
|
||||||
{% block main %}
|
|
||||||
<div id="distribusiverse" class="maincontent">
|
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('search.searchpage') }}">
|
|
||||||
{{ searchform.csrf_token }}
|
|
||||||
<fieldset class="required">
|
|
||||||
{{ searchform.searchfield.label }}
|
|
||||||
{{ searchform.searchfield }}
|
|
||||||
{% for message in searchform.searchfield.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="button required">
|
|
||||||
{{ searchform.submit }}
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
<div class="searchresults">
|
|
||||||
{% for found_distribusi in found_distribusis %}
|
|
||||||
<a href='{{ url_for('shortstashurl')}}/{{found_distribusi}}/index.html'>{{found_distribusi}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||||||
title = "Varia Archive X Distribusi-Verse"
|
|
||||||
categories = [ "article", "booklaunch", "broadcast", "curriculum", "game",
|
|
||||||
"gathering", "lecture", "opencall", "party", "performance", "presentation",
|
|
||||||
"publication", "report", "screening", "statement", "workgroup", "worksession",
|
|
||||||
"workshop"]
|
|
||||||
start_time = 2017-11-03
|
|
||||||
end_time = 2025-12-31
|
|
152
verse/start.py
152
verse/start.py
@ -1,45 +1,50 @@
|
|||||||
"""This is the main flask distribusi page"""
|
"""This is the main flask distribusi page"""
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
|
||||||
redirect,
|
|
||||||
render_template,
|
render_template,
|
||||||
send_from_directory,
|
redirect,
|
||||||
session,
|
|
||||||
url_for,
|
url_for,
|
||||||
|
session,
|
||||||
|
send_from_directory,
|
||||||
|
Blueprint,
|
||||||
)
|
)
|
||||||
from flask_login import current_user, login_required, logout_user
|
from flask_login import (
|
||||||
|
logout_user,
|
||||||
|
login_required,
|
||||||
|
current_user,
|
||||||
|
)
|
||||||
|
from flask_mail import Mail
|
||||||
from flask_wtf.csrf import CSRFError
|
from flask_wtf.csrf import CSRFError
|
||||||
|
from app import create_app, login_manager
|
||||||
|
|
||||||
from admin import is_adminuser
|
from usermodel import User
|
||||||
from admin_page.admin_page import admin_page
|
from distribusimodel import Distribusis
|
||||||
from app import create_app, login_manager, db
|
|
||||||
from describer.describe_files import describer
|
# Use upload form to populate filters
|
||||||
from distribusikan.distribusikan import distribusikan
|
from forms.uploadform import UploadForm
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
# Interface! these are seperate files in main folder
|
||||||
from models.distribusi_model import Distribusis
|
from adminpage import AdminPage
|
||||||
from models.user_model import User
|
from editor import Editor
|
||||||
from statuspengguna.forgotpassword import forgot_password
|
from themeselector import ThemeSelector
|
||||||
|
from distribusiworkflow import DistribusiWorkflow
|
||||||
|
from distribusiselector import DistribusiSelector
|
||||||
|
from uploadpage import UploadPage
|
||||||
|
|
||||||
|
# UserPengguna
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
from statuspengguna.loginuser import login_section
|
from statuspengguna.loginuser import LoginUser
|
||||||
from statuspengguna.registeruser import register_user
|
from statuspengguna.registeruser import RegisterUser
|
||||||
from search.search import searchpages
|
from statuspengguna.forgotpassword import ForgotPassword
|
||||||
from search.search_index import init_search_index
|
from statuspengguna.resetpassword import ResetPassword
|
||||||
|
|
||||||
|
# Distribusi Information
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
APP = create_app()
|
APP = create_app()
|
||||||
stash_page = Blueprint("stash_page", __name__, static_folder="stash")
|
stash_page = Blueprint("stash_page", __name__, static_folder="stash")
|
||||||
APP.register_blueprint(stash_page)
|
APP.register_blueprint(stash_page)
|
||||||
APP.register_blueprint(describer, url_prefix="/describer")
|
mail = Mail(APP)
|
||||||
APP.register_blueprint(login_section, url_prefix="/login")
|
|
||||||
APP.register_blueprint(register_user, url_prefix="/register")
|
|
||||||
APP.register_blueprint(forgot_password, url_prefix="/login/forgotpassword")
|
|
||||||
APP.register_blueprint(admin_page, url_prefix="/admin")
|
|
||||||
APP.register_blueprint(distribusikan, url_prefix="/distribusikan")
|
|
||||||
APP.register_blueprint(searchpages, url_prefix="/search")
|
|
||||||
init_search_index(APP)
|
|
||||||
|
|
||||||
|
|
||||||
@APP.before_request
|
@APP.before_request
|
||||||
@ -51,28 +56,32 @@ def session_handler():
|
|||||||
@APP.route("/")
|
@APP.route("/")
|
||||||
def index():
|
def index():
|
||||||
UserHelper.reset_user_state()
|
UserHelper.reset_user_state()
|
||||||
|
# http://localhost:5000/themes/publicthemes/RomeroTape/blueskies.css
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
distribusis = DistribusisInfo.visible_distribusis()
|
distribusis = DistribusisInfo.visibledistribusis()
|
||||||
distribusisindex = {}
|
distribusisindex = {}
|
||||||
for distribusi in distribusis:
|
for distribusi in distribusis:
|
||||||
user = User.query.filter_by(id=distribusi.userid).first()
|
user = User.query.filter_by(id=distribusi.userid).first()
|
||||||
singledistribusi = {
|
singledistribusi = {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"publictheme": distribusi.publictheme,
|
"publictheme": distribusi.publictheme,
|
||||||
"category": distribusi.category,
|
"term": distribusi.term,
|
||||||
|
"course": distribusi.course,
|
||||||
"year": distribusi.year,
|
"year": distribusi.year,
|
||||||
"tags": distribusi.tags.split(","),
|
"tags": distribusi.tags.split(","),
|
||||||
}
|
}
|
||||||
distribusisindex[distribusi.distribusiname] = singledistribusi
|
distribusisindex[distribusi.distribusiname] = singledistribusi
|
||||||
|
years = uploadform.academicyear.choices
|
||||||
|
terms = uploadform.term.choices
|
||||||
|
courses = uploadform.course.choices
|
||||||
|
|
||||||
years = uploadform.year.choices
|
adminuser = isadminuser()
|
||||||
categories = uploadform.category.choices
|
|
||||||
adminuser = is_adminuser()
|
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"base/index.html",
|
"index.html",
|
||||||
distribusisindex=distribusisindex,
|
distribusisindex=distribusisindex,
|
||||||
years=years,
|
years=years,
|
||||||
categories=categories,
|
terms=terms,
|
||||||
|
courses=courses,
|
||||||
adminuser=adminuser,
|
adminuser=adminuser,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
@ -80,7 +89,26 @@ def index():
|
|||||||
|
|
||||||
@APP.route("/help")
|
@APP.route("/help")
|
||||||
def help():
|
def help():
|
||||||
return render_template("base/help.html")
|
return render_template("help.html")
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/distribusi", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def distribusi():
|
||||||
|
return DistribusiWorkflow()
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/upload", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def upload():
|
||||||
|
uploadfolder = APP.config["UPLOAD_FOLDER"]
|
||||||
|
return UploadPage(uploadfolder)
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/theme", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def theme():
|
||||||
|
return ThemeSelector()
|
||||||
|
|
||||||
|
|
||||||
@APP.route("/publicthemes/<path>")
|
@APP.route("/publicthemes/<path>")
|
||||||
@ -88,14 +116,35 @@ def publicthemes(path):
|
|||||||
distribusi = Distribusis.query.filter_by(distribusiname=path).first()
|
distribusi = Distribusis.query.filter_by(distribusiname=path).first()
|
||||||
publicthemefolder = f"publicthemes/{distribusi.distribusiname}/"
|
publicthemefolder = f"publicthemes/{distribusi.distribusiname}/"
|
||||||
cssfile = f"{publicthemefolder}/{distribusi.publictheme}.css"
|
cssfile = f"{publicthemefolder}/{distribusi.publictheme}.css"
|
||||||
|
print(cssfile)
|
||||||
return send_from_directory("themes", cssfile, as_attachment=True)
|
return send_from_directory("themes", cssfile, as_attachment=True)
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/editor", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def editor():
|
||||||
|
return Editor()
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/selector", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def selector():
|
||||||
|
return DistribusiSelector()
|
||||||
|
|
||||||
|
|
||||||
@APP.route("/stash")
|
@APP.route("/stash")
|
||||||
def shortstashurl():
|
def shortstashurl():
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/admin", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def admin():
|
||||||
|
if not isadminuser():
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
return AdminPage()
|
||||||
|
|
||||||
|
|
||||||
@APP.route("/logout")
|
@APP.route("/logout")
|
||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
@ -103,6 +152,26 @@ def logout():
|
|||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/login", methods=["GET", "POST"])
|
||||||
|
def login():
|
||||||
|
return LoginUser()
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/register", methods=["GET", "POST"])
|
||||||
|
def register():
|
||||||
|
return RegisterUser()
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/forgotpassword", methods=["GET", "POST"])
|
||||||
|
def forgotpassword():
|
||||||
|
return ForgotPassword(mail)
|
||||||
|
|
||||||
|
|
||||||
|
@APP.route("/resetpassword/<path>", methods=["GET", "POST"])
|
||||||
|
def resetpassword(path):
|
||||||
|
return ResetPassword(path)
|
||||||
|
|
||||||
|
|
||||||
@APP.errorhandler(CSRFError)
|
@APP.errorhandler(CSRFError)
|
||||||
def handle_csrf_error(e):
|
def handle_csrf_error(e):
|
||||||
return render_template("csrf_error.html", reason=e.description), 400
|
return render_template("csrf_error.html", reason=e.description), 400
|
||||||
@ -110,7 +179,14 @@ def handle_csrf_error(e):
|
|||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
return db.session.get(User, int(user_id))
|
return User.query.get(int(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
def isadminuser():
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return False
|
||||||
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
|
return user.admin
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* Dropdown Button */
|
/* Dropdown Button */
|
||||||
/* for sorting on year and category
|
/* for sorting on Academicyear, Term, Course
|
||||||
*/
|
*/
|
||||||
button {
|
button {
|
||||||
background-color: #9de457;
|
background-color: #E0B0FF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ button {
|
|||||||
.dropdown-content {
|
.dropdown-content {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: #9de457;
|
background-color: #E0B0FF;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
.editarea {
|
.editarea {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
border: 3px solid #9de457;
|
border: 3px solid #E0B0FF;
|
||||||
border-style: outset;
|
border-style: outset;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@ -31,7 +31,7 @@ textarea {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
background: #9de457;
|
background: #E0B0FF;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: Courier, sans-serif;
|
font-family: Courier, sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -45,6 +45,6 @@ iframe {
|
|||||||
height: 30em;
|
height: 30em;
|
||||||
}
|
}
|
||||||
#html {
|
#html {
|
||||||
background-color: #9de457;
|
background-color: #60337F;
|
||||||
color: black;
|
color: lightgrey;
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
max-width: 20em;
|
max-width: 20em;
|
||||||
position: relative;
|
position: relative;
|
||||||
border: none;
|
border: none;
|
||||||
background: #9de457;
|
background: #E0B0FF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -18,7 +18,7 @@
|
|||||||
max-width: 20em;
|
max-width: 20em;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background-color: #9de457;
|
background-color: #E0B0FF;
|
||||||
background-image: none;
|
background-image: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
select.selector option{
|
select.selector option{
|
||||||
color: white;
|
color: white;
|
||||||
background-color: #9de457;
|
background-color: #60337F;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,5 +49,5 @@ select.selector option{
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.selector-style select option:hover {
|
.selector-style select option:hover {
|
||||||
background: #9de457;
|
background: #60337F;
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,64 @@ body
|
|||||||
{
|
{
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
background-color: #fdfdfd;
|
background-color: #272a33;
|
||||||
color:#091411;
|
color:#E0B0FF;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div#login{
|
||||||
|
width: 30%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
background-color:#272a33;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#login form {
|
||||||
|
width: 24em;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-left: 15%;
|
||||||
|
padding-right: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text], input[type=password], input[type=file] {
|
||||||
|
color: white;
|
||||||
|
width: 18em;
|
||||||
|
max-width: 18em;
|
||||||
|
background-color: #2D3039;
|
||||||
|
border: 1px solid #E0B0FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#upload form {
|
||||||
|
padding-right: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow{
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
padding-left: auto;
|
||||||
|
padding-right: auto;
|
||||||
|
width: 31em;
|
||||||
|
background-color:#30333f;
|
||||||
|
text-decoration: none;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
border-style: outset;
|
||||||
|
}
|
||||||
|
.workflow > p {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
.workflow > h2 {
|
||||||
|
padding-left: 0.4em;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow input{
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
#mainworkflow
|
#mainworkflow
|
||||||
{
|
{
|
||||||
width: 40em;
|
width: 30em;
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,16 +69,7 @@ body
|
|||||||
#distribusi-index {
|
#distribusi-index {
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
.description > textarea {
|
|
||||||
width: 100%;
|
|
||||||
height: 10em;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
textarea#description {
|
|
||||||
width: 100%;
|
|
||||||
height: 20em;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
div#buttons{
|
div#buttons{
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0.5em;
|
top: 0.5em;
|
||||||
@ -44,13 +82,13 @@ div#buttons{
|
|||||||
|
|
||||||
div#buttons .distribusi input{
|
div#buttons .distribusi input{
|
||||||
border: none;
|
border: none;
|
||||||
background: #9de457;
|
background: #fff600;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin: 0.2em;
|
margin: 0.2em;
|
||||||
}
|
}
|
||||||
div#buttons .distribusi input:hover{
|
div#buttons .distribusi input:hover{
|
||||||
background: #091411;
|
background: #ffbf00;
|
||||||
color: #6df2cc;
|
|
||||||
}
|
}
|
||||||
fieldset.required {
|
fieldset.required {
|
||||||
border: none;
|
border: none;
|
||||||
@ -63,15 +101,21 @@ fieldset.required > ul {
|
|||||||
fieldset.required > ul > li{
|
fieldset.required > ul > li{
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset.tagfield > input {
|
fieldset.tagfield > input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
#publicthemes > ul {
|
||||||
|
max-height: 20em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#publicthemes > ul > li{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: none;
|
border: none;
|
||||||
background: #9de457;
|
background: #E0B0FF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -80,9 +124,7 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input:hover {
|
input:hover {
|
||||||
background: #091411;
|
background: #60337F;
|
||||||
color: #6df2cc;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="submit"]:disabled:hover,
|
input[type="submit"]:disabled:hover,
|
||||||
@ -102,11 +144,37 @@ input[type="submit"]:disabled:focus {
|
|||||||
background-color: #F92020;
|
background-color: #F92020;
|
||||||
}
|
}
|
||||||
|
|
||||||
#update, #describe {
|
#update {
|
||||||
color: black;
|
color: black;
|
||||||
background-color: #62b264;
|
background-color: #62b264;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tutors {
|
||||||
|
color: black;
|
||||||
|
background-color: #62b264;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* unvisited link */
|
||||||
|
a:link {
|
||||||
|
color: #fff600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visited link */
|
||||||
|
a:visited {
|
||||||
|
color: #d28cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mouse over link */
|
||||||
|
a:hover {
|
||||||
|
color: #60337F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* selected link */
|
||||||
|
a:active {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
/* STOLEN GOODS */
|
/* STOLEN GOODS */
|
||||||
#fancyboi::before {
|
#fancyboi::before {
|
||||||
content: "$ ";
|
content: "$ ";
|
||||||
@ -127,7 +195,7 @@ input[type="submit"]:disabled:focus {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
animation: reveal 4s linear;
|
animation: reveal 4s linear;
|
||||||
text-overflow: "█";
|
text-overflow: "█";
|
||||||
background-color: #9de457;
|
background-color: #2D3039;
|
||||||
}
|
}
|
||||||
#fancyboi::after {
|
#fancyboi::after {
|
||||||
content: "█";
|
content: "█";
|
||||||
@ -137,7 +205,7 @@ input[type="submit"]:disabled:focus {
|
|||||||
|
|
||||||
div.maincontent{
|
div.maincontent{
|
||||||
width: 55%;
|
width: 55%;
|
||||||
border: 3px #9de457;
|
border: 3px #E0B0FF;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-style: outset;
|
border-style: outset;
|
||||||
@ -176,6 +244,8 @@ div.maincontent{
|
|||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -60px;
|
margin-left: -60px;
|
||||||
|
|
||||||
|
/* Fade in tooltip - takes 1 second to go from 0% to 100% opac: */
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 2s;
|
transition: opacity 2s;
|
||||||
}
|
}
|
||||||
@ -190,7 +260,7 @@ div.maincontent{
|
|||||||
color: black;
|
color: black;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #9de457;
|
background: #E0B0FF;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: Courier, sans-serif;
|
font-family: Courier, sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -198,11 +268,11 @@ div.maincontent{
|
|||||||
/*
|
/*
|
||||||
Project colors so far.
|
Project colors so far.
|
||||||
light
|
light
|
||||||
#9de457
|
#E0B0FF
|
||||||
medium
|
medium
|
||||||
#d28cff
|
#d28cff
|
||||||
dark
|
dark
|
||||||
#9de457
|
#60337F
|
||||||
|
|
||||||
background dark
|
background dark
|
||||||
#2D3039
|
#2D3039
|
||||||
|
@ -24,8 +24,9 @@ function filterSelection(c, name, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetDropDownButtons(){
|
function resetDropDownButtons(){
|
||||||
document.getElementById("Year").innerText = "Year";
|
document.getElementById("Academicyear").innerText = "Academic year";
|
||||||
document.getElementById("Category").innerText = "Category";
|
document.getElementById("Term").innerText = "Term";
|
||||||
|
document.getElementById("Course").innerText = "Course";
|
||||||
allactivebuttons = document.getElementsByClassName("activebtn");
|
allactivebuttons = document.getElementsByClassName("activebtn");
|
||||||
for(var i = 0;allactivebuttons.length; i++) {
|
for(var i = 0;allactivebuttons.length; i++) {
|
||||||
removeClass(allactivebuttons[i], "activebtn");
|
removeClass(allactivebuttons[i], "activebtn");
|
||||||
|
@ -1,31 +1,17 @@
|
|||||||
from datetime import datetime
|
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
|
from datetime import datetime
|
||||||
from flask import Blueprint, render_template
|
|
||||||
from flask_mail import Mail, Message
|
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
from flask import render_template
|
||||||
|
from flask_mail import Message
|
||||||
|
|
||||||
from app import db, get_app
|
from usermodel import User
|
||||||
from statuspengguna.forms.forgotpasswordform import ForgotPasswordForm
|
from forms.forgotpasswordform import ForgotPasswordForm
|
||||||
from models.user_model import User
|
from app import db
|
||||||
|
|
||||||
mail = Mail(get_app())
|
|
||||||
forgot_password = Blueprint(
|
|
||||||
"forgotpassword",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/statuspengguna",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@forgot_password.route("/", methods=["GET", "POST"])
|
|
||||||
def forgotpassword():
|
|
||||||
return ForgotPassword(mail)
|
|
||||||
|
|
||||||
|
|
||||||
def ForgotPassword(mail):
|
def ForgotPassword(mail):
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import flash
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
from flask import flash
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from usermodel import User
|
||||||
|
from distribusimodel import Distribusis
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
from app import db
|
from app import db
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
|
||||||
from models.distribusi_model import Distribusis
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
|
|
||||||
class UserHelper:
|
class UserHelper:
|
||||||
@ -76,10 +75,11 @@ class UserHelper:
|
|||||||
|
|
||||||
def distribusi_limit_reached():
|
def distribusi_limit_reached():
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
distribusiamount = len(
|
distribusiamount = len(DistribusisInfo.getuserdistribusis(user.email))
|
||||||
DistribusisInfo.get_user_distribusis(user.email)
|
if user.tutor and distribusiamount > 14:
|
||||||
)
|
print("tutor already has 15 distribusis")
|
||||||
if distribusiamount > 19:
|
return True
|
||||||
print("user already has 20 distribusis")
|
if not user.tutor and distribusiamount > 4:
|
||||||
|
print("user already has 5 distribusis")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1,29 +1,15 @@
|
|||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
|
||||||
abort,
|
|
||||||
flash,
|
|
||||||
redirect,
|
|
||||||
render_template,
|
render_template,
|
||||||
|
redirect,
|
||||||
request,
|
request,
|
||||||
|
flash,
|
||||||
url_for,
|
url_for,
|
||||||
|
abort,
|
||||||
)
|
)
|
||||||
from flask_bcrypt import check_password_hash
|
from usermodel import User
|
||||||
|
from forms.loginform import LoginForm
|
||||||
from flask_login import login_user
|
from flask_login import login_user
|
||||||
|
from flask_bcrypt import check_password_hash
|
||||||
from statuspengguna.forms.loginform import LoginForm
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
login_section = Blueprint(
|
|
||||||
"login",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/statuspengguna",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_section.route("/", methods=["GET", "POST"])
|
|
||||||
def login():
|
|
||||||
return LoginUser()
|
|
||||||
|
|
||||||
|
|
||||||
def LoginUser():
|
def LoginUser():
|
||||||
@ -39,9 +25,7 @@ def LoginUser():
|
|||||||
flash("Logged in successfully.", "success")
|
flash("Logged in successfully.", "success")
|
||||||
next = request.args.get("next")
|
next = request.args.get("next")
|
||||||
if next is not None and not is_safe_url(next): # noqa: F821
|
if next is not None and not is_safe_url(next): # noqa: F821
|
||||||
print(next)
|
|
||||||
return abort(400)
|
return abort(400)
|
||||||
print("index")
|
|
||||||
return redirect(next or url_for("index"))
|
return redirect(next or url_for("index"))
|
||||||
else:
|
else:
|
||||||
flash("Invalid email or password!", "danger")
|
flash("Invalid email or password!", "danger")
|
||||||
|
@ -1,30 +1,22 @@
|
|||||||
from flask import Blueprint, flash, redirect, render_template, url_for
|
from flask import (
|
||||||
from flask_bcrypt import generate_password_hash
|
render_template,
|
||||||
from flask_login import login_user
|
redirect,
|
||||||
|
flash,
|
||||||
|
url_for,
|
||||||
|
)
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
IntegrityError,
|
IntegrityError,
|
||||||
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
from werkzeug.routing import BuildError
|
from werkzeug.routing import BuildError
|
||||||
|
from usermodel import User
|
||||||
|
from forms.registerform import RegisterForm
|
||||||
|
from flask_login import login_user
|
||||||
|
from flask_bcrypt import generate_password_hash
|
||||||
from app import db
|
from app import db
|
||||||
from statuspengguna.forms.registerform import RegisterForm
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
register_user = Blueprint(
|
|
||||||
"register",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/statuspengguna",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register_user.route("/", methods=["GET", "POST"])
|
|
||||||
def register():
|
|
||||||
return RegisterUser()
|
|
||||||
|
|
||||||
|
|
||||||
def RegisterUser():
|
def RegisterUser():
|
||||||
|
@ -1,32 +1,23 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from flask import (
|
||||||
from flask import Blueprint, flash, redirect, render_template, url_for
|
render_template,
|
||||||
from flask_bcrypt import generate_password_hash
|
redirect,
|
||||||
from flask_login import login_user
|
flash,
|
||||||
|
url_for,
|
||||||
|
)
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
IntegrityError,
|
IntegrityError,
|
||||||
|
DataError,
|
||||||
|
DatabaseError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
from werkzeug.routing import BuildError
|
from werkzeug.routing import BuildError
|
||||||
|
from usermodel import User
|
||||||
|
from forms.resetpasswordform import ResetPasswordForm
|
||||||
|
from flask_login import login_user
|
||||||
|
from flask_bcrypt import generate_password_hash
|
||||||
from app import db
|
from app import db
|
||||||
from statuspengguna.forms.resetpasswordform import ResetPasswordForm
|
|
||||||
from models.user_model import User
|
|
||||||
|
|
||||||
reset_password = Blueprint(
|
|
||||||
"reset_password",
|
|
||||||
__name__,
|
|
||||||
template_folder="templates/statuspengguna",
|
|
||||||
static_folder="static",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@reset_password.route("/resetpassword/<path>", methods=["GET", "POST"])
|
|
||||||
def resetpassword(path):
|
|
||||||
return ResetPassword(path)
|
|
||||||
|
|
||||||
|
|
||||||
def ResetPassword(path):
|
def ResetPassword(path):
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
div#login{
|
|
||||||
width: 30%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
background-color:#fdfdfd;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#login form {
|
|
||||||
width: 24em;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-left: 15%;
|
|
||||||
padding-right: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text], input[type=password], input[type=file] {
|
|
||||||
color: #091411;
|
|
||||||
width: 18em;
|
|
||||||
max-width: 18em;
|
|
||||||
background-color: #fdfdfd;
|
|
||||||
border: 1px solid #9de457;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<div class="overview">
|
<div class="overview">
|
||||||
@ -14,6 +14,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="maincontent">
|
<div class="maincontent">
|
||||||
<h2>Admin Page</h2>
|
<h2>Admin Page</h2>
|
||||||
|
<p>Here you can bulk delete distribusis and users or make users into tutors
|
||||||
|
<strong> These actions cannot be undone! </strong>
|
||||||
|
</p>
|
||||||
<div id="distribusiverse" class="maincontent">
|
<div id="distribusiverse" class="maincontent">
|
||||||
<h2>List of distribusis</h2>
|
<h2>List of distribusis</h2>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('admin') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('admin') }}">
|
||||||
@ -50,6 +53,12 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<fieldset class="button required">
|
||||||
|
{{ adminuserform.tutors }}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="button required">
|
||||||
|
{{ adminuserform.nottutors }}
|
||||||
|
</fieldset>
|
||||||
<fieldset class="button required">
|
<fieldset class="button required">
|
||||||
{{ adminuserform.delete }}
|
{{ adminuserform.delete }}
|
||||||
</fieldset>
|
</fieldset>
|
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{title}}</title>
|
<title>Autonomous Practices X Distribusi-Verse</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css')}}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css')}}">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/selector.css')}}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/selector.css')}}">
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='icons/favicon.ico') }}">
|
<link rel="shortcut icon" href="{{ url_for('static', filename='icons/favicon.ico') }}">
|
@ -1,20 +0,0 @@
|
|||||||
{% block menu %}
|
|
||||||
<button onclick="filterSelection('all')" id="removefilter">Remove filter</button>
|
|
||||||
<div class="dropdown">
|
|
||||||
<button id="Year" class="dropbtn">Year</button>
|
|
||||||
<div class="dropdown-content">
|
|
||||||
{% for year in years %}
|
|
||||||
<button type="button" name="button" onclick="filterSelection('{{ year[0] }}', '{{ year[1] }}', 'Year')" >{{ year[1] }}</button>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown">
|
|
||||||
<button id="Category" class="dropbtn">Category</button>
|
|
||||||
<div class="dropdown-content">
|
|
||||||
{% for category in categories %}
|
|
||||||
<button type="button" name="button" onclick="filterSelection('{{ category[0] }}', '{{ category[1] }}', 'Category')" >{{ category[1] }}</button>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input id="tagsearch" type="text" placeholder="Search..">
|
|
||||||
{% endblock menu %}
|
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<div class="overview">
|
<div class="overview">
|
||||||
@ -20,28 +20,27 @@
|
|||||||
<div id="mainworkflow">
|
<div id="mainworkflow">
|
||||||
{% if selectorvisible %}
|
{% if selectorvisible %}
|
||||||
{% block selector %}
|
{% block selector %}
|
||||||
{% include "distribusi_workflow/selector.html" %}
|
{% include "distribusiworkflow/selector.html" %}
|
||||||
{% endblock selector%}
|
{% endblock selector%}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% block upload %}
|
{% block upload %}
|
||||||
{% include "distribusi_workflow/upload.html" %}
|
{% include "distribusiworkflow/upload.html" %}
|
||||||
{% endblock upload%}
|
{% endblock upload%}
|
||||||
<img src="{{ url_for('static', filename='svg/arrow_1.svg')}}" />
|
<img src="{{ url_for('static', filename='svg/arrow_1.svg')}}" />
|
||||||
{% block theme %}
|
{% block theme %}
|
||||||
{% include "distribusi_workflow/theme.html" %}
|
{% include "distribusiworkflow/theme.html" %}
|
||||||
{% endblock theme%}
|
{% endblock theme%}
|
||||||
<img src="{{ url_for('static', filename='svg/arrow_2.svg')}}" />
|
<img src="{{ url_for('static', filename='svg/arrow_2.svg')}}" />
|
||||||
{% block editcss %}
|
{% block editcss %}
|
||||||
{% include "distribusi_workflow/editcss.html" %}
|
{% include "distribusiworkflow/editcss.html" %}
|
||||||
{% endblock editcss%}
|
{% endblock editcss%}
|
||||||
<img src="{{ url_for('static', filename='svg/arrow_3.svg')}}" />
|
<img src="{{ url_for('static', filename='svg/arrow_3.svg')}}" />
|
||||||
{% block launch %}
|
{% block launch %}
|
||||||
{% include "distribusi_workflow/launch.html" %}
|
{% include "distribusiworkflow/launch.html" %}
|
||||||
{% endblock launch%}
|
{% endblock launch%}
|
||||||
{%endif%}
|
{%endif%}
|
||||||
</div>
|
</div>
|
||||||
{% if css_selected %}
|
{% if css_selected %}
|
||||||
<p id="cssSelected" hidden>css selected</p>
|
<p id="cssSelected" hidden>css selected</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.static', filename='css/distribusikan.css')}}">
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
@ -1,10 +1,10 @@
|
|||||||
<div id="edit" class="workflow">
|
<div id="edit" class="workflow">
|
||||||
<h2>Step 3: Edit Custom CSS (Optional)</h2>
|
<h2>Step 3: Edit Custom CSS (Optional)</h2>
|
||||||
{% if files_uploaded or distribusi_live %}
|
{% if files_uploaded or distribusi_live %}
|
||||||
<p><a href="/distribusikan/editor">Go to CSS editor</a></p>
|
<p><a href="/editor">Go to CSS editor</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
You need to upload your files first before you can select a css theme
|
You need to upload your files first before you can a css theme
|
||||||
for your files.
|
for your files.
|
||||||
</p>
|
</p>
|
||||||
<p><a href="#upload">Go to Step 1</a></p>
|
<p><a href="#upload">Go to Step 1</a></p>
|
@ -3,7 +3,7 @@
|
|||||||
<p class="tooltip">Run distribusi on your files. This will generate your website and make
|
<p class="tooltip">Run distribusi on your files. This will generate your website and make
|
||||||
your content public. <span class="tooltiptext">Distribusi will unpack your zip file and turn it into a website!
|
your content public. <span class="tooltiptext">Distribusi will unpack your zip file and turn it into a website!
|
||||||
</span></p>
|
</span></p>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.distribusi') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusi') }}">
|
||||||
{{ distribusiform.csrf_token }}
|
{{ distribusiform.csrf_token }}
|
||||||
{% if files_uploaded or distribusi_live %}
|
{% if files_uploaded or distribusi_live %}
|
||||||
<fieldset class="button required">
|
<fieldset class="button required">
|
@ -1,7 +1,7 @@
|
|||||||
<div id="distribusi" class="workflow">
|
<div id="distribusi" class="workflow">
|
||||||
<h2>Welcome back to your Distribusi</h2>
|
<h2>Welcome back to your Distribusi</h2>
|
||||||
<p>You have already uploaded a distribusi website:</p>
|
<p>You have already uploaded a distribusi website:</p>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.selector') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('selector') }}">
|
||||||
{{ selectorform.csrf_token }}
|
{{ selectorform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ selectorform.distribusis.label }}
|
{{ selectorform.distribusis.label }}
|
||||||
@ -18,13 +18,6 @@
|
|||||||
{{ selectorform.update }}
|
{{ selectorform.update }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<hr>
|
<hr>
|
||||||
<p>
|
|
||||||
Describe your distribusi files. Add description texts, tags for search
|
|
||||||
or add alt-text for images</p>
|
|
||||||
<fieldset class="button required multiselect">
|
|
||||||
{{ selectorform.describe }}
|
|
||||||
</fieldset>
|
|
||||||
<hr>
|
|
||||||
<p>
|
<p>
|
||||||
This will delete your distribusi site.
|
This will delete your distribusi site.
|
||||||
<strong> This action cannot be undone! </strong>
|
<strong> This action cannot be undone! </strong>
|
||||||
@ -36,7 +29,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="new_divider"></div>
|
|
||||||
{% if limit_reached %}
|
{% if limit_reached %}
|
||||||
<p>You have reached your limit of distribusi websites</p>
|
<p>You have reached your limit of distribusi websites</p>
|
||||||
{% else %}
|
{% else %}
|
@ -4,7 +4,7 @@
|
|||||||
step 3.</p>
|
step 3.</p>
|
||||||
<p>Don't forget to press Save</p>
|
<p>Don't forget to press Save</p>
|
||||||
<hr>
|
<hr>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.theme') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('theme') }}">
|
||||||
{{ themeform.csrf_token }}
|
{{ themeform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ themeform.theme.label }}
|
{{ themeform.theme.label }}
|
||||||
@ -16,14 +16,14 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
You need to upload your files first before you can select a css theme
|
You need to upload your files first before you can a css theme
|
||||||
for your files.
|
for your files.
|
||||||
</p>
|
</p>
|
||||||
<a href="#upload">Go to Step 1</a>
|
<a href="#upload">Go to Step 1</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
<hr>
|
<hr>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.theme') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('theme') }}">
|
||||||
{{ publicthemeform.csrf_token }}
|
{{ publicthemeform.csrf_token }}
|
||||||
<fieldset id="publicthemes" class="required">
|
<fieldset id="publicthemes" class="required">
|
||||||
{{ publicthemeform.publicthemes.label }}
|
{{ publicthemeform.publicthemes.label }}
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
You need to upload your files first before you can select a css theme
|
You need to upload your files first before you can a css theme
|
||||||
for your files.
|
for your files.
|
||||||
</p>
|
</p>
|
||||||
<a href="#upload">Go to Step 1</a>
|
<a href="#upload">Go to Step 1</a>
|
@ -1,7 +1,7 @@
|
|||||||
<div id="upload" class="workflow">
|
<div id="upload" class="workflow">
|
||||||
<h2>Step 1: Upload</h2>
|
<h2>Step 1: Upload</h2>
|
||||||
<p>Upload your files here:</p>
|
<p>Upload your files here:</p>
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.upload') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('upload') }}">
|
||||||
{{ uploadform.csrf_token }}
|
{{ uploadform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.sitename.label }}
|
{{ uploadform.sitename.label }}
|
||||||
@ -11,26 +11,28 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.description.label }}
|
{{ uploadform.academicyear.label }}
|
||||||
{{ uploadform.description }}
|
|
||||||
{% for message in uploadform.description.errors %}
|
|
||||||
<div class="error">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="required">
|
|
||||||
{{ uploadform.year.label }}
|
|
||||||
<div class="selector-style">
|
<div class="selector-style">
|
||||||
{{ uploadform.year }}
|
{{ uploadform.academicyear }}
|
||||||
{% for message in uploadform.year.errors %}
|
{% for message in uploadform.academicyear.errors %}
|
||||||
<div class="error">{{ message }}</div>
|
<div class="error">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.category.label }}
|
{{ uploadform.term.label }}
|
||||||
<div class="selector-style">
|
<div class="selector-style">
|
||||||
{{ uploadform.category }}
|
{{ uploadform.term }}
|
||||||
{% for message in uploadform.category.errors %}
|
{% for message in uploadform.term.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ uploadform.course.label }}
|
||||||
|
<div class="selector-style">
|
||||||
|
{{ uploadform.course }}
|
||||||
|
{% for message in uploadform.course.errors %}
|
||||||
<div class="error">{{ message }}</div>
|
<div class="error">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
@ -1,11 +1,11 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.editor') }}" class="editform">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('editor') }}" class="editform">
|
||||||
<div class="editareas">
|
<div class="editareas">
|
||||||
<div class="editarea editor">
|
<div class="editarea editor">
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
<textarea id="html" placeholder="Write some test HTML here" readonly>
|
<textarea id="html" placeholder="Write some test HTML here" readonly>
|
||||||
{{html_placeholder}}
|
{{htmlplaceholder}}
|
||||||
</textarea>
|
</textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -54,6 +54,6 @@
|
|||||||
</form>
|
</form>
|
||||||
<iframe id="code"></iframe>
|
<iframe id="code"></iframe>
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.static', filename='css/editor.css')}}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/editor.css')}}">
|
||||||
<script src="{{ url_for('static', filename='js/editorupdate.js')}}"></script>
|
<script src="{{ url_for('static', filename='js/editorupdate.js')}}"></script>
|
||||||
{% endblock main %}
|
{% endblock main %}
|
28
verse/templates/filtermenu.html
Normal file
28
verse/templates/filtermenu.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% block menu %}
|
||||||
|
<button onclick="filterSelection('all')" id="removefilter">Remove filter</button>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="Academicyear" class="dropbtn">Academic year</button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
{% for year in years %}
|
||||||
|
<button type="button" name="button" onclick="filterSelection('{{ year[0] }}', '{{ year[1] }}', 'Academicyear')" >{{ year[1] }}</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="Term" class="dropbtn">Term</button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
{% for term in terms %}
|
||||||
|
<button type="button" name="button" onclick="filterSelection('{{ term[0] }}', '{{ term[1] }}', 'Term')" >{{ term[1] }}</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="Course" class="dropbtn">Course</button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
{% for course in courses %}
|
||||||
|
<button type="button" name="button" onclick="filterSelection('{{ course[0] }}' , '{{ course[1] }}', 'Course')" >{{ course[1] }}</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input id="tagsearch" type="text" placeholder="Search..">
|
||||||
|
{% endblock menu %}
|
@ -1,13 +1,12 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="mainworkflow">
|
<div id="mainworkflow">
|
||||||
<div class="login">
|
<div class="workflow">
|
||||||
<h2>Forgot your password?</h2>
|
<h2>Forgot your password?</h2>
|
||||||
<p>
|
<p>
|
||||||
Enter the email address that was used to register with Distribusiverse.
|
Enter the email address that was used to register with Distribusiverse.
|
||||||
</p>
|
</p>
|
||||||
<form class="form" action="{{ url_for('forgotpassword.forgotpassword') }}"
|
<form class="form" action="{{ url_for('forgotpassword') }}" method="post">
|
||||||
method="post">
|
|
||||||
{{ forgotpasswordform.csrf_token }}
|
{{ forgotpasswordform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ forgotpasswordform.email.label }}
|
{{ forgotpasswordform.email.label }}
|
||||||
@ -26,6 +25,5 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('forgotpassword.static', filename='css/login.css')}}">
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<div class="overview">
|
<div class="overview">
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="distribusi">
|
<div class="distribusi">
|
||||||
<a href="distribusikan/distribusi">
|
<a href="/distribusi">
|
||||||
<input type="button" name="button" value="Distribusi"></input>
|
<input type="button" name="button" value="Distribusi"></input>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
|
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="distribusi">
|
<div class="distribusi">
|
||||||
<a href="distribusikan/distribusi">
|
<a href="/distribusi">
|
||||||
<input type="button" name="button" value="Distribusi"></input>
|
<input type="button" name="button" value="Distribusi"></input>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -46,14 +46,19 @@
|
|||||||
<div class="maincontent">
|
<div class="maincontent">
|
||||||
<p>Distribusi is a content management system for the web that produces static index pages based on folders in the files system. It is inspired by the automatic index functions featured in several popular web servers. Distribusi works by traversing the file system and directory hierarchy to automatically list all the files in the directory, detect the file types and providing them with relevant html classes and tags for easy styling.
|
<p>Distribusi is a content management system for the web that produces static index pages based on folders in the files system. It is inspired by the automatic index functions featured in several popular web servers. Distribusi works by traversing the file system and directory hierarchy to automatically list all the files in the directory, detect the file types and providing them with relevant html classes and tags for easy styling.
|
||||||
</p>
|
</p>
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
This particular work in progress project <strong>Distribusi-verse</strong> is an attempt to make distribusi into a webinterface that can be operated remotely without any knowlegde of CLI. Attempting to combine the ideas of distribusi with the ideas of a <a href="https://tildeverse.org/">Tildeverse</a> and <a href="https://tilde.club/">Tilde club</a>, but also be neither of these ideas. See a full list of tildeverse members <a href="https://tildeverse.org/members/">here</a>.</p>
|
||||||
|
<hr>
|
||||||
|
<p>This project is made for <a href="https://www.wdka.nl/practices/autonomous-practices/">Autonomous Practices </a>at the <a href="https://www.wdka.nl/">WDKA</a> in Rotterdam.</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- a div with all the distribusis listed in the distribusiverse -->
|
<!-- a div with all the distribusis listed in the distribusiverse -->
|
||||||
<div id="distribusiverse" class="maincontent">
|
<div id="distribusiverse" class="maincontent">
|
||||||
<h2>List of distribusis</h2>
|
<h2>List of distribusis</h2>
|
||||||
{% include 'base/filtermenu.html' %}
|
{% include 'filtermenu.html' %}
|
||||||
<ul id="distribusi-index">
|
<ul id="distribusi-index">
|
||||||
{% for name, distribusi in distribusisindex.items() %}
|
{% for name, distribusi in distribusisindex.items() %}
|
||||||
<li class='distribusi filter {{ distribusi["category"] }} {{ distribusi["year"] }} '>
|
<li class='distribusi filter {{ distribusi["term"] }} {{ distribusi["year"] }} {{ distribusi["course"] }}'>
|
||||||
<a href='stash/{{name}}/index.html'>{{distribusi["username"]}}:{{name}}</a>
|
<a href='stash/{{name}}/index.html'>{{distribusi["username"]}}:{{name}}</a>
|
||||||
{% for tag in distribusi["tags"] %}
|
{% for tag in distribusi["tags"] %}
|
||||||
<span class="tags">{{tag}}</span>
|
<span class="tags">{{tag}}</span>
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
<form class="form" action="{{ url_for('login.login') }}" method="post">
|
<form class="form" action="{{ url_for('login') }}" method="post">
|
||||||
{{ loginform.csrf_token }}
|
{{ loginform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ loginform.email.label }}
|
{{ loginform.email.label }}
|
||||||
@ -19,9 +19,8 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="button required">
|
<fieldset class="button required">
|
||||||
{{ loginform.submit }}
|
{{ loginform.submit }}
|
||||||
<a href="forgotpassword">Forgot Password?</a>
|
<a href="/forgotpassword">Forgot Password?</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('login.static', filename='css/login.css')}}">
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
<form class="form" action="{{ url_for('register.register') }}" method="post">
|
<form class="form" action="{{ url_for('register') }}" method="post">
|
||||||
{{ registerform.csrf_token }}
|
{{ registerform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ registerform.username.label }}
|
{{ registerform.username.label }}
|
||||||
@ -36,5 +36,4 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('register.static', filename='css/login.css')}}">
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base/base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
{% if linkvalid%}
|
{% if linkvalid%}
|
||||||
@ -26,5 +26,4 @@
|
|||||||
<h3>Password reset link no longer valid.</h3>
|
<h3>Password reset link no longer valid.</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('reset_password.static', filename='css/login.css')}}">
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
<div id="willemdekooning-logo" class="png">
|
<div id="willemdekooning-logo" class="png">
|
||||||
<figure>
|
<figure>
|
||||||
<img class="image" src="https://varia.zone/archive/00-varia-server/welcome.png">
|
<img class="image" src="https://www.wdka.nl/build/img/willemdekooning-logo.png">
|
||||||
<figcaption>logo.png</figcaption>
|
<figcaption>logo.png</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="example_video" class="mp4">
|
<div id="example_video" class="mp4">
|
||||||
<video controls>
|
<video controls>
|
||||||
<source src="https://vvvvvvaria.org/~crunk/box/tomb_of_doom-2021-02-13_21.41.03.mp4">
|
<source src="example_video.mp4">
|
||||||
</video>
|
</video>
|
||||||
<span class="filename">example_video.mp4</span>
|
<span class="filename">example_video.mp4</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,47 +1,49 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
|
||||||
from distribusikan.forms.distribusiform import DistribusiForm
|
|
||||||
from distribusikan.forms.publicthemeform import PublicThemeForm
|
|
||||||
from distribusikan.forms.selectorform import SelectorForm
|
|
||||||
from distribusikan.forms.themeform import ThemeForm
|
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
|
from forms.uploadform import UploadForm
|
||||||
|
from forms.distribusiform import DistribusiForm
|
||||||
|
from forms.themeform import ThemeForm
|
||||||
|
from forms.publicthemeform import PublicThemeForm
|
||||||
|
from forms.selectorform import SelectorForm
|
||||||
|
|
||||||
|
|
||||||
def theme_selector():
|
def ThemeSelector():
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
if themeform.validate_on_submit():
|
if themeform.validate_on_submit():
|
||||||
copycssfile = os.path.join(
|
copycssfile = os.path.join(
|
||||||
"themes",
|
"themes",
|
||||||
f"{themeform.theme.data}.css",
|
f"{themeform.theme.data}.css",
|
||||||
)
|
)
|
||||||
move_css_to_user_folder(current_distribusi, copycssfile)
|
MoveCssToUserFolder(current_distribusi, copycssfile)
|
||||||
if publicthemeform.validate_on_submit():
|
if publicthemeform.validate_on_submit():
|
||||||
copycssfile = os.path.join(
|
copycssfile = os.path.join(
|
||||||
"themes/publicthemes/",
|
"themes/publicthemes/",
|
||||||
f"{publicthemeform.publicthemes.data}.css",
|
f"{publicthemeform.publicthemes.data}.css",
|
||||||
)
|
)
|
||||||
move_css_to_user_folder(current_distribusi, copycssfile)
|
MoveCssToUserFolder(current_distribusi, copycssfile)
|
||||||
return render_distribusi_template(
|
return RenderDistribusiTemplate(
|
||||||
themeform, publicthemeform, current_distribusi
|
themeform,
|
||||||
|
publicthemeform,
|
||||||
|
current_distribusi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def move_css_to_user_folder(current_distribusi, copycssfile):
|
def MoveCssToUserFolder(current_distribusi, copycssfile):
|
||||||
newcssfolder = os.path.join("themes/userthemes", current_distribusi)
|
newcssfolder = os.path.join("themes/userthemes", current_distribusi)
|
||||||
if not os.path.exists(newcssfolder):
|
if not os.path.exists(newcssfolder):
|
||||||
os.mkdir(newcssfolder)
|
os.mkdir(newcssfolder)
|
||||||
shutil.copy(copycssfile, newcssfolder)
|
shutil.copy(copycssfile, newcssfolder)
|
||||||
|
|
||||||
|
|
||||||
def render_distribusi_template(themeform, publicthemeform, current_distribusi):
|
def RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
@ -1,25 +1,25 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DatabaseError,
|
|
||||||
DataError,
|
|
||||||
IntegrityError,
|
IntegrityError,
|
||||||
InterfaceError,
|
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
|
DataError,
|
||||||
|
InterfaceError,
|
||||||
|
DatabaseError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from distribusikan.distribusi_selector import select_current_distribusi
|
|
||||||
from distribusikan.forms.uploadform import UploadForm
|
from usermodel import User
|
||||||
from models.distribusi_model import Distribusis
|
from distribusimodel import Distribusis
|
||||||
from models.user_model import User
|
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
|
from distribusiselector import SelectCurrentDistribusi
|
||||||
|
from forms.uploadform import UploadForm
|
||||||
|
|
||||||
|
|
||||||
def upload_new_distribusi(uploadfolder):
|
def UploadNewDistribusi(uploadfolder):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
if uploadform.validate_on_submit():
|
if uploadform.validate_on_submit():
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
@ -27,9 +27,9 @@ def upload_new_distribusi(uploadfolder):
|
|||||||
newdistribusi = Distribusis(
|
newdistribusi = Distribusis(
|
||||||
distribusiname=uploadform.sitename.data,
|
distribusiname=uploadform.sitename.data,
|
||||||
userid=user.id,
|
userid=user.id,
|
||||||
category=uploadform.category.data,
|
term=uploadform.term.data,
|
||||||
description=uploadform.description.data,
|
course=uploadform.course.data,
|
||||||
year=uploadform.year.data,
|
year=uploadform.academicyear.data,
|
||||||
tags=uploadform.tags.data,
|
tags=uploadform.tags.data,
|
||||||
)
|
)
|
||||||
user.currentdistribusi = uploadform.sitename.data
|
user.currentdistribusi = uploadform.sitename.data
|
||||||
@ -47,7 +47,7 @@ def upload_new_distribusi(uploadfolder):
|
|||||||
uploadform.sitename.errors.append("Something went wrong!")
|
uploadform.sitename.errors.append("Something went wrong!")
|
||||||
flash("Something went wrong!", "danger")
|
flash("Something went wrong!", "danger")
|
||||||
return uploadform
|
return uploadform
|
||||||
select_current_distribusi(newdistribusi.distribusiname)
|
SelectCurrentDistribusi(newdistribusi.distribusiname)
|
||||||
zipfilename = "{}.zip".format(newdistribusi.distribusiname)
|
zipfilename = "{}.zip".format(newdistribusi.distribusiname)
|
||||||
zipfile = uploadform.zipfile.data
|
zipfile = uploadform.zipfile.data
|
||||||
zipfile.save(os.path.join(uploadfolder, zipfilename))
|
zipfile.save(os.path.join(uploadfolder, zipfilename))
|
||||||
@ -63,7 +63,7 @@ def upload_new_distribusi(uploadfolder):
|
|||||||
return uploadform
|
return uploadform
|
||||||
|
|
||||||
|
|
||||||
def upload_updates_files(uploadfolder):
|
def UploadUpdatedFiles(uploadfolder):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
if uploadform.validate_on_submit():
|
if uploadform.validate_on_submit():
|
||||||
try:
|
try:
|
||||||
@ -71,9 +71,9 @@ def upload_updates_files(uploadfolder):
|
|||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
).first()
|
).first()
|
||||||
distribusi.category = uploadform.category.data
|
distribusi.term = uploadform.term.data
|
||||||
distribusi.description = (uploadform.description.data,)
|
distribusi.course = uploadform.course.data
|
||||||
distribusi.year = uploadform.year.data
|
distribusi.year = uploadform.academicyear.data
|
||||||
distribusi.tags = uploadform.tags.data
|
distribusi.tags = uploadform.tags.data
|
||||||
distribusi.visible = False
|
distribusi.visible = False
|
||||||
db.session.commit()
|
db.session.commit()
|
@ -1,34 +1,33 @@
|
|||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
|
||||||
from app import APP
|
from forms.distribusiform import DistribusiForm
|
||||||
from distribusikan.distribusi_selector import selector_visible
|
from forms.themeform import ThemeForm
|
||||||
from distribusikan.distribusis_info import DistribusisInfo
|
from forms.publicthemeform import PublicThemeForm
|
||||||
from distribusikan.upload import upload_new_distribusi, upload_updates_files
|
from forms.selectorform import SelectorForm
|
||||||
from distribusikan.forms.distribusiform import DistribusiForm
|
|
||||||
from distribusikan.forms.publicthemeform import PublicThemeForm
|
|
||||||
from distribusikan.forms.selectorform import SelectorForm
|
|
||||||
from distribusikan.forms.themeform import ThemeForm
|
|
||||||
|
|
||||||
# UserPengguna
|
# UserPengguna
|
||||||
from statuspengguna.helper import UserHelper
|
from statuspengguna.helper import UserHelper
|
||||||
|
|
||||||
|
from upload import UploadNewDistribusi, UploadUpdatedFiles
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
from distribusiselector import SelectorVisible
|
||||||
|
|
||||||
def upload_page():
|
|
||||||
|
def UploadPage(uploadfolder):
|
||||||
"render upload page section of distribusi workflow"
|
"render upload page section of distribusi workflow"
|
||||||
uploadfolder = APP.config["UPLOAD_FOLDER"]
|
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
selectorvisible = selector_visible()
|
selectorvisible = SelectorVisible()
|
||||||
|
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
if current_distribusi == "new" or UserHelper.has_distribusi() is False:
|
if current_distribusi == "new" or UserHelper.has_distribusi() is False:
|
||||||
uploadform = upload_new_distribusi(uploadfolder)
|
uploadform = UploadNewDistribusi(uploadfolder)
|
||||||
else:
|
else:
|
||||||
uploadform = upload_updates_files(uploadfolder)
|
uploadform = UploadUpdatedFiles(uploadfolder)
|
||||||
|
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(uploadform.sitename.data)
|
files_uploaded = UserHelper.is_zip_uploaded(uploadform.sitename.data)
|
||||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
@ -1,6 +1,5 @@
|
|||||||
from flask_login import UserMixin
|
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
@ -15,6 +14,8 @@ class User(UserMixin, db.Model):
|
|||||||
currentdistribusi = db.Column(db.String(300), nullable=True, unique=False)
|
currentdistribusi = db.Column(db.String(300), nullable=True, unique=False)
|
||||||
resethash = db.Column(db.String(300), nullable=True, unique=True)
|
resethash = db.Column(db.String(300), nullable=True, unique=True)
|
||||||
resettime = db.Column(db.DateTime)
|
resettime = db.Column(db.DateTime)
|
||||||
|
#active = db.Column(db.Boolean, default=False)
|
||||||
|
tutor = db.Column(db.Boolean, default=False)
|
||||||
admin = db.Column(db.Boolean, default=False)
|
admin = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
Loading…
Reference in New Issue
Block a user