forked from crunk/distribusi-verse
Compare commits
71 Commits
current_wd
...
main
Author | SHA1 | Date | |
---|---|---|---|
b2d0cbea6d | |||
b6313e2dc4 | |||
d9d33a877a | |||
0ac59269e7 | |||
d6f5d41fb7 | |||
dd228c6c73 | |||
69dff0b24c | |||
74035637ee | |||
b9cc82b8f2 | |||
64feae3475 | |||
098727f282 | |||
955e29e19d | |||
c435250a61 | |||
ad1a710803 | |||
c8b1171cad | |||
e5d8da2568 | |||
b7310c837b | |||
1c9af6e6e6 | |||
03792eadeb | |||
62428e84ae | |||
10b56e7479 | |||
7b76f63ed8 | |||
94efe6ad83 | |||
681c34e05e | |||
4425888116 | |||
102032bd6e | |||
280ce2f196 | |||
b5c9bfc8d7 | |||
475a412354 | |||
90d4251fd9 | |||
b71a694777 | |||
659acc902e | |||
2a018ab863 | |||
7456eea083 | |||
8cf5ca198b | |||
30f69cf03c | |||
d301ab8b53 | |||
23ac3e7699 | |||
0ddbe7fdd1 | |||
77238e687d | |||
2620890ddb | |||
bbf2fcf047 | |||
ff8215a7de | |||
0fb8929a80 | |||
8cf699214c | |||
8567af2ed4 | |||
8f960a1a3b | |||
37c1a7a1de | |||
bb1bd62250 | |||
d7ef42dddb | |||
50e27875e6 | |||
48b6e6f211 | |||
7f6a373e8d | |||
c854c2687e | |||
cfe1b646e4 | |||
bd519d6925 | |||
1cf75379b2 | |||
1b6104896f | |||
2db481989a | |||
e824430d95 | |||
f5632ccfd2 | |||
9e3bb6605f | |||
963b33f170 | |||
cefdc8cd11 | |||
67acc5ec56 | |||
2c9e882f2f | |||
545e5e205a | |||
9e48758767 | |||
dca9666984 | |||
18457f9ced | |||
3f54cbf72f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,10 +3,12 @@
|
|||||||
*.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,10 +8,15 @@ 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 is made for Autonomous Practices at the WDKA in Rotterdam.
|
This project was 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
|
||||||
|
|
||||||
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.
|
Currently this repo is transforming the academic based search filters into adjustable
|
||||||
|
small Autonomous space filters.
|
||||||
|
|
||||||
## Start your engines!
|
## Start your engines!
|
||||||
|
|
||||||
|
34
notes.md
34
notes.md
@ -1,34 +0,0 @@
|
|||||||
# 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,25 +1,49 @@
|
|||||||
[tool.black]
|
[tool.ruff]
|
||||||
line-length = 79
|
line-length = 79
|
||||||
target-version = ['py37', 'py38', 'py39']
|
target-version = "py311"
|
||||||
include = '\.pyi?$'
|
#include = '\.pyi?$'
|
||||||
exclude = '''
|
exclude = [
|
||||||
/(
|
".bzr",
|
||||||
\.eggs
|
".direnv",
|
||||||
| \.git
|
".eggs",
|
||||||
| \.hg
|
".git",
|
||||||
| \.mypy_cache
|
".git-rewrite",
|
||||||
| \.tox
|
".hg",
|
||||||
| \.venv
|
".ipynb_checkpoints",
|
||||||
| _build
|
".mypy_cache",
|
||||||
| buck-out
|
".nox",
|
||||||
| build
|
".pants.d",
|
||||||
| dist
|
".pyenv",
|
||||||
|
".pytest_cache",
|
||||||
# The following are specific to Black, you probably don't want those.
|
".pytype",
|
||||||
| blib2to3
|
".ruff_cache",
|
||||||
| tests/data
|
".svn",
|
||||||
| profiling
|
".tox",
|
||||||
)/
|
".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,48 +1,57 @@
|
|||||||
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==21.11b1
|
black==24.4.0
|
||||||
bleach==4.1.0
|
bleach==4.1.0
|
||||||
bleach-allowlist==1.0.3
|
bleach-allowlist==1.0.3
|
||||||
blinker==1.4
|
blinker==1.7.0
|
||||||
cffi==1.15.0
|
cffi
|
||||||
click==8.0.3
|
click==8.1.7
|
||||||
|
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==2.0.2
|
Flask==3.0.3
|
||||||
|
Flask-APScheduler==1.13.1
|
||||||
Flask-BabelEx==0.9.4
|
Flask-BabelEx==0.9.4
|
||||||
Flask-Bcrypt==0.7.1
|
Flask-Bcrypt==1.0.1
|
||||||
Flask-Login==0.5.0
|
Flask-Login==0.6.3
|
||||||
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==2.5.1
|
Flask-SQLAlchemy==3.1.1
|
||||||
Flask-WTF==1.0.0
|
Flask-WTF==1.2.1
|
||||||
greenlet==1.1.2
|
greenlet==3.0.3
|
||||||
idna==3.3
|
idna==3.3
|
||||||
itsdangerous==2.0.1
|
isort==5.13.2
|
||||||
Jinja2==3.0.3
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.3
|
||||||
Mako==1.1.6
|
Mako==1.1.6
|
||||||
MarkupSafe==2.0.1
|
MarkupSafe==2.1.5
|
||||||
|
msgpack==1.0.8
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
packaging==21.3
|
neovim==0.3.1
|
||||||
|
packaging==24.0
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
pathspec==0.9.0
|
pathspec==0.9.0
|
||||||
Pillow==8.3.2
|
Pillow
|
||||||
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==2021.11.10
|
regex
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
speaklater==1.3
|
speaklater==1.3
|
||||||
SQLAlchemy==1.4.27
|
SQLAlchemy==2.0.29
|
||||||
tomli==1.2.2
|
tomli==1.2.2
|
||||||
typing_extensions==4.0.1
|
typing_extensions==4.11.0
|
||||||
|
tzlocal==5.2
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
Werkzeug==2.0.2
|
Werkzeug==3.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 setup, find_packages
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
setup(name='library', version='1.0', packages=find_packages())
|
setup(name="library", version="1.0", packages=find_packages())
|
||||||
|
9
verse/admin.py
Normal file
9
verse/admin.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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,36 +1,44 @@
|
|||||||
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 (
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
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
|
||||||
|
|
||||||
from usermodel import User
|
admin_page = Blueprint(
|
||||||
from distribusimodel import Distribusis
|
"admin",
|
||||||
from distribusisinfo import DistribusisInfo
|
__name__,
|
||||||
|
template_folder="templates/describe_files",
|
||||||
from forms.adminuserform import AdminUserForm
|
static_folder="static",
|
||||||
from forms.admindistribusiform import AdminDistribusiForm
|
)
|
||||||
|
|
||||||
|
|
||||||
def AdminPage():
|
@admin_page.route("/admin", methods=["GET", "POST"])
|
||||||
adminuserform = AddUsersToForm(AdminUserForm())
|
@login_required
|
||||||
admindistribusiform = AddDistribusisToForm(AdminDistribusiForm())
|
def admin():
|
||||||
|
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():
|
||||||
DeleteDistribusis(admindistribusiform)
|
delete_distribusis(admindistribusiform)
|
||||||
|
|
||||||
if adminuserform.validate_on_submit():
|
if adminuserform.validate_on_submit():
|
||||||
if adminuserform.delete.data:
|
if adminuserform.delete.data:
|
||||||
DeleteUsers(adminuserform)
|
delete_users(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",
|
||||||
@ -40,36 +48,18 @@ def AdminPage():
|
|||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def DeleteUsers(adminuserform):
|
def delete_users(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()
|
||||||
DeleteUserDistribusis(user)
|
delete_User_distribusis(user)
|
||||||
DeleteUserFromDb(user)
|
delete_user_from_db(user)
|
||||||
userform.errors.append(f"User {useremail} deleted!")
|
userform.errors.append(f"User {useremail} deleted!")
|
||||||
|
|
||||||
|
|
||||||
def ToggleUsersAsTutors(adminuserform, is_tutor):
|
def delete_user_from_db(user):
|
||||||
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()
|
||||||
@ -77,14 +67,14 @@ def DeleteUserFromDb(user):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
def DeleteUserDistribusis(user):
|
def delete_User_distribusis(user):
|
||||||
distribusis = DistribusisInfo.getuserdistribusis(user.email)
|
distribusis = DistribusisInfo.get_user_distribusis(user.email)
|
||||||
for distribusi in distribusis:
|
for distribusi in distribusis:
|
||||||
DeleteDistribusiFiles(distribusi.distribusiname)
|
delete_distribusi_files(distribusi.distribusiname)
|
||||||
DeleteDistribusiFromDb(distribusi)
|
delete_distribusi_from_db(distribusi)
|
||||||
|
|
||||||
|
|
||||||
def DeleteDistribusis(admindistribusiform):
|
def delete_distribusis(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:
|
||||||
@ -92,12 +82,12 @@ def DeleteDistribusis(admindistribusiform):
|
|||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=distribusiname
|
distribusiname=distribusiname
|
||||||
).first()
|
).first()
|
||||||
DeleteDistribusiFromDb(distribusi)
|
delete_distribusi_from_db(distribusi)
|
||||||
DeleteDistribusiFiles(distribusiname)
|
delete_distribusi_files(distribusiname)
|
||||||
distribusiform.errors.append("Deleted distribusi")
|
distribusiform.errors.append("Deleted distribusi")
|
||||||
|
|
||||||
|
|
||||||
def DeleteDistribusiFromDb(distribusi):
|
def delete_distribusi_from_db(distribusi):
|
||||||
try:
|
try:
|
||||||
db.session.delete(distribusi)
|
db.session.delete(distribusi)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -105,7 +95,7 @@ def DeleteDistribusiFromDb(distribusi):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
def DeleteDistribusiFiles(distribusiname):
|
def delete_distribusi_files(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)
|
||||||
@ -114,15 +104,15 @@ def DeleteDistribusiFiles(distribusiname):
|
|||||||
shutil.rmtree(cssfolder)
|
shutil.rmtree(cssfolder)
|
||||||
|
|
||||||
|
|
||||||
def AddDistribusisToForm(admindistribusiform):
|
def add_distribusis_to_form(admindistribusiform):
|
||||||
distribusis = DistribusisInfo.visibledistribusis()
|
distribusis = DistribusisInfo.visible_distribusis()
|
||||||
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
|
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
|
||||||
distribusis
|
distribusis
|
||||||
)
|
)
|
||||||
return admindistribusiform
|
return admindistribusiform
|
||||||
|
|
||||||
|
|
||||||
def AddUsersToForm(adminuserform):
|
def add_users_to_form(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,9 +1,7 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import BooleanField, SubmitField
|
||||||
BooleanField,
|
|
||||||
SubmitField,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AdminDistribusiForm(FlaskForm):
|
class AdminDistribusiForm(FlaskForm):
|
||||||
@ -15,7 +13,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,9 +1,7 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import BooleanField, SubmitField
|
||||||
SubmitField,
|
|
||||||
BooleanField,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AdminUserForm(FlaskForm):
|
class AdminUserForm(FlaskForm):
|
||||||
@ -13,7 +11,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}",
|
||||||
@ -22,8 +20,4 @@ class AdminUserForm(FlaskForm):
|
|||||||
|
|
||||||
return UserListForm()
|
return UserListForm()
|
||||||
|
|
||||||
tutors = SubmitField("Are tutors")
|
|
||||||
|
|
||||||
nottutors = SubmitField("Are not tutors")
|
|
||||||
|
|
||||||
delete = SubmitField("Delete")
|
delete = SubmitField("Delete")
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<div class="overview">
|
<div class="overview">
|
||||||
@ -14,9 +14,6 @@
|
|||||||
</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') }}">
|
||||||
@ -53,12 +50,6 @@
|
|||||||
</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>
|
@ -1,13 +1,15 @@
|
|||||||
import sys
|
import sys
|
||||||
from app import create_app, db
|
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
InvalidRequestError,
|
|
||||||
InterfaceError,
|
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
|
InterfaceError,
|
||||||
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
from usermodel import User # noqa: F401
|
|
||||||
from distribusimodel import Distribusis # noqa: F401
|
from app import create_app, db
|
||||||
|
from models.distribusi_model import Distribusis # noqa: F401
|
||||||
|
from models.user_model import User # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
def admintool():
|
def admintool():
|
||||||
|
53
verse/app.py
53
verse/app.py
@ -1,13 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
from flask import Flask
|
import tomllib
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
|
||||||
from flask_bcrypt import Bcrypt
|
|
||||||
from flask_migrate import Migrate
|
|
||||||
from flask_wtf.csrf import CSRFProtect
|
|
||||||
from flask_login import (
|
|
||||||
LoginManager,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
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_wtf.csrf import CSRFProtect
|
||||||
|
|
||||||
|
|
||||||
|
APP = Flask(__name__, static_folder="static")
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
migrate = Migrate()
|
migrate = Migrate()
|
||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
@ -15,18 +17,16 @@ login_manager = LoginManager()
|
|||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
APP = Flask(__name__, static_folder="static")
|
APP.secret_key = os.urandom(24)
|
||||||
|
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db"
|
||||||
APP.secret_key = "secret-key"
|
|
||||||
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data/distribusiverse.db"
|
|
||||||
APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
|
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,10 +38,35 @@ 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
3
verse/data/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*
|
|
||||||
*/
|
|
||||||
!.gitignore
|
|
@ -1,11 +1,13 @@
|
|||||||
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 flask_migrate import upgrade, migrate, init, stamp
|
from models.distribusi_model import Distribusis # noqa: F401
|
||||||
|
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 usermodel import User # noqa: F401
|
from models.user_model import User # noqa: F401
|
||||||
from distribusimodel import Distribusis # noqa: F401
|
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
app.app_context().push()
|
app.app_context().push()
|
||||||
|
160
verse/describer/describe_files.py
Normal file
160
verse/describer/describe_files.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
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
|
48
verse/describer/forms/describe_files_form.py
Normal file
48
verse/describer/forms/describe_files_form.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""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', {})})
|
8
verse/describer/forms/redistribusi_form.py
Normal file
8
verse/describer/forms/redistribusi_form.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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!")
|
32
verse/describer/static/css/describer.css
Normal file
32
verse/describer/static/css/describer.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#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%;
|
||||||
|
}
|
78
verse/describer/templates/describe_files/describe.html
Normal file
78
verse/describer/templates/describe_files/describe.html
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{% 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 %}
|
13
verse/describer/templates/describe_files/redistribusi.html
Normal file
13
verse/describer/templates/describe_files/redistribusi.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
82
verse/distribusikan/add_files_to_describer.py
Normal file
82
verse/distribusikan/add_files_to_describer.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
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,41 +1,40 @@
|
|||||||
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 (
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from distribusimodel import Distribusis
|
from distribusikan.distribusis_info import DistribusisInfo
|
||||||
|
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 Editor():
|
def css_editor_page():
|
||||||
editorform = EditorForm()
|
editorform = EditorForm()
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
if editorform.validate_on_submit():
|
if editorform.validate_on_submit():
|
||||||
ValidateEditCssForm(editorform, current_distribusi)
|
validate_edit_css_form(editorform, current_distribusi)
|
||||||
return RenderDistribusiTemplate(current_distribusi)
|
return render_distribusi_template(current_distribusi)
|
||||||
|
|
||||||
return RenderEditorTemplate(editorform, current_distribusi)
|
return render_editor_template(editorform, current_distribusi)
|
||||||
|
|
||||||
|
|
||||||
def ValidateEditCssForm(editorform, current_distribusi):
|
def validate_edit_css_form(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)
|
||||||
@ -45,22 +44,22 @@ def ValidateEditCssForm(editorform, current_distribusi):
|
|||||||
shutil.rmtree(publicfolder)
|
shutil.rmtree(publicfolder)
|
||||||
|
|
||||||
if editorform.public.data:
|
if editorform.public.data:
|
||||||
MakePublicTheme(editorform, current_distribusi)
|
make_public_theme(editorform, current_distribusi)
|
||||||
if editorform.cssfile.data:
|
if editorform.cssfile.data:
|
||||||
SaveUploadCssFile(editorform, publicfolder)
|
save_upload_css_file(editorform, publicfolder)
|
||||||
CopyPublicToUserFolder(editorform, publicfolder, newcssfolder)
|
copy_public_to_user_folder(editorform, publicfolder, newcssfolder)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
WriteCssToFile(editorform, publicfolder)
|
write_css_to_file(editorform, publicfolder)
|
||||||
|
|
||||||
if editorform.cssfile.data:
|
if editorform.cssfile.data:
|
||||||
SaveUploadCssFile(editorform, newcssfolder)
|
save_upload_css_file(editorform, newcssfolder)
|
||||||
return
|
return
|
||||||
if editorform.cssname.data:
|
if editorform.cssname.data:
|
||||||
WriteCssToFile(editorform, newcssfolder)
|
write_css_to_file(editorform, newcssfolder)
|
||||||
|
|
||||||
|
|
||||||
def SaveUploadCssFile(editorform, newcssfolder):
|
def save_upload_css_file(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
|
||||||
@ -68,38 +67,37 @@ def SaveUploadCssFile(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 WriteCssToFile(editorform, newcssfolder):
|
def write_css_to_file(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 CopyPublicToUserFolder(editorform, publicfolder, newcssfolder):
|
def copy_public_to_user_folder(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,
|
publicfolder, f"{secure_filename(editorform.cssname.data)}.css"
|
||||||
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 MakePublicTheme(editorform, current_distribusi):
|
def make_public_theme(editorform, current_distribusi):
|
||||||
try:
|
try:
|
||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
@ -121,12 +119,12 @@ def MakePublicTheme(editorform, current_distribusi):
|
|||||||
editorform.public.errors.append("Error connecting to the database")
|
editorform.public.errors.append("Error connecting to the database")
|
||||||
|
|
||||||
|
|
||||||
def RenderDistribusiTemplate(current_distribusi):
|
def render_distribusi_template(current_distribusi):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
|
|
||||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||||
@ -150,11 +148,11 @@ def RenderDistribusiTemplate(current_distribusi):
|
|||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def RenderEditorTemplate(editorform, current_distribusi):
|
def render_editor_template(editorform, current_distribusi):
|
||||||
htmlplaceholder = HtmlPlaceholder()
|
html_placeholder = get_html_placeholder()
|
||||||
|
|
||||||
cssplaceholder = CssPlaceholder(current_distribusi)
|
css_placeholder = get_css_placeholder(current_distribusi)
|
||||||
editorform.css.data = cssplaceholder
|
editorform.css.data = css_placeholder
|
||||||
|
|
||||||
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)
|
||||||
@ -163,33 +161,33 @@ def RenderEditorTemplate(editorform, current_distribusi):
|
|||||||
files_uploaded=files_uploaded,
|
files_uploaded=files_uploaded,
|
||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
editorform=editorform,
|
editorform=editorform,
|
||||||
htmlplaceholder=htmlplaceholder,
|
html_placeholder=html_placeholder,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def CssPlaceholder(current_distribusi):
|
def get_css_placeholder(current_distribusi):
|
||||||
cssplaceholder = "Try out your CSS here"
|
css_placeholder = "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:
|
||||||
cssplaceholder = GetPublicCssFile(distribusi)
|
css_placeholder = get_public_css_file(distribusi)
|
||||||
else:
|
else:
|
||||||
with open("themes/editor/placeholder.css") as f:
|
with open("themes/editor/placeholder.css") as f:
|
||||||
cssplaceholder = f.read()
|
css_placeholder = f.read()
|
||||||
return cssplaceholder
|
return css_placeholder
|
||||||
|
|
||||||
|
|
||||||
def HtmlPlaceholder():
|
def get_html_placeholder():
|
||||||
htmlplaceholder = "Write some test HTML here"
|
html_placeholder = "Write some test HTML here"
|
||||||
with open("themes/editor/placeholder.html") as f:
|
with open("themes/editor/placeholder.html") as f:
|
||||||
htmlplaceholder = f.read()
|
html_placeholder = f.read()
|
||||||
return htmlplaceholder
|
return html_placeholder
|
||||||
|
|
||||||
|
|
||||||
def GetPublicCssFile(distribusi):
|
def get_public_css_file(distribusi):
|
||||||
cssplaceholder = ""
|
css_placeholder = ""
|
||||||
publicthemefolder = os.path.join(
|
publicthemefolder = os.path.join(
|
||||||
"themes/publicthemes", distribusi.distribusiname
|
"themes/publicthemes", distribusi.distribusiname
|
||||||
)
|
)
|
||||||
@ -197,5 +195,5 @@ def GetPublicCssFile(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:
|
||||||
cssplaceholder = f.read()
|
css_placeholder = f.read()
|
||||||
return cssplaceholder
|
return css_placeholder
|
@ -1,77 +1,89 @@
|
|||||||
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 (
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from usermodel import User
|
from app import db
|
||||||
from distribusimodel import Distribusis
|
from distribusikan.distribusis_info import DistribusisInfo
|
||||||
from distribusisinfo import DistribusisInfo
|
from distribusikan.forms.distribusiform import DistribusiForm
|
||||||
|
from distribusikan.forms.publicthemeform import PublicThemeForm
|
||||||
from forms.selectorform import SelectorForm
|
from distribusikan.forms.selectorform import SelectorForm
|
||||||
from forms.uploadform import UploadForm
|
from distribusikan.forms.themeform import ThemeForm
|
||||||
from forms.distribusiform import DistribusiForm
|
from distribusikan.forms.uploadform import UploadForm
|
||||||
from forms.themeform import ThemeForm
|
from models.distribusi_model import Distribusis
|
||||||
from forms.publicthemeform import PublicThemeForm
|
from models.user_model import User
|
||||||
|
|
||||||
# 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.userdistribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||||
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:
|
||||||
SelectNewDistribusi()
|
select_new_distribusi()
|
||||||
|
if selectorform.describe.data:
|
||||||
|
return select_describe_distribusi(selectorform.distribusis.data)
|
||||||
if selectorform.delete.data:
|
if selectorform.delete.data:
|
||||||
selectorform = DeleteDistribusi(selectorform.distribusis.data)
|
selectorform = delete_distribusi(selectorform.distribusis.data)
|
||||||
selectorform.distribusis.choices = (
|
selectorform.distribusis.choices = (
|
||||||
DistribusisInfo.userdistribusinames()
|
DistribusisInfo.user_distribusinames()
|
||||||
)
|
)
|
||||||
if selectorform.update.data:
|
if selectorform.update.data:
|
||||||
SelectUpdateDistribusi(selectorform.distribusis.data)
|
select_update_distribusi(selectorform.distribusis.data)
|
||||||
current_distribusi = UserHelper.current_distribusi()
|
current_distribusi = UserHelper.current_distribusi()
|
||||||
uploadform = AutoFillInUploadForm(uploadform, current_distribusi)
|
uploadform = auto_fill_in_upload_form(
|
||||||
|
uploadform, current_distribusi
|
||||||
|
)
|
||||||
|
|
||||||
return RenderDistribusiTemplate(
|
return render_distribusi_template(
|
||||||
selectorform, uploadform, current_distribusi
|
selectorform, uploadform, current_distribusi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def AutoFillInUploadForm(uploadform, current_distribusi):
|
def auto_fill_in_upload_form(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.term.data = distribusi.term
|
uploadform.description.data = distribusi.description
|
||||||
uploadform.course.data = distribusi.course
|
uploadform.category.data = distribusi.category
|
||||||
uploadform.academicyear.data = distribusi.year
|
uploadform.year.data = distribusi.year
|
||||||
uploadform.tags.data = distribusi.tags
|
uploadform.tags.data = distribusi.tags
|
||||||
return uploadform
|
return uploadform
|
||||||
|
|
||||||
|
|
||||||
def SelectNewDistribusi():
|
def select_new_distribusi():
|
||||||
print("make a new distribusi")
|
print("make a new distribusi")
|
||||||
SelectCurrentDistribusi("new")
|
select_current_distribusi("new")
|
||||||
|
|
||||||
|
|
||||||
def SelectUpdateDistribusi(distribusiname):
|
def select_describe_distribusi(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}")
|
||||||
SelectCurrentDistribusi(distribusiname)
|
select_current_distribusi(distribusiname)
|
||||||
|
|
||||||
|
|
||||||
def DeleteDistribusi(distribusiname):
|
def delete_distribusi(distribusiname):
|
||||||
print(f"delete this distribusi {distribusiname}")
|
print(f"delete this distribusi {distribusiname}")
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
try:
|
try:
|
||||||
@ -105,7 +117,7 @@ def DeleteDistribusi(distribusiname):
|
|||||||
return selectorform
|
return selectorform
|
||||||
|
|
||||||
|
|
||||||
def SelectCurrentDistribusi(distribusiname):
|
def select_current_distribusi(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()
|
||||||
@ -117,35 +129,35 @@ def SelectCurrentDistribusi(distribusiname):
|
|||||||
flash("An error occured !", "danger")
|
flash("An error occured !", "danger")
|
||||||
|
|
||||||
|
|
||||||
def DistribusiSelected():
|
def distribusi_selected():
|
||||||
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 SelectorVisible():
|
def selector_visible():
|
||||||
has_distribusi = UserHelper.has_distribusi()
|
has_distribusi = UserHelper.has_distribusi()
|
||||||
distribusi_selected = DistribusiSelected()
|
is_distribusi_selected = distribusi_selected()
|
||||||
if distribusi_selected:
|
if is_distribusi_selected:
|
||||||
return False
|
return False
|
||||||
if not has_distribusi:
|
if not has_distribusi:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def RenderDistribusiTemplate(selectorform, uploadform, current_distribusi):
|
def render_distribusi_template(selectorform, uploadform, current_distribusi):
|
||||||
distribusiform = DistribusiForm()
|
distribusiform = DistribusiForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||||
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 = SelectorVisible()
|
selectorvisible = selector_visible()
|
||||||
limit_reached = UserHelper.distribusi_limit_reached()
|
limit_reached = UserHelper.distribusi_limit_reached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
@ -1,43 +1,38 @@
|
|||||||
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 DistribusiWorkflow():
|
def distribusi_workflow():
|
||||||
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()
|
||||||
@ -47,18 +42,19 @@ def DistribusiWorkflow():
|
|||||||
|
|
||||||
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 = GetCssFile(distribusi)
|
cssfile = get_css_file(distribusi)
|
||||||
UnzipDistribusiFiles(distribusi, userfolder)
|
unzip_distribusi_files(distribusi, userfolder)
|
||||||
CleanUpDistribusiFiles(userfolder)
|
clean_up_distribusi_files(userfolder)
|
||||||
RunDistribusi(userfolder, cssfile)
|
add_distribusi_files_to_db(distribusi.distribusiname)
|
||||||
SetDistribusiToVisible(distribusi, user)
|
run_distribusi(userfolder, cssfile)
|
||||||
DeleteCssFile(cssfile)
|
set_distribusi_to_visible(distribusi, user)
|
||||||
|
delete_css_file(cssfile)
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
return RenderDistribusiTemplate(distribusiform, current_distribusi)
|
return render_distribusi_template(distribusiform, current_distribusi)
|
||||||
|
|
||||||
|
|
||||||
def UnzipDistribusiFiles(distribusi, userfolder):
|
def unzip_distribusi_files(distribusi, userfolder):
|
||||||
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
||||||
unzipfile = os.path.join(userfolder, zipfilename)
|
unzipfile = os.path.join(userfolder, zipfilename)
|
||||||
|
|
||||||
@ -70,15 +66,15 @@ def UnzipDistribusiFiles(distribusi, userfolder):
|
|||||||
os.remove(os.path.join(userfolder, zipfilename))
|
os.remove(os.path.join(userfolder, zipfilename))
|
||||||
|
|
||||||
|
|
||||||
def CleanUpDistribusiFiles(userfolder):
|
def clean_up_distribusi_files(userfolder):
|
||||||
if os.path.exists(userfolder):
|
if os.path.exists(userfolder):
|
||||||
RemoveMacFolders(userfolder)
|
remove_mac_folders(userfolder)
|
||||||
|
|
||||||
|
|
||||||
def RemoveMacFolders(path):
|
def remove_mac_folders(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:
|
||||||
@ -86,10 +82,10 @@ def RemoveMacFolders(path):
|
|||||||
if filename == "__MACOSX":
|
if filename == "__MACOSX":
|
||||||
shutil.rmtree(fullpath)
|
shutil.rmtree(fullpath)
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
RemoveMacFolders(fullpath)
|
remove_mac_folders(fullpath)
|
||||||
|
|
||||||
|
|
||||||
def GetCssFile(distribusi):
|
def get_css_file(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):
|
||||||
@ -99,13 +95,14 @@ def GetCssFile(distribusi):
|
|||||||
return cssfile
|
return cssfile
|
||||||
|
|
||||||
|
|
||||||
def RunDistribusi(userfolder, cssfile):
|
def run_distribusi(userfolder, cssfile):
|
||||||
|
print(f"Run distribusi on this folder: {userfolder} with css:{cssfile}")
|
||||||
parser = build_argparser()
|
parser = build_argparser()
|
||||||
args = parser.parse_args(["--menu-with-index", "-s", cssfile])
|
args = parser.parse_args(["-t", "-a", "--menu-with-index", "-s", cssfile])
|
||||||
distribusify(args, userfolder)
|
distribusify(args, userfolder)
|
||||||
|
|
||||||
|
|
||||||
def SetDistribusiToVisible(distribusi, user):
|
def set_distribusi_to_visible(distribusi, user):
|
||||||
try:
|
try:
|
||||||
distribusi.visible = True
|
distribusi.visible = True
|
||||||
user.currentdistribusi = None
|
user.currentdistribusi = None
|
||||||
@ -115,19 +112,19 @@ def SetDistribusiToVisible(distribusi, user):
|
|||||||
flash("Unknown error occured!")
|
flash("Unknown error occured!")
|
||||||
|
|
||||||
|
|
||||||
def DeleteCssFile(cssfile):
|
def delete_css_file(cssfile):
|
||||||
if os.path.exists(cssfile):
|
if os.path.exists(cssfile):
|
||||||
os.remove(cssfile)
|
os.remove(cssfile)
|
||||||
|
|
||||||
|
|
||||||
def RenderDistribusiTemplate(distribusiform, current_distribusi):
|
def render_distribusi_template(distribusiform, current_distribusi):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||||
selectorvisible = SelectorVisible()
|
selectorvisible = selector_visible()
|
||||||
|
|
||||||
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)
|
47
verse/distribusikan/distribusikan.py
Normal file
47
verse/distribusikan/distribusikan.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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,18 +1,18 @@
|
|||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from usermodel import User
|
from models.distribusi_model import Distribusis
|
||||||
from distribusimodel import Distribusis
|
from models.user_model import User
|
||||||
|
|
||||||
|
|
||||||
class DistribusisInfo:
|
class DistribusisInfo:
|
||||||
def userdistribusinames():
|
def user_distribusinames():
|
||||||
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 publicthemes():
|
def public_themes():
|
||||||
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 visibledistribusis():
|
def visible_distribusis():
|
||||||
distribusis = Distribusis.query.filter(
|
distribusis = Distribusis.query.filter(
|
||||||
Distribusis.visible.isnot(False)
|
Distribusis.visible.isnot(False)
|
||||||
).all()
|
).all()
|
||||||
return distribusis
|
return distribusis
|
||||||
|
|
||||||
def getuserdistribusis(useremail):
|
def get_user_distribusis(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,15 +1,15 @@
|
|||||||
"""Form to save your CSS editor work."""
|
"""Form to save your CSS editor work."""
|
||||||
from wtforms import (
|
|
||||||
StringField,
|
|
||||||
TextAreaField,
|
|
||||||
BooleanField,
|
|
||||||
SubmitField,
|
|
||||||
)
|
|
||||||
|
|
||||||
from wtforms import validators
|
|
||||||
from wtforms.validators import Length
|
|
||||||
from flask_wtf.file import FileField, FileAllowed, FileSize
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
|
from flask_wtf.file import FileAllowed, FileField, FileSize
|
||||||
|
from wtforms import (
|
||||||
|
BooleanField,
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
TextAreaField,
|
||||||
|
validators,
|
||||||
|
)
|
||||||
|
from wtforms.validators import Length
|
||||||
|
|
||||||
|
|
||||||
class EditorForm(FlaskForm):
|
class EditorForm(FlaskForm):
|
@ -1,9 +1,7 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import RadioField, SubmitField
|
||||||
RadioField,
|
|
||||||
SubmitField,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PublicThemeForm(FlaskForm):
|
class PublicThemeForm(FlaskForm):
|
@ -1,5 +1,5 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import SubmitField, SelectField
|
from wtforms import SelectField, SubmitField
|
||||||
|
|
||||||
|
|
||||||
class SelectorForm(FlaskForm):
|
class SelectorForm(FlaskForm):
|
||||||
@ -9,4 +9,6 @@ class SelectorForm(FlaskForm):
|
|||||||
|
|
||||||
update = SubmitField("update")
|
update = SubmitField("update")
|
||||||
|
|
||||||
|
describe = SubmitField("describe")
|
||||||
|
|
||||||
delete = SubmitField("delete")
|
delete = SubmitField("delete")
|
@ -1,9 +1,7 @@
|
|||||||
"""Form object declaration."""
|
"""Form object declaration."""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import RadioField, SubmitField
|
||||||
RadioField,
|
|
||||||
SubmitField,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ThemeForm(FlaskForm):
|
class ThemeForm(FlaskForm):
|
94
verse/distribusikan/forms/uploadform.py
Normal file
94
verse/distribusikan/forms/uploadform.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
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")
|
32
verse/distribusikan/static/css/distribusikan.css
Normal file
32
verse/distribusikan/static/css/distribusikan.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#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;
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
.editarea {
|
.editarea {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
border: 3px solid #E0B0FF;
|
border: 3px solid #9de457;
|
||||||
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: #E0B0FF;
|
background: #9de457;
|
||||||
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: #60337F;
|
background-color: #9de457;
|
||||||
color: lightgrey;
|
color: black;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<div class="overview">
|
<div class="overview">
|
||||||
@ -20,27 +20,28 @@
|
|||||||
<div id="mainworkflow">
|
<div id="mainworkflow">
|
||||||
{% if selectorvisible %}
|
{% if selectorvisible %}
|
||||||
{% block selector %}
|
{% block selector %}
|
||||||
{% include "distribusiworkflow/selector.html" %}
|
{% include "distribusi_workflow/selector.html" %}
|
||||||
{% endblock selector%}
|
{% endblock selector%}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% block upload %}
|
{% block upload %}
|
||||||
{% include "distribusiworkflow/upload.html" %}
|
{% include "distribusi_workflow/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 "distribusiworkflow/theme.html" %}
|
{% include "distribusi_workflow/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 "distribusiworkflow/editcss.html" %}
|
{% include "distribusi_workflow/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 "distribusiworkflow/launch.html" %}
|
{% include "distribusi_workflow/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="/editor">Go to CSS editor</a></p>
|
<p><a href="/distribusikan/editor">Go to CSS editor</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
You need to upload your files first before you can a css theme
|
You need to upload your files first before you can select 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('distribusi') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.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('selector') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.selector') }}">
|
||||||
{{ selectorform.csrf_token }}
|
{{ selectorform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ selectorform.distribusis.label }}
|
{{ selectorform.distribusis.label }}
|
||||||
@ -18,6 +18,13 @@
|
|||||||
{{ 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>
|
||||||
@ -29,6 +36,7 @@
|
|||||||
{% 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('theme') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.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 a css theme
|
You need to upload your files first before you can select 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('theme') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.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 a css theme
|
You need to upload your files first before you can select 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('upload') }}">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.upload') }}">
|
||||||
{{ uploadform.csrf_token }}
|
{{ uploadform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.sitename.label }}
|
{{ uploadform.sitename.label }}
|
||||||
@ -11,28 +11,26 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.academicyear.label }}
|
{{ uploadform.description.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.academicyear }}
|
{{ uploadform.year }}
|
||||||
{% for message in uploadform.academicyear.errors %}
|
{% for message in uploadform.year.errors %}
|
||||||
<div class="error">{{ message }}</div>
|
<div class="error">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ uploadform.term.label }}
|
{{ uploadform.category.label }}
|
||||||
<div class="selector-style">
|
<div class="selector-style">
|
||||||
{{ uploadform.term }}
|
{{ uploadform.category }}
|
||||||
{% for message in uploadform.term.errors %}
|
{% for message in uploadform.category.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.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('editor') }}" class="editform">
|
<form method="POST" enctype="multipart/form-data" action="{{ url_for('distribusikan.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>
|
||||||
{{htmlplaceholder}}
|
{{html_placeholder}}
|
||||||
</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('static', filename='css/editor.css')}}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.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 %}
|
@ -1,49 +1,47 @@
|
|||||||
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 ThemeSelector():
|
def theme_selector():
|
||||||
themeform = ThemeForm()
|
themeform = ThemeForm()
|
||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||||
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",
|
||||||
)
|
)
|
||||||
MoveCssToUserFolder(current_distribusi, copycssfile)
|
move_css_to_user_folder(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",
|
||||||
)
|
)
|
||||||
MoveCssToUserFolder(current_distribusi, copycssfile)
|
move_css_to_user_folder(current_distribusi, copycssfile)
|
||||||
return RenderDistribusiTemplate(
|
return render_distribusi_template(
|
||||||
themeform,
|
themeform, publicthemeform, current_distribusi
|
||||||
publicthemeform,
|
|
||||||
current_distribusi
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def MoveCssToUserFolder(current_distribusi, copycssfile):
|
def move_css_to_user_folder(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 RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi):
|
def render_distribusi_template(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 (
|
||||||
IntegrityError,
|
|
||||||
InvalidRequestError,
|
|
||||||
DataError,
|
|
||||||
InterfaceError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
|
IntegrityError,
|
||||||
|
InterfaceError,
|
||||||
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from distribusikan.distribusi_selector import select_current_distribusi
|
||||||
from usermodel import User
|
from distribusikan.forms.uploadform import UploadForm
|
||||||
from distribusimodel import Distribusis
|
from models.distribusi_model 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 UploadNewDistribusi(uploadfolder):
|
def upload_new_distribusi(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 UploadNewDistribusi(uploadfolder):
|
|||||||
newdistribusi = Distribusis(
|
newdistribusi = Distribusis(
|
||||||
distribusiname=uploadform.sitename.data,
|
distribusiname=uploadform.sitename.data,
|
||||||
userid=user.id,
|
userid=user.id,
|
||||||
term=uploadform.term.data,
|
category=uploadform.category.data,
|
||||||
course=uploadform.course.data,
|
description=uploadform.description.data,
|
||||||
year=uploadform.academicyear.data,
|
year=uploadform.year.data,
|
||||||
tags=uploadform.tags.data,
|
tags=uploadform.tags.data,
|
||||||
)
|
)
|
||||||
user.currentdistribusi = uploadform.sitename.data
|
user.currentdistribusi = uploadform.sitename.data
|
||||||
@ -47,7 +47,7 @@ def UploadNewDistribusi(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
|
||||||
SelectCurrentDistribusi(newdistribusi.distribusiname)
|
select_current_distribusi(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 UploadNewDistribusi(uploadfolder):
|
|||||||
return uploadform
|
return uploadform
|
||||||
|
|
||||||
|
|
||||||
def UploadUpdatedFiles(uploadfolder):
|
def upload_updates_files(uploadfolder):
|
||||||
uploadform = UploadForm()
|
uploadform = UploadForm()
|
||||||
if uploadform.validate_on_submit():
|
if uploadform.validate_on_submit():
|
||||||
try:
|
try:
|
||||||
@ -71,9 +71,9 @@ def UploadUpdatedFiles(uploadfolder):
|
|||||||
distribusi = Distribusis.query.filter_by(
|
distribusi = Distribusis.query.filter_by(
|
||||||
distribusiname=current_distribusi
|
distribusiname=current_distribusi
|
||||||
).first()
|
).first()
|
||||||
distribusi.term = uploadform.term.data
|
distribusi.category = uploadform.category.data
|
||||||
distribusi.course = uploadform.course.data
|
distribusi.description = (uploadform.description.data,)
|
||||||
distribusi.year = uploadform.academicyear.data
|
distribusi.year = uploadform.year.data
|
||||||
distribusi.tags = uploadform.tags.data
|
distribusi.tags = uploadform.tags.data
|
||||||
distribusi.visible = False
|
distribusi.visible = False
|
||||||
db.session.commit()
|
db.session.commit()
|
@ -1,33 +1,34 @@
|
|||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
|
||||||
from forms.distribusiform import DistribusiForm
|
from app import APP
|
||||||
from forms.themeform import ThemeForm
|
from distribusikan.distribusi_selector import selector_visible
|
||||||
from forms.publicthemeform import PublicThemeForm
|
from distribusikan.distribusis_info import DistribusisInfo
|
||||||
from forms.selectorform import SelectorForm
|
from distribusikan.upload import upload_new_distribusi, upload_updates_files
|
||||||
|
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.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||||
selectorvisible = SelectorVisible()
|
selectorvisible = selector_visible()
|
||||||
|
|
||||||
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 = UploadNewDistribusi(uploadfolder)
|
uploadform = upload_new_distribusi(uploadfolder)
|
||||||
else:
|
else:
|
||||||
uploadform = UploadUpdatedFiles(uploadfolder)
|
uploadform = upload_updates_files(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,88 +0,0 @@
|
|||||||
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,17 +14,19 @@ 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:
|
||||||
@ -65,20 +67,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():
|
||||||
|
18
verse/models/distribusi_file_model.py
Normal file
18
verse/models/distribusi_file_model.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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
|
@ -9,14 +9,11 @@ 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"))
|
||||||
term = db.Column(db.String(5), nullable=False, unique=False)
|
category = db.Column(db.String(500), nullable=True, 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,6 +1,7 @@
|
|||||||
from app import db
|
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
"""User model class for a user in distribusi-verse"""
|
"""User model class for a user in distribusi-verse"""
|
||||||
@ -14,8 +15,6 @@ 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):
|
11
verse/search/forms/searchform.py
Normal file
11
verse/search/forms/searchform.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
"""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")
|
42
verse/search/search.py
Normal file
42
verse/search/search.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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
|
52
verse/search/search_index.py
Normal file
52
verse/search/search_index.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
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
|
23
verse/search/templates/search/search.html
Normal file
23
verse/search/templates/search/search.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% 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 %}
|
7
verse/settings.toml
Normal file
7
verse/settings.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
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
|
154
verse/start.py
154
verse/start.py
@ -1,50 +1,45 @@
|
|||||||
"""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 (
|
||||||
render_template,
|
|
||||||
redirect,
|
|
||||||
url_for,
|
|
||||||
session,
|
|
||||||
send_from_directory,
|
|
||||||
Blueprint,
|
Blueprint,
|
||||||
|
redirect,
|
||||||
|
render_template,
|
||||||
|
send_from_directory,
|
||||||
|
session,
|
||||||
|
url_for,
|
||||||
)
|
)
|
||||||
from flask_login import (
|
from flask_login import current_user, login_required, logout_user
|
||||||
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 usermodel import User
|
from admin import is_adminuser
|
||||||
from distribusimodel import Distribusis
|
from admin_page.admin_page import admin_page
|
||||||
|
from app import create_app, login_manager, db
|
||||||
# Use upload form to populate filters
|
from describer.describe_files import describer
|
||||||
from forms.uploadform import UploadForm
|
from distribusikan.distribusikan import distribusikan
|
||||||
|
from distribusikan.distribusis_info import DistribusisInfo
|
||||||
# Interface! these are seperate files in main folder
|
from distribusikan.forms.uploadform import UploadForm
|
||||||
from adminpage import AdminPage
|
from models.distribusi_model import Distribusis
|
||||||
from editor import Editor
|
from models.user_model import User
|
||||||
from themeselector import ThemeSelector
|
from statuspengguna.forgotpassword import forgot_password
|
||||||
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 LoginUser
|
from statuspengguna.loginuser import login_section
|
||||||
from statuspengguna.registeruser import RegisterUser
|
from statuspengguna.registeruser import register_user
|
||||||
from statuspengguna.forgotpassword import ForgotPassword
|
from search.search import searchpages
|
||||||
from statuspengguna.resetpassword import ResetPassword
|
from search.search_index import init_search_index
|
||||||
|
|
||||||
# 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)
|
||||||
mail = Mail(APP)
|
APP.register_blueprint(describer, url_prefix="/describer")
|
||||||
|
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
|
||||||
@ -56,32 +51,28 @@ 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.visibledistribusis()
|
distribusis = DistribusisInfo.visible_distribusis()
|
||||||
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,
|
||||||
"term": distribusi.term,
|
"category": distribusi.category,
|
||||||
"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
|
|
||||||
|
|
||||||
adminuser = isadminuser()
|
years = uploadform.year.choices
|
||||||
|
categories = uploadform.category.choices
|
||||||
|
adminuser = is_adminuser()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"index.html",
|
"base/index.html",
|
||||||
distribusisindex=distribusisindex,
|
distribusisindex=distribusisindex,
|
||||||
years=years,
|
years=years,
|
||||||
terms=terms,
|
categories=categories,
|
||||||
courses=courses,
|
|
||||||
adminuser=adminuser,
|
adminuser=adminuser,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
@ -89,26 +80,7 @@ def index():
|
|||||||
|
|
||||||
@APP.route("/help")
|
@APP.route("/help")
|
||||||
def help():
|
def help():
|
||||||
return render_template("help.html")
|
return render_template("base/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>")
|
||||||
@ -116,35 +88,14 @@ 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():
|
||||||
@ -152,26 +103,6 @@ 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
|
||||||
@ -179,14 +110,7 @@ def handle_csrf_error(e):
|
|||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
return User.query.get(int(user_id))
|
return db.session.get(User, 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 Academicyear, Term, Course
|
/* for sorting on year and category
|
||||||
*/
|
*/
|
||||||
button {
|
button {
|
||||||
background-color: #E0B0FF;
|
background-color: #9de457;
|
||||||
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: #E0B0FF;
|
background-color: #9de457;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
max-width: 20em;
|
max-width: 20em;
|
||||||
position: relative;
|
position: relative;
|
||||||
border: none;
|
border: none;
|
||||||
background: #E0B0FF;
|
background: #9de457;
|
||||||
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: #E0B0FF;
|
background-color: #9de457;
|
||||||
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: #60337F;
|
background-color: #9de457;
|
||||||
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: #60337F;
|
background: #9de457;
|
||||||
}
|
}
|
||||||
|
@ -2,64 +2,17 @@ body
|
|||||||
{
|
{
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
background-color: #272a33;
|
background-color: #fdfdfd;
|
||||||
color:#E0B0FF;
|
color:#091411;
|
||||||
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: 30em;
|
width: 40em;
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +22,16 @@ div#upload form {
|
|||||||
#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;
|
||||||
@ -82,13 +44,13 @@ div#buttons{
|
|||||||
|
|
||||||
div#buttons .distribusi input{
|
div#buttons .distribusi input{
|
||||||
border: none;
|
border: none;
|
||||||
background: #fff600;
|
background: #9de457;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin: 0.2em;
|
margin: 0.2em;
|
||||||
}
|
}
|
||||||
div#buttons .distribusi input:hover{
|
div#buttons .distribusi input:hover{
|
||||||
background: #ffbf00;
|
background: #091411;
|
||||||
|
color: #6df2cc;
|
||||||
}
|
}
|
||||||
fieldset.required {
|
fieldset.required {
|
||||||
border: none;
|
border: none;
|
||||||
@ -101,21 +63,15 @@ 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: #E0B0FF;
|
background: #9de457;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -124,7 +80,9 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input:hover {
|
input:hover {
|
||||||
background: #60337F;
|
background: #091411;
|
||||||
|
color: #6df2cc;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="submit"]:disabled:hover,
|
input[type="submit"]:disabled:hover,
|
||||||
@ -144,37 +102,11 @@ input[type="submit"]:disabled:focus {
|
|||||||
background-color: #F92020;
|
background-color: #F92020;
|
||||||
}
|
}
|
||||||
|
|
||||||
#update {
|
#update, #describe {
|
||||||
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: "$ ";
|
||||||
@ -195,7 +127,7 @@ a:active {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
animation: reveal 4s linear;
|
animation: reveal 4s linear;
|
||||||
text-overflow: "█";
|
text-overflow: "█";
|
||||||
background-color: #2D3039;
|
background-color: #9de457;
|
||||||
}
|
}
|
||||||
#fancyboi::after {
|
#fancyboi::after {
|
||||||
content: "█";
|
content: "█";
|
||||||
@ -205,7 +137,7 @@ a:active {
|
|||||||
|
|
||||||
div.maincontent{
|
div.maincontent{
|
||||||
width: 55%;
|
width: 55%;
|
||||||
border: 3px #E0B0FF;
|
border: 3px #9de457;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-style: outset;
|
border-style: outset;
|
||||||
@ -244,8 +176,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -260,7 +190,7 @@ div.maincontent{
|
|||||||
color: black;
|
color: black;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #E0B0FF;
|
background: #9de457;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: Courier, sans-serif;
|
font-family: Courier, sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -268,11 +198,11 @@ div.maincontent{
|
|||||||
/*
|
/*
|
||||||
Project colors so far.
|
Project colors so far.
|
||||||
light
|
light
|
||||||
#E0B0FF
|
#9de457
|
||||||
medium
|
medium
|
||||||
#d28cff
|
#d28cff
|
||||||
dark
|
dark
|
||||||
#60337F
|
#9de457
|
||||||
|
|
||||||
background dark
|
background dark
|
||||||
#2D3039
|
#2D3039
|
||||||
|
@ -24,9 +24,8 @@ function filterSelection(c, name, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetDropDownButtons(){
|
function resetDropDownButtons(){
|
||||||
document.getElementById("Academicyear").innerText = "Academic year";
|
document.getElementById("Year").innerText = "Year";
|
||||||
document.getElementById("Term").innerText = "Term";
|
document.getElementById("Category").innerText = "Category";
|
||||||
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");
|
||||||
|
0
verse/statuspengguna/__init__.py
Normal file
0
verse/statuspengguna/__init__.py
Normal file
@ -1,17 +1,31 @@
|
|||||||
from uuid import uuid1
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from uuid import uuid1
|
||||||
|
|
||||||
|
from flask import Blueprint, render_template
|
||||||
|
from flask_mail import Mail, Message
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
InterfaceError,
|
InterfaceError,
|
||||||
InvalidRequestError,
|
InvalidRequestError,
|
||||||
)
|
)
|
||||||
from flask import render_template
|
|
||||||
from flask_mail import Message
|
|
||||||
|
|
||||||
from usermodel import User
|
from app import db, get_app
|
||||||
from forms.forgotpasswordform import ForgotPasswordForm
|
from statuspengguna.forms.forgotpasswordform import ForgotPasswordForm
|
||||||
from app import db
|
from models.user_model import User
|
||||||
|
|
||||||
|
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,12 +1,8 @@
|
|||||||
"""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,13 +1,8 @@
|
|||||||
"""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,22 +1,13 @@
|
|||||||
"""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)],
|
||||||
@ -27,9 +18,8 @@ class RegisterForm(FlaskForm):
|
|||||||
validators=[
|
validators=[
|
||||||
validators.InputRequired(),
|
validators.InputRequired(),
|
||||||
Email(),
|
Email(),
|
||||||
Length(6, 64),
|
Length(6, 128),
|
||||||
hremail,
|
],
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
password = PasswordField(
|
password = PasswordField(
|
@ -1,12 +1,8 @@
|
|||||||
"""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,17 +1,18 @@
|
|||||||
import os
|
import os
|
||||||
from flask_login import current_user
|
|
||||||
from flask import flash
|
from flask import flash
|
||||||
|
from flask_login import current_user
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
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:
|
||||||
@ -75,11 +76,10 @@ 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(DistribusisInfo.getuserdistribusis(user.email))
|
distribusiamount = len(
|
||||||
if user.tutor and distribusiamount > 14:
|
DistribusisInfo.get_user_distribusis(user.email)
|
||||||
print("tutor already has 15 distribusis")
|
)
|
||||||
return True
|
if distribusiamount > 19:
|
||||||
if not user.tutor and distribusiamount > 4:
|
print("user already has 20 distribusis")
|
||||||
print("user already has 5 distribusis")
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
from flask import (
|
from flask import (
|
||||||
render_template,
|
Blueprint,
|
||||||
redirect,
|
|
||||||
request,
|
|
||||||
flash,
|
|
||||||
url_for,
|
|
||||||
abort,
|
abort,
|
||||||
|
flash,
|
||||||
|
redirect,
|
||||||
|
render_template,
|
||||||
|
request,
|
||||||
|
url_for,
|
||||||
)
|
)
|
||||||
from usermodel import User
|
|
||||||
from forms.loginform import LoginForm
|
|
||||||
from flask_login import login_user
|
|
||||||
from flask_bcrypt import check_password_hash
|
from flask_bcrypt import check_password_hash
|
||||||
|
from flask_login import login_user
|
||||||
|
|
||||||
|
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():
|
||||||
@ -25,7 +39,9 @@ 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,22 +1,30 @@
|
|||||||
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 (
|
||||||
IntegrityError,
|
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
|
IntegrityError,
|
||||||
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,23 +1,32 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import (
|
|
||||||
render_template,
|
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||||
redirect,
|
from flask_bcrypt import generate_password_hash
|
||||||
flash,
|
from flask_login import login_user
|
||||||
url_for,
|
|
||||||
)
|
|
||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
IntegrityError,
|
|
||||||
DataError,
|
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
DataError,
|
||||||
|
IntegrityError,
|
||||||
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):
|
||||||
|
22
verse/statuspengguna/static/css/login.css
Normal file
22
verse/statuspengguna/static/css/login.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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,12 +1,13 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="mainworkflow">
|
<div id="mainworkflow">
|
||||||
<div class="workflow">
|
<div class="login">
|
||||||
<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') }}" method="post">
|
<form class="form" action="{{ url_for('forgotpassword.forgotpassword') }}"
|
||||||
|
method="post">
|
||||||
{{ forgotpasswordform.csrf_token }}
|
{{ forgotpasswordform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ forgotpasswordform.email.label }}
|
{{ forgotpasswordform.email.label }}
|
||||||
@ -25,5 +26,6 @@
|
|||||||
</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,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
<form class="form" action="{{ url_for('login') }}" method="post">
|
<form class="form" action="{{ url_for('login.login') }}" method="post">
|
||||||
{{ loginform.csrf_token }}
|
{{ loginform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ loginform.email.label }}
|
{{ loginform.email.label }}
|
||||||
@ -19,8 +19,9 @@
|
|||||||
</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.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
<form class="form" action="{{ url_for('register') }}" method="post">
|
<form class="form" action="{{ url_for('register.register') }}" method="post">
|
||||||
{{ registerform.csrf_token }}
|
{{ registerform.csrf_token }}
|
||||||
<fieldset class="required">
|
<fieldset class="required">
|
||||||
{{ registerform.username.label }}
|
{{ registerform.username.label }}
|
||||||
@ -36,4 +36,5 @@
|
|||||||
</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.html" %}
|
{% extends "base/base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="login">
|
<div id="login">
|
||||||
{% if linkvalid%}
|
{% if linkvalid%}
|
||||||
@ -26,4 +26,5 @@
|
|||||||
<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 %}
|
@ -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>Autonomous Practices X Distribusi-Verse</title>
|
<title>{{title}}</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') }}">
|
20
verse/templates/base/filtermenu.html
Normal file
20
verse/templates/base/filtermenu.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% 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.html" %}
|
{% extends "base/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="/distribusi">
|
<a href="distribusikan/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.html" %}
|
{% extends "base/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="/distribusi">
|
<a href="distribusikan/distribusi">
|
||||||
<input type="button" name="button" value="Distribusi"></input>
|
<input type="button" name="button" value="Distribusi"></input>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -46,19 +46,14 @@
|
|||||||
<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 'filtermenu.html' %}
|
{% include 'base/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["term"] }} {{ distribusi["year"] }} {{ distribusi["course"] }}'>
|
<li class='distribusi filter {{ distribusi["category"] }} {{ distribusi["year"] }} '>
|
||||||
<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,28 +0,0 @@
|
|||||||
{% 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 %}
|
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
<div id="willemdekooning-logo" class="png">
|
<div id="willemdekooning-logo" class="png">
|
||||||
<figure>
|
<figure>
|
||||||
<img class="image" src="https://www.wdka.nl/build/img/willemdekooning-logo.png">
|
<img class="image" src="https://varia.zone/archive/00-varia-server/welcome.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="example_video.mp4">
|
<source src="https://vvvvvvaria.org/~crunk/box/tomb_of_doom-2021-02-13_21.41.03.mp4">
|
||||||
</video>
|
</video>
|
||||||
<span class="filename">example_video.mp4</span>
|
<span class="filename">example_video.mp4</span>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user