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
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
.ruff_cache
|
||||
build/
|
||||
dist/
|
||||
pip-wheel-metadata/
|
||||
|
||||
verse/tmpupload/*
|
||||
verse/stash/*
|
||||
verse/search/searchdata/*
|
||||
*.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 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
|
||||
|
||||
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!
|
||||
|
||||
|
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
|
||||
target-version = ['py37', 'py38', 'py39']
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
/(
|
||||
\.eggs
|
||||
| \.git
|
||||
| \.hg
|
||||
| \.mypy_cache
|
||||
| \.tox
|
||||
| \.venv
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
|
||||
# The following are specific to Black, you probably don't want those.
|
||||
| blib2to3
|
||||
| tests/data
|
||||
| profiling
|
||||
)/
|
||||
'''
|
||||
target-version = "py311"
|
||||
#include = '\.pyi?$'
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".ipynb_checkpoints",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".pytype",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".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
|
||||
APScheduler==3.10.4
|
||||
Babel==2.9.1
|
||||
bcrypt==3.2.0
|
||||
black==21.11b1
|
||||
black==24.4.0
|
||||
bleach==4.1.0
|
||||
bleach-allowlist==1.0.3
|
||||
blinker==1.4
|
||||
cffi==1.15.0
|
||||
click==8.0.3
|
||||
blinker==1.7.0
|
||||
cffi
|
||||
click==8.1.7
|
||||
distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi@3eefd6e5ca7048555d441df8c6fbf4f2e255acac
|
||||
dnspython==2.1.0
|
||||
email-validator==1.1.3
|
||||
Flask==2.0.2
|
||||
Flask==3.0.3
|
||||
Flask-APScheduler==1.13.1
|
||||
Flask-BabelEx==0.9.4
|
||||
Flask-Bcrypt==0.7.1
|
||||
Flask-Login==0.5.0
|
||||
Flask-Bcrypt==1.0.1
|
||||
Flask-Login==0.6.3
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Migrate==3.1.0
|
||||
Flask-Principal==0.4.0
|
||||
Flask-Security==3.0.0
|
||||
Flask-Security-Too==4.1.3
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
Flask-WTF==1.0.0
|
||||
greenlet==1.1.2
|
||||
Flask-SQLAlchemy==3.1.1
|
||||
Flask-WTF==1.2.1
|
||||
greenlet==3.0.3
|
||||
idna==3.3
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.3
|
||||
isort==5.13.2
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.3
|
||||
Mako==1.1.6
|
||||
MarkupSafe==2.0.1
|
||||
MarkupSafe==2.1.5
|
||||
msgpack==1.0.8
|
||||
mypy-extensions==0.4.3
|
||||
packaging==21.3
|
||||
neovim==0.3.1
|
||||
packaging==24.0
|
||||
passlib==1.7.4
|
||||
pathspec==0.9.0
|
||||
Pillow==8.3.2
|
||||
Pillow
|
||||
platformdirs==2.4.0
|
||||
pycparser==2.21
|
||||
pynvim==0.5.0
|
||||
pyparsing==3.0.7
|
||||
python-dateutil==2.9.0.post0
|
||||
python-magic==0.4.24
|
||||
pytz==2021.3
|
||||
regex==2021.11.10
|
||||
regex
|
||||
six==1.16.0
|
||||
speaklater==1.3
|
||||
SQLAlchemy==1.4.27
|
||||
SQLAlchemy==2.0.29
|
||||
tomli==1.2.2
|
||||
typing_extensions==4.0.1
|
||||
typing_extensions==4.11.0
|
||||
tzlocal==5.2
|
||||
webencodings==0.5.1
|
||||
Werkzeug==2.0.2
|
||||
Werkzeug==3.0.2
|
||||
Whoosh==2.7.4
|
||||
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 shutil
|
||||
from flask import render_template
|
||||
|
||||
from flask import render_template, Blueprint
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy.exc import (
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
|
||||
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
|
||||
from distribusimodel import Distribusis
|
||||
from distribusisinfo import DistribusisInfo
|
||||
|
||||
from forms.adminuserform import AdminUserForm
|
||||
from forms.admindistribusiform import AdminDistribusiForm
|
||||
admin_page = Blueprint(
|
||||
"admin",
|
||||
__name__,
|
||||
template_folder="templates/describe_files",
|
||||
static_folder="static",
|
||||
)
|
||||
|
||||
|
||||
def AdminPage():
|
||||
adminuserform = AddUsersToForm(AdminUserForm())
|
||||
admindistribusiform = AddDistribusisToForm(AdminDistribusiForm())
|
||||
@admin_page.route("/admin", methods=["GET", "POST"])
|
||||
@login_required
|
||||
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():
|
||||
DeleteDistribusis(admindistribusiform)
|
||||
delete_distribusis(admindistribusiform)
|
||||
|
||||
if adminuserform.validate_on_submit():
|
||||
if adminuserform.delete.data:
|
||||
DeleteUsers(adminuserform)
|
||||
if adminuserform.tutors.data:
|
||||
ToggleUsersAsTutors(adminuserform, True)
|
||||
if adminuserform.nottutors.data:
|
||||
ToggleUsersAsTutors(adminuserform, False)
|
||||
delete_users(adminuserform)
|
||||
|
||||
template = render_template(
|
||||
"admin.html",
|
||||
@ -40,36 +48,18 @@ def AdminPage():
|
||||
return template
|
||||
|
||||
|
||||
def DeleteUsers(adminuserform):
|
||||
def delete_users(adminuserform):
|
||||
for userform in adminuserform:
|
||||
if "user" in userform.id:
|
||||
if userform.data:
|
||||
useremail = userform.label.text
|
||||
user = User.query.filter_by(email=useremail).first()
|
||||
DeleteUserDistribusis(user)
|
||||
DeleteUserFromDb(user)
|
||||
delete_User_distribusis(user)
|
||||
delete_user_from_db(user)
|
||||
userform.errors.append(f"User {useremail} deleted!")
|
||||
|
||||
|
||||
def ToggleUsersAsTutors(adminuserform, is_tutor):
|
||||
for userform in adminuserform:
|
||||
if "user" in userform.id:
|
||||
if userform.data:
|
||||
useremail = userform.label.text
|
||||
user = User.query.filter_by(email=useremail).first()
|
||||
ToggleUserTutorinDb(user, is_tutor)
|
||||
userform.errors.append(f"Is User {useremail} tutor {is_tutor}")
|
||||
|
||||
|
||||
def ToggleUserTutorinDb(user, is_tutor):
|
||||
try:
|
||||
user.tutor = is_tutor
|
||||
db.session.commit()
|
||||
except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
|
||||
db.session.rollback()
|
||||
|
||||
|
||||
def DeleteUserFromDb(user):
|
||||
def delete_user_from_db(user):
|
||||
try:
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
@ -77,14 +67,14 @@ def DeleteUserFromDb(user):
|
||||
db.session.rollback()
|
||||
|
||||
|
||||
def DeleteUserDistribusis(user):
|
||||
distribusis = DistribusisInfo.getuserdistribusis(user.email)
|
||||
def delete_User_distribusis(user):
|
||||
distribusis = DistribusisInfo.get_user_distribusis(user.email)
|
||||
for distribusi in distribusis:
|
||||
DeleteDistribusiFiles(distribusi.distribusiname)
|
||||
DeleteDistribusiFromDb(distribusi)
|
||||
delete_distribusi_files(distribusi.distribusiname)
|
||||
delete_distribusi_from_db(distribusi)
|
||||
|
||||
|
||||
def DeleteDistribusis(admindistribusiform):
|
||||
def delete_distribusis(admindistribusiform):
|
||||
for distribusiform in admindistribusiform:
|
||||
if "distribusi" in distribusiform.id:
|
||||
if distribusiform.data:
|
||||
@ -92,12 +82,12 @@ def DeleteDistribusis(admindistribusiform):
|
||||
distribusi = Distribusis.query.filter_by(
|
||||
distribusiname=distribusiname
|
||||
).first()
|
||||
DeleteDistribusiFromDb(distribusi)
|
||||
DeleteDistribusiFiles(distribusiname)
|
||||
delete_distribusi_from_db(distribusi)
|
||||
delete_distribusi_files(distribusiname)
|
||||
distribusiform.errors.append("Deleted distribusi")
|
||||
|
||||
|
||||
def DeleteDistribusiFromDb(distribusi):
|
||||
def delete_distribusi_from_db(distribusi):
|
||||
try:
|
||||
db.session.delete(distribusi)
|
||||
db.session.commit()
|
||||
@ -105,7 +95,7 @@ def DeleteDistribusiFromDb(distribusi):
|
||||
db.session.rollback()
|
||||
|
||||
|
||||
def DeleteDistribusiFiles(distribusiname):
|
||||
def delete_distribusi_files(distribusiname):
|
||||
userfolder = os.path.join("stash", distribusiname)
|
||||
if os.path.exists(userfolder):
|
||||
shutil.rmtree(userfolder)
|
||||
@ -114,15 +104,15 @@ def DeleteDistribusiFiles(distribusiname):
|
||||
shutil.rmtree(cssfolder)
|
||||
|
||||
|
||||
def AddDistribusisToForm(admindistribusiform):
|
||||
distribusis = DistribusisInfo.visibledistribusis()
|
||||
def add_distribusis_to_form(admindistribusiform):
|
||||
distribusis = DistribusisInfo.visible_distribusis()
|
||||
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
|
||||
distribusis
|
||||
)
|
||||
return admindistribusiform
|
||||
|
||||
|
||||
def AddUsersToForm(adminuserform):
|
||||
def add_users_to_form(adminuserform):
|
||||
users = User.query.all()
|
||||
adminuserform = AdminUserForm.user_list_form_builder(users)
|
||||
return adminuserform
|
@ -1,9 +1,7 @@
|
||||
"""Form object declaration."""
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
SubmitField,
|
||||
)
|
||||
from wtforms import BooleanField, SubmitField
|
||||
|
||||
|
||||
class AdminDistribusiForm(FlaskForm):
|
||||
@ -15,7 +13,7 @@ class AdminDistribusiForm(FlaskForm):
|
||||
class DistribusiListForm(AdminDistribusiForm):
|
||||
pass
|
||||
|
||||
for (i, distribusi) in enumerate(distribusis):
|
||||
for i, distribusi in enumerate(distribusis):
|
||||
setattr(
|
||||
DistribusiListForm,
|
||||
f"distribusi_{i}",
|
@ -1,9 +1,7 @@
|
||||
"""Form object declaration."""
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
SubmitField,
|
||||
BooleanField,
|
||||
)
|
||||
from wtforms import BooleanField, SubmitField
|
||||
|
||||
|
||||
class AdminUserForm(FlaskForm):
|
||||
@ -13,7 +11,7 @@ class AdminUserForm(FlaskForm):
|
||||
class UserListForm(AdminUserForm):
|
||||
pass
|
||||
|
||||
for (i, user) in enumerate(users):
|
||||
for i, user in enumerate(users):
|
||||
setattr(
|
||||
UserListForm,
|
||||
f"user_{i}",
|
||||
@ -22,8 +20,4 @@ class AdminUserForm(FlaskForm):
|
||||
|
||||
return UserListForm()
|
||||
|
||||
tutors = SubmitField("Are tutors")
|
||||
|
||||
nottutors = SubmitField("Are not tutors")
|
||||
|
||||
delete = SubmitField("Delete")
|
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<div id="buttons">
|
||||
<div class="overview">
|
||||
@ -14,9 +14,6 @@
|
||||
</div>
|
||||
<div class="maincontent">
|
||||
<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">
|
||||
<h2>List of distribusis</h2>
|
||||
<form method="POST" enctype="multipart/form-data" action="{{ url_for('admin') }}">
|
||||
@ -53,12 +50,6 @@
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<fieldset class="button required">
|
||||
{{ adminuserform.tutors }}
|
||||
</fieldset>
|
||||
<fieldset class="button required">
|
||||
{{ adminuserform.nottutors }}
|
||||
</fieldset>
|
||||
<fieldset class="button required">
|
||||
{{ adminuserform.delete }}
|
||||
</fieldset>
|
@ -1,13 +1,15 @@
|
||||
import sys
|
||||
from app import create_app, db
|
||||
|
||||
from sqlalchemy.exc import (
|
||||
InvalidRequestError,
|
||||
InterfaceError,
|
||||
DataError,
|
||||
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():
|
||||
|
53
verse/app.py
53
verse/app.py
@ -1,13 +1,15 @@
|
||||
import os
|
||||
from flask import Flask
|
||||
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,
|
||||
)
|
||||
import tomllib
|
||||
|
||||
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()
|
||||
migrate = Migrate()
|
||||
bcrypt = Bcrypt()
|
||||
@ -15,18 +17,16 @@ login_manager = LoginManager()
|
||||
|
||||
|
||||
def create_app():
|
||||
APP = Flask(__name__, static_folder="static")
|
||||
|
||||
APP.secret_key = "secret-key"
|
||||
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data/distribusiverse.db"
|
||||
APP.secret_key = os.urandom(24)
|
||||
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db"
|
||||
APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
|
||||
APP.config["MAX_CONTENT_LENGTH"] = 150 * 1024 * 1024
|
||||
|
||||
APP.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024
|
||||
APP.config["MAIL_SERVER"] = "mail.autonomic.zone"
|
||||
APP.config["MAIL_PORT"] = 587
|
||||
APP.config["MAIL_USE_SSL"] = False
|
||||
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.login_view = "index"
|
||||
@ -38,10 +38,35 @@ def create_app():
|
||||
APP.config["UPLOAD_FOLDER"] = "tmpupload"
|
||||
APP.config["PUBLIC_THEMES"] = "themes/publicthemes"
|
||||
|
||||
# user settings_file
|
||||
settings()
|
||||
|
||||
csrf.init_app(APP)
|
||||
login_manager.init_app(APP)
|
||||
db.init_app(APP)
|
||||
migrate.init_app(APP, db, render_as_batch=True)
|
||||
bcrypt.init_app(APP)
|
||||
|
||||
@APP.context_processor
|
||||
def inject_title():
|
||||
return dict(title=APP.config["title"])
|
||||
|
||||
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():
|
||||
"""Run deployment of database."""
|
||||
from flask_migrate import init, migrate, stamp, upgrade
|
||||
|
||||
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
|
||||
from usermodel import User # noqa: F401
|
||||
from distribusimodel import Distribusis # noqa: F401
|
||||
from models.user_model import User # noqa: F401
|
||||
|
||||
app = create_app()
|
||||
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 shutil
|
||||
|
||||
import bleach
|
||||
from bleach_allowlist import all_styles
|
||||
import shutil
|
||||
from flask import render_template
|
||||
|
||||
from werkzeug.utils import secure_filename
|
||||
from sqlalchemy.exc import (
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
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 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()
|
||||
current_distribusi = UserHelper.current_distribusi()
|
||||
if editorform.validate_on_submit():
|
||||
ValidateEditCssForm(editorform, current_distribusi)
|
||||
return RenderDistribusiTemplate(current_distribusi)
|
||||
validate_edit_css_form(editorform, 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)
|
||||
if os.path.exists(newcssfolder):
|
||||
shutil.rmtree(newcssfolder)
|
||||
@ -45,22 +44,22 @@ def ValidateEditCssForm(editorform, current_distribusi):
|
||||
shutil.rmtree(publicfolder)
|
||||
|
||||
if editorform.public.data:
|
||||
MakePublicTheme(editorform, current_distribusi)
|
||||
make_public_theme(editorform, current_distribusi)
|
||||
if editorform.cssfile.data:
|
||||
SaveUploadCssFile(editorform, publicfolder)
|
||||
CopyPublicToUserFolder(editorform, publicfolder, newcssfolder)
|
||||
save_upload_css_file(editorform, publicfolder)
|
||||
copy_public_to_user_folder(editorform, publicfolder, newcssfolder)
|
||||
return
|
||||
else:
|
||||
WriteCssToFile(editorform, publicfolder)
|
||||
write_css_to_file(editorform, publicfolder)
|
||||
|
||||
if editorform.cssfile.data:
|
||||
SaveUploadCssFile(editorform, newcssfolder)
|
||||
save_upload_css_file(editorform, newcssfolder)
|
||||
return
|
||||
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):
|
||||
os.mkdir(newcssfolder)
|
||||
cssfile = editorform.cssfile.data
|
||||
@ -68,38 +67,37 @@ def SaveUploadCssFile(editorform, newcssfolder):
|
||||
cssfile.save(os.path.join(newcssfolder, cssfilename))
|
||||
openfile = open(os.path.join(newcssfolder, cssfilename), "r")
|
||||
cleancss = bleach.clean(openfile.read(), all_styles)
|
||||
cleancss = cleancss.replace('>', '>')
|
||||
cleancss = cleancss.replace(">", ">")
|
||||
openfile.close()
|
||||
cleanfile = open(os.path.join(newcssfolder, cssfilename), "w")
|
||||
cleanfile.write(cleancss)
|
||||
cleanfile.close()
|
||||
|
||||
|
||||
def WriteCssToFile(editorform, newcssfolder):
|
||||
def write_css_to_file(editorform, newcssfolder):
|
||||
if not os.path.exists(newcssfolder):
|
||||
os.mkdir(newcssfolder)
|
||||
|
||||
cssfilename = f"{secure_filename(editorform.cssname.data)}.css"
|
||||
cleancss = bleach.clean(editorform.css.data, all_styles)
|
||||
cleancss = cleancss.replace('>', '>')
|
||||
cleancss = cleancss.replace(">", ">")
|
||||
with open(os.path.join(newcssfolder, cssfilename), "w") as cssfile:
|
||||
cssfile.write(cleancss)
|
||||
cssfile.close
|
||||
|
||||
|
||||
def CopyPublicToUserFolder(editorform, publicfolder, newcssfolder):
|
||||
def copy_public_to_user_folder(editorform, publicfolder, newcssfolder):
|
||||
if not os.path.exists(newcssfolder):
|
||||
os.mkdir(newcssfolder)
|
||||
copycssfile = os.path.join(
|
||||
publicfolder,
|
||||
f"{secure_filename(editorform.cssname.data)}.css"
|
||||
publicfolder, f"{secure_filename(editorform.cssname.data)}.css"
|
||||
)
|
||||
print(f"copying file: {copycssfile}")
|
||||
print(f"to folder: {newcssfolder}")
|
||||
shutil.copy(copycssfile, newcssfolder)
|
||||
|
||||
|
||||
def MakePublicTheme(editorform, current_distribusi):
|
||||
def make_public_theme(editorform, current_distribusi):
|
||||
try:
|
||||
distribusi = Distribusis.query.filter_by(
|
||||
distribusiname=current_distribusi
|
||||
@ -121,12 +119,12 @@ def MakePublicTheme(editorform, current_distribusi):
|
||||
editorform.public.errors.append("Error connecting to the database")
|
||||
|
||||
|
||||
def RenderDistribusiTemplate(current_distribusi):
|
||||
def render_distribusi_template(current_distribusi):
|
||||
uploadform = UploadForm()
|
||||
distribusiform = DistribusiForm()
|
||||
themeform = ThemeForm()
|
||||
publicthemeform = PublicThemeForm()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||
selectorform = SelectorForm()
|
||||
|
||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||
@ -150,11 +148,11 @@ def RenderDistribusiTemplate(current_distribusi):
|
||||
return template
|
||||
|
||||
|
||||
def RenderEditorTemplate(editorform, current_distribusi):
|
||||
htmlplaceholder = HtmlPlaceholder()
|
||||
def render_editor_template(editorform, current_distribusi):
|
||||
html_placeholder = get_html_placeholder()
|
||||
|
||||
cssplaceholder = CssPlaceholder(current_distribusi)
|
||||
editorform.css.data = cssplaceholder
|
||||
css_placeholder = get_css_placeholder(current_distribusi)
|
||||
editorform.css.data = css_placeholder
|
||||
|
||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
||||
@ -163,33 +161,33 @@ def RenderEditorTemplate(editorform, current_distribusi):
|
||||
files_uploaded=files_uploaded,
|
||||
distribusi_live=distribusi_live,
|
||||
editorform=editorform,
|
||||
htmlplaceholder=htmlplaceholder,
|
||||
html_placeholder=html_placeholder,
|
||||
)
|
||||
return template
|
||||
|
||||
|
||||
def CssPlaceholder(current_distribusi):
|
||||
cssplaceholder = "Try out your CSS here"
|
||||
def get_css_placeholder(current_distribusi):
|
||||
css_placeholder = "Try out your CSS here"
|
||||
distribusi = Distribusis.query.filter_by(
|
||||
distribusiname=current_distribusi
|
||||
).first()
|
||||
if distribusi is not None and distribusi.publictheme is not None:
|
||||
cssplaceholder = GetPublicCssFile(distribusi)
|
||||
css_placeholder = get_public_css_file(distribusi)
|
||||
else:
|
||||
with open("themes/editor/placeholder.css") as f:
|
||||
cssplaceholder = f.read()
|
||||
return cssplaceholder
|
||||
css_placeholder = f.read()
|
||||
return css_placeholder
|
||||
|
||||
|
||||
def HtmlPlaceholder():
|
||||
htmlplaceholder = "Write some test HTML here"
|
||||
def get_html_placeholder():
|
||||
html_placeholder = "Write some test HTML here"
|
||||
with open("themes/editor/placeholder.html") as f:
|
||||
htmlplaceholder = f.read()
|
||||
return htmlplaceholder
|
||||
html_placeholder = f.read()
|
||||
return html_placeholder
|
||||
|
||||
|
||||
def GetPublicCssFile(distribusi):
|
||||
cssplaceholder = ""
|
||||
def get_public_css_file(distribusi):
|
||||
css_placeholder = ""
|
||||
publicthemefolder = os.path.join(
|
||||
"themes/publicthemes", distribusi.distribusiname
|
||||
)
|
||||
@ -197,5 +195,5 @@ def GetPublicCssFile(distribusi):
|
||||
if filename.endswith(".css"):
|
||||
cssfile = os.path.join(publicthemefolder, filename)
|
||||
with open(cssfile) as f:
|
||||
cssplaceholder = f.read()
|
||||
return cssplaceholder
|
||||
css_placeholder = f.read()
|
||||
return css_placeholder
|
@ -1,77 +1,89 @@
|
||||
import os
|
||||
import shutil
|
||||
from flask import flash, render_template
|
||||
|
||||
from flask import flash, render_template, redirect, url_for
|
||||
from flask_login import current_user
|
||||
from sqlalchemy.exc import (
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
|
||||
from usermodel import User
|
||||
from distribusimodel import Distribusis
|
||||
from distribusisinfo import DistribusisInfo
|
||||
|
||||
from forms.selectorform import SelectorForm
|
||||
from forms.uploadform import UploadForm
|
||||
from forms.distribusiform import DistribusiForm
|
||||
from forms.themeform import ThemeForm
|
||||
from forms.publicthemeform import PublicThemeForm
|
||||
from app import db
|
||||
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 models.distribusi_model import Distribusis
|
||||
from models.user_model import User
|
||||
|
||||
# UserPengguna
|
||||
from statuspengguna.helper import UserHelper
|
||||
|
||||
from app import db
|
||||
|
||||
|
||||
def DistribusiSelector():
|
||||
def distribusi_selector():
|
||||
uploadform = UploadForm()
|
||||
selectorform = SelectorForm()
|
||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||
current_distribusi = UserHelper.current_distribusi()
|
||||
if selectorform.validate_on_submit():
|
||||
if selectorform.new.data:
|
||||
SelectNewDistribusi()
|
||||
select_new_distribusi()
|
||||
if selectorform.describe.data:
|
||||
return select_describe_distribusi(selectorform.distribusis.data)
|
||||
if selectorform.delete.data:
|
||||
selectorform = DeleteDistribusi(selectorform.distribusis.data)
|
||||
selectorform = delete_distribusi(selectorform.distribusis.data)
|
||||
selectorform.distribusis.choices = (
|
||||
DistribusisInfo.userdistribusinames()
|
||||
DistribusisInfo.user_distribusinames()
|
||||
)
|
||||
if selectorform.update.data:
|
||||
SelectUpdateDistribusi(selectorform.distribusis.data)
|
||||
select_update_distribusi(selectorform.distribusis.data)
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def AutoFillInUploadForm(uploadform, current_distribusi):
|
||||
def auto_fill_in_upload_form(uploadform, current_distribusi):
|
||||
distribusi = Distribusis.query.filter_by(
|
||||
distribusiname=current_distribusi
|
||||
).first()
|
||||
uploadform.sitename.data = distribusi.distribusiname
|
||||
uploadform.sitename.render_kw = {"readonly": True}
|
||||
uploadform.term.data = distribusi.term
|
||||
uploadform.course.data = distribusi.course
|
||||
uploadform.academicyear.data = distribusi.year
|
||||
uploadform.description.data = distribusi.description
|
||||
uploadform.category.data = distribusi.category
|
||||
uploadform.year.data = distribusi.year
|
||||
uploadform.tags.data = distribusi.tags
|
||||
return uploadform
|
||||
|
||||
|
||||
def SelectNewDistribusi():
|
||||
def select_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}")
|
||||
SelectCurrentDistribusi(distribusiname)
|
||||
select_current_distribusi(distribusiname)
|
||||
|
||||
|
||||
def DeleteDistribusi(distribusiname):
|
||||
def delete_distribusi(distribusiname):
|
||||
print(f"delete this distribusi {distribusiname}")
|
||||
selectorform = SelectorForm()
|
||||
try:
|
||||
@ -105,7 +117,7 @@ def DeleteDistribusi(distribusiname):
|
||||
return selectorform
|
||||
|
||||
|
||||
def SelectCurrentDistribusi(distribusiname):
|
||||
def select_current_distribusi(distribusiname):
|
||||
if not current_user.is_authenticated:
|
||||
return
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
@ -117,35 +129,35 @@ def SelectCurrentDistribusi(distribusiname):
|
||||
flash("An error occured !", "danger")
|
||||
|
||||
|
||||
def DistribusiSelected():
|
||||
def distribusi_selected():
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
if user.currentdistribusi is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def SelectorVisible():
|
||||
def selector_visible():
|
||||
has_distribusi = UserHelper.has_distribusi()
|
||||
distribusi_selected = DistribusiSelected()
|
||||
if distribusi_selected:
|
||||
is_distribusi_selected = distribusi_selected()
|
||||
if is_distribusi_selected:
|
||||
return False
|
||||
if not has_distribusi:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def RenderDistribusiTemplate(selectorform, uploadform, current_distribusi):
|
||||
def render_distribusi_template(selectorform, uploadform, current_distribusi):
|
||||
distribusiform = DistribusiForm()
|
||||
themeform = ThemeForm()
|
||||
publicthemeform = PublicThemeForm()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
|
||||
distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
|
||||
|
||||
# because the user has chosen to update his distribusi, we assume
|
||||
# no selected css.
|
||||
css_selected = False
|
||||
selectorvisible = SelectorVisible()
|
||||
selectorvisible = selector_visible()
|
||||
limit_reached = UserHelper.distribusi_limit_reached()
|
||||
template = render_template(
|
||||
"distribusi.html",
|
@ -1,43 +1,38 @@
|
||||
import os
|
||||
import shutil
|
||||
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!
|
||||
from distribusi.cli import build_argparser
|
||||
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()
|
||||
current_distribusi = UserHelper.current_distribusi()
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
@ -47,18 +42,19 @@ def DistribusiWorkflow():
|
||||
|
||||
if distribusiform.validate_on_submit():
|
||||
userfolder = os.path.join("stash", distribusi.distribusiname)
|
||||
cssfile = GetCssFile(distribusi)
|
||||
UnzipDistribusiFiles(distribusi, userfolder)
|
||||
CleanUpDistribusiFiles(userfolder)
|
||||
RunDistribusi(userfolder, cssfile)
|
||||
SetDistribusiToVisible(distribusi, user)
|
||||
DeleteCssFile(cssfile)
|
||||
cssfile = get_css_file(distribusi)
|
||||
unzip_distribusi_files(distribusi, userfolder)
|
||||
clean_up_distribusi_files(userfolder)
|
||||
add_distribusi_files_to_db(distribusi.distribusiname)
|
||||
run_distribusi(userfolder, cssfile)
|
||||
set_distribusi_to_visible(distribusi, user)
|
||||
delete_css_file(cssfile)
|
||||
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)
|
||||
unzipfile = os.path.join(userfolder, zipfilename)
|
||||
|
||||
@ -70,15 +66,15 @@ def UnzipDistribusiFiles(distribusi, userfolder):
|
||||
os.remove(os.path.join(userfolder, zipfilename))
|
||||
|
||||
|
||||
def CleanUpDistribusiFiles(userfolder):
|
||||
def clean_up_distribusi_files(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):
|
||||
fullpath = os.path.join(path, filename)
|
||||
if filename.startswith('.'):
|
||||
if filename.startswith("."):
|
||||
if os.path.isdir(fullpath):
|
||||
shutil.rmtree(fullpath)
|
||||
else:
|
||||
@ -86,10 +82,10 @@ def RemoveMacFolders(path):
|
||||
if filename == "__MACOSX":
|
||||
shutil.rmtree(fullpath)
|
||||
if os.path.isdir(fullpath):
|
||||
RemoveMacFolders(fullpath)
|
||||
remove_mac_folders(fullpath)
|
||||
|
||||
|
||||
def GetCssFile(distribusi):
|
||||
def get_css_file(distribusi):
|
||||
cssfile = ""
|
||||
cssfolder = os.path.join("themes/userthemes", distribusi.distribusiname)
|
||||
if os.path.exists(cssfolder):
|
||||
@ -99,13 +95,14 @@ def GetCssFile(distribusi):
|
||||
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()
|
||||
args = parser.parse_args(["--menu-with-index", "-s", cssfile])
|
||||
args = parser.parse_args(["-t", "-a", "--menu-with-index", "-s", cssfile])
|
||||
distribusify(args, userfolder)
|
||||
|
||||
|
||||
def SetDistribusiToVisible(distribusi, user):
|
||||
def set_distribusi_to_visible(distribusi, user):
|
||||
try:
|
||||
distribusi.visible = True
|
||||
user.currentdistribusi = None
|
||||
@ -115,19 +112,19 @@ def SetDistribusiToVisible(distribusi, user):
|
||||
flash("Unknown error occured!")
|
||||
|
||||
|
||||
def DeleteCssFile(cssfile):
|
||||
def delete_css_file(cssfile):
|
||||
if os.path.exists(cssfile):
|
||||
os.remove(cssfile)
|
||||
|
||||
|
||||
def RenderDistribusiTemplate(distribusiform, current_distribusi):
|
||||
def render_distribusi_template(distribusiform, current_distribusi):
|
||||
uploadform = UploadForm()
|
||||
themeform = ThemeForm()
|
||||
publicthemeform = PublicThemeForm()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||
selectorform = SelectorForm()
|
||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||
selectorvisible = SelectorVisible()
|
||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||
selectorvisible = selector_visible()
|
||||
|
||||
files_uploaded = UserHelper.is_zip_uploaded(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 usermodel import User
|
||||
from distribusimodel import Distribusis
|
||||
from models.distribusi_model import Distribusis
|
||||
from models.user_model import User
|
||||
|
||||
|
||||
class DistribusisInfo:
|
||||
def userdistribusinames():
|
||||
def user_distribusinames():
|
||||
distribusinames = []
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
for distribusi in Distribusis.query.filter_by(userid=user.id).all():
|
||||
distribusinames.append(distribusi.distribusiname)
|
||||
return distribusinames
|
||||
|
||||
def publicthemes():
|
||||
def public_themes():
|
||||
publicthemes = []
|
||||
distribusis = Distribusis.query.filter(
|
||||
Distribusis.publictheme.isnot(None)
|
||||
@ -27,12 +27,12 @@ made by {user.username}""",
|
||||
publicthemes.append(publictheme)
|
||||
return publicthemes
|
||||
|
||||
def visibledistribusis():
|
||||
def visible_distribusis():
|
||||
distribusis = Distribusis.query.filter(
|
||||
Distribusis.visible.isnot(False)
|
||||
).all()
|
||||
return distribusis
|
||||
|
||||
def getuserdistribusis(useremail):
|
||||
def get_user_distribusis(useremail):
|
||||
user = User.query.filter_by(email=useremail).first()
|
||||
return Distribusis.query.filter_by(userid=user.id).all()
|
@ -1,15 +1,15 @@
|
||||
"""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.file import FileAllowed, FileField, FileSize
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
StringField,
|
||||
SubmitField,
|
||||
TextAreaField,
|
||||
validators,
|
||||
)
|
||||
from wtforms.validators import Length
|
||||
|
||||
|
||||
class EditorForm(FlaskForm):
|
@ -1,9 +1,7 @@
|
||||
"""Form object declaration."""
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
RadioField,
|
||||
SubmitField,
|
||||
)
|
||||
from wtforms import RadioField, SubmitField
|
||||
|
||||
|
||||
class PublicThemeForm(FlaskForm):
|
@ -1,5 +1,5 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField, SelectField
|
||||
from wtforms import SelectField, SubmitField
|
||||
|
||||
|
||||
class SelectorForm(FlaskForm):
|
||||
@ -9,4 +9,6 @@ class SelectorForm(FlaskForm):
|
||||
|
||||
update = SubmitField("update")
|
||||
|
||||
describe = SubmitField("describe")
|
||||
|
||||
delete = SubmitField("delete")
|
@ -1,9 +1,7 @@
|
||||
"""Form object declaration."""
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
RadioField,
|
||||
SubmitField,
|
||||
)
|
||||
from wtforms import RadioField, SubmitField
|
||||
|
||||
|
||||
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 {
|
||||
width: 30%;
|
||||
border: 3px solid #E0B0FF;
|
||||
border: 3px solid #9de457;
|
||||
border-style: outset;
|
||||
margin-right: 1em;
|
||||
margin-left: 0;
|
||||
@ -31,7 +31,7 @@ textarea {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
min-height: 250px;
|
||||
background: #E0B0FF;
|
||||
background: #9de457;
|
||||
outline: none;
|
||||
font-family: Courier, sans-serif;
|
||||
font-size: 16px;
|
||||
@ -45,6 +45,6 @@ iframe {
|
||||
height: 30em;
|
||||
}
|
||||
#html {
|
||||
background-color: #60337F;
|
||||
color: lightgrey;
|
||||
background-color: #9de457;
|
||||
color: black;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<div id="buttons">
|
||||
<div class="overview">
|
||||
@ -20,27 +20,28 @@
|
||||
<div id="mainworkflow">
|
||||
{% if selectorvisible %}
|
||||
{% block selector %}
|
||||
{% include "distribusiworkflow/selector.html" %}
|
||||
{% include "distribusi_workflow/selector.html" %}
|
||||
{% endblock selector%}
|
||||
{% else %}
|
||||
{% block upload %}
|
||||
{% include "distribusiworkflow/upload.html" %}
|
||||
{% include "distribusi_workflow/upload.html" %}
|
||||
{% endblock upload%}
|
||||
<img src="{{ url_for('static', filename='svg/arrow_1.svg')}}" />
|
||||
{% block theme %}
|
||||
{% include "distribusiworkflow/theme.html" %}
|
||||
{% include "distribusi_workflow/theme.html" %}
|
||||
{% endblock theme%}
|
||||
<img src="{{ url_for('static', filename='svg/arrow_2.svg')}}" />
|
||||
{% block editcss %}
|
||||
{% include "distribusiworkflow/editcss.html" %}
|
||||
{% include "distribusi_workflow/editcss.html" %}
|
||||
{% endblock editcss%}
|
||||
<img src="{{ url_for('static', filename='svg/arrow_3.svg')}}" />
|
||||
{% block launch %}
|
||||
{% include "distribusiworkflow/launch.html" %}
|
||||
{% include "distribusi_workflow/launch.html" %}
|
||||
{% endblock launch%}
|
||||
{%endif%}
|
||||
</div>
|
||||
{% if css_selected %}
|
||||
<p id="cssSelected" hidden>css selected</p>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.static', filename='css/distribusikan.css')}}">
|
||||
{% endblock main %}
|
@ -1,10 +1,10 @@
|
||||
<div id="edit" class="workflow">
|
||||
<h2>Step 3: Edit Custom CSS (Optional)</h2>
|
||||
{% 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 %}
|
||||
<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.
|
||||
</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
|
||||
your content public. <span class="tooltiptext">Distribusi will unpack your zip file and turn it into a website!
|
||||
</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 }}
|
||||
{% if files_uploaded or distribusi_live %}
|
||||
<fieldset class="button required">
|
@ -1,7 +1,7 @@
|
||||
<div id="distribusi" class="workflow">
|
||||
<h2>Welcome back to your Distribusi</h2>
|
||||
<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 }}
|
||||
<fieldset class="required">
|
||||
{{ selectorform.distribusis.label }}
|
||||
@ -18,6 +18,13 @@
|
||||
{{ selectorform.update }}
|
||||
</fieldset>
|
||||
<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>
|
||||
This will delete your distribusi site.
|
||||
<strong> This action cannot be undone! </strong>
|
||||
@ -29,6 +36,7 @@
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<hr>
|
||||
<div class="new_divider"></div>
|
||||
{% if limit_reached %}
|
||||
<p>You have reached your limit of distribusi websites</p>
|
||||
{% else %}
|
@ -4,7 +4,7 @@
|
||||
step 3.</p>
|
||||
<p>Don't forget to press Save</p>
|
||||
<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 }}
|
||||
<fieldset class="required">
|
||||
{{ themeform.theme.label }}
|
||||
@ -16,14 +16,14 @@
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<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.
|
||||
</p>
|
||||
<a href="#upload">Go to Step 1</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
<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 }}
|
||||
<fieldset id="publicthemes" class="required">
|
||||
{{ publicthemeform.publicthemes.label }}
|
||||
@ -35,7 +35,7 @@
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<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.
|
||||
</p>
|
||||
<a href="#upload">Go to Step 1</a>
|
@ -1,7 +1,7 @@
|
||||
<div id="upload" class="workflow">
|
||||
<h2>Step 1: Upload</h2>
|
||||
<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 }}
|
||||
<fieldset class="required">
|
||||
{{ uploadform.sitename.label }}
|
||||
@ -11,28 +11,26 @@
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<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">
|
||||
{{ uploadform.academicyear }}
|
||||
{% for message in uploadform.academicyear.errors %}
|
||||
{{ uploadform.year }}
|
||||
{% for message in uploadform.year.errors %}
|
||||
<div class="error">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="required">
|
||||
{{ uploadform.term.label }}
|
||||
{{ uploadform.category.label }}
|
||||
<div class="selector-style">
|
||||
{{ uploadform.term }}
|
||||
{% for message in uploadform.term.errors %}
|
||||
<div class="error">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="required">
|
||||
{{ uploadform.course.label }}
|
||||
<div class="selector-style">
|
||||
{{ uploadform.course }}
|
||||
{% for message in uploadform.course.errors %}
|
||||
{{ uploadform.category }}
|
||||
{% for message in uploadform.category.errors %}
|
||||
<div class="error">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
@ -1,11 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% 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="editarea editor">
|
||||
<fieldset class="required">
|
||||
<textarea id="html" placeholder="Write some test HTML here" readonly>
|
||||
{{htmlplaceholder}}
|
||||
{{html_placeholder}}
|
||||
</textarea>
|
||||
</fieldset>
|
||||
</div>
|
||||
@ -54,6 +54,6 @@
|
||||
</form>
|
||||
<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>
|
||||
{% endblock main %}
|
@ -1,49 +1,47 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
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 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()
|
||||
publicthemeform = PublicThemeForm()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||
current_distribusi = UserHelper.current_distribusi()
|
||||
if themeform.validate_on_submit():
|
||||
copycssfile = os.path.join(
|
||||
"themes",
|
||||
f"{themeform.theme.data}.css",
|
||||
)
|
||||
MoveCssToUserFolder(current_distribusi, copycssfile)
|
||||
move_css_to_user_folder(current_distribusi, copycssfile)
|
||||
if publicthemeform.validate_on_submit():
|
||||
copycssfile = os.path.join(
|
||||
"themes/publicthemes/",
|
||||
f"{publicthemeform.publicthemes.data}.css",
|
||||
)
|
||||
MoveCssToUserFolder(current_distribusi, copycssfile)
|
||||
return RenderDistribusiTemplate(
|
||||
themeform,
|
||||
publicthemeform,
|
||||
current_distribusi
|
||||
move_css_to_user_folder(current_distribusi, copycssfile)
|
||||
return render_distribusi_template(
|
||||
themeform, 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)
|
||||
if not os.path.exists(newcssfolder):
|
||||
os.mkdir(newcssfolder)
|
||||
shutil.copy(copycssfile, newcssfolder)
|
||||
|
||||
|
||||
def RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi):
|
||||
def render_distribusi_template(themeform, publicthemeform, current_distribusi):
|
||||
uploadform = UploadForm()
|
||||
distribusiform = DistribusiForm()
|
||||
selectorform = SelectorForm()
|
@ -1,25 +1,25 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from flask import flash
|
||||
from flask_login import current_user
|
||||
from sqlalchemy.exc import (
|
||||
IntegrityError,
|
||||
InvalidRequestError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
IntegrityError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
|
||||
from app import db
|
||||
|
||||
from usermodel import User
|
||||
from distribusimodel import Distribusis
|
||||
|
||||
from distribusikan.distribusi_selector import select_current_distribusi
|
||||
from distribusikan.forms.uploadform import UploadForm
|
||||
from models.distribusi_model import Distribusis
|
||||
from models.user_model import User
|
||||
from statuspengguna.helper import UserHelper
|
||||
from distribusiselector import SelectCurrentDistribusi
|
||||
from forms.uploadform import UploadForm
|
||||
|
||||
|
||||
def UploadNewDistribusi(uploadfolder):
|
||||
def upload_new_distribusi(uploadfolder):
|
||||
uploadform = UploadForm()
|
||||
if uploadform.validate_on_submit():
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
@ -27,9 +27,9 @@ def UploadNewDistribusi(uploadfolder):
|
||||
newdistribusi = Distribusis(
|
||||
distribusiname=uploadform.sitename.data,
|
||||
userid=user.id,
|
||||
term=uploadform.term.data,
|
||||
course=uploadform.course.data,
|
||||
year=uploadform.academicyear.data,
|
||||
category=uploadform.category.data,
|
||||
description=uploadform.description.data,
|
||||
year=uploadform.year.data,
|
||||
tags=uploadform.tags.data,
|
||||
)
|
||||
user.currentdistribusi = uploadform.sitename.data
|
||||
@ -47,7 +47,7 @@ def UploadNewDistribusi(uploadfolder):
|
||||
uploadform.sitename.errors.append("Something went wrong!")
|
||||
flash("Something went wrong!", "danger")
|
||||
return uploadform
|
||||
SelectCurrentDistribusi(newdistribusi.distribusiname)
|
||||
select_current_distribusi(newdistribusi.distribusiname)
|
||||
zipfilename = "{}.zip".format(newdistribusi.distribusiname)
|
||||
zipfile = uploadform.zipfile.data
|
||||
zipfile.save(os.path.join(uploadfolder, zipfilename))
|
||||
@ -63,7 +63,7 @@ def UploadNewDistribusi(uploadfolder):
|
||||
return uploadform
|
||||
|
||||
|
||||
def UploadUpdatedFiles(uploadfolder):
|
||||
def upload_updates_files(uploadfolder):
|
||||
uploadform = UploadForm()
|
||||
if uploadform.validate_on_submit():
|
||||
try:
|
||||
@ -71,9 +71,9 @@ def UploadUpdatedFiles(uploadfolder):
|
||||
distribusi = Distribusis.query.filter_by(
|
||||
distribusiname=current_distribusi
|
||||
).first()
|
||||
distribusi.term = uploadform.term.data
|
||||
distribusi.course = uploadform.course.data
|
||||
distribusi.year = uploadform.academicyear.data
|
||||
distribusi.category = uploadform.category.data
|
||||
distribusi.description = (uploadform.description.data,)
|
||||
distribusi.year = uploadform.year.data
|
||||
distribusi.tags = uploadform.tags.data
|
||||
distribusi.visible = False
|
||||
db.session.commit()
|
@ -1,33 +1,34 @@
|
||||
from flask import render_template
|
||||
|
||||
from forms.distribusiform import DistribusiForm
|
||||
from forms.themeform import ThemeForm
|
||||
from forms.publicthemeform import PublicThemeForm
|
||||
from forms.selectorform import SelectorForm
|
||||
from app import APP
|
||||
from distribusikan.distribusi_selector import selector_visible
|
||||
from distribusikan.distribusis_info import DistribusisInfo
|
||||
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
|
||||
from statuspengguna.helper import UserHelper
|
||||
|
||||
from upload import UploadNewDistribusi, UploadUpdatedFiles
|
||||
from distribusisinfo import DistribusisInfo
|
||||
from distribusiselector import SelectorVisible
|
||||
|
||||
|
||||
def UploadPage(uploadfolder):
|
||||
def upload_page():
|
||||
"render upload page section of distribusi workflow"
|
||||
uploadfolder = APP.config["UPLOAD_FOLDER"]
|
||||
distribusiform = DistribusiForm()
|
||||
themeform = ThemeForm()
|
||||
publicthemeform = PublicThemeForm()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||
publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
|
||||
selectorform = SelectorForm()
|
||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||
selectorvisible = SelectorVisible()
|
||||
selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
|
||||
selectorvisible = selector_visible()
|
||||
|
||||
current_distribusi = UserHelper.current_distribusi()
|
||||
if current_distribusi == "new" or UserHelper.has_distribusi() is False:
|
||||
uploadform = UploadNewDistribusi(uploadfolder)
|
||||
uploadform = upload_new_distribusi(uploadfolder)
|
||||
else:
|
||||
uploadform = UploadUpdatedFiles(uploadfolder)
|
||||
uploadform = upload_updates_files(uploadfolder)
|
||||
|
||||
files_uploaded = UserHelper.is_zip_uploaded(uploadform.sitename.data)
|
||||
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.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger('alembic.env')
|
||||
logger = logging.getLogger("alembic.env")
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
config.set_main_option(
|
||||
'sqlalchemy.url',
|
||||
str(current_app.extensions['migrate'].db.get_engine().url).replace(
|
||||
'%', '%%'))
|
||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||
"sqlalchemy.url",
|
||||
str(current_app.extensions["migrate"].db.get_engine().url).replace(
|
||||
"%", "%%"
|
||||
),
|
||||
)
|
||||
target_metadata = current_app.extensions["migrate"].db.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
@ -65,20 +67,20 @@ def run_migrations_online():
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
if getattr(config.cmd_opts, "autogenerate", False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
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:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args
|
||||
**current_app.extensions["migrate"].configure_args,
|
||||
)
|
||||
|
||||
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)
|
||||
distribusiname = db.Column(db.String(300), nullable=False, unique=True)
|
||||
userid = db.Column(db.Integer, db.ForeignKey("users.id"))
|
||||
term = db.Column(db.String(5), nullable=False, unique=False)
|
||||
course = db.Column(db.String(500), nullable=True, unique=False)
|
||||
|
||||
# Academic year eg:2020-2021, so no need for a Datetime object
|
||||
category = db.Column(db.String(500), 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)
|
||||
publictheme = db.Column(db.String(300), unique=True, nullable=True)
|
||||
|
||||
visible = db.Column(db.Boolean, default=False)
|
||||
|
||||
def __repr__(self):
|
@ -1,6 +1,7 @@
|
||||
from app import db
|
||||
from flask_login import UserMixin
|
||||
|
||||
from app import db
|
||||
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
"""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)
|
||||
resethash = db.Column(db.String(300), nullable=True, unique=True)
|
||||
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)
|
||||
|
||||
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"""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
session,
|
||||
send_from_directory,
|
||||
Blueprint,
|
||||
redirect,
|
||||
render_template,
|
||||
send_from_directory,
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import (
|
||||
logout_user,
|
||||
login_required,
|
||||
current_user,
|
||||
)
|
||||
from flask_mail import Mail
|
||||
from flask_login import current_user, login_required, logout_user
|
||||
from flask_wtf.csrf import CSRFError
|
||||
from app import create_app, login_manager
|
||||
|
||||
from usermodel import User
|
||||
from distribusimodel import Distribusis
|
||||
|
||||
# Use upload form to populate filters
|
||||
from forms.uploadform import UploadForm
|
||||
|
||||
# Interface! these are seperate files in main folder
|
||||
from adminpage import AdminPage
|
||||
from editor import Editor
|
||||
from themeselector import ThemeSelector
|
||||
from distribusiworkflow import DistribusiWorkflow
|
||||
from distribusiselector import DistribusiSelector
|
||||
from uploadpage import UploadPage
|
||||
|
||||
# UserPengguna
|
||||
from admin import is_adminuser
|
||||
from admin_page.admin_page import admin_page
|
||||
from app import create_app, login_manager, db
|
||||
from describer.describe_files import describer
|
||||
from distribusikan.distribusikan import distribusikan
|
||||
from distribusikan.distribusis_info import DistribusisInfo
|
||||
from distribusikan.forms.uploadform import UploadForm
|
||||
from models.distribusi_model import Distribusis
|
||||
from models.user_model import User
|
||||
from statuspengguna.forgotpassword import forgot_password
|
||||
from statuspengguna.helper import UserHelper
|
||||
from statuspengguna.loginuser import LoginUser
|
||||
from statuspengguna.registeruser import RegisterUser
|
||||
from statuspengguna.forgotpassword import ForgotPassword
|
||||
from statuspengguna.resetpassword import ResetPassword
|
||||
|
||||
# Distribusi Information
|
||||
from distribusisinfo import DistribusisInfo
|
||||
from statuspengguna.loginuser import login_section
|
||||
from statuspengguna.registeruser import register_user
|
||||
from search.search import searchpages
|
||||
from search.search_index import init_search_index
|
||||
|
||||
APP = create_app()
|
||||
stash_page = Blueprint("stash_page", __name__, static_folder="stash")
|
||||
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
|
||||
@ -56,32 +51,28 @@ def session_handler():
|
||||
@APP.route("/")
|
||||
def index():
|
||||
UserHelper.reset_user_state()
|
||||
# http://localhost:5000/themes/publicthemes/RomeroTape/blueskies.css
|
||||
uploadform = UploadForm()
|
||||
distribusis = DistribusisInfo.visibledistribusis()
|
||||
distribusis = DistribusisInfo.visible_distribusis()
|
||||
distribusisindex = {}
|
||||
for distribusi in distribusis:
|
||||
user = User.query.filter_by(id=distribusi.userid).first()
|
||||
singledistribusi = {
|
||||
"username": user.username,
|
||||
"publictheme": distribusi.publictheme,
|
||||
"term": distribusi.term,
|
||||
"course": distribusi.course,
|
||||
"category": distribusi.category,
|
||||
"year": distribusi.year,
|
||||
"tags": distribusi.tags.split(","),
|
||||
}
|
||||
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(
|
||||
"index.html",
|
||||
"base/index.html",
|
||||
distribusisindex=distribusisindex,
|
||||
years=years,
|
||||
terms=terms,
|
||||
courses=courses,
|
||||
categories=categories,
|
||||
adminuser=adminuser,
|
||||
)
|
||||
return template
|
||||
@ -89,26 +80,7 @@ def index():
|
||||
|
||||
@APP.route("/help")
|
||||
def help():
|
||||
return render_template("help.html")
|
||||
|
||||
|
||||
@APP.route("/distribusi", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def distribusi():
|
||||
return DistribusiWorkflow()
|
||||
|
||||
|
||||
@APP.route("/upload", methods=["POST"])
|
||||
@login_required
|
||||
def upload():
|
||||
uploadfolder = APP.config["UPLOAD_FOLDER"]
|
||||
return UploadPage(uploadfolder)
|
||||
|
||||
|
||||
@APP.route("/theme", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def theme():
|
||||
return ThemeSelector()
|
||||
return render_template("base/help.html")
|
||||
|
||||
|
||||
@APP.route("/publicthemes/<path>")
|
||||
@ -116,35 +88,14 @@ def publicthemes(path):
|
||||
distribusi = Distribusis.query.filter_by(distribusiname=path).first()
|
||||
publicthemefolder = f"publicthemes/{distribusi.distribusiname}/"
|
||||
cssfile = f"{publicthemefolder}/{distribusi.publictheme}.css"
|
||||
print(cssfile)
|
||||
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")
|
||||
def shortstashurl():
|
||||
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")
|
||||
@login_required
|
||||
def logout():
|
||||
@ -152,26 +103,6 @@ def logout():
|
||||
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)
|
||||
def handle_csrf_error(e):
|
||||
return render_template("csrf_error.html", reason=e.description), 400
|
||||
@ -179,14 +110,7 @@ def handle_csrf_error(e):
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(int(user_id))
|
||||
|
||||
|
||||
def isadminuser():
|
||||
if not current_user.is_authenticated:
|
||||
return False
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
return user.admin
|
||||
return db.session.get(User, int(user_id))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* Dropdown Button */
|
||||
/* for sorting on Academicyear, Term, Course
|
||||
/* for sorting on year and category
|
||||
*/
|
||||
button {
|
||||
background-color: #E0B0FF;
|
||||
background-color: #9de457;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
}
|
||||
@ -26,7 +26,7 @@ button {
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #E0B0FF;
|
||||
background-color: #9de457;
|
||||
min-width: 120px;
|
||||
border: 2px solid;
|
||||
z-index: 1;
|
||||
|
@ -4,7 +4,7 @@
|
||||
max-width: 20em;
|
||||
position: relative;
|
||||
border: none;
|
||||
background: #E0B0FF;
|
||||
background: #9de457;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -18,7 +18,7 @@
|
||||
max-width: 20em;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: #E0B0FF;
|
||||
background-color: #9de457;
|
||||
background-image: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
@ -41,7 +41,7 @@
|
||||
}
|
||||
select.selector option{
|
||||
color: white;
|
||||
background-color: #60337F;
|
||||
background-color: #9de457;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@ -49,5 +49,5 @@ select.selector option{
|
||||
outline: none;
|
||||
}
|
||||
.selector-style select option:hover {
|
||||
background: #60337F;
|
||||
background: #9de457;
|
||||
}
|
||||
|
@ -2,64 +2,17 @@ body
|
||||
{
|
||||
font-family: monospace, monospace;
|
||||
font-size: 15px;
|
||||
background-color: #272a33;
|
||||
color:#E0B0FF;
|
||||
background-color: #fdfdfd;
|
||||
color:#091411;
|
||||
word-wrap: break-word;
|
||||
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
|
||||
{
|
||||
width: 30em;
|
||||
width: 40em;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
@ -69,7 +22,16 @@ div#upload form {
|
||||
#distribusi-index {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.description > textarea {
|
||||
width: 100%;
|
||||
height: 10em;
|
||||
resize: none;
|
||||
}
|
||||
textarea#description {
|
||||
width: 100%;
|
||||
height: 20em;
|
||||
resize: none;
|
||||
}
|
||||
div#buttons{
|
||||
position: fixed;
|
||||
top: 0.5em;
|
||||
@ -82,13 +44,13 @@ div#buttons{
|
||||
|
||||
div#buttons .distribusi input{
|
||||
border: none;
|
||||
background: #fff600;
|
||||
background: #9de457;
|
||||
text-decoration: none;
|
||||
margin: 0.2em;
|
||||
}
|
||||
div#buttons .distribusi input:hover{
|
||||
background: #ffbf00;
|
||||
|
||||
background: #091411;
|
||||
color: #6df2cc;
|
||||
}
|
||||
fieldset.required {
|
||||
border: none;
|
||||
@ -101,21 +63,15 @@ fieldset.required > ul {
|
||||
fieldset.required > ul > li{
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
fieldset.tagfield > input {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
#publicthemes > ul {
|
||||
max-height: 20em;
|
||||
overflow: auto;
|
||||
}
|
||||
#publicthemes > ul > li{
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
input {
|
||||
border: none;
|
||||
background: #E0B0FF;
|
||||
background: #9de457;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -124,7 +80,9 @@ input {
|
||||
}
|
||||
|
||||
input:hover {
|
||||
background: #60337F;
|
||||
background: #091411;
|
||||
color: #6df2cc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="submit"]:disabled:hover,
|
||||
@ -144,37 +102,11 @@ input[type="submit"]:disabled:focus {
|
||||
background-color: #F92020;
|
||||
}
|
||||
|
||||
#update {
|
||||
#update, #describe {
|
||||
color: black;
|
||||
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 */
|
||||
#fancyboi::before {
|
||||
content: "$ ";
|
||||
@ -195,7 +127,7 @@ a:active {
|
||||
white-space: nowrap;
|
||||
animation: reveal 4s linear;
|
||||
text-overflow: "█";
|
||||
background-color: #2D3039;
|
||||
background-color: #9de457;
|
||||
}
|
||||
#fancyboi::after {
|
||||
content: "█";
|
||||
@ -205,7 +137,7 @@ a:active {
|
||||
|
||||
div.maincontent{
|
||||
width: 55%;
|
||||
border: 3px #E0B0FF;
|
||||
border: 3px #9de457;
|
||||
margin-top: 0.5em;
|
||||
padding: 0.5em;
|
||||
border-style: outset;
|
||||
@ -244,8 +176,6 @@ div.maincontent{
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -60px;
|
||||
|
||||
/* Fade in tooltip - takes 1 second to go from 0% to 100% opac: */
|
||||
opacity: 0;
|
||||
transition: opacity 2s;
|
||||
}
|
||||
@ -260,7 +190,7 @@ div.maincontent{
|
||||
color: black;
|
||||
padding: 1em;
|
||||
box-sizing: border-box;
|
||||
background: #E0B0FF;
|
||||
background: #9de457;
|
||||
outline: none;
|
||||
font-family: Courier, sans-serif;
|
||||
font-size: 16px;
|
||||
@ -268,11 +198,11 @@ div.maincontent{
|
||||
/*
|
||||
Project colors so far.
|
||||
light
|
||||
#E0B0FF
|
||||
#9de457
|
||||
medium
|
||||
#d28cff
|
||||
dark
|
||||
#60337F
|
||||
#9de457
|
||||
|
||||
background dark
|
||||
#2D3039
|
||||
|
@ -24,9 +24,8 @@ function filterSelection(c, name, id) {
|
||||
}
|
||||
|
||||
function resetDropDownButtons(){
|
||||
document.getElementById("Academicyear").innerText = "Academic year";
|
||||
document.getElementById("Term").innerText = "Term";
|
||||
document.getElementById("Course").innerText = "Course";
|
||||
document.getElementById("Year").innerText = "Year";
|
||||
document.getElementById("Category").innerText = "Category";
|
||||
allactivebuttons = document.getElementsByClassName("activebtn");
|
||||
for(var i = 0;allactivebuttons.length; i++) {
|
||||
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 uuid import uuid1
|
||||
|
||||
from flask import Blueprint, render_template
|
||||
from flask_mail import Mail, Message
|
||||
from sqlalchemy.exc import (
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
from flask import render_template
|
||||
from flask_mail import Message
|
||||
|
||||
from usermodel import User
|
||||
from forms.forgotpasswordform import ForgotPasswordForm
|
||||
from app import db
|
||||
from app import db, get_app
|
||||
from statuspengguna.forms.forgotpasswordform import ForgotPasswordForm
|
||||
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):
|
||||
|
@ -1,12 +1,8 @@
|
||||
"""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 wtforms import StringField, SubmitField, validators
|
||||
from wtforms.validators import Email, Length
|
||||
|
||||
|
||||
class ForgotPasswordForm(FlaskForm):
|
@ -1,13 +1,8 @@
|
||||
"""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 wtforms import PasswordField, StringField, SubmitField, validators
|
||||
from wtforms.validators import Email, Length
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
@ -1,22 +1,13 @@
|
||||
"""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 wtforms import PasswordField, StringField, SubmitField, validators
|
||||
from wtforms.validators import Email, EqualTo, Length
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
"""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:",
|
||||
validators=[validators.InputRequired(), Length(3, 150)],
|
||||
@ -27,9 +18,8 @@ class RegisterForm(FlaskForm):
|
||||
validators=[
|
||||
validators.InputRequired(),
|
||||
Email(),
|
||||
Length(6, 64),
|
||||
hremail,
|
||||
]
|
||||
Length(6, 128),
|
||||
],
|
||||
)
|
||||
|
||||
password = PasswordField(
|
@ -1,12 +1,8 @@
|
||||
"""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 wtforms import PasswordField, SubmitField, validators
|
||||
from wtforms.validators import EqualTo, Length
|
||||
|
||||
|
||||
class ResetPasswordForm(FlaskForm):
|
@ -1,17 +1,18 @@
|
||||
import os
|
||||
from flask_login import current_user
|
||||
|
||||
from flask import flash
|
||||
from flask_login import current_user
|
||||
from sqlalchemy.exc import (
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
|
||||
from usermodel import User
|
||||
from distribusimodel import Distribusis
|
||||
from distribusisinfo import DistribusisInfo
|
||||
from app import db
|
||||
from distribusikan.distribusis_info import DistribusisInfo
|
||||
from models.distribusi_model import Distribusis
|
||||
from models.user_model import User
|
||||
|
||||
|
||||
class UserHelper:
|
||||
@ -75,11 +76,10 @@ class UserHelper:
|
||||
|
||||
def distribusi_limit_reached():
|
||||
user = User.query.filter_by(email=current_user.email).first()
|
||||
distribusiamount = len(DistribusisInfo.getuserdistribusis(user.email))
|
||||
if user.tutor and distribusiamount > 14:
|
||||
print("tutor already has 15 distribusis")
|
||||
return True
|
||||
if not user.tutor and distribusiamount > 4:
|
||||
print("user already has 5 distribusis")
|
||||
distribusiamount = len(
|
||||
DistribusisInfo.get_user_distribusis(user.email)
|
||||
)
|
||||
if distribusiamount > 19:
|
||||
print("user already has 20 distribusis")
|
||||
return True
|
||||
return False
|
||||
|
@ -1,15 +1,29 @@
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
request,
|
||||
flash,
|
||||
url_for,
|
||||
Blueprint,
|
||||
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_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():
|
||||
@ -25,7 +39,9 @@ def LoginUser():
|
||||
flash("Logged in successfully.", "success")
|
||||
next = request.args.get("next")
|
||||
if next is not None and not is_safe_url(next): # noqa: F821
|
||||
print(next)
|
||||
return abort(400)
|
||||
print("index")
|
||||
return redirect(next or url_for("index"))
|
||||
else:
|
||||
flash("Invalid email or password!", "danger")
|
||||
|
@ -1,22 +1,30 @@
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
flash,
|
||||
url_for,
|
||||
)
|
||||
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||
from flask_bcrypt import generate_password_hash
|
||||
from flask_login import login_user
|
||||
from sqlalchemy.exc import (
|
||||
IntegrityError,
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
IntegrityError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
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 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():
|
||||
|
@ -1,23 +1,32 @@
|
||||
from datetime import datetime
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
flash,
|
||||
url_for,
|
||||
)
|
||||
|
||||
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||
from flask_bcrypt import generate_password_hash
|
||||
from flask_login import login_user
|
||||
from sqlalchemy.exc import (
|
||||
IntegrityError,
|
||||
DataError,
|
||||
DatabaseError,
|
||||
DataError,
|
||||
IntegrityError,
|
||||
InterfaceError,
|
||||
InvalidRequestError,
|
||||
)
|
||||
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 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):
|
||||
|
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 %}
|
||||
<div id="mainworkflow">
|
||||
<div class="workflow">
|
||||
<div class="login">
|
||||
<h2>Forgot your password?</h2>
|
||||
<p>
|
||||
Enter the email address that was used to register with Distribusiverse.
|
||||
</p>
|
||||
<form class="form" action="{{ url_for('forgotpassword') }}" method="post">
|
||||
<form class="form" action="{{ url_for('forgotpassword.forgotpassword') }}"
|
||||
method="post">
|
||||
{{ forgotpasswordform.csrf_token }}
|
||||
<fieldset class="required">
|
||||
{{ forgotpasswordform.email.label }}
|
||||
@ -25,5 +26,6 @@
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div
|
||||
</div>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('forgotpassword.static', filename='css/login.css')}}">
|
||||
{% endblock main %}
|
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<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 }}
|
||||
<fieldset class="required">
|
||||
{{ loginform.email.label }}
|
||||
@ -19,8 +19,9 @@
|
||||
</fieldset>
|
||||
<fieldset class="button required">
|
||||
{{ loginform.submit }}
|
||||
<a href="/forgotpassword">Forgot Password?</a>
|
||||
<a href="forgotpassword">Forgot Password?</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('login.static', filename='css/login.css')}}">
|
||||
{% endblock main %}
|
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<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 }}
|
||||
<fieldset class="required">
|
||||
{{ registerform.username.label }}
|
||||
@ -36,4 +36,5 @@
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('register.static', filename='css/login.css')}}">
|
||||
{% endblock main %}
|
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<div id="login">
|
||||
{% if linkvalid%}
|
||||
@ -26,4 +26,5 @@
|
||||
<h3>Password reset link no longer valid.</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('reset_password.static', filename='css/login.css')}}">
|
||||
{% endblock main %}
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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/selector.css')}}">
|
||||
<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 %}
|
||||
<div id="buttons">
|
||||
<div class="overview">
|
||||
@ -19,7 +19,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="distribusi">
|
||||
<a href="/distribusi">
|
||||
<a href="distribusikan/distribusi">
|
||||
<input type="button" name="button" value="Distribusi"></input>
|
||||
</a>
|
||||
</div>
|
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base/base.html" %}
|
||||
{% block main %}
|
||||
<div id="buttons">
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="distribusi">
|
||||
<a href="/distribusi">
|
||||
<a href="distribusikan/distribusi">
|
||||
<input type="button" name="button" value="Distribusi"></input>
|
||||
</a>
|
||||
</div>
|
||||
@ -46,19 +46,14 @@
|
||||
<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>
|
||||
<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>
|
||||
<!-- a div with all the distribusis listed in the distribusiverse -->
|
||||
<div id="distribusiverse" class="maincontent">
|
||||
<h2>List of distribusis</h2>
|
||||
{% include 'filtermenu.html' %}
|
||||
{% include 'base/filtermenu.html' %}
|
||||
<ul id="distribusi-index">
|
||||
{% 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>
|
||||
{% for tag in distribusi["tags"] %}
|
||||
<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">
|
||||
<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>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div id="example_video" class="mp4">
|
||||
<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>
|
||||
<span class="filename">example_video.mp4</span>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user