From 3f54cbf72fd44d018252ee1f0f88f44a1407747d Mon Sep 17 00:00:00 2001 From: crunk Date: Wed, 17 Apr 2024 20:53:40 +0200 Subject: [PATCH 01/20] scrub index of bad practices --- verse/templates/index.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/verse/templates/index.html b/verse/templates/index.html index 5aeaf74..a61b4bb 100644 --- a/verse/templates/index.html +++ b/verse/templates/index.html @@ -46,11 +46,6 @@

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.

-
-

- This particular work in progress project Distribusi-verse 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 Tildeverse and Tilde club, but also be neither of these ideas. See a full list of tildeverse members here.

-
-

This project is made for Autonomous Practices at the WDKA in Rotterdam.

From 18457f9ced060ba2a24463760c425e12a8043145 Mon Sep 17 00:00:00 2001 From: crunk Date: Fri, 19 Apr 2024 22:33:52 +0200 Subject: [PATCH 02/20] late nite freaks --- README.md | 9 +++++++-- pyproject.toml | 38 +++++++++++++++++++------------------- requirements.txt | 1 - verse/forms/uploadform.py | 35 ++++++++++++++++++++--------------- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 877ef9d..f9fef6f 100644 --- a/README.md +++ b/README.md @@ -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! diff --git a/pyproject.toml b/pyproject.toml index 27f62cd..9b286c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,25 @@ -[tool.black] -line-length = 79 -target-version = ['py37', 'py38', 'py39'] -include = '\.pyi?$' -exclude = ''' -/( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - +[tool.black] +line-length = 79 +target-version = ['py311'] +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 + | tests/data | profiling )/ -''' +''' diff --git a/requirements.txt b/requirements.txt index bd1311a..fc2668f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,6 @@ 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 idna==3.3 itsdangerous==2.0.1 Jinja2==3.0.3 diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 8f3e8da..c0df023 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -18,38 +18,43 @@ from wtforms import ( class UploadForm(FlaskForm): """File upload class for a new site in distribusi-verse""" - def distribusiname(form, field): + 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], + "Name of your archive section:", + validators=[validators.InputRequired(), Length(2, 100), _distribusiname], ) academicyear = SelectField( - "Academic year:", + "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'), + (u'2017', u'2017'), + (u'2018', u'2018'), + (u'2019', u'2019'), + (u'2020', u'2020'), + (u'2021', u'2021'), + (u'2022', u'2022'), + (u'2023', u'2023'), + (u'2024', u'2024'), + (u'2025', u'2025'), ], option_widget=None, validators=[DataRequired()] ) term = SelectField( - "Term:", + "Category:", 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'), + (u'event', u'event'), + (u'gathering', u'gathering'), + (u'work session', u'work session'), + (u'workgroup', u'workgroup'), + (u'performance', u'performance'), + (u'music event', u'music event'), ], option_widget=None, validators=[DataRequired()] From dca966698474ad1b3b6dd98f47575b158547d1ab Mon Sep 17 00:00:00 2001 From: crunk Date: Fri, 19 Apr 2024 22:55:07 +0200 Subject: [PATCH 03/20] further removal of academicness --- verse/forms/uploadform.py | 20 +++----------------- verse/start.py | 10 ++++------ verse/templates/filtermenu.html | 18 +++++------------- verse/templates/index.html | 2 +- 4 files changed, 13 insertions(+), 37 deletions(-) diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index c0df023..aa04ebf 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -26,8 +26,8 @@ class UploadForm(FlaskForm): "Name of your archive section:", validators=[validators.InputRequired(), Length(2, 100), _distribusiname], ) - academicyear = SelectField( - "Year", + year = SelectField( + "Year:", validate_choice=True, coerce=str, choices=[ @@ -44,7 +44,7 @@ class UploadForm(FlaskForm): option_widget=None, validators=[DataRequired()] ) - term = SelectField( + category = SelectField( "Category:", validate_choice=True, coerce=str, @@ -64,20 +64,6 @@ class UploadForm(FlaskForm): 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=[ diff --git a/verse/start.py b/verse/start.py index 740be4d..5f95b92 100644 --- a/verse/start.py +++ b/verse/start.py @@ -65,23 +65,21 @@ def index(): singledistribusi = { "username": user.username, "publictheme": distribusi.publictheme, - "term": distribusi.term, + "category": distribusi.term, "course": distribusi.course, "year": distribusi.year, "tags": distribusi.tags.split(","), } distribusisindex[distribusi.distribusiname] = singledistribusi - years = uploadform.academicyear.choices - terms = uploadform.term.choices - courses = uploadform.course.choices + years = uploadform.year.choices + categories = uploadform.category.choices adminuser = isadminuser() template = render_template( "index.html", distribusisindex=distribusisindex, years=years, - terms=terms, - courses=courses, + categories=categories, adminuser=adminuser, ) return template diff --git a/verse/templates/filtermenu.html b/verse/templates/filtermenu.html index c84abb7..f641751 100644 --- a/verse/templates/filtermenu.html +++ b/verse/templates/filtermenu.html @@ -1,28 +1,20 @@ {% block menu %} - {% endblock menu %} diff --git a/verse/templates/index.html b/verse/templates/index.html index a61b4bb..d18caf6 100644 --- a/verse/templates/index.html +++ b/verse/templates/index.html @@ -53,7 +53,7 @@ {% include 'filtermenu.html' %}
    {% for name, distribusi in distribusisindex.items() %} -
  • +
  • {{distribusi["username"]}}:{{name}} {% for tag in distribusi["tags"] %} {{tag}} From 9e487587676bb2fbf2f3968e6bb3d622b7fe016b Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 20:44:08 +0200 Subject: [PATCH 04/20] sort, format and move models to folder --- verse/adminpage.py | 17 +++----- verse/admintool.py | 14 +++--- verse/app.py | 9 ++-- verse/deploydb.py | 8 ++-- verse/distribusiselector.py | 32 ++++++-------- verse/distribusisinfo.py | 4 +- verse/distribusiworkflow.py | 50 +++++++++------------- verse/editor.py | 32 ++++++-------- verse/forms/admindistribusiform.py | 8 ++-- verse/forms/adminuserform.py | 8 ++-- verse/forms/editorform.py | 13 ++---- verse/forms/forgotpasswordform.py | 8 +--- verse/forms/loginform.py | 9 +--- verse/forms/publicthemeform.py | 6 +-- verse/forms/registerform.py | 11 ++--- verse/forms/resetpasswordform.py | 8 +--- verse/forms/selectorform.py | 2 +- verse/forms/themeform.py | 6 +-- verse/forms/uploadform.py | 59 ++++++++++++-------------- verse/migrations/env.py | 23 +++++----- verse/models/distribusimodel.py | 23 ++++++++++ verse/models/usermodel.py | 23 ++++++++++ verse/start.py | 48 ++++++++------------- verse/statuspengguna/forgotpassword.py | 15 +++---- verse/statuspengguna/helper.py | 19 ++++----- verse/statuspengguna/loginuser.py | 16 +++---- verse/statuspengguna/registeruser.py | 25 ++++------- verse/statuspengguna/resetpassword.py | 26 ++++-------- verse/themeselector.py | 12 +++--- verse/upload.py | 19 +++------ verse/uploadpage.py | 8 ++-- 31 files changed, 244 insertions(+), 317 deletions(-) create mode 100644 verse/models/distribusimodel.py create mode 100644 verse/models/usermodel.py diff --git a/verse/adminpage.py b/verse/adminpage.py index c310fde..9c35dc1 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -1,21 +1,16 @@ import os import shutil + from flask import render_template +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) -from sqlalchemy.exc import ( - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) from app import db - -from usermodel import User -from distribusimodel import Distribusis from distribusisinfo import DistribusisInfo - -from forms.adminuserform import AdminUserForm from forms.admindistribusiform import AdminDistribusiForm +from forms.adminuserform import AdminUserForm +from models.distribusimodel import Distribusis +from models.usermodel import User def AdminPage(): diff --git a/verse/admintool.py b/verse/admintool.py index 9410eaa..21aa782 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -1,13 +1,11 @@ import sys + +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + from app import create_app, db -from sqlalchemy.exc import ( - InvalidRequestError, - InterfaceError, - DataError, - DatabaseError, -) -from usermodel import User # noqa: F401 -from distribusimodel import Distribusis # noqa: F401 +from models.distribusimodel import Distribusis # noqa: F401 +from models.usermodel import User # noqa: F401 def admintool(): diff --git a/verse/app.py b/verse/app.py index 18dceb3..2852623 100644 --- a/verse/app.py +++ b/verse/app.py @@ -1,12 +1,11 @@ import os + from flask import Flask -from flask_sqlalchemy import SQLAlchemy 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 -from flask_login import ( - LoginManager, -) db = SQLAlchemy() migrate = Migrate() @@ -26,7 +25,7 @@ def create_app(): 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" diff --git a/verse/deploydb.py b/verse/deploydb.py index 236baf8..26c2e5b 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -1,11 +1,11 @@ def deploy(): """Run deployment of database.""" - from app import create_app, db - from flask_migrate import upgrade, migrate, init, stamp + from flask_migrate import init, migrate, stamp, upgrade + from app import create_app, db + from models.distribusimodel import Distribusis # 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.usermodel import User # noqa: F401 app = create_app() app.app_context().push() diff --git a/verse/distribusiselector.py b/verse/distribusiselector.py index 788ec35..986eef7 100644 --- a/verse/distribusiselector.py +++ b/verse/distribusiselector.py @@ -1,28 +1,22 @@ import os import shutil + from flask import flash, render_template from flask_login import current_user -from sqlalchemy.exc import ( - DataError, - DatabaseError, - 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 - -# UserPengguna -from statuspengguna.helper import UserHelper +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) from app import db +from distribusisinfo import DistribusisInfo +from forms.distribusiform import DistribusiForm +from forms.publicthemeform import PublicThemeForm +from forms.selectorform import SelectorForm +from forms.themeform import ThemeForm +from forms.uploadform import UploadForm +from models.distribusimodel import Distribusis +from models.usermodel import User +# UserPengguna +from statuspengguna.helper import UserHelper def DistribusiSelector(): diff --git a/verse/distribusisinfo.py b/verse/distribusisinfo.py index 9bbd80e..5a47d5f 100644 --- a/verse/distribusisinfo.py +++ b/verse/distribusisinfo.py @@ -1,7 +1,7 @@ from flask_login import current_user -from usermodel import User -from distribusimodel import Distribusis +from models.distribusimodel import Distribusis +from models.usermodel import User class DistribusisInfo: diff --git a/verse/distribusiworkflow.py b/verse/distribusiworkflow.py index cea215b..9546ac8 100644 --- a/verse/distribusiworkflow.py +++ b/verse/distribusiworkflow.py @@ -1,40 +1,28 @@ 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 distribusiselector import SelectorVisible +from distribusisinfo import DistribusisInfo +from forms.distribusiform import DistribusiForm +from forms.publicthemeform import PublicThemeForm +from forms.selectorform import SelectorForm +from forms.themeform import ThemeForm +# Forms! +from forms.uploadform import UploadForm +from models.distribusimodel import Distribusis +from models.usermodel import User +# UserPengguna +from statuspengguna.helper import UserHelper def DistribusiWorkflow(): @@ -78,7 +66,7 @@ def CleanUpDistribusiFiles(userfolder): def RemoveMacFolders(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: diff --git a/verse/editor.py b/verse/editor.py index a515d53..6966d7d 100644 --- a/verse/editor.py +++ b/verse/editor.py @@ -1,28 +1,23 @@ import os +import shutil + import bleach from bleach_allowlist import all_styles -import shutil from flask import render_template - +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) from werkzeug.utils import secure_filename -from sqlalchemy.exc import ( - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) + from app import db -from distribusimodel 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.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm +from forms.themeform import ThemeForm +from forms.uploadform import UploadForm +from models.distribusimodel import Distribusis +from statuspengguna.helper import UserHelper def Editor(): @@ -68,7 +63,7 @@ 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) @@ -81,7 +76,7 @@ def WriteCssToFile(editorform, 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 @@ -91,8 +86,7 @@ def CopyPublicToUserFolder(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}") diff --git a/verse/forms/admindistribusiform.py b/verse/forms/admindistribusiform.py index c2002fc..f00f3db 100644 --- a/verse/forms/admindistribusiform.py +++ b/verse/forms/admindistribusiform.py @@ -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}", diff --git a/verse/forms/adminuserform.py b/verse/forms/adminuserform.py index 3d7ae22..1a6ffae 100644 --- a/verse/forms/adminuserform.py +++ b/verse/forms/adminuserform.py @@ -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}", diff --git a/verse/forms/editorform.py b/verse/forms/editorform.py index 49a6436..0f12ba1 100644 --- a/verse/forms/editorform.py +++ b/verse/forms/editorform.py @@ -1,15 +1,10 @@ """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): diff --git a/verse/forms/forgotpasswordform.py b/verse/forms/forgotpasswordform.py index 3737181..385327d 100644 --- a/verse/forms/forgotpasswordform.py +++ b/verse/forms/forgotpasswordform.py @@ -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): diff --git a/verse/forms/loginform.py b/verse/forms/loginform.py index f9e650d..b446850 100644 --- a/verse/forms/loginform.py +++ b/verse/forms/loginform.py @@ -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): diff --git a/verse/forms/publicthemeform.py b/verse/forms/publicthemeform.py index 482de0f..103fbeb 100644 --- a/verse/forms/publicthemeform.py +++ b/verse/forms/publicthemeform.py @@ -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): diff --git a/verse/forms/registerform.py b/verse/forms/registerform.py index 7a8d1b1..3482054 100644 --- a/verse/forms/registerform.py +++ b/verse/forms/registerform.py @@ -1,13 +1,8 @@ """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, ValidationError class RegisterForm(FlaskForm): @@ -29,7 +24,7 @@ class RegisterForm(FlaskForm): Email(), Length(6, 64), hremail, - ] + ], ) password = PasswordField( diff --git a/verse/forms/resetpasswordform.py b/verse/forms/resetpasswordform.py index ed32281..481cb02 100644 --- a/verse/forms/resetpasswordform.py +++ b/verse/forms/resetpasswordform.py @@ -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): diff --git a/verse/forms/selectorform.py b/verse/forms/selectorform.py index bcdad85..d726971 100644 --- a/verse/forms/selectorform.py +++ b/verse/forms/selectorform.py @@ -1,5 +1,5 @@ from flask_wtf import FlaskForm -from wtforms import SubmitField, SelectField +from wtforms import SelectField, SubmitField class SelectorForm(FlaskForm): diff --git a/verse/forms/themeform.py b/verse/forms/themeform.py index 5968f04..a1c3e02 100644 --- a/verse/forms/themeform.py +++ b/verse/forms/themeform.py @@ -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): diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index aa04ebf..5108177 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -1,18 +1,9 @@ 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, -) +from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize +from wtforms import (IntegerField, SelectField, StringField, SubmitField, + validators) +from wtforms.validators import (DataRequired, Length, NumberRange, + ValidationError) class UploadForm(FlaskForm): @@ -24,40 +15,44 @@ class UploadForm(FlaskForm): sitename = StringField( "Name of your archive section:", - validators=[validators.InputRequired(), Length(2, 100), _distribusiname], + validators=[ + validators.InputRequired(), + Length(2, 100), + _distribusiname, + ], ) year = SelectField( "Year:", validate_choice=True, coerce=str, choices=[ - (u'2017', u'2017'), - (u'2018', u'2018'), - (u'2019', u'2019'), - (u'2020', u'2020'), - (u'2021', u'2021'), - (u'2022', u'2022'), - (u'2023', u'2023'), - (u'2024', u'2024'), - (u'2025', u'2025'), + ("2017", "2017"), + ("2018", "2018"), + ("2019", "2019"), + ("2020", "2020"), + ("2021", "2021"), + ("2022", "2022"), + ("2023", "2023"), + ("2024", "2024"), + ("2025", "2025"), ], option_widget=None, - validators=[DataRequired()] + validators=[DataRequired()], ) category = SelectField( "Category:", validate_choice=True, coerce=str, choices=[ - (u'event', u'event'), - (u'gathering', u'gathering'), - (u'work session', u'work session'), - (u'workgroup', u'workgroup'), - (u'performance', u'performance'), - (u'music event', u'music event'), + ("event", "event"), + ("gathering", "gathering"), + ("work session", "work session"), + ("workgroup", "workgroup"), + ("performance", "performance"), + ("music event", "music event"), ], option_widget=None, - validators=[DataRequired()] + validators=[DataRequired()], ) tags = StringField( "Add tags, seperated by commas. No need for the '#' sign:", diff --git a/verse/migrations/env.py b/verse/migrations/env.py index 68feded..847c719 100644 --- a/verse/migrations/env.py +++ b/verse/migrations/env.py @@ -3,9 +3,8 @@ from __future__ import with_statement import logging from logging.config import fileConfig -from flask import current_app - from alembic import context +from flask import current_app # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -14,17 +13,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 +66,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(): diff --git a/verse/models/distribusimodel.py b/verse/models/distribusimodel.py new file mode 100644 index 0000000..11aad04 --- /dev/null +++ b/verse/models/distribusimodel.py @@ -0,0 +1,23 @@ +from app import db + + +class Distribusis(db.Model): + """distribusi model class for a single distribusi in distribusi-verse""" + + __tablename__ = "distribusis" + + 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) + category = db.Column(db.String(500), nullable=True, unique=False) + + # Academic year eg:2020-2021, so no need for a Datetime object + year = db.Column(db.String(9), nullable=True, unique=False) + 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): + return "" % self.distribusiname diff --git a/verse/models/usermodel.py b/verse/models/usermodel.py new file mode 100644 index 0000000..254fe3f --- /dev/null +++ b/verse/models/usermodel.py @@ -0,0 +1,23 @@ +from flask_login import UserMixin + +from app import db + + +class User(UserMixin, db.Model): + """User model class for a user in distribusi-verse""" + + __tablename__ = "users" + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(150), unique=True, nullable=False) + email = db.Column(db.String(150), unique=True, nullable=False) + password = db.Column(db.String(300), nullable=False, unique=False) + 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): + return "" % self.email diff --git a/verse/start.py b/verse/start.py index 5f95b92..5952ce3 100644 --- a/verse/start.py +++ b/verse/start.py @@ -1,45 +1,33 @@ """This is the main flask distribusi page""" + from datetime import timedelta -from flask import ( - render_template, - redirect, - url_for, - session, - send_from_directory, - Blueprint, -) -from flask_login import ( - logout_user, - login_required, - current_user, -) + +from flask import (Blueprint, redirect, render_template, send_from_directory, + session, url_for) +from flask_login import current_user, login_required, logout_user from flask_mail import Mail 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 app import create_app, login_manager from distribusiselector import DistribusiSelector -from uploadpage import UploadPage - +# Distribusi Information +from distribusisinfo import DistribusisInfo +from distribusiworkflow import DistribusiWorkflow +from editor import Editor +# Use upload form to populate filters +from forms.uploadform import UploadForm +from models.distribusimodel import Distribusis +from models.usermodel import User +from statuspengguna.forgotpassword import ForgotPassword # UserPengguna 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 themeselector import ThemeSelector +from uploadpage import UploadPage APP = create_app() stash_page = Blueprint("stash_page", __name__, static_folder="stash") @@ -56,7 +44,6 @@ def session_handler(): @APP.route("/") def index(): UserHelper.reset_user_state() - # http://localhost:5000/themes/publicthemes/RomeroTape/blueskies.css uploadform = UploadForm() distribusis = DistribusisInfo.visibledistribusis() distribusisindex = {} @@ -66,7 +53,6 @@ def index(): "username": user.username, "publictheme": distribusi.publictheme, "category": distribusi.term, - "course": distribusi.course, "year": distribusi.year, "tags": distribusi.tags.split(","), } diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py index 2d09c38..eaeb395 100644 --- a/verse/statuspengguna/forgotpassword.py +++ b/verse/statuspengguna/forgotpassword.py @@ -1,17 +1,14 @@ -from uuid import uuid1 from datetime import datetime -from sqlalchemy.exc import ( - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) +from uuid import uuid1 + from flask import render_template from flask_mail import Message +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) -from usermodel import User -from forms.forgotpasswordform import ForgotPasswordForm from app import db +from forms.forgotpasswordform import ForgotPasswordForm +from models.usermodel import User def ForgotPassword(mail): diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index 3a6d2de..5daad75 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -1,17 +1,14 @@ import os -from flask_login import current_user -from flask import flash -from sqlalchemy.exc import ( - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) -from usermodel import User -from distribusimodel import Distribusis -from distribusisinfo import DistribusisInfo +from flask import flash +from flask_login import current_user +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + from app import db +from distribusisinfo import DistribusisInfo +from models.distribusimodel import Distribusis +from models.usermodel import User class UserHelper: diff --git a/verse/statuspengguna/loginuser.py b/verse/statuspengguna/loginuser.py index 2d5e349..a7c5424 100644 --- a/verse/statuspengguna/loginuser.py +++ b/verse/statuspengguna/loginuser.py @@ -1,15 +1,9 @@ -from flask import ( - render_template, - redirect, - request, - flash, - url_for, - abort, -) -from usermodel import User -from forms.loginform import LoginForm -from flask_login import login_user +from flask import abort, flash, redirect, render_template, request, url_for from flask_bcrypt import check_password_hash +from flask_login import login_user + +from forms.loginform import LoginForm +from models.usermodel import User def LoginUser(): diff --git a/verse/statuspengguna/registeruser.py b/verse/statuspengguna/registeruser.py index 722f3c8..ac8167e 100644 --- a/verse/statuspengguna/registeruser.py +++ b/verse/statuspengguna/registeruser.py @@ -1,22 +1,13 @@ -from flask import ( - render_template, - redirect, - flash, - url_for, -) -from sqlalchemy.exc import ( - IntegrityError, - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) -from werkzeug.routing import BuildError -from usermodel import User -from forms.registerform import RegisterForm -from flask_login import login_user +from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash +from flask_login import login_user +from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, + InterfaceError, InvalidRequestError) +from werkzeug.routing import BuildError + from app import db +from forms.registerform import RegisterForm +from models.usermodel import User def RegisterUser(): diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py index b802dab..d91b2e6 100644 --- a/verse/statuspengguna/resetpassword.py +++ b/verse/statuspengguna/resetpassword.py @@ -1,23 +1,15 @@ from datetime import datetime -from flask import ( - render_template, - redirect, - flash, - url_for, -) -from sqlalchemy.exc import ( - IntegrityError, - DataError, - DatabaseError, - InterfaceError, - InvalidRequestError, -) -from werkzeug.routing import BuildError -from usermodel import User -from forms.resetpasswordform import ResetPasswordForm -from flask_login import login_user + +from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash +from flask_login import login_user +from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, + InterfaceError, InvalidRequestError) +from werkzeug.routing import BuildError + from app import db +from forms.resetpasswordform import ResetPasswordForm +from models.usermodel import User def ResetPassword(path): diff --git a/verse/themeselector.py b/verse/themeselector.py index 8ea5bfa..2582809 100644 --- a/verse/themeselector.py +++ b/verse/themeselector.py @@ -1,15 +1,15 @@ import os import shutil + from flask import render_template -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 +from forms.themeform import ThemeForm +from forms.uploadform import UploadForm +from statuspengguna.helper import UserHelper def ThemeSelector(): @@ -30,9 +30,7 @@ def ThemeSelector(): ) MoveCssToUserFolder(current_distribusi, copycssfile) return RenderDistribusiTemplate( - themeform, - publicthemeform, - current_distribusi + themeform, publicthemeform, current_distribusi ) diff --git a/verse/upload.py b/verse/upload.py index 5f12794..2ffbca3 100644 --- a/verse/upload.py +++ b/verse/upload.py @@ -1,22 +1,17 @@ import os import shutil + from flask import flash from flask_login import current_user -from sqlalchemy.exc import ( - IntegrityError, - InvalidRequestError, - DataError, - InterfaceError, - DatabaseError, -) +from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, + InterfaceError, InvalidRequestError) + from app import db - -from usermodel import User -from distribusimodel import Distribusis - -from statuspengguna.helper import UserHelper from distribusiselector import SelectCurrentDistribusi from forms.uploadform import UploadForm +from models.distribusimodel import Distribusis +from models.usermodel import User +from statuspengguna.helper import UserHelper def UploadNewDistribusi(uploadfolder): diff --git a/verse/uploadpage.py b/verse/uploadpage.py index a1349f6..1bd0c6a 100644 --- a/verse/uploadpage.py +++ b/verse/uploadpage.py @@ -1,16 +1,14 @@ from flask import render_template +from distribusiselector import SelectorVisible +from distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm -from forms.themeform import ThemeForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm - +from 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): From 545e5e205a197d5252945e65b02d1c0dd6f09ddf Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 20:47:33 +0200 Subject: [PATCH 05/20] delete moved files --- verse/distribusimodel.py | 23 ----------------------- verse/usermodel.py | 22 ---------------------- 2 files changed, 45 deletions(-) delete mode 100644 verse/distribusimodel.py delete mode 100644 verse/usermodel.py diff --git a/verse/distribusimodel.py b/verse/distribusimodel.py deleted file mode 100644 index 6e1b5c0..0000000 --- a/verse/distribusimodel.py +++ /dev/null @@ -1,23 +0,0 @@ -from app import db - - -class Distribusis(db.Model): - """distribusi model class for a single distribusi in distribusi-verse""" - - __tablename__ = "distribusis" - - 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 - year = db.Column(db.String(9), 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): - return "" % self.distribusiname diff --git a/verse/usermodel.py b/verse/usermodel.py deleted file mode 100644 index 333d3e5..0000000 --- a/verse/usermodel.py +++ /dev/null @@ -1,22 +0,0 @@ -from app import db -from flask_login import UserMixin - - -class User(UserMixin, db.Model): - """User model class for a user in distribusi-verse""" - - __tablename__ = "users" - - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(150), unique=True, nullable=False) - email = db.Column(db.String(150), unique=True, nullable=False) - password = db.Column(db.String(300), nullable=False, unique=False) - 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): - return "" % self.email From 2c9e882f2ff35a179344073a3efe1755c7da46e4 Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 20:48:56 +0200 Subject: [PATCH 06/20] Out with the old, in with the new --- verse/templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verse/templates/base.html b/verse/templates/base.html index 93102fb..b0e22e3 100644 --- a/verse/templates/base.html +++ b/verse/templates/base.html @@ -3,7 +3,7 @@ - Autonomous Practices X Distribusi-Verse + Varia Archive X Distribusi-Verse From 67acc5ec560fe592e553304c5571a57b39f3be43 Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 21:08:51 +0200 Subject: [PATCH 07/20] remove hro only email validation --- verse/forms/registerform.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/verse/forms/registerform.py b/verse/forms/registerform.py index 3482054..cca23ab 100644 --- a/verse/forms/registerform.py +++ b/verse/forms/registerform.py @@ -8,10 +8,6 @@ from wtforms.validators import Email, EqualTo, Length, ValidationError 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)], @@ -22,8 +18,7 @@ class RegisterForm(FlaskForm): validators=[ validators.InputRequired(), Email(), - Length(6, 64), - hremail, + Length(6, 128), ], ) From cefdc8cd11b1bfb9223ad189996c7b8a1043c60d Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 21:28:44 +0200 Subject: [PATCH 08/20] more text edits --- verse/distribusiselector.py | 5 ++-- verse/migrations/env.py | 23 +++++++++---------- verse/start.py | 2 +- verse/static/css/dropdown.css | 2 +- .../templates/distribusiworkflow/editcss.html | 2 +- verse/templates/distribusiworkflow/theme.html | 4 ++-- .../templates/distribusiworkflow/upload.html | 21 +++++------------ 7 files changed, 24 insertions(+), 35 deletions(-) diff --git a/verse/distribusiselector.py b/verse/distribusiselector.py index 986eef7..60203b2 100644 --- a/verse/distribusiselector.py +++ b/verse/distribusiselector.py @@ -48,9 +48,8 @@ def AutoFillInUploadForm(uploadform, 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.category.data = distribusi.category + uploadform.year.data = distribusi.year uploadform.tags.data = distribusi.tags return uploadform diff --git a/verse/migrations/env.py b/verse/migrations/env.py index 847c719..68feded 100644 --- a/verse/migrations/env.py +++ b/verse/migrations/env.py @@ -3,9 +3,10 @@ from __future__ import with_statement import logging from logging.config import fileConfig -from alembic import context from flask import current_app +from alembic import context + # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config @@ -13,19 +14,17 @@ 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: @@ -66,20 +65,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(): diff --git a/verse/start.py b/verse/start.py index 5952ce3..1ddc540 100644 --- a/verse/start.py +++ b/verse/start.py @@ -52,7 +52,7 @@ def index(): singledistribusi = { "username": user.username, "publictheme": distribusi.publictheme, - "category": distribusi.term, + "category": distribusi.category, "year": distribusi.year, "tags": distribusi.tags.split(","), } diff --git a/verse/static/css/dropdown.css b/verse/static/css/dropdown.css index 5111481..07509d5 100644 --- a/verse/static/css/dropdown.css +++ b/verse/static/css/dropdown.css @@ -1,5 +1,5 @@ /* Dropdown Button */ -/* for sorting on Academicyear, Term, Course +/* for sorting on year and category */ button { background-color: #E0B0FF; diff --git a/verse/templates/distribusiworkflow/editcss.html b/verse/templates/distribusiworkflow/editcss.html index 4b850b5..0170599 100644 --- a/verse/templates/distribusiworkflow/editcss.html +++ b/verse/templates/distribusiworkflow/editcss.html @@ -4,7 +4,7 @@

    Go to CSS editor

    {% else %}

    - 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.

    Go to Step 1

    diff --git a/verse/templates/distribusiworkflow/theme.html b/verse/templates/distribusiworkflow/theme.html index 3a371d1..2b92907 100644 --- a/verse/templates/distribusiworkflow/theme.html +++ b/verse/templates/distribusiworkflow/theme.html @@ -16,7 +16,7 @@ {% else %}

    - 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.

    Go to Step 1 @@ -35,7 +35,7 @@ {% else %}

    - 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.

    Go to Step 1 diff --git a/verse/templates/distribusiworkflow/upload.html b/verse/templates/distribusiworkflow/upload.html index f01bb73..1dba794 100644 --- a/verse/templates/distribusiworkflow/upload.html +++ b/verse/templates/distribusiworkflow/upload.html @@ -11,28 +11,19 @@ {% endfor %}
    - {{ uploadform.academicyear.label }} + {{ uploadform.year.label }}
    - {{ uploadform.academicyear }} - {% for message in uploadform.academicyear.errors %} + {{ uploadform.year }} + {% for message in uploadform.year.errors %}
    {{ message }}
    {% endfor %}
    - {{ uploadform.term.label }} + {{ uploadform.category.label }}
    - {{ uploadform.term }} - {% for message in uploadform.term.errors %} -
    {{ message }}
    - {% endfor %} -
    -
    -
    - {{ uploadform.course.label }} -
    - {{ uploadform.course }} - {% for message in uploadform.course.errors %} + {{ uploadform.category }} + {% for message in uploadform.category.errors %}
    {{ message }}
    {% endfor %}
    From 963b33f170867a6750ed7b1004cdd560ec97db6d Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 21 Apr 2024 22:09:44 +0200 Subject: [PATCH 09/20] working version alpha --- verse/models/distribusimodel.py | 4 ---- verse/start.py | 2 -- verse/static/js/dropdown.js | 5 ++--- verse/upload.py | 10 ++++------ 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/verse/models/distribusimodel.py b/verse/models/distribusimodel.py index 11aad04..d8f114e 100644 --- a/verse/models/distribusimodel.py +++ b/verse/models/distribusimodel.py @@ -9,14 +9,10 @@ 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) category = db.Column(db.String(500), nullable=True, unique=False) - - # Academic year eg:2020-2021, so no need for a Datetime object year = db.Column(db.String(9), nullable=True, unique=False) 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): diff --git a/verse/start.py b/verse/start.py index 1ddc540..e3da5d0 100644 --- a/verse/start.py +++ b/verse/start.py @@ -59,7 +59,6 @@ def index(): distribusisindex[distribusi.distribusiname] = singledistribusi years = uploadform.year.choices categories = uploadform.category.choices - adminuser = isadminuser() template = render_template( "index.html", @@ -100,7 +99,6 @@ 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) diff --git a/verse/static/js/dropdown.js b/verse/static/js/dropdown.js index e0ce738..505d4a8 100644 --- a/verse/static/js/dropdown.js +++ b/verse/static/js/dropdown.js @@ -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"); diff --git a/verse/upload.py b/verse/upload.py index 2ffbca3..6e3a336 100644 --- a/verse/upload.py +++ b/verse/upload.py @@ -22,9 +22,8 @@ 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, + year=uploadform.year.data, tags=uploadform.tags.data, ) user.currentdistribusi = uploadform.sitename.data @@ -66,9 +65,8 @@ 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.year = uploadform.year.data distribusi.tags = uploadform.tags.data distribusi.visible = False db.session.commit() From 9e3bb6605fd7ead2471a0f20b597188d0ae6527a Mon Sep 17 00:00:00 2001 From: crunk Date: Mon, 22 Apr 2024 20:34:33 +0200 Subject: [PATCH 10/20] settings toml file --- verse/app.py | 27 +++++++++++++++++++++++++-- verse/forms/uploadform.py | 23 +++++++++++++---------- verse/settings.toml | 4 ++++ verse/start.py | 3 +-- verse/templates/base.html | 2 +- verse/uploadpage.py | 4 +++- 6 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 verse/settings.toml diff --git a/verse/app.py b/verse/app.py index 2852623..438e982 100644 --- a/verse/app.py +++ b/verse/app.py @@ -1,4 +1,5 @@ import os +import tomllib from flask import Flask from flask_bcrypt import Bcrypt @@ -7,6 +8,7 @@ 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() @@ -14,8 +16,6 @@ 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.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True @@ -37,10 +37,33 @@ 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 settings_from_file(): + settings = {} + 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) diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 5108177..1c952e5 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -5,20 +5,30 @@ from wtforms import (IntegerField, SelectField, StringField, SubmitField, from wtforms.validators import (DataRequired, Length, NumberRange, ValidationError) +from app import settings + class UploadForm(FlaskForm): """File upload class for a new site in distribusi-verse""" - def _distribusiname(form, field): + 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 + sitename = StringField( "Name of your archive section:", validators=[ validators.InputRequired(), Length(2, 100), - _distribusiname, + distribusiname, ], ) year = SelectField( @@ -43,14 +53,7 @@ class UploadForm(FlaskForm): "Category:", validate_choice=True, coerce=str, - choices=[ - ("event", "event"), - ("gathering", "gathering"), - ("work session", "work session"), - ("workgroup", "workgroup"), - ("performance", "performance"), - ("music event", "music event"), - ], + choices=category_choices, option_widget=None, validators=[DataRequired()], ) diff --git a/verse/settings.toml b/verse/settings.toml new file mode 100644 index 0000000..763173b --- /dev/null +++ b/verse/settings.toml @@ -0,0 +1,4 @@ +title = "Varia Archive X Distribusi-Verse" +categories = ["event","gathering","work session","workgroup","performance","music event"] +start_time = 2017-11-03 +end_time = 2024-12-31 diff --git a/verse/start.py b/verse/start.py index e3da5d0..80af292 100644 --- a/verse/start.py +++ b/verse/start.py @@ -84,8 +84,7 @@ def distribusi(): @APP.route("/upload", methods=["POST"]) @login_required def upload(): - uploadfolder = APP.config["UPLOAD_FOLDER"] - return UploadPage(uploadfolder) + return UploadPage() @APP.route("/theme", methods=["GET", "POST"]) diff --git a/verse/templates/base.html b/verse/templates/base.html index b0e22e3..d1d6e54 100644 --- a/verse/templates/base.html +++ b/verse/templates/base.html @@ -3,7 +3,7 @@ - Varia Archive X Distribusi-Verse + {{title}} diff --git a/verse/uploadpage.py b/verse/uploadpage.py index 1bd0c6a..dc8e9bb 100644 --- a/verse/uploadpage.py +++ b/verse/uploadpage.py @@ -8,11 +8,13 @@ from forms.selectorform import SelectorForm from forms.themeform import ThemeForm # UserPengguna from statuspengguna.helper import UserHelper +from app import APP from upload import UploadNewDistribusi, UploadUpdatedFiles -def UploadPage(uploadfolder): +def UploadPage(): "render upload page section of distribusi workflow" + uploadfolder = APP.config["UPLOAD_FOLDER"] distribusiform = DistribusiForm() themeform = ThemeForm() publicthemeform = PublicThemeForm() From f5632ccfd23c7388ffec3078a3f7b6e3d465acf0 Mon Sep 17 00:00:00 2001 From: crunk Date: Mon, 22 Apr 2024 20:34:55 +0200 Subject: [PATCH 11/20] remove old notes --- notes.md | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 notes.md diff --git a/notes.md b/notes.md deleted file mode 100644 index 7b002ba..0000000 --- a/notes.md +++ /dev/null @@ -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 From e824430d952c396dfc5ec1f291ea96b8851e94fc Mon Sep 17 00:00:00 2001 From: crunk Date: Mon, 22 Apr 2024 20:59:26 +0200 Subject: [PATCH 12/20] year range in settings file --- verse/forms/uploadform.py | 23 ++++++++++++----------- verse/settings.toml | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 1c952e5..3d47e72 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -23,6 +23,17 @@ class UploadForm(FlaskForm): 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=[ @@ -35,17 +46,7 @@ class UploadForm(FlaskForm): "Year:", validate_choice=True, coerce=str, - choices=[ - ("2017", "2017"), - ("2018", "2018"), - ("2019", "2019"), - ("2020", "2020"), - ("2021", "2021"), - ("2022", "2022"), - ("2023", "2023"), - ("2024", "2024"), - ("2025", "2025"), - ], + choices=year_choices, option_widget=None, validators=[DataRequired()], ) diff --git a/verse/settings.toml b/verse/settings.toml index 763173b..287c705 100644 --- a/verse/settings.toml +++ b/verse/settings.toml @@ -1,4 +1,4 @@ title = "Varia Archive X Distribusi-Verse" categories = ["event","gathering","work session","workgroup","performance","music event"] start_time = 2017-11-03 -end_time = 2024-12-31 +end_time = 2025-12-31 From 2db481989a0108349d50d7dbefe35829d8bae7a4 Mon Sep 17 00:00:00 2001 From: crunk Date: Wed, 24 Apr 2024 23:05:36 +0200 Subject: [PATCH 13/20] changed autoformatter --- setup.py | 4 ++-- verse/adminpage.py | 7 +++---- verse/admintool.py | 5 ++--- verse/app.py | 1 - verse/deploydb.py | 3 +-- verse/distribusiselector.py | 9 ++++----- verse/distribusisinfo.py | 1 - verse/distribusiworkflow.py | 14 +++++++------- verse/editor.py | 11 +++++------ verse/forms/uploadform.py | 4 +--- verse/migrations/env.py | 23 ++++++++++++----------- verse/models/usermodel.py | 3 +-- verse/start.py | 11 +++++------ verse/statuspengguna/forgotpassword.py | 7 +++---- verse/statuspengguna/helper.py | 9 ++++----- verse/statuspengguna/loginuser.py | 1 - verse/statuspengguna/registeruser.py | 7 +++---- verse/statuspengguna/resetpassword.py | 7 +++---- verse/themeselector.py | 3 +-- verse/upload.py | 9 ++++----- verse/uploadpage.py | 5 ++--- 21 files changed, 63 insertions(+), 81 deletions(-) diff --git a/setup.py b/setup.py index 340f994..ce7462a 100644 --- a/setup.py +++ b/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()) diff --git a/verse/adminpage.py b/verse/adminpage.py index 9c35dc1..c12fac3 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -1,16 +1,15 @@ import os import shutil -from flask import render_template -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) - from app import db from distribusisinfo import DistribusisInfo +from flask import render_template from forms.admindistribusiform import AdminDistribusiForm from forms.adminuserform import AdminUserForm from models.distribusimodel import Distribusis from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) def AdminPage(): diff --git a/verse/admintool.py b/verse/admintool.py index 21aa782..8fc9e2e 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -1,11 +1,10 @@ import sys -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) - from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 from models.usermodel import User # noqa: F401 +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) def admintool(): diff --git a/verse/app.py b/verse/app.py index 438e982..2e6a698 100644 --- a/verse/app.py +++ b/verse/app.py @@ -59,7 +59,6 @@ def settings(): return APP - def settings_from_file(): settings = {} if os.path.isfile("settings_development.toml"): diff --git a/verse/deploydb.py b/verse/deploydb.py index 26c2e5b..6ebd4eb 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -1,8 +1,7 @@ def deploy(): """Run deployment of database.""" - from flask_migrate import init, migrate, stamp, upgrade - from app import create_app, db + from flask_migrate import init, migrate, stamp, upgrade from models.distribusimodel import Distribusis # noqa: F401 # This model is required for flask_migrate to make the table from models.usermodel import User # noqa: F401 diff --git a/verse/distribusiselector.py b/verse/distribusiselector.py index 60203b2..3a5d72f 100644 --- a/verse/distribusiselector.py +++ b/verse/distribusiselector.py @@ -1,13 +1,10 @@ import os import shutil -from flask import flash, render_template -from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) - from app import db from distribusisinfo import DistribusisInfo +from flask import flash, render_template +from flask_login import current_user from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -15,6 +12,8 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusisinfo.py b/verse/distribusisinfo.py index 5a47d5f..0b40c1b 100644 --- a/verse/distribusisinfo.py +++ b/verse/distribusisinfo.py @@ -1,5 +1,4 @@ from flask_login import current_user - from models.distribusimodel import Distribusis from models.usermodel import User diff --git a/verse/distribusiworkflow.py b/verse/distribusiworkflow.py index 9546ac8..b4ce328 100644 --- a/verse/distribusiworkflow.py +++ b/verse/distribusiworkflow.py @@ -2,17 +2,13 @@ import os import shutil import zipfile +from app import db # 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 distribusiselector import SelectorVisible from distribusisinfo import DistribusisInfo +from flask import flash, redirect, render_template, url_for +from flask_login import current_user from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -21,9 +17,13 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper +from distribusi.distribusi import distribusify + def DistribusiWorkflow(): distribusiform = DistribusiForm() diff --git a/verse/editor.py b/verse/editor.py index 6966d7d..bfbfaeb 100644 --- a/verse/editor.py +++ b/verse/editor.py @@ -2,14 +2,10 @@ import os import shutil import bleach -from bleach_allowlist import all_styles -from flask import render_template -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) -from werkzeug.utils import secure_filename - from app import db +from bleach_allowlist import all_styles from distribusisinfo import DistribusisInfo +from flask import render_template from forms.distribusiform import DistribusiForm from forms.editorform import EditorForm from forms.publicthemeform import PublicThemeForm @@ -17,7 +13,10 @@ from forms.selectorform import SelectorForm from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) from statuspengguna.helper import UserHelper +from werkzeug.utils import secure_filename def Editor(): diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 3d47e72..4087463 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -1,3 +1,4 @@ +from app import settings from flask_wtf import FlaskForm from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize from wtforms import (IntegerField, SelectField, StringField, SubmitField, @@ -5,8 +6,6 @@ from wtforms import (IntegerField, SelectField, StringField, SubmitField, from wtforms.validators import (DataRequired, Length, NumberRange, ValidationError) -from app import settings - class UploadForm(FlaskForm): """File upload class for a new site in distribusi-verse""" @@ -33,7 +32,6 @@ class UploadForm(FlaskForm): year_choices.append((str(year), str(year))) return year_choices - sitename = StringField( "Name of your archive section:", validators=[ diff --git a/verse/migrations/env.py b/verse/migrations/env.py index 68feded..847c719 100644 --- a/verse/migrations/env.py +++ b/verse/migrations/env.py @@ -3,9 +3,8 @@ from __future__ import with_statement import logging from logging.config import fileConfig -from flask import current_app - from alembic import context +from flask import current_app # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -14,17 +13,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 +66,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(): diff --git a/verse/models/usermodel.py b/verse/models/usermodel.py index 254fe3f..43633ab 100644 --- a/verse/models/usermodel.py +++ b/verse/models/usermodel.py @@ -1,6 +1,5 @@ -from flask_login import UserMixin - from app import db +from flask_login import UserMixin class User(UserMixin, db.Model): diff --git a/verse/start.py b/verse/start.py index 80af292..b352c9b 100644 --- a/verse/start.py +++ b/verse/start.py @@ -2,12 +2,6 @@ from datetime import timedelta -from flask import (Blueprint, redirect, render_template, send_from_directory, - session, url_for) -from flask_login import current_user, login_required, logout_user -from flask_mail import Mail -from flask_wtf.csrf import CSRFError - # Interface! these are seperate files in main folder from adminpage import AdminPage from app import create_app, login_manager @@ -16,6 +10,11 @@ from distribusiselector import DistribusiSelector from distribusisinfo import DistribusisInfo from distribusiworkflow import DistribusiWorkflow from editor import Editor +from flask import (Blueprint, redirect, render_template, send_from_directory, + session, url_for) +from flask_login import current_user, login_required, logout_user +from flask_mail import Mail +from flask_wtf.csrf import CSRFError # Use upload form to populate filters from forms.uploadform import UploadForm from models.distribusimodel import Distribusis diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py index eaeb395..546cb0a 100644 --- a/verse/statuspengguna/forgotpassword.py +++ b/verse/statuspengguna/forgotpassword.py @@ -1,14 +1,13 @@ from datetime import datetime from uuid import uuid1 +from app import db from flask import render_template from flask_mail import Message -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) - -from app import db from forms.forgotpasswordform import ForgotPasswordForm from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) def ForgotPassword(mail): diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index 5daad75..1724bce 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -1,14 +1,13 @@ import os -from flask import flash -from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) - from app import db from distribusisinfo import DistribusisInfo +from flask import flash +from flask_login import current_user from models.distribusimodel import Distribusis from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) class UserHelper: diff --git a/verse/statuspengguna/loginuser.py b/verse/statuspengguna/loginuser.py index a7c5424..1968f56 100644 --- a/verse/statuspengguna/loginuser.py +++ b/verse/statuspengguna/loginuser.py @@ -1,7 +1,6 @@ from flask import abort, flash, redirect, render_template, request, url_for from flask_bcrypt import check_password_hash from flask_login import login_user - from forms.loginform import LoginForm from models.usermodel import User diff --git a/verse/statuspengguna/registeruser.py b/verse/statuspengguna/registeruser.py index ac8167e..4bf8da6 100644 --- a/verse/statuspengguna/registeruser.py +++ b/verse/statuspengguna/registeruser.py @@ -1,14 +1,13 @@ +from app import db from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash from flask_login import login_user +from forms.registerform import RegisterForm +from models.usermodel import User from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, InterfaceError, InvalidRequestError) from werkzeug.routing import BuildError -from app import db -from forms.registerform import RegisterForm -from models.usermodel import User - def RegisterUser(): registerform = RegisterForm() diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py index d91b2e6..57f0615 100644 --- a/verse/statuspengguna/resetpassword.py +++ b/verse/statuspengguna/resetpassword.py @@ -1,16 +1,15 @@ from datetime import datetime +from app import db from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash from flask_login import login_user +from forms.resetpasswordform import ResetPasswordForm +from models.usermodel import User from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, InterfaceError, InvalidRequestError) from werkzeug.routing import BuildError -from app import db -from forms.resetpasswordform import ResetPasswordForm -from models.usermodel import User - def ResetPassword(path): linkvalid = False diff --git a/verse/themeselector.py b/verse/themeselector.py index 2582809..68e2e1b 100644 --- a/verse/themeselector.py +++ b/verse/themeselector.py @@ -1,9 +1,8 @@ import os import shutil -from flask import render_template - from distribusisinfo import DistribusisInfo +from flask import render_template from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm diff --git a/verse/upload.py b/verse/upload.py index 6e3a336..a332413 100644 --- a/verse/upload.py +++ b/verse/upload.py @@ -1,16 +1,15 @@ import os import shutil -from flask import flash -from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, - InterfaceError, InvalidRequestError) - from app import db from distribusiselector import SelectCurrentDistribusi +from flask import flash +from flask_login import current_user from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User +from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, + InterfaceError, InvalidRequestError) from statuspengguna.helper import UserHelper diff --git a/verse/uploadpage.py b/verse/uploadpage.py index dc8e9bb..8718ec2 100644 --- a/verse/uploadpage.py +++ b/verse/uploadpage.py @@ -1,14 +1,13 @@ -from flask import render_template - +from app import APP from distribusiselector import SelectorVisible from distribusisinfo import DistribusisInfo +from flask import render_template from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm # UserPengguna from statuspengguna.helper import UserHelper -from app import APP from upload import UploadNewDistribusi, UploadUpdatedFiles From 1b6104896f3f71ea4e6ffb39133cd54b636f5b26 Mon Sep 17 00:00:00 2001 From: crunk Date: Fri, 26 Apr 2024 11:12:59 +0200 Subject: [PATCH 14/20] upgrade flask for whoosh --- requirements.txt | 42 ++++++++++++++++++----------- verse/app.py | 2 +- verse/{data => instance}/.gitignore | 0 3 files changed, 27 insertions(+), 17 deletions(-) rename verse/{data => instance}/.gitignore (100%) diff --git a/requirements.txt b/requirements.txt index fc2668f..952979d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,47 +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 +blinker==1.7.0 cffi==1.15.0 -click==8.0.3 +click==8.1.7 +distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi@e291e7497e40211c2ebd54ca32a1f4bdaed71230 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 +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 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 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 diff --git a/verse/app.py b/verse/app.py index 2e6a698..c21cf6d 100644 --- a/verse/app.py +++ b/verse/app.py @@ -17,7 +17,7 @@ login_manager = LoginManager() def create_app(): APP.secret_key = "secret-key" - APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data/distribusiverse.db" + APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db" APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True APP.config["MAX_CONTENT_LENGTH"] = 150 * 1024 * 1024 diff --git a/verse/data/.gitignore b/verse/instance/.gitignore similarity index 100% rename from verse/data/.gitignore rename to verse/instance/.gitignore From 1cf75379b2818f02cddf362c0be73fa753dba1bd Mon Sep 17 00:00:00 2001 From: crunk Date: Sat, 27 Apr 2024 11:26:19 +0200 Subject: [PATCH 15/20] make uploading of a 1GB possible --- verse/app.py | 2 +- verse/distribusiworkflow.py | 2 +- verse/forms/uploadform.py | 3 ++- verse/static/css/style.css | 12 ++++++------ 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/verse/app.py b/verse/app.py index c21cf6d..8112e94 100644 --- a/verse/app.py +++ b/verse/app.py @@ -19,7 +19,7 @@ def create_app(): APP.secret_key = "secret-key" 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 diff --git a/verse/distribusiworkflow.py b/verse/distribusiworkflow.py index b4ce328..f073232 100644 --- a/verse/distribusiworkflow.py +++ b/verse/distribusiworkflow.py @@ -89,7 +89,7 @@ def GetCssFile(distribusi): def RunDistribusi(userfolder, cssfile): parser = build_argparser() - args = parser.parse_args(["--menu-with-index", "-s", cssfile]) + args = parser.parse_args(["-t", "--menu-with-index", "-s", cssfile]) distribusify(args, userfolder) diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 4087463..89e56f4 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -56,6 +56,7 @@ class UploadForm(FlaskForm): option_widget=None, validators=[DataRequired()], ) + tags = StringField( "Add tags, seperated by commas. No need for the '#' sign:", validators=[validators.InputRequired(), Length(2, 500)], @@ -67,7 +68,7 @@ class UploadForm(FlaskForm): FileAllowed(["zip"], "Zip archives only!"), FileRequired(), FileSize( - max_size=104857600, + max_size=1073741824, message="Zipfile size must be smaller than 100MB", ), ], diff --git a/verse/static/css/style.css b/verse/static/css/style.css index ee67d83..d244b7e 100644 --- a/verse/static/css/style.css +++ b/verse/static/css/style.css @@ -2,8 +2,8 @@ body { font-family: monospace, monospace; font-size: 15px; - background-color: #272a33; - color:#E0B0FF; + background-color: #fdfdfd; + color:#29d148; word-wrap: break-word; line-height: 1.1; } @@ -12,7 +12,7 @@ div#login{ width: 30%; margin-left: auto; margin-right: auto; - background-color:#272a33; + background-color:#fdfdfd; text-decoration: none; } @@ -24,10 +24,10 @@ div#login form { } input[type=text], input[type=password], input[type=file] { - color: white; + color: #C397DF; width: 18em; max-width: 18em; - background-color: #2D3039; + background-color: #fdfdfd; border: 1px solid #E0B0FF; } @@ -41,7 +41,7 @@ div#upload form { padding-left: auto; padding-right: auto; width: 31em; - background-color:#30333f; + background-color:#fdfdfd; text-decoration: none; scroll-behavior: smooth; border-style: outset; From bd519d6925570d556316211462dbf9732f687daf Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 13:04:07 +0200 Subject: [PATCH 16/20] leave no file untouched --- verse/adminpage.py | 9 +- verse/admintool.py | 5 +- verse/app.py | 6 +- verse/deploydb.py | 3 +- verse/distribusikan/distribusikan.py | 46 +++ .../{ => distribusikan}/distribusiselector.py | 9 +- verse/{ => distribusikan}/distribusisinfo.py | 1 + .../{ => distribusikan}/distribusiworkflow.py | 14 +- verse/{ => distribusikan}/editor.py | 11 +- .../templates/distribusikan}/distribusi.html | 2 +- .../distribusiworkflow/editcss.html | 0 .../distribusiworkflow/launch.html | 2 +- .../distribusiworkflow/selector.html | 2 +- .../distribusiworkflow/theme.html | 4 +- .../distribusiworkflow/upload.html | 2 +- .../templates/distribusikan}/editor.html | 2 +- verse/{ => distribusikan}/themeselector.py | 3 +- verse/{ => distribusikan}/upload.py | 9 +- verse/{ => distribusikan}/uploadpage.py | 9 +- verse/forms/uploadform.py | 3 +- verse/models/usermodel.py | 3 +- verse/start.py | 83 +---- verse/statuspengguna/__init__.py | 0 verse/statuspengguna/forgotpassword.py | 24 +- verse/statuspengguna/helper.py | 9 +- verse/statuspengguna/loginuser.py | 19 +- verse/statuspengguna/registeruser.py | 21 +- verse/statuspengguna/resetpassword.py | 20 +- verse/statuspengguna/static/css/dropdown.css | 69 +++++ verse/statuspengguna/static/css/editor.css | 50 +++ verse/statuspengguna/static/css/selector.css | 53 ++++ verse/statuspengguna/static/css/style.css | 286 ++++++++++++++++++ verse/statuspengguna/static/icons/about.txt | 6 + .../static/icons/android-chrome-192x192.png | Bin 0 -> 5479 bytes .../static/icons/android-chrome-512x512.png | Bin 0 -> 12656 bytes .../static/icons/apple-touch-icon.png | Bin 0 -> 4441 bytes .../static/icons/favicon-16x16.png | Bin 0 -> 322 bytes .../static/icons/favicon-32x32.png | Bin 0 -> 666 bytes verse/statuspengguna/static/icons/favicon.ico | Bin 0 -> 15406 bytes .../static/icons/site.webmanifest | 1 + verse/statuspengguna/static/js/dropdown.js | 92 ++++++ .../statuspengguna/static/js/editorupdate.js | 19 ++ verse/statuspengguna/static/js/script.js | 25 ++ verse/statuspengguna/static/svg/arrow_1.svg | 75 +++++ verse/statuspengguna/static/svg/arrow_2.svg | 12 + verse/statuspengguna/static/svg/arrow_3.svg | 12 + .../statuspengguna}/forgotpassword.html | 5 +- .../templates/statuspengguna}/login.html | 6 +- .../templates/statuspengguna}/register.html | 4 +- .../statuspengguna}/resetpassword.html | 2 +- verse/templates/{ => base}/admin.html | 2 +- verse/templates/{ => base}/base.html | 0 verse/templates/{ => base}/filtermenu.html | 0 verse/templates/{ => base}/help.html | 2 +- verse/templates/{ => base}/index.html | 4 +- 55 files changed, 907 insertions(+), 139 deletions(-) create mode 100644 verse/distribusikan/distribusikan.py rename verse/{ => distribusikan}/distribusiselector.py (98%) rename verse/{ => distribusikan}/distribusisinfo.py (99%) rename verse/{ => distribusikan}/distribusiworkflow.py (97%) rename verse/{ => distribusikan}/editor.py (99%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusi.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/editcss.html (100%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/launch.html (95%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/selector.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/theme.html (95%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/upload.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/editor.html (98%) rename verse/{ => distribusikan}/themeselector.py (97%) rename verse/{ => distribusikan}/upload.py (97%) rename verse/{ => distribusikan}/uploadpage.py (89%) create mode 100644 verse/statuspengguna/__init__.py create mode 100644 verse/statuspengguna/static/css/dropdown.css create mode 100644 verse/statuspengguna/static/css/editor.css create mode 100644 verse/statuspengguna/static/css/selector.css create mode 100644 verse/statuspengguna/static/css/style.css create mode 100644 verse/statuspengguna/static/icons/about.txt create mode 100644 verse/statuspengguna/static/icons/android-chrome-192x192.png create mode 100644 verse/statuspengguna/static/icons/android-chrome-512x512.png create mode 100644 verse/statuspengguna/static/icons/apple-touch-icon.png create mode 100644 verse/statuspengguna/static/icons/favicon-16x16.png create mode 100644 verse/statuspengguna/static/icons/favicon-32x32.png create mode 100644 verse/statuspengguna/static/icons/favicon.ico create mode 100644 verse/statuspengguna/static/icons/site.webmanifest create mode 100644 verse/statuspengguna/static/js/dropdown.js create mode 100644 verse/statuspengguna/static/js/editorupdate.js create mode 100644 verse/statuspengguna/static/js/script.js create mode 100644 verse/statuspengguna/static/svg/arrow_1.svg create mode 100644 verse/statuspengguna/static/svg/arrow_2.svg create mode 100644 verse/statuspengguna/static/svg/arrow_3.svg rename verse/{templates => statuspengguna/templates/statuspengguna}/forgotpassword.html (86%) rename verse/{templates => statuspengguna/templates/statuspengguna}/login.html (80%) rename verse/{templates => statuspengguna/templates/statuspengguna}/register.html (91%) rename verse/{templates => statuspengguna/templates/statuspengguna}/resetpassword.html (96%) rename verse/templates/{ => base}/admin.html (98%) rename verse/templates/{ => base}/base.html (100%) rename verse/templates/{ => base}/filtermenu.html (100%) rename verse/templates/{ => base}/help.html (99%) rename verse/templates/{ => base}/index.html (97%) diff --git a/verse/adminpage.py b/verse/adminpage.py index c12fac3..7504d30 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -1,15 +1,16 @@ import os import shutil -from app import db -from distribusisinfo import DistribusisInfo from flask import render_template +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.admindistribusiform import AdminDistribusiForm from forms.adminuserform import AdminUserForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) def AdminPage(): diff --git a/verse/admintool.py b/verse/admintool.py index 8fc9e2e..21aa782 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -1,10 +1,11 @@ import sys +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 from models.usermodel import User # noqa: F401 -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) def admintool(): diff --git a/verse/app.py b/verse/app.py index 8112e94..56dd762 100644 --- a/verse/app.py +++ b/verse/app.py @@ -16,7 +16,7 @@ login_manager = LoginManager() def create_app(): - APP.secret_key = "secret-key" + 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"] = 1024 * 1024 * 1024 @@ -59,6 +59,10 @@ def settings(): return APP +def get_app(): + return APP + + def settings_from_file(): settings = {} if os.path.isfile("settings_development.toml"): diff --git a/verse/deploydb.py b/verse/deploydb.py index 6ebd4eb..26c2e5b 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -1,7 +1,8 @@ def deploy(): """Run deployment of database.""" - from app import create_app, db from flask_migrate import init, migrate, stamp, upgrade + + from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 # This model is required for flask_migrate to make the table from models.usermodel import User # noqa: F401 diff --git a/verse/distribusikan/distribusikan.py b/verse/distribusikan/distribusikan.py new file mode 100644 index 0000000..6350228 --- /dev/null +++ b/verse/distribusikan/distribusikan.py @@ -0,0 +1,46 @@ +from flask import Blueprint +from flask_login import login_required + +from distribusikan.distribusiselector import DistribusiSelector +# Distribusi Information +from distribusikan.distribusisinfo import DistribusisInfo +from distribusikan.distribusiworkflow import DistribusiWorkflow +from distribusikan.editor import Editor +from distribusikan.themeselector import ThemeSelector +from distribusikan.uploadpage import UploadPage + +distribusikan = Blueprint( + "distribusikan", + __name__, + template_folder="templates/distribusikan", + static_folder="static", +) + + +@distribusikan.route("/distribusi", methods=["GET", "POST"]) +@login_required +def distribusi(): + return DistribusiWorkflow() + + +@distribusikan.route("/upload", methods=["POST"]) +@login_required +def upload(): + return UploadPage() + + +@distribusikan.route("/theme", methods=["GET", "POST"]) +@login_required +def theme(): + return ThemeSelector() + +@distribusikan.route("/editor", methods=["GET", "POST"]) +@login_required +def editor(): + return Editor() + + +@distribusikan.route("/selector", methods=["GET", "POST"]) +@login_required +def selector(): + return DistribusiSelector() diff --git a/verse/distribusiselector.py b/verse/distribusikan/distribusiselector.py similarity index 98% rename from verse/distribusiselector.py rename to verse/distribusikan/distribusiselector.py index 3a5d72f..64a8ef4 100644 --- a/verse/distribusiselector.py +++ b/verse/distribusikan/distribusiselector.py @@ -1,10 +1,13 @@ import os import shutil -from app import db -from distribusisinfo import DistribusisInfo from flask import flash, render_template from flask_login import current_user +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -12,8 +15,6 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusisinfo.py b/verse/distribusikan/distribusisinfo.py similarity index 99% rename from verse/distribusisinfo.py rename to verse/distribusikan/distribusisinfo.py index 0b40c1b..5a47d5f 100644 --- a/verse/distribusisinfo.py +++ b/verse/distribusikan/distribusisinfo.py @@ -1,4 +1,5 @@ from flask_login import current_user + from models.distribusimodel import Distribusis from models.usermodel import User diff --git a/verse/distribusiworkflow.py b/verse/distribusikan/distribusiworkflow.py similarity index 97% rename from verse/distribusiworkflow.py rename to verse/distribusikan/distribusiworkflow.py index f073232..bd7260f 100644 --- a/verse/distribusiworkflow.py +++ b/verse/distribusikan/distribusiworkflow.py @@ -2,13 +2,17 @@ import os import shutil import zipfile -from app import db # Tada! from distribusi.cli import build_argparser -from distribusiselector import SelectorVisible -from distribusisinfo import DistribusisInfo +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.distribusiselector import SelectorVisible +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -17,13 +21,9 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper -from distribusi.distribusi import distribusify - def DistribusiWorkflow(): distribusiform = DistribusiForm() diff --git a/verse/editor.py b/verse/distribusikan/editor.py similarity index 99% rename from verse/editor.py rename to verse/distribusikan/editor.py index bfbfaeb..8fb0c9c 100644 --- a/verse/editor.py +++ b/verse/distribusikan/editor.py @@ -2,10 +2,14 @@ import os import shutil import bleach -from app import db from bleach_allowlist import all_styles -from distribusisinfo import DistribusisInfo from flask import render_template +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) +from werkzeug.utils import secure_filename + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.editorform import EditorForm from forms.publicthemeform import PublicThemeForm @@ -13,10 +17,7 @@ from forms.selectorform import SelectorForm from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) from statuspengguna.helper import UserHelper -from werkzeug.utils import secure_filename def Editor(): diff --git a/verse/templates/distribusi.html b/verse/distribusikan/templates/distribusikan/distribusi.html similarity index 97% rename from verse/templates/distribusi.html rename to verse/distribusikan/templates/distribusikan/distribusi.html index 8182e00..e85f04c 100644 --- a/verse/templates/distribusi.html +++ b/verse/distribusikan/templates/distribusikan/distribusi.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    diff --git a/verse/templates/distribusiworkflow/editcss.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/editcss.html similarity index 100% rename from verse/templates/distribusiworkflow/editcss.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/editcss.html diff --git a/verse/templates/distribusiworkflow/launch.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html similarity index 95% rename from verse/templates/distribusiworkflow/launch.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html index 3c4685a..562b616 100644 --- a/verse/templates/distribusiworkflow/launch.html +++ b/verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html @@ -3,7 +3,7 @@

    Run distribusi on your files. This will generate your website and make your content public. Distribusi will unpack your zip file and turn it into a website!

    -
    + {{ distribusiform.csrf_token }} {% if files_uploaded or distribusi_live %}
    diff --git a/verse/templates/distribusiworkflow/selector.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/selector.html similarity index 97% rename from verse/templates/distribusiworkflow/selector.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/selector.html index 44d6da4..88dac97 100644 --- a/verse/templates/distribusiworkflow/selector.html +++ b/verse/distribusikan/templates/distribusikan/distribusiworkflow/selector.html @@ -1,7 +1,7 @@

    Welcome back to your Distribusi

    You have already uploaded a distribusi website:

    - + {{ selectorform.csrf_token }}
    {{ selectorform.distribusis.label }} diff --git a/verse/templates/distribusiworkflow/theme.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/theme.html similarity index 95% rename from verse/templates/distribusiworkflow/theme.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/theme.html index 2b92907..e6a7b1e 100644 --- a/verse/templates/distribusiworkflow/theme.html +++ b/verse/distribusikan/templates/distribusikan/distribusiworkflow/theme.html @@ -4,7 +4,7 @@ step 3.

    Don't forget to press Save


    - + {{ themeform.csrf_token }}
    {{ themeform.theme.label }} @@ -23,7 +23,7 @@ {% endif %}
    -
    + {{ publicthemeform.csrf_token }}
    {{ publicthemeform.publicthemes.label }} diff --git a/verse/templates/distribusiworkflow/upload.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/upload.html similarity index 97% rename from verse/templates/distribusiworkflow/upload.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/upload.html index 1dba794..fe3aa78 100644 --- a/verse/templates/distribusiworkflow/upload.html +++ b/verse/distribusikan/templates/distribusikan/distribusiworkflow/upload.html @@ -1,7 +1,7 @@

    Step 1: Upload

    Upload your files here:

    - + {{ uploadform.csrf_token }}
    {{ uploadform.sitename.label }} diff --git a/verse/templates/editor.html b/verse/distribusikan/templates/distribusikan/editor.html similarity index 98% rename from verse/templates/editor.html rename to verse/distribusikan/templates/distribusikan/editor.html index 69fc8e8..7ce1c24 100644 --- a/verse/templates/editor.html +++ b/verse/distribusikan/templates/distribusikan/editor.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    diff --git a/verse/themeselector.py b/verse/distribusikan/themeselector.py similarity index 97% rename from verse/themeselector.py rename to verse/distribusikan/themeselector.py index 68e2e1b..96b2d8a 100644 --- a/verse/themeselector.py +++ b/verse/distribusikan/themeselector.py @@ -1,8 +1,9 @@ import os import shutil -from distribusisinfo import DistribusisInfo from flask import render_template + +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm diff --git a/verse/upload.py b/verse/distribusikan/upload.py similarity index 97% rename from verse/upload.py rename to verse/distribusikan/upload.py index a332413..32d8eaa 100644 --- a/verse/upload.py +++ b/verse/distribusikan/upload.py @@ -1,15 +1,16 @@ import os import shutil -from app import db -from distribusiselector import SelectCurrentDistribusi from flask import flash from flask_login import current_user +from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, + InterfaceError, InvalidRequestError) + +from app import db +from distribusikan.distribusiselector import SelectCurrentDistribusi from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, - InterfaceError, InvalidRequestError) from statuspengguna.helper import UserHelper diff --git a/verse/uploadpage.py b/verse/distribusikan/uploadpage.py similarity index 89% rename from verse/uploadpage.py rename to verse/distribusikan/uploadpage.py index 8718ec2..aaf9e25 100644 --- a/verse/uploadpage.py +++ b/verse/distribusikan/uploadpage.py @@ -1,14 +1,15 @@ -from app import APP -from distribusiselector import SelectorVisible -from distribusisinfo import DistribusisInfo from flask import render_template + +from app import APP +from distribusikan.distribusiselector import SelectorVisible +from distribusikan.distribusisinfo import DistribusisInfo +from distribusikan.upload import UploadNewDistribusi, UploadUpdatedFiles from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm # UserPengguna from statuspengguna.helper import UserHelper -from upload import UploadNewDistribusi, UploadUpdatedFiles def UploadPage(): diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 89e56f4..0d9b00a 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -1,4 +1,3 @@ -from app import settings from flask_wtf import FlaskForm from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize from wtforms import (IntegerField, SelectField, StringField, SubmitField, @@ -6,6 +5,8 @@ from wtforms import (IntegerField, SelectField, StringField, SubmitField, from wtforms.validators import (DataRequired, Length, NumberRange, ValidationError) +from app import settings + class UploadForm(FlaskForm): """File upload class for a new site in distribusi-verse""" diff --git a/verse/models/usermodel.py b/verse/models/usermodel.py index 43633ab..254fe3f 100644 --- a/verse/models/usermodel.py +++ b/verse/models/usermodel.py @@ -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""" diff --git a/verse/start.py b/verse/start.py index b352c9b..cf7ccb1 100644 --- a/verse/start.py +++ b/verse/start.py @@ -2,36 +2,33 @@ from datetime import timedelta -# Interface! these are seperate files in main folder -from adminpage import AdminPage -from app import create_app, login_manager -from distribusiselector import DistribusiSelector -# Distribusi Information -from distribusisinfo import DistribusisInfo -from distribusiworkflow import DistribusiWorkflow -from editor import Editor from flask import (Blueprint, redirect, render_template, send_from_directory, session, url_for) from flask_login import current_user, login_required, logout_user from flask_mail import Mail from flask_wtf.csrf import CSRFError + +# Interface! these are seperate files in main folder +from adminpage import AdminPage +from app import create_app, login_manager +from distribusikan.distribusikan import distribusikan +from distribusikan.distribusisinfo import DistribusisInfo # Use upload form to populate filters from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from statuspengguna.forgotpassword import ForgotPassword -# UserPengguna +from statuspengguna.forgotpassword import forgot_password from statuspengguna.helper import UserHelper -from statuspengguna.loginuser import LoginUser -from statuspengguna.registeruser import RegisterUser -from statuspengguna.resetpassword import ResetPassword -from themeselector import ThemeSelector -from uploadpage import UploadPage +from statuspengguna.loginuser import login_section +from statuspengguna.registeruser import register_user APP = create_app() stash_page = Blueprint("stash_page", __name__, static_folder="stash") +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(distribusikan) APP.register_blueprint(stash_page) -mail = Mail(APP) @APP.before_request @@ -60,7 +57,7 @@ def index(): categories = uploadform.category.choices adminuser = isadminuser() template = render_template( - "index.html", + "base/index.html", distribusisindex=distribusisindex, years=years, categories=categories, @@ -71,25 +68,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(): - return UploadPage() - - -@APP.route("/theme", methods=["GET", "POST"]) -@login_required -def theme(): - return ThemeSelector() + return render_template("base/help.html") @APP.route("/publicthemes/") @@ -100,18 +79,6 @@ def publicthemes(path): 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")) @@ -132,26 +99,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/", 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 diff --git a/verse/statuspengguna/__init__.py b/verse/statuspengguna/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py index 546cb0a..6c0b2f9 100644 --- a/verse/statuspengguna/forgotpassword.py +++ b/verse/statuspengguna/forgotpassword.py @@ -1,14 +1,28 @@ from datetime import datetime from uuid import uuid1 -from app import db -from flask import render_template -from flask_mail import Message -from forms.forgotpasswordform import ForgotPasswordForm -from models.usermodel import User +from flask import Blueprint, render_template +from flask_mail import Mail, Message from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, InvalidRequestError) +from app import db, get_app +from forms.forgotpasswordform import ForgotPasswordForm +from models.usermodel 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): forgotpasswordform = ForgotPasswordForm() diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index 1724bce..d90e495 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -1,14 +1,15 @@ import os -from app import db -from distribusisinfo import DistribusisInfo from flask import flash from flask_login import current_user -from models.distribusimodel import Distribusis -from models.usermodel import User from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, InvalidRequestError) +from app import db +from distribusikan.distribusisinfo import DistribusisInfo +from models.distribusimodel import Distribusis +from models.usermodel import User + class UserHelper: def is_zip_uploaded(distribusiname): diff --git a/verse/statuspengguna/loginuser.py b/verse/statuspengguna/loginuser.py index 1968f56..a1a138a 100644 --- a/verse/statuspengguna/loginuser.py +++ b/verse/statuspengguna/loginuser.py @@ -1,9 +1,23 @@ -from flask import abort, flash, redirect, render_template, request, url_for +from flask import (Blueprint, abort, flash, redirect, render_template, request, + send_from_directory, session, url_for) from flask_bcrypt import check_password_hash from flask_login import login_user + from forms.loginform import LoginForm from models.usermodel 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(): loginform = LoginForm() @@ -14,11 +28,14 @@ def LoginUser(): loginform.password.errors.append("Invalid email or password!") return render_template("login.html", loginform=loginform) if check_password_hash(user.password, loginform.password.data): + print(type(user)) login_user(user) 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") diff --git a/verse/statuspengguna/registeruser.py b/verse/statuspengguna/registeruser.py index 4bf8da6..d9f6a61 100644 --- a/verse/statuspengguna/registeruser.py +++ b/verse/statuspengguna/registeruser.py @@ -1,13 +1,26 @@ -from app import db -from flask import flash, redirect, render_template, 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 forms.registerform import RegisterForm -from models.usermodel import User from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, InterfaceError, InvalidRequestError) from werkzeug.routing import BuildError +from app import db +from forms.registerform import RegisterForm +from models.usermodel 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(): registerform = RegisterForm() diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py index 57f0615..ef1735a 100644 --- a/verse/statuspengguna/resetpassword.py +++ b/verse/statuspengguna/resetpassword.py @@ -1,15 +1,29 @@ from datetime import datetime -from app import db from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash from flask_login import login_user -from forms.resetpasswordform import ResetPasswordForm -from models.usermodel import User from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, InterfaceError, InvalidRequestError) from werkzeug.routing import BuildError +from app import db +from forms.resetpasswordform import ResetPasswordForm +from models.usermodel import User +from statuspengguna import statuspengguna + +reset_password = Blueprint( + "reset_password", + __name__, + template_folder="templates/statuspengguna", + static_folder="static", +) + + +@reset_password.route("/resetpassword/", methods=["GET", "POST"]) +def resetpassword(path): + return ResetPassword(path) + def ResetPassword(path): linkvalid = False diff --git a/verse/statuspengguna/static/css/dropdown.css b/verse/statuspengguna/static/css/dropdown.css new file mode 100644 index 0000000..07509d5 --- /dev/null +++ b/verse/statuspengguna/static/css/dropdown.css @@ -0,0 +1,69 @@ +/* Dropdown Button */ +/* for sorting on year and category +*/ +button { + background-color: #E0B0FF; + text-decoration: none; + border: none; +} +.filter { + display: none; +} + +.activebtn { + background-color: #62b264; +} + +.show { + display: block; +} +.dropdown { + position: relative; + display: inline-block; +} + +/* Dropdown Content (Hidden by Default) */ +.dropdown-content { + display: none; + position: absolute; + background-color: #E0B0FF; + min-width: 120px; + border: 2px solid; + z-index: 1; + border-style: outset; +} + +/* Links inside the dropdown */ +.dropdown-content button { + color: black; + padding: 6px; + border: none; + min-width: inherit; + text-align: left; + text-decoration: none; + display: block; +} +.dropbtn { + margin-top: 1em; +} +/* Change color of dropdown links on hover */ +.dropdown-content button:hover {background-color: #62b264;} + + +/* Show the dropdown menu on hover */ +.dropdown:hover .dropdown-content {display: block;} + +/* Change the background color of the dropdown button when the dropdown content is shown */ +.dropdown:hover .dropbtn {background-color: #62b264;} + +@media only screen and (min-device-width: 320px) and (max-device-width: 480px) { + .dropdown-content button { + font-size: 0.7em; + } + .container > button { + font-size: 0.7em; + } + .dropdown > button { + font-size: 0.7em; + } +} diff --git a/verse/statuspengguna/static/css/editor.css b/verse/statuspengguna/static/css/editor.css new file mode 100644 index 0000000..d087622 --- /dev/null +++ b/verse/statuspengguna/static/css/editor.css @@ -0,0 +1,50 @@ +.editareas { + margin: auto; + display: flex; + justify-content: flex-start; +} +.editarea { + width: 30%; + border: 3px solid #E0B0FF; + border-style: outset; + margin-right: 1em; + margin-left: 0; +} + +.editor { + min-width: 35%; +} +.editform { + width: 100%%; + margin: 0 auto; +} +#editorsubmitform { + padding-top: 1em; +} +.required label { + display: block; + padding-bottom: 2px; + width: 100% +} +textarea { + width: 100%; + height: 100%; + box-sizing: border-box; + min-height: 250px; + background: #E0B0FF; + outline: none; + font-family: Courier, sans-serif; + font-size: 16px; +} + +iframe { + bottom: 0; + position: relative; + margin-top: 1em; + width: 100%; + height: 30em; +} +#html { + background-color: #60337F; + color: lightgrey; +} diff --git a/verse/statuspengguna/static/css/selector.css b/verse/statuspengguna/static/css/selector.css new file mode 100644 index 0000000..a500a61 --- /dev/null +++ b/verse/statuspengguna/static/css/selector.css @@ -0,0 +1,53 @@ +.selector-style { + padding: 0; + width: 20em; + max-width: 20em; + position: relative; + border: none; + background: #E0B0FF; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin: 1px; +} + +.selector-style select { + padding: 0.2em 0.2em; + width: 20em; + max-width: 20em; + border: none; + box-shadow: none; + background-color: #E0B0FF; + background-image: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.selector-style:after { + top: 50%; + left: 95%; + border: solid; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(0, 0, 0, 0); + border-top-color: #000000; + margin-top: -2px; + z-index: 100; +} +select.selector option{ + color: white; + background-color: #60337F; + padding: 0 10px; +} + +.selector-style select:focus { + outline: none; +} +.selector-style select option:hover { + background: #60337F; +} diff --git a/verse/statuspengguna/static/css/style.css b/verse/statuspengguna/static/css/style.css new file mode 100644 index 0000000..d244b7e --- /dev/null +++ b/verse/statuspengguna/static/css/style.css @@ -0,0 +1,286 @@ +body +{ + font-family: monospace, monospace; + font-size: 15px; + background-color: #fdfdfd; + color:#29d148; + word-wrap: break-word; + line-height: 1.1; +} + +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: #C397DF; + width: 18em; + max-width: 18em; + background-color: #fdfdfd; + 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:#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; +} + +#mainworkflow +{ + width: 30em; + margin:0 auto; +} + +#distribusiverse { + margin-bottom: 11em; +} +#distribusi-index { + padding-left: 1em; +} + +div#buttons{ + position: fixed; + top: 0.5em; + right: 0.5em; + display:flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +div#buttons .distribusi input{ + border: none; + background: #fff600; + text-decoration: none; + margin: 0.2em; +} +div#buttons .distribusi input:hover{ + background: #ffbf00; + +} +fieldset.required { + border: none; +} + +fieldset.required > ul { + padding-left: 0px; +} + +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; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin: 0.2em; +} + +input:hover { + background: #60337F; +} + +input[type="submit"]:disabled:hover, +input[type="submit"]:disabled, +input[type="submit"]:disabled:focus { + background-color: #2D3039; + color: #d28cff; +} + +.error { + font-size: 110%; + color: #F92020; +} + +#delete { + color: black; + background-color: #F92020; +} + +#update { + 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: "$ "; +} + +@media (prefers-reduced-motion: no-preference) { + @keyframes flash { + 50% { opacity: 0; } + } + @keyframes reveal { + from { width: 2em; } /* Width of ::before */ + to { width: 55%; } + } + #fancyboi { + width: 55%; + padding: 0.5em; + overflow: hidden; + white-space: nowrap; + animation: reveal 4s linear; + text-overflow: "â–ˆ"; + background-color: #2D3039; + } + #fancyboi::after { + content: "â–ˆ"; + animation: flash 0.5s step-end infinite; + } +} + +div.maincontent{ + width: 55%; + border: 3px #E0B0FF; + margin-top: 0.5em; + padding: 0.5em; + border-style: outset; +} + +.tags{ + background-color: #000; + color: #fff; + display: inline-block; + padding-left: 4px; + padding-right: 4px; + text-align: center; + margin: 1px; +} + +.searched { + background: #fff600 !important; + color: black !important; +} + +.tooltip { + position: relative; + display: inline-block; + border-bottom: 1px dotted black; +} + +.tooltip .tooltiptext { + visibility: hidden; + width: 120px; + background-color: black; + color: #fff; + text-align: center; + padding: 5px 0; + position: absolute; + z-index: 1; + 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; +} + +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.code-example { + width: 100%; + color: black; + padding: 1em; + box-sizing: border-box; + background: #E0B0FF; + outline: none; + font-family: Courier, sans-serif; + font-size: 16px; +} +/* +Project colors so far. +light +#E0B0FF +medium +#d28cff +dark +#60337F + +background dark +#2D3039 + +yellow important +#fff600 + +red: danger +ff5a5a +backgrounds +*/ diff --git a/verse/statuspengguna/static/icons/about.txt b/verse/statuspengguna/static/icons/about.txt new file mode 100644 index 0000000..f6a9f45 --- /dev/null +++ b/verse/statuspengguna/static/icons/about.txt @@ -0,0 +1,6 @@ +This favicon was generated using the following font: + +- Font Title: Klee One +- Font Author: Copyright 2020 The Klee Project Authors (https://github.com/fontworks-fonts/Klee) +- Font Source: http://fonts.gstatic.com/s/kleeone/v5/LDIxapCLNRc6A8oT4q4AOeekWPrP.ttf +- Font License: SIL Open Font License, 1.1 (http://scripts.sil.org/OFL)) diff --git a/verse/statuspengguna/static/icons/android-chrome-192x192.png b/verse/statuspengguna/static/icons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..3b958ff2fe8d23b1731a3b4ae5bfedd63de5351d GIT binary patch literal 5479 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 UF$%_tssI2007*qoM6N<$f)4Gf%>V!Z literal 0 HcmV?d00001 diff --git a/verse/statuspengguna/static/icons/favicon-32x32.png b/verse/statuspengguna/static/icons/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..8478c1eef1176757365a114418036d6fd10fcc68 GIT binary patch literal 666 zcmV;L0%iS)P)AUOi6{<1x<3iAVd_Me+ye?4&|KV;#qwkJU_no+q~j-V;S{vR8tw# zRCG;+5J%Tk#xx%;KoM=CJk+I=rC$6#KAH&lASRZhspw_{nE8PI)&S=Zb+W&tfem@3 zl>d!14v15AR;J=g(Pb#1=;Hf z5E15t&1Gj%6>r)%UeVQXJN%3&;34~KJNgbd6PseV{ zByvGCBRxNi2WTp|%DspPy`Gom0J7`*rgxc+=%mF;U@D357ZpUeJ z0By(bBO)|aT{j1?Z}SmEgg)z^mG672tt_|0We}8oGiL%6HBRC zfPiQ|TxJ1Adub528_RD*z@TyHnvZ~{QV%G<0LThWlbn&F^8f$<07*qoM6N<$f(fl4 ATmS$7 literal 0 HcmV?d00001 diff --git a/verse/statuspengguna/static/icons/favicon.ico b/verse/statuspengguna/static/icons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f3757b92dd496a92dafd7e5252e463ab68f6ccc2 GIT binary patch literal 15406 zcmeHO2T)X36#Xb50tOhdVM7tc7D^O5f`VAE#D*2I#g36^G%5X^Nu_g+lVwXi@ zCW*#LV#OL`kA)>MYD_g6jT*Bj_dZ-_%`D6Q7n430qG)?F|rauA~1Mj^ICKYThj3mTQ$DE$%# zyp26G*S;wG{o11#8#x%gTXx1@+rNGBURSQ}_y5+{=B32Uz@%=&Os@YdCm)S#)yMtK z7Yw#hlzhf9GCUf2@8_CapKW?X`uSShf=Fn$m_Q=f*7Rt2?IDRiciiY+e*rOZ0b9vTfo0fq9z|85n|qI*m}@ zs}360@Ig@BCJ1fPlJ>H$Vol48TMYFFht0AEJ=l5?M^biy<}SK5>wr(@WMR{krAUdH zjuGMg(afhQ9Lm~@{8kNuuzlJJqxVxe_P3MlFgjwOULEgkK93J4q@aOUU4bL4X=~ih zJfXL&9GSXh4awJ*d%E*Fc#kb(R|d5!Radl6jFGR!u{nO3z~JxQ5RZ3URm2W*RzkJ=d&%(+l)zz ztRnx?^3R3*?P4KMX zfePhaaBA^KipV~f_L;C{Q9c&;HLr1lLVAMjRJ~$VoLID55!u`?O~|+9aa_jEPVDhf zbPgF#zB}XT*`gC_d3uR_Cx>$6N5|r~P2VUco8|LaV#_<}nALA028Q&YvsWu|9&1$7 z7kuW43GI!w6Bgo^jb{~=Tilo|&6meZ>9ZI6AC&Iu%d^Bgl6?!Rf&@#*(kp{~VD`TY z`MNu75547R9w_y`(B|axvfoYqH_h$;^@n*9qG+X9N``n6%9e<;aHsJylsJQ&=HVF9{!L8m8iS;PQ?NcR6^D~|;_~tXxSesrsJl3G>#=m?9CQw9 zk2+Oq3g5)h-T}eB0d&vhPaJMNRCTZPf;Z)Do&6^EmD@E6#st!f{ntC>chJ03?A#?s z=chZb;Sb_fXFTVyssCsaCfdCbY*?Y zJ37Ia`06Di5)|?On>b*Kz{FhO59^K_v?br+?wYU)}kpWESa|6uDSF}EG<9fc0A zPA=fP4t;XW_0ClMx_FqA@uKm^W68tS4M&o9S!`Y_fuH$1<~NwrB4-W#=-%m93vnGv z;OD-0LiZpXqriQ@<7w~g42z5-rSP*3ch{d1+#c(|u?PQHU~f8O{%fnggsYRY&>?+vlFi}gIHkU|w_O%IIpc3wv!2c2f3)oioxOVr`}u+=T`EG;9J&&L0 zHc>W*|K{3bbQY>2@Gpv`@GIIA%$by4e|EmRO85mF<8hwu#mtGF+g1`k$BFWtERUTw ze$5n#izrTirSLQMHRF;6cJ`&WPE9wv?Uk}W`!ab8Rq&}=Ti}oIZ;K!4ELWWEl*m`Y z{y&kw&*O#t;R@wl#o5^sXU~$_pW||wvuN2MP}rUChdCxkiFhDm`^HrJvwxK~DiOZb zyajgly%PGzDHhLV41Qj#nfu`Ng87r1>+%eaSsv$iGEaizPU;cvat>O*-5FVvQwe+V zG4jUXGPX=~+wKB8w`^3Bz*M_ZjhDDWtv>pm9E<5kF`^?n_7_~%eTCvr^%0HgyJ+mW8I2JC9=T35uu2` literal 0 HcmV?d00001 diff --git a/verse/statuspengguna/static/icons/site.webmanifest b/verse/statuspengguna/static/icons/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/verse/statuspengguna/static/icons/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/verse/statuspengguna/static/js/dropdown.js b/verse/statuspengguna/static/js/dropdown.js new file mode 100644 index 0000000..505d4a8 --- /dev/null +++ b/verse/statuspengguna/static/js/dropdown.js @@ -0,0 +1,92 @@ +filterSelection("all", "None"); +function filterSelection(c, name, id) { + resetDropDownButtons(); + var i; + var button = document.getElementById(id); + if(button){ + button.innerText = name; + addClass(button, "activebtn"); + } + var alldistribusis = document.getElementsByClassName("filter"); + if (c == "all") { + for (i = 0; i < alldistribusis.length; i++) { + addClass(alldistribusis[i], "show"); + } + } + else { + for (i = 0; i < alldistribusis.length; i++) { + removeClass(alldistribusis[i], "show"); + if (alldistribusis[i].className.indexOf(c) > -1) { + addClass(alldistribusis[i], "show"); + } + } + } +} + +function resetDropDownButtons(){ + 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"); + } +} +function addClass(element, name) { + var i, arr1, arr2; + arr1 = element.className.split(" "); + arr2 = name.split(" "); + for (i = 0; i < arr2.length; i++) { + if (arr1.indexOf(arr2[i]) == -1) {element.className += " " + arr2[i];} + } +} + +function removeClass(element, name) { + var i, arr1, arr2; + arr1 = element.className.split(" "); + arr2 = name.split(" "); + for (i = 0; i < arr2.length; i++) { + while (arr1.indexOf(arr2[i]) > -1) { + arr1.splice(arr1.indexOf(arr2[i]), 1); + } + } + element.className = arr1.join(" "); +} + + +let searchInput = document.getElementById('tagsearch'); +let timeout = null; +// Listen for keystroke events +searchInput.addEventListener('keyup', function (e) { + // Clear the timeout if it has already been set. + clearTimeout(timeout); + // Make a new timeout set to go off in 1000ms (1 second) + timeout = setTimeout(function () { + console.log('Input Value:', searchInput.value); + if (searchInput.value.length > 2) { + searchTags(searchInput.value); + } else { + clearSearchTags(); + } + }, 1000); +}); + +function searchTags(searchInput) { + let tag_ele = document.getElementsByClassName('tags'); + for (var i = 0; i < tag_ele.length; ++i) { + let searchText = searchInput.toLowerCase().trim(); + let tagtext = tag_ele[i].innerText.toLowerCase(); + if(searchText.includes(tagtext) || tagtext.includes(searchText)) { + addClass(tag_ele[i], "searched"); + } + else { + removeClass(tag_ele[i], "searched"); + } + } +} + +function clearSearchTags() { + let tag_ele = document.getElementsByClassName('tags'); + for (var i = 0; i < tag_ele.length; ++i) { + removeClass(tag_ele[i], "searched"); + } +} diff --git a/verse/statuspengguna/static/js/editorupdate.js b/verse/statuspengguna/static/js/editorupdate.js new file mode 100644 index 0000000..f76219e --- /dev/null +++ b/verse/statuspengguna/static/js/editorupdate.js @@ -0,0 +1,19 @@ +function update() { + +var html = document.getElementById("html"); +var css = document.getElementById("css"); +var code = document.getElementById("code").contentWindow.document; + +document.body.onkeyup = function(){ + code.open(); + code.writeln(html.value+""); + code.close(); + }; +document.addEventListener("DOMContentLoaded", function(){ + code.open(); + code.writeln(html.value+""); + code.close(); + }); +}; + +update(); diff --git a/verse/statuspengguna/static/js/script.js b/verse/statuspengguna/static/js/script.js new file mode 100644 index 0000000..2bd662b --- /dev/null +++ b/verse/statuspengguna/static/js/script.js @@ -0,0 +1,25 @@ +console.log("everything is still smooth") + +function scrollToTheme() { + var uploadsuccessful = document.getElementById("uploadsuccessful"); + if(uploadsuccessful){ + const theme = document.getElementById('theme') + theme.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } +} + +function scrollToLaunch() { + var cssSelected = document.getElementById("cssSelected"); + if(cssSelected){ + const launch = document.getElementById('launch') + launch.scrollIntoView({ behavior: 'smooth', block: 'end' }); + } +} + + +document.addEventListener("DOMContentLoaded", scrollToTheme); +document.addEventListener("DOMContentLoaded", scrollToLaunch); + +// function(e) { +// (e.keyCode === 13 || e.keyCode === 32) && $(this).trigger("click") +// } diff --git a/verse/statuspengguna/static/svg/arrow_1.svg b/verse/statuspengguna/static/svg/arrow_1.svg new file mode 100644 index 0000000..21073fd --- /dev/null +++ b/verse/statuspengguna/static/svg/arrow_1.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + diff --git a/verse/statuspengguna/static/svg/arrow_2.svg b/verse/statuspengguna/static/svg/arrow_2.svg new file mode 100644 index 0000000..ea2a026 --- /dev/null +++ b/verse/statuspengguna/static/svg/arrow_2.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/verse/statuspengguna/static/svg/arrow_3.svg b/verse/statuspengguna/static/svg/arrow_3.svg new file mode 100644 index 0000000..c447811 --- /dev/null +++ b/verse/statuspengguna/static/svg/arrow_3.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/verse/templates/forgotpassword.html b/verse/statuspengguna/templates/statuspengguna/forgotpassword.html similarity index 86% rename from verse/templates/forgotpassword.html rename to verse/statuspengguna/templates/statuspengguna/forgotpassword.html index 3932ca0..bed8afc 100644 --- a/verse/templates/forgotpassword.html +++ b/verse/statuspengguna/templates/statuspengguna/forgotpassword.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    @@ -6,7 +6,8 @@

    Enter the email address that was used to register with Distribusiverse.

    - + {{ forgotpasswordform.csrf_token }}
    {{ forgotpasswordform.email.label }} diff --git a/verse/templates/login.html b/verse/statuspengguna/templates/statuspengguna/login.html similarity index 80% rename from verse/templates/login.html rename to verse/statuspengguna/templates/statuspengguna/login.html index b21c11f..70a02b2 100644 --- a/verse/templates/login.html +++ b/verse/statuspengguna/templates/statuspengguna/login.html @@ -1,7 +1,7 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    - + {{ loginform.csrf_token }}
    {{ loginform.email.label }} @@ -19,7 +19,7 @@
    {{ loginform.submit }} - Forgot Password? + Forgot Password?
    diff --git a/verse/templates/register.html b/verse/statuspengguna/templates/statuspengguna/register.html similarity index 91% rename from verse/templates/register.html rename to verse/statuspengguna/templates/statuspengguna/register.html index 729ba7d..dcdcbad 100644 --- a/verse/templates/register.html +++ b/verse/statuspengguna/templates/statuspengguna/register.html @@ -1,7 +1,7 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    -
    + {{ registerform.csrf_token }}
    {{ registerform.username.label }} diff --git a/verse/templates/resetpassword.html b/verse/statuspengguna/templates/statuspengguna/resetpassword.html similarity index 96% rename from verse/templates/resetpassword.html rename to verse/statuspengguna/templates/statuspengguna/resetpassword.html index cb7cf54..bf2438e 100644 --- a/verse/templates/resetpassword.html +++ b/verse/statuspengguna/templates/statuspengguna/resetpassword.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    {% if linkvalid%} diff --git a/verse/templates/admin.html b/verse/templates/base/admin.html similarity index 98% rename from verse/templates/admin.html rename to verse/templates/base/admin.html index c1fe24a..2272dc2 100644 --- a/verse/templates/admin.html +++ b/verse/templates/base/admin.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    diff --git a/verse/templates/base.html b/verse/templates/base/base.html similarity index 100% rename from verse/templates/base.html rename to verse/templates/base/base.html diff --git a/verse/templates/filtermenu.html b/verse/templates/base/filtermenu.html similarity index 100% rename from verse/templates/filtermenu.html rename to verse/templates/base/filtermenu.html diff --git a/verse/templates/help.html b/verse/templates/base/help.html similarity index 99% rename from verse/templates/help.html rename to verse/templates/base/help.html index 953a795..2abef0c 100644 --- a/verse/templates/help.html +++ b/verse/templates/base/help.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    diff --git a/verse/templates/index.html b/verse/templates/base/index.html similarity index 97% rename from verse/templates/index.html rename to verse/templates/base/index.html index d18caf6..e828dcd 100644 --- a/verse/templates/index.html +++ b/verse/templates/base/index.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
    @@ -50,7 +50,7 @@

    List of distribusis

    - {% include 'filtermenu.html' %} + {% include 'base/filtermenu.html' %}
      {% for name, distribusi in distribusisindex.items() %}
    • From cfe1b646e4b24d9201bfc7b7224c04a75262731a Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 13:15:00 +0200 Subject: [PATCH 17/20] build fix --- verse/distribusikan/templates/distribusikan/editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verse/distribusikan/templates/distribusikan/editor.html b/verse/distribusikan/templates/distribusikan/editor.html index 7ce1c24..b6e39f4 100644 --- a/verse/distribusikan/templates/distribusikan/editor.html +++ b/verse/distribusikan/templates/distribusikan/editor.html @@ -1,6 +1,6 @@ {% extends "base/base.html" %} {% block main %} - +
      From c854c2687e26910b7034aa31b6792f8c0efffda0 Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 13:15:23 +0200 Subject: [PATCH 18/20] black and isort sometimes want different things --- verse/adminpage.py | 8 ++++++-- verse/admintool.py | 8 ++++++-- verse/deploydb.py | 1 + verse/distribusikan/distribusikan.py | 2 ++ verse/distribusikan/distribusiselector.py | 9 +++++++-- verse/distribusikan/distribusiworkflow.py | 10 ++++++++-- verse/distribusikan/editor.py | 8 ++++++-- verse/distribusikan/upload.py | 9 +++++++-- verse/distribusikan/uploadpage.py | 1 + verse/forms/editorform.py | 9 +++++++-- verse/forms/uploadform.py | 17 +++++++++++++---- verse/start.py | 11 +++++++++-- verse/statuspengguna/forgotpassword.py | 8 ++++++-- verse/statuspengguna/helper.py | 8 ++++++-- verse/statuspengguna/loginuser.py | 13 +++++++++++-- verse/statuspengguna/registeruser.py | 9 +++++++-- verse/statuspengguna/resetpassword.py | 9 +++++++-- 17 files changed, 110 insertions(+), 30 deletions(-) diff --git a/verse/adminpage.py b/verse/adminpage.py index 7504d30..1dd2b42 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -2,8 +2,12 @@ import os import shutil from flask import render_template -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import db from distribusikan.distribusisinfo import DistribusisInfo diff --git a/verse/admintool.py b/verse/admintool.py index 21aa782..9d392ab 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -1,7 +1,11 @@ import sys -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 diff --git a/verse/deploydb.py b/verse/deploydb.py index 26c2e5b..fe3186d 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -4,6 +4,7 @@ def deploy(): from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 + # This model is required for flask_migrate to make the table from models.usermodel import User # noqa: F401 diff --git a/verse/distribusikan/distribusikan.py b/verse/distribusikan/distribusikan.py index 6350228..4ba50ae 100644 --- a/verse/distribusikan/distribusikan.py +++ b/verse/distribusikan/distribusikan.py @@ -2,6 +2,7 @@ from flask import Blueprint from flask_login import login_required from distribusikan.distribusiselector import DistribusiSelector + # Distribusi Information from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusiworkflow import DistribusiWorkflow @@ -34,6 +35,7 @@ def upload(): def theme(): return ThemeSelector() + @distribusikan.route("/editor", methods=["GET", "POST"]) @login_required def editor(): diff --git a/verse/distribusikan/distribusiselector.py b/verse/distribusikan/distribusiselector.py index 64a8ef4..58d2f1b 100644 --- a/verse/distribusikan/distribusiselector.py +++ b/verse/distribusikan/distribusiselector.py @@ -3,8 +3,12 @@ import shutil from flask import flash, render_template from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import db from distribusikan.distribusisinfo import DistribusisInfo @@ -15,6 +19,7 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User + # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusikan/distribusiworkflow.py b/verse/distribusikan/distribusiworkflow.py index bd7260f..82270aa 100644 --- a/verse/distribusikan/distribusiworkflow.py +++ b/verse/distribusikan/distribusiworkflow.py @@ -7,8 +7,12 @@ 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 sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import db from distribusikan.distribusiselector import SelectorVisible @@ -17,10 +21,12 @@ from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm + # Forms! from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User + # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusikan/editor.py b/verse/distribusikan/editor.py index 8fb0c9c..794d879 100644 --- a/verse/distribusikan/editor.py +++ b/verse/distribusikan/editor.py @@ -4,8 +4,12 @@ import shutil import bleach from bleach_allowlist import all_styles from flask import render_template -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from werkzeug.utils import secure_filename from app import db diff --git a/verse/distribusikan/upload.py b/verse/distribusikan/upload.py index 32d8eaa..f2be184 100644 --- a/verse/distribusikan/upload.py +++ b/verse/distribusikan/upload.py @@ -3,8 +3,13 @@ import shutil from flask import flash from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, - InterfaceError, InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + IntegrityError, + InterfaceError, + InvalidRequestError, +) from app import db from distribusikan.distribusiselector import SelectCurrentDistribusi diff --git a/verse/distribusikan/uploadpage.py b/verse/distribusikan/uploadpage.py index aaf9e25..a8f2510 100644 --- a/verse/distribusikan/uploadpage.py +++ b/verse/distribusikan/uploadpage.py @@ -8,6 +8,7 @@ from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm + # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/forms/editorform.py b/verse/forms/editorform.py index 0f12ba1..284082d 100644 --- a/verse/forms/editorform.py +++ b/verse/forms/editorform.py @@ -2,8 +2,13 @@ from flask_wtf import FlaskForm from flask_wtf.file import FileAllowed, FileField, FileSize -from wtforms import (BooleanField, StringField, SubmitField, TextAreaField, - validators) +from wtforms import ( + BooleanField, + StringField, + SubmitField, + TextAreaField, + validators, +) from wtforms.validators import Length diff --git a/verse/forms/uploadform.py b/verse/forms/uploadform.py index 0d9b00a..5d1f21f 100644 --- a/verse/forms/uploadform.py +++ b/verse/forms/uploadform.py @@ -1,9 +1,18 @@ from flask_wtf import FlaskForm from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize -from wtforms import (IntegerField, SelectField, StringField, SubmitField, - validators) -from wtforms.validators import (DataRequired, Length, NumberRange, - ValidationError) +from wtforms import ( + IntegerField, + SelectField, + StringField, + SubmitField, + validators, +) +from wtforms.validators import ( + DataRequired, + Length, + NumberRange, + ValidationError, +) from app import settings diff --git a/verse/start.py b/verse/start.py index cf7ccb1..f2e9fc8 100644 --- a/verse/start.py +++ b/verse/start.py @@ -2,8 +2,14 @@ from datetime import timedelta -from flask import (Blueprint, redirect, render_template, send_from_directory, - session, url_for) +from flask import ( + Blueprint, + redirect, + render_template, + send_from_directory, + session, + url_for, +) from flask_login import current_user, login_required, logout_user from flask_mail import Mail from flask_wtf.csrf import CSRFError @@ -13,6 +19,7 @@ from adminpage import AdminPage from app import create_app, login_manager from distribusikan.distribusikan import distribusikan from distribusikan.distribusisinfo import DistribusisInfo + # Use upload form to populate filters from forms.uploadform import UploadForm from models.distribusimodel import Distribusis diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py index 6c0b2f9..676a5c8 100644 --- a/verse/statuspengguna/forgotpassword.py +++ b/verse/statuspengguna/forgotpassword.py @@ -3,8 +3,12 @@ from uuid import uuid1 from flask import Blueprint, render_template from flask_mail import Mail, Message -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import db, get_app from forms.forgotpasswordform import ForgotPasswordForm diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index d90e495..76c0c4b 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -2,8 +2,12 @@ import os from flask import flash from flask_login import current_user -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + InterfaceError, + InvalidRequestError, +) from app import db from distribusikan.distribusisinfo import DistribusisInfo diff --git a/verse/statuspengguna/loginuser.py b/verse/statuspengguna/loginuser.py index a1a138a..cab7403 100644 --- a/verse/statuspengguna/loginuser.py +++ b/verse/statuspengguna/loginuser.py @@ -1,5 +1,14 @@ -from flask import (Blueprint, abort, flash, redirect, render_template, request, - send_from_directory, session, url_for) +from flask import ( + Blueprint, + abort, + flash, + redirect, + render_template, + request, + send_from_directory, + session, + url_for, +) from flask_bcrypt import check_password_hash from flask_login import login_user diff --git a/verse/statuspengguna/registeruser.py b/verse/statuspengguna/registeruser.py index d9f6a61..7a7fe46 100644 --- a/verse/statuspengguna/registeruser.py +++ b/verse/statuspengguna/registeruser.py @@ -1,8 +1,13 @@ 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 (DatabaseError, DataError, IntegrityError, - InterfaceError, InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + IntegrityError, + InterfaceError, + InvalidRequestError, +) from werkzeug.routing import BuildError from app import db diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py index ef1735a..fea2ae9 100644 --- a/verse/statuspengguna/resetpassword.py +++ b/verse/statuspengguna/resetpassword.py @@ -3,8 +3,13 @@ from datetime import datetime from flask import flash, redirect, render_template, url_for from flask_bcrypt import generate_password_hash from flask_login import login_user -from sqlalchemy.exc import (DatabaseError, DataError, IntegrityError, - InterfaceError, InvalidRequestError) +from sqlalchemy.exc import ( + DatabaseError, + DataError, + IntegrityError, + InterfaceError, + InvalidRequestError, +) from werkzeug.routing import BuildError from app import db From 7f6a373e8db745a48fb83fdb4b7e83392961ec40 Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 15:22:33 +0200 Subject: [PATCH 19/20] No Gods, No Managers, No Tutors --- verse/adminpage.py | 21 --------------------- verse/forms/adminuserform.py | 4 ---- verse/models/distribusi_file_model.py | 0 verse/models/usermodel.py | 2 -- verse/static/css/style.css | 6 ------ verse/statuspengguna/helper.py | 7 ++----- verse/statuspengguna/static/css/style.css | 5 ----- verse/templates/base/admin.html | 9 --------- 8 files changed, 2 insertions(+), 52 deletions(-) create mode 100644 verse/models/distribusi_file_model.py diff --git a/verse/adminpage.py b/verse/adminpage.py index 1dd2b42..b158900 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -26,10 +26,6 @@ def AdminPage(): 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) template = render_template( "admin.html", @@ -50,23 +46,6 @@ def DeleteUsers(adminuserform): 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): try: diff --git a/verse/forms/adminuserform.py b/verse/forms/adminuserform.py index 1a6ffae..aeda2e4 100644 --- a/verse/forms/adminuserform.py +++ b/verse/forms/adminuserform.py @@ -20,8 +20,4 @@ class AdminUserForm(FlaskForm): return UserListForm() - tutors = SubmitField("Are tutors") - - nottutors = SubmitField("Are not tutors") - delete = SubmitField("Delete") diff --git a/verse/models/distribusi_file_model.py b/verse/models/distribusi_file_model.py new file mode 100644 index 0000000..e69de29 diff --git a/verse/models/usermodel.py b/verse/models/usermodel.py index 254fe3f..9522024 100644 --- a/verse/models/usermodel.py +++ b/verse/models/usermodel.py @@ -15,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): diff --git a/verse/static/css/style.css b/verse/static/css/style.css index d244b7e..2f772a8 100644 --- a/verse/static/css/style.css +++ b/verse/static/css/style.css @@ -149,12 +149,6 @@ input[type="submit"]:disabled:focus { background-color: #62b264; } -#tutors { - color: black; - background-color: #62b264; -} - - /* unvisited link */ a:link { color: #fff600; diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index 76c0c4b..022326e 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -77,10 +77,7 @@ 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") + if distribusiamount > 19: + print("user already has 20 distribusis") return True return False diff --git a/verse/statuspengguna/static/css/style.css b/verse/statuspengguna/static/css/style.css index d244b7e..fb743fc 100644 --- a/verse/statuspengguna/static/css/style.css +++ b/verse/statuspengguna/static/css/style.css @@ -149,11 +149,6 @@ input[type="submit"]:disabled:focus { background-color: #62b264; } -#tutors { - color: black; - background-color: #62b264; -} - /* unvisited link */ a:link { diff --git a/verse/templates/base/admin.html b/verse/templates/base/admin.html index 2272dc2..e0f4982 100644 --- a/verse/templates/base/admin.html +++ b/verse/templates/base/admin.html @@ -14,9 +14,6 @@

      Admin Page

      -

      Here you can bulk delete distribusis and users or make users into tutors - These actions cannot be undone! -

      List of distribusis

      @@ -53,12 +50,6 @@
    {% endif %} {% endfor %} -
    - {{ adminuserform.tutors }} -
    -
    - {{ adminuserform.nottutors }} -
    {{ adminuserform.delete }}
    From 48b6e6f211ecf84aa088b7f3c470bf87438cd903 Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 15:34:29 +0200 Subject: [PATCH 20/20] snake case model files --- verse/adminpage.py | 4 ++-- verse/admintool.py | 4 ++-- verse/deploydb.py | 4 ++-- verse/distribusikan/distribusiselector.py | 4 ++-- verse/distribusikan/distribusisinfo.py | 4 ++-- verse/distribusikan/distribusiworkflow.py | 4 ++-- verse/distribusikan/editor.py | 2 +- verse/distribusikan/upload.py | 4 ++-- verse/models/distribusi_file_model.py | 17 +++++++++++++++++ .../{distribusimodel.py => distribusi_model.py} | 0 verse/models/{usermodel.py => user_model.py} | 0 verse/start.py | 4 ++-- verse/statuspengguna/forgotpassword.py | 2 +- verse/statuspengguna/helper.py | 4 ++-- verse/statuspengguna/loginuser.py | 2 +- verse/statuspengguna/registeruser.py | 2 +- verse/statuspengguna/resetpassword.py | 2 +- 17 files changed, 40 insertions(+), 23 deletions(-) rename verse/models/{distribusimodel.py => distribusi_model.py} (100%) rename verse/models/{usermodel.py => user_model.py} (100%) diff --git a/verse/adminpage.py b/verse/adminpage.py index b158900..4d78d26 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -13,8 +13,8 @@ from app import db from distribusikan.distribusisinfo import DistribusisInfo from forms.admindistribusiform import AdminDistribusiForm from forms.adminuserform import AdminUserForm -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User def AdminPage(): diff --git a/verse/admintool.py b/verse/admintool.py index 9d392ab..c8ddac3 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -8,8 +8,8 @@ from sqlalchemy.exc import ( ) from app import create_app, db -from models.distribusimodel import Distribusis # noqa: F401 -from models.usermodel import User # noqa: F401 +from models.distribusi_model import Distribusis # noqa: F401 +from models.user_model import User # noqa: F401 def admintool(): diff --git a/verse/deploydb.py b/verse/deploydb.py index fe3186d..9a33cdc 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -3,10 +3,10 @@ def deploy(): from flask_migrate import init, migrate, stamp, upgrade from app import create_app, db - from models.distribusimodel import Distribusis # noqa: F401 + from models.distribusi_model import Distribusis # noqa: F401 # This model is required for flask_migrate to make the table - from models.usermodel import User # noqa: F401 + from models.user_model import User # noqa: F401 app = create_app() app.app_context().push() diff --git a/verse/distribusikan/distribusiselector.py b/verse/distribusikan/distribusiselector.py index 58d2f1b..ccc5770 100644 --- a/verse/distribusikan/distribusiselector.py +++ b/verse/distribusikan/distribusiselector.py @@ -17,8 +17,8 @@ from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm from forms.uploadform import UploadForm -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusikan/distribusisinfo.py b/verse/distribusikan/distribusisinfo.py index 5a47d5f..19ef284 100644 --- a/verse/distribusikan/distribusisinfo.py +++ b/verse/distribusikan/distribusisinfo.py @@ -1,7 +1,7 @@ from flask_login import current_user -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User class DistribusisInfo: diff --git a/verse/distribusikan/distribusiworkflow.py b/verse/distribusikan/distribusiworkflow.py index 82270aa..4e3a376 100644 --- a/verse/distribusikan/distribusiworkflow.py +++ b/verse/distribusikan/distribusiworkflow.py @@ -24,8 +24,8 @@ from forms.themeform import ThemeForm # Forms! from forms.uploadform import UploadForm -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusikan/editor.py b/verse/distribusikan/editor.py index 794d879..88e63c2 100644 --- a/verse/distribusikan/editor.py +++ b/verse/distribusikan/editor.py @@ -20,7 +20,7 @@ from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm from forms.themeform import ThemeForm from forms.uploadform import UploadForm -from models.distribusimodel import Distribusis +from models.distribusi_model import Distribusis from statuspengguna.helper import UserHelper diff --git a/verse/distribusikan/upload.py b/verse/distribusikan/upload.py index f2be184..83110b5 100644 --- a/verse/distribusikan/upload.py +++ b/verse/distribusikan/upload.py @@ -14,8 +14,8 @@ from sqlalchemy.exc import ( from app import db from distribusikan.distribusiselector import SelectCurrentDistribusi from forms.uploadform import UploadForm -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User from statuspengguna.helper import UserHelper diff --git a/verse/models/distribusi_file_model.py b/verse/models/distribusi_file_model.py index e69de29..503f7bb 100644 --- a/verse/models/distribusi_file_model.py +++ b/verse/models/distribusi_file_model.py @@ -0,0 +1,17 @@ +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(300), nullable=False, unique=True) + distribusi = db.Column(db.Integer, db.ForeignKey("distribusis.id")) + path = db.Column(db.String(4096), nullable=True, unique=False) + description = db.Column(db.String(9), nullable=True, unique=False) + tags = db.Column(db.String(500), nullable=True, unique=False) + + def __repr__(self): + return "" % self.distribusiname diff --git a/verse/models/distribusimodel.py b/verse/models/distribusi_model.py similarity index 100% rename from verse/models/distribusimodel.py rename to verse/models/distribusi_model.py diff --git a/verse/models/usermodel.py b/verse/models/user_model.py similarity index 100% rename from verse/models/usermodel.py rename to verse/models/user_model.py diff --git a/verse/start.py b/verse/start.py index f2e9fc8..5cbe8c5 100644 --- a/verse/start.py +++ b/verse/start.py @@ -22,8 +22,8 @@ from distribusikan.distribusisinfo import DistribusisInfo # Use upload form to populate filters from forms.uploadform import UploadForm -from models.distribusimodel import Distribusis -from models.usermodel import User +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 login_section diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py index 676a5c8..dbb8049 100644 --- a/verse/statuspengguna/forgotpassword.py +++ b/verse/statuspengguna/forgotpassword.py @@ -12,7 +12,7 @@ from sqlalchemy.exc import ( from app import db, get_app from forms.forgotpasswordform import ForgotPasswordForm -from models.usermodel import User +from models.user_model import User mail = Mail(get_app()) forgot_password = Blueprint( diff --git a/verse/statuspengguna/helper.py b/verse/statuspengguna/helper.py index 022326e..a8b26bd 100644 --- a/verse/statuspengguna/helper.py +++ b/verse/statuspengguna/helper.py @@ -11,8 +11,8 @@ from sqlalchemy.exc import ( from app import db from distribusikan.distribusisinfo import DistribusisInfo -from models.distribusimodel import Distribusis -from models.usermodel import User +from models.distribusi_model import Distribusis +from models.user_model import User class UserHelper: diff --git a/verse/statuspengguna/loginuser.py b/verse/statuspengguna/loginuser.py index cab7403..db08cac 100644 --- a/verse/statuspengguna/loginuser.py +++ b/verse/statuspengguna/loginuser.py @@ -13,7 +13,7 @@ from flask_bcrypt import check_password_hash from flask_login import login_user from forms.loginform import LoginForm -from models.usermodel import User +from models.user_model import User login_section = Blueprint( "login", diff --git a/verse/statuspengguna/registeruser.py b/verse/statuspengguna/registeruser.py index 7a7fe46..e97f1ee 100644 --- a/verse/statuspengguna/registeruser.py +++ b/verse/statuspengguna/registeruser.py @@ -12,7 +12,7 @@ from werkzeug.routing import BuildError from app import db from forms.registerform import RegisterForm -from models.usermodel import User +from models.user_model import User register_user = Blueprint( "register", diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py index fea2ae9..a75f86e 100644 --- a/verse/statuspengguna/resetpassword.py +++ b/verse/statuspengguna/resetpassword.py @@ -14,7 +14,7 @@ from werkzeug.routing import BuildError from app import db from forms.resetpasswordform import ResetPasswordForm -from models.usermodel import User +from models.user_model import User from statuspengguna import statuspengguna reset_password = Blueprint(