main #12

Merged
crunk merged 49 commits from Toolsheds/distribusi-verse:main into main 3 months ago
  1. 2
      .gitignore
  2. 68
      pyproject.toml
  3. 8
      requirements.txt
  4. 3
      src/.gitignore
  5. 9
      verse/admin.py
  6. 64
      verse/admin_page/admin_page.py
  7. 0
      verse/admin_page/forms/admindistribusiform.py
  8. 0
      verse/admin_page/forms/adminuserform.py
  9. 0
      verse/admin_page/templates/admin.html
  10. 4
      verse/app.py
  11. 1
      verse/deploydb.py
  12. 160
      verse/describer/describe_files.py
  13. 48
      verse/describer/forms/describe_files_form.py
  14. 8
      verse/describer/forms/redistribusi_form.py
  15. 32
      verse/describer/static/css/describer.css
  16. 78
      verse/describer/templates/describe_files/describe.html
  17. 13
      verse/describer/templates/describe_files/redistribusi.html
  18. 82
      verse/distribusikan/add_files_to_describer.py
  19. 84
      verse/distribusikan/css_editor_page.py
  20. 72
      verse/distribusikan/distribusi_selector.py
  21. 61
      verse/distribusikan/distribusi_workflow.py
  22. 21
      verse/distribusikan/distribusikan.py
  23. 8
      verse/distribusikan/distribusis_info.py
  24. 0
      verse/distribusikan/forms/distribusiform.py
  25. 0
      verse/distribusikan/forms/editorform.py
  26. 0
      verse/distribusikan/forms/publicthemeform.py
  27. 2
      verse/distribusikan/forms/selectorform.py
  28. 0
      verse/distribusikan/forms/themeform.py
  29. 17
      verse/distribusikan/forms/uploadform.py
  30. 32
      verse/distribusikan/static/css/distribusikan.css
  31. 8
      verse/distribusikan/static/css/editor.css
  32. 11
      verse/distribusikan/templates/distribusikan/distribusi.html
  33. 2
      verse/distribusikan/templates/distribusikan/distribusi_workflow/editcss.html
  34. 0
      verse/distribusikan/templates/distribusikan/distribusi_workflow/launch.html
  35. 8
      verse/distribusikan/templates/distribusikan/distribusi_workflow/selector.html
  36. 0
      verse/distribusikan/templates/distribusikan/distribusi_workflow/theme.html
  37. 7
      verse/distribusikan/templates/distribusikan/distribusi_workflow/upload.html
  38. 4
      verse/distribusikan/templates/distribusikan/editor.html
  39. 26
      verse/distribusikan/theme_selector.py
  40. 12
      verse/distribusikan/upload.py
  41. 26
      verse/distribusikan/upload_page.py
  42. 3
      verse/migrations/env.py
  43. 9
      verse/models/distribusi_file_model.py
  44. 1
      verse/models/distribusi_model.py
  45. 11
      verse/search/forms/searchform.py
  46. 42
      verse/search/search.py
  47. 52
      verse/search/search_index.py
  48. 23
      verse/search/templates/search/search.html
  49. 5
      verse/settings.toml
  50. 46
      verse/start.py
  51. 4
      verse/static/css/dropdown.css
  52. 8
      verse/static/css/selector.css
  53. 116
      verse/static/css/style.css
  54. 2
      verse/statuspengguna/forgotpassword.py
  55. 0
      verse/statuspengguna/forms/forgotpasswordform.py
  56. 0
      verse/statuspengguna/forms/loginform.py
  57. 2
      verse/statuspengguna/forms/registerform.py
  58. 0
      verse/statuspengguna/forms/resetpasswordform.py
  59. 6
      verse/statuspengguna/helper.py
  60. 5
      verse/statuspengguna/loginuser.py
  61. 2
      verse/statuspengguna/registeruser.py
  62. 5
      verse/statuspengguna/resetpassword.py
  63. 69
      verse/statuspengguna/static/css/dropdown.css
  64. 50
      verse/statuspengguna/static/css/editor.css
  65. 22
      verse/statuspengguna/static/css/login.css
  66. 53
      verse/statuspengguna/static/css/selector.css
  67. 281
      verse/statuspengguna/static/css/style.css
  68. 6
      verse/statuspengguna/static/icons/about.txt
  69. BIN
      verse/statuspengguna/static/icons/android-chrome-192x192.png
  70. BIN
      verse/statuspengguna/static/icons/android-chrome-512x512.png
  71. BIN
      verse/statuspengguna/static/icons/apple-touch-icon.png
  72. BIN
      verse/statuspengguna/static/icons/favicon-16x16.png
  73. BIN
      verse/statuspengguna/static/icons/favicon-32x32.png
  74. BIN
      verse/statuspengguna/static/icons/favicon.ico
  75. 1
      verse/statuspengguna/static/icons/site.webmanifest
  76. 92
      verse/statuspengguna/static/js/dropdown.js
  77. 19
      verse/statuspengguna/static/js/editorupdate.js
  78. 25
      verse/statuspengguna/static/js/script.js
  79. 75
      verse/statuspengguna/static/svg/arrow_1.svg
  80. 12
      verse/statuspengguna/static/svg/arrow_2.svg
  81. 12
      verse/statuspengguna/static/svg/arrow_3.svg
  82. 5
      verse/statuspengguna/templates/statuspengguna/forgotpassword.html
  83. 1
      verse/statuspengguna/templates/statuspengguna/login.html
  84. 1
      verse/statuspengguna/templates/statuspengguna/register.html
  85. 1
      verse/statuspengguna/templates/statuspengguna/resetpassword.html
  86. 2
      verse/templates/base/help.html
  87. 2
      verse/templates/base/index.html
  88. 4
      verse/themes/editor/placeholder.html

2
.gitignore

@ -3,10 +3,12 @@
*.pyc *.pyc
*.egg-info/ *.egg-info/
.eggs/ .eggs/
.ruff_cache
build/ build/
dist/ dist/
pip-wheel-metadata/ pip-wheel-metadata/
verse/tmpupload/* verse/tmpupload/*
verse/stash/* verse/stash/*
verse/search/searchdata/*
*.db *.db

68
pyproject.toml

@ -1,25 +1,49 @@
[tool.black] [tool.ruff]
line-length = 79 line-length = 79
target-version = ['py311'] target-version = "py311"
include = '\.pyi?$' #include = '\.pyi?$'
exclude = ''' exclude = [
/( ".bzr",
\.eggs ".direnv",
| \.git ".eggs",
| \.hg ".git",
| \.mypy_cache ".git-rewrite",
| \.tox ".hg",
| \.venv ".ipynb_checkpoints",
| _build ".mypy_cache",
| buck-out ".nox",
| build ".pants.d",
| dist ".pyenv",
".pytest_cache",
# The following are specific to Black, you probably don't want those. ".pytype",
| blib2to3 ".ruff_cache",
| tests/data ".svn",
| profiling ".tox",
)/ ".venv",
''' ".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
line-ending = "auto"
skip-magic-trailing-comma = false

8
requirements.txt

@ -6,9 +6,9 @@ black==24.4.0
bleach==4.1.0 bleach==4.1.0
bleach-allowlist==1.0.3 bleach-allowlist==1.0.3
blinker==1.7.0 blinker==1.7.0
cffi==1.15.0 cffi
click==8.1.7 click==8.1.7
distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi@e291e7497e40211c2ebd54ca32a1f4bdaed71230 distribusi @ git+https://git.vvvvvvaria.org/crunk/distribusi@3eefd6e5ca7048555d441df8c6fbf4f2e255acac
dnspython==2.1.0 dnspython==2.1.0
email-validator==1.1.3 email-validator==1.1.3
Flask==3.0.3 Flask==3.0.3
@ -36,7 +36,7 @@ neovim==0.3.1
packaging==24.0 packaging==24.0
passlib==1.7.4 passlib==1.7.4
pathspec==0.9.0 pathspec==0.9.0
Pillow==8.3.2 Pillow
platformdirs==2.4.0 platformdirs==2.4.0
pycparser==2.21 pycparser==2.21
pynvim==0.5.0 pynvim==0.5.0
@ -44,7 +44,7 @@ pyparsing==3.0.7
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
python-magic==0.4.24 python-magic==0.4.24
pytz==2021.3 pytz==2021.3
regex==2021.11.10 regex
six==1.16.0 six==1.16.0
speaklater==1.3 speaklater==1.3
SQLAlchemy==2.0.29 SQLAlchemy==2.0.29

3
src/.gitignore

@ -1,3 +0,0 @@
*
*/
!.gitignore

9
verse/admin.py

@ -0,0 +1,9 @@
from flask_login import current_user
from models.user_model import User
def is_adminuser():
if not current_user.is_authenticated:
return False
user = User.query.filter_by(email=current_user.email).first()
return user.admin

64
verse/adminpage.py → verse/admin_page/admin_page.py

@ -1,7 +1,8 @@
import os import os
import shutil import shutil
from flask import render_template from flask import render_template, Blueprint
from flask_login import current_user, login_required
from sqlalchemy.exc import ( from sqlalchemy.exc import (
DatabaseError, DatabaseError,
DataError, DataError,
@ -10,22 +11,34 @@ from sqlalchemy.exc import (
) )
from app import db from app import db
from distribusikan.distribusisinfo import DistribusisInfo from admin import is_adminuser
from forms.admindistribusiform import AdminDistribusiForm from distribusikan.distribusis_info import DistribusisInfo
from forms.adminuserform import AdminUserForm from admin_page.forms.admindistribusiform import AdminDistribusiForm
from admin_page.forms.adminuserform import AdminUserForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
admin_page = Blueprint(
"admin",
__name__,
template_folder="templates/describe_files",
static_folder="static",
)
def AdminPage(): @admin_page.route("/admin", methods=["GET", "POST"])
adminuserform = AddUsersToForm(AdminUserForm()) @login_required
admindistribusiform = AddDistribusisToForm(AdminDistribusiForm()) def admin():
if not is_adminuser():
return redirect(url_for("index"))
adminuserform = add_users_to_form(AdminUserForm())
admindistribusiform = add_distribusis_to_form(AdminDistribusiForm())
if admindistribusiform.validate_on_submit(): if admindistribusiform.validate_on_submit():
DeleteDistribusis(admindistribusiform) delete_distribusis(admindistribusiform)
if adminuserform.validate_on_submit(): if adminuserform.validate_on_submit():
if adminuserform.delete.data: if adminuserform.delete.data:
DeleteUsers(adminuserform) delete_users(adminuserform)
template = render_template( template = render_template(
"admin.html", "admin.html",
@ -35,19 +48,18 @@ def AdminPage():
return template return template
def DeleteUsers(adminuserform): def delete_users(adminuserform):
for userform in adminuserform: for userform in adminuserform:
if "user" in userform.id: if "user" in userform.id:
if userform.data: if userform.data:
useremail = userform.label.text useremail = userform.label.text
user = User.query.filter_by(email=useremail).first() user = User.query.filter_by(email=useremail).first()
DeleteUserDistribusis(user) delete_User_distribusis(user)
DeleteUserFromDb(user) delete_user_from_db(user)
userform.errors.append(f"User {useremail} deleted!") userform.errors.append(f"User {useremail} deleted!")
def delete_user_from_db(user):
def DeleteUserFromDb(user):
try: try:
db.session.delete(user) db.session.delete(user)
db.session.commit() db.session.commit()
@ -55,14 +67,14 @@ def DeleteUserFromDb(user):
db.session.rollback() db.session.rollback()
def DeleteUserDistribusis(user): def delete_User_distribusis(user):
distribusis = DistribusisInfo.getuserdistribusis(user.email) distribusis = DistribusisInfo.get_user_distribusis(user.email)
for distribusi in distribusis: for distribusi in distribusis:
DeleteDistribusiFiles(distribusi.distribusiname) delete_distribusi_files(distribusi.distribusiname)
DeleteDistribusiFromDb(distribusi) delete_distribusi_from_db(distribusi)
def DeleteDistribusis(admindistribusiform): def delete_distribusis(admindistribusiform):
for distribusiform in admindistribusiform: for distribusiform in admindistribusiform:
if "distribusi" in distribusiform.id: if "distribusi" in distribusiform.id:
if distribusiform.data: if distribusiform.data:
@ -70,12 +82,12 @@ def DeleteDistribusis(admindistribusiform):
distribusi = Distribusis.query.filter_by( distribusi = Distribusis.query.filter_by(
distribusiname=distribusiname distribusiname=distribusiname
).first() ).first()
DeleteDistribusiFromDb(distribusi) delete_distribusi_from_db(distribusi)
DeleteDistribusiFiles(distribusiname) delete_distribusi_files(distribusiname)
distribusiform.errors.append("Deleted distribusi") distribusiform.errors.append("Deleted distribusi")
def DeleteDistribusiFromDb(distribusi): def delete_distribusi_from_db(distribusi):
try: try:
db.session.delete(distribusi) db.session.delete(distribusi)
db.session.commit() db.session.commit()
@ -83,7 +95,7 @@ def DeleteDistribusiFromDb(distribusi):
db.session.rollback() db.session.rollback()
def DeleteDistribusiFiles(distribusiname): def delete_distribusi_files(distribusiname):
userfolder = os.path.join("stash", distribusiname) userfolder = os.path.join("stash", distribusiname)
if os.path.exists(userfolder): if os.path.exists(userfolder):
shutil.rmtree(userfolder) shutil.rmtree(userfolder)
@ -92,15 +104,15 @@ def DeleteDistribusiFiles(distribusiname):
shutil.rmtree(cssfolder) shutil.rmtree(cssfolder)
def AddDistribusisToForm(admindistribusiform): def add_distribusis_to_form(admindistribusiform):
distribusis = DistribusisInfo.visibledistribusis() distribusis = DistribusisInfo.visible_distribusis()
admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder( admindistribusiform = AdminDistribusiForm.distribusi_list_form_builder(
distribusis distribusis
) )
return admindistribusiform return admindistribusiform
def AddUsersToForm(adminuserform): def add_users_to_form(adminuserform):
users = User.query.all() users = User.query.all()
adminuserform = AdminUserForm.user_list_form_builder(users) adminuserform = AdminUserForm.user_list_form_builder(users)
return adminuserform return adminuserform

0
verse/forms/admindistribusiform.py → verse/admin_page/forms/admindistribusiform.py

0
verse/forms/adminuserform.py → verse/admin_page/forms/adminuserform.py

0
verse/templates/base/admin.html → verse/admin_page/templates/admin.html

4
verse/app.py

@ -8,6 +8,7 @@ from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect from flask_wtf.csrf import CSRFProtect
APP = Flask(__name__, static_folder="static") APP = Flask(__name__, static_folder="static")
db = SQLAlchemy() db = SQLAlchemy()
migrate = Migrate() migrate = Migrate()
@ -19,8 +20,8 @@ def create_app():
APP.secret_key = os.urandom(24) APP.secret_key = os.urandom(24)
APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db" APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db"
APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
APP.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024
APP.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024
APP.config["MAIL_SERVER"] = "mail.autonomic.zone" APP.config["MAIL_SERVER"] = "mail.autonomic.zone"
APP.config["MAIL_PORT"] = 587 APP.config["MAIL_PORT"] = 587
APP.config["MAIL_USE_SSL"] = False APP.config["MAIL_USE_SSL"] = False
@ -64,7 +65,6 @@ def get_app():
def settings_from_file(): def settings_from_file():
settings = {}
if os.path.isfile("settings_development.toml"): if os.path.isfile("settings_development.toml"):
with open("settings_development.toml", "rb") as settings_file: with open("settings_development.toml", "rb") as settings_file:
return tomllib.load(settings_file) return tomllib.load(settings_file)

1
verse/deploydb.py

@ -4,6 +4,7 @@ def deploy():
from app import create_app, db from app import create_app, db
from models.distribusi_model import Distribusis # noqa: F401 from models.distribusi_model import Distribusis # noqa: F401
from models.distribusi_file_model import DistribusiFiles # noqa: F401
# This model is required for flask_migrate to make the table # This model is required for flask_migrate to make the table
from models.user_model import User # noqa: F401 from models.user_model import User # noqa: F401

160
verse/describer/describe_files.py

@ -0,0 +1,160 @@
import os
from flask import (
Blueprint,
render_template,
redirect,
url_for,
send_from_directory,
flash,
)
from flask_login import current_user, login_required
from sqlalchemy.exc import (
DatabaseError,
DataError,
IntegrityError,
InterfaceError,
InvalidRequestError,
)
import piexif
from PIL import Image
from app import db
from models.distribusi_model import Distribusis
from models.distribusi_file_model import DistribusiFiles
from describer.forms.describe_files_form import DescribeFilesForm
from describer.forms.redistribusi_form import ReDistribusiForm
from distribusikan.distribusi_workflow import run_distribusi, get_css_file
describer = Blueprint(
"describer",
__name__,
template_folder="templates/describe_files",
static_folder="static",
)
@describer.route("/<string:distribusiname>")
@login_required
def show_distribusi_files(distribusiname):
if not current_user.is_authenticated:
return redirect(url_for("index"))
distribusi = Distribusis.query.filter_by(
distribusiname=distribusiname
).first()
redistribusi_form = ReDistribusiForm()
distribusi_file_forms = get_distribusi_file_forms(distribusi.id)
return render_template(
"describe.html",
distribusiname=distribusiname,
redistribusi_form=redistribusi_form,
distribusi_file_forms=distribusi_file_forms,
)
@describer.route("/redistribusi/<string:distribusiname>", methods=["POST"])
@login_required
def re_distribusi_files(distribusiname):
distribusi = Distribusis.query.filter_by(
distribusiname=distribusiname
).first()
redistribusi_form = ReDistribusiForm()
if redistribusi_form.validate_on_submit():
userfolder = os.path.join("stash", distribusi.distribusiname)
cssfile = get_css_file(distribusi)
run_distribusi(userfolder, cssfile)
return redirect(
url_for(
"describer.show_distribusi_files",
distribusiname=distribusi.distribusiname,
)
)
@describer.route("/describe_file/<int:file_id>", methods=["POST"])
@login_required
def describe_file(file_id):
distribusi_file = DistribusiFiles.query.filter_by(id=file_id).first()
describe_form = DescribeFilesForm(
distribusi_file.id, distribusi_file.path, distribusi_file.type
)
save_described_file_to_db(describe_form, distribusi_file)
add_alttext_to_file(describe_form, distribusi_file)
add_description_to_file(describe_form, distribusi_file)
distribusi = Distribusis.query.filter_by(
id=distribusi_file.distribusi
).first()
return redirect(
url_for(
"describer.show_distribusi_files",
distribusiname=distribusi.distribusiname,
)
)
@describer.route("/stash/<path:path>")
def send_stash_file(path):
return send_from_directory("stash", path)
def get_distribusi_file_forms(distribusi_id):
distribusi_file_forms = {}
distribusi_files = DistribusiFiles.query.filter_by(
distribusi=distribusi_id
).all()
for distribusi_file in distribusi_files:
describe_form = DescribeFilesForm(
distribusi_file.id, distribusi_file.path, distribusi_file.type
)
describe_form.description.data = distribusi_file.description
describe_form.alttext.data = distribusi_file.alttext
describe_form.searchtags.data = distribusi_file.tags
distribusi_file_forms[distribusi_file.id] = describe_form
return distribusi_file_forms
def save_described_file_to_db(describe_form, distribusi_file):
try:
if describe_form.description.data:
print(distribusi_file.id)
distribusi_file.description = describe_form.description.data
if describe_form.alttext.data:
distribusi_file.alttext = describe_form.alttext.data
if describe_form.searchtags.data:
distribusi_file.tags = describe_form.searchtags.data
db.session.add(distribusi_file)
db.session.commit()
except (InvalidRequestError, IntegrityError):
db.session.rollback()
describe_form.save.errors.append("Something went wrong!")
flash("Something went wrong!", "danger")
except DataError:
db.session.rollback()
describe_form.save.errors.append("Invalid Entry")
flash("Invalid Entry", "warning")
except InterfaceError:
db.session.rollback()
describe_form.save.errors.append("Error connecting to the database")
flash("Error connecting to the database", "danger")
except DatabaseError:
db.session.rollback()
describe_form.save.errors.append("Error connecting to the database")
flash("Error connecting to the database", "danger")
def add_alttext_to_file(describe_form, distribusi_file):
if not describe_form.alttext.data:
return
filename_no_ext = os.path.splitext(distribusi_file.path)[0]
with open(f"{filename_no_ext}_alttext.txt", "w") as alttext_file:
alttext_file.write(describe_form.alttext.data)
return
def add_description_to_file(describe_form, distribusi_file):
if not describe_form.description.data:
return
filename_no_ext = os.path.splitext(distribusi_file.path)[0]
with open(
f"{filename_no_ext}_dv_description.txt", "w"
) as description_file:
description_file.write(describe_form.description.data)
return

48
verse/describer/forms/describe_files_form.py

@ -0,0 +1,48 @@
"""Describe File Form to describe files in the distribusi archive"""
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, validators
from wtforms.validators import Length
from wtforms.widgets import TextArea
class DescribeFilesForm(FlaskForm):
"""DescribeFileForm selection form."""
alttext = StringField(
"Alt-text for this file:",
validators=[
Length(3, 255),
],
)
searchtags = StringField(
"Add search tags, seperated by commas. No need for the '#' sign:",
validators=[
Length(3, 500),
],
)
description = StringField(
"Description of this file:",
validators=[
Length(3, 4096),
],
widget=TextArea(),
)
save = SubmitField("Save")
def __init__(self, id, file_path=None, type=None):
super(DescribeFilesForm, self).__init__()
self.id = id
self.file_path = file_path
self.type = type
self.alttext.id = f"alttext-{id}"
self.searchtags.id = f"searchtags-{id}"
self.description.id = f"description-{id}"
self.save.id = f"save-{id}"
# def StringFieldWithId(form_name, **kwargs):
# name = kwargs.get('name', 'Bar')
# return wtf.StringField(name, render_kw={
# 'id': f'{}{}'.format(form_name, name.lower(),
# **kwargs.get('render_kw', {})})

8
verse/describer/forms/redistribusi_form.py

@ -0,0 +1,8 @@
from flask_wtf import FlaskForm
from wtforms import SubmitField
class ReDistribusiForm(FlaskForm):
"""Re-Distribusi form class to re-distrusi with desribed files"""
submit = SubmitField("Re-Distribusi!")

32
verse/describer/static/css/describer.css

@ -0,0 +1,32 @@
#describe_workflow {
display: flex;
width: 60em;
margin:0 auto;
}
.redistribusi {
position: sticky;
top: 2em;
margin-top: 2em;
margin-left: 1em;
width: 15em;
height: 10em;
border-style: outset;
float: right;
padding: 0.5em;
}
.distribusi_file {
margin-top: 2em;
padding: 0.5em;
width: 42em;
background-color:#fdfdfd;
text-decoration: none;
scroll-behavior: smooth;
border-style: outset;
}
.distribusi_file > img, video {
max-width: 100%;
max-height: 100%;
}

78
verse/describer/templates/describe_files/describe.html

@ -0,0 +1,78 @@
{% extends "base/base.html" %}
{% block main %}
<div id="buttons">
{% if current_user.is_authenticated %}
<div class="distribusi">
<a href="{{ url_for('distribusikan.distribusi') }}">
<input type="button" name="button" value="Distribusi"></input>
</a>
</div>
{% if adminuser %}
<div class="admin">
<a href="/admin">
<input type="button" name="button" value="Admin"></input>
</a>
</div>
{% endif %}
<div class="logout">
<a href="/logout">
<input type="button" name="button" value="Logout"></input>
</a>
</div>
{% endif %}
<div class="help">
<a href="/help">
<input type="button" name="button" value="Help"></input>
</a>
</div>
</div>
<div id="describe_workflow">
<div class="distribusi_files">
{% for id, describe_form in distribusi_file_forms.items() %}
<div class="distribusi_file">
{% if describe_form.type == "image" %}
<img src="{{describe_form.file_path}}" alt="" data-src="{{describe_form.file_path}}" loading="lazy"/>
{% elif describe_form.type == "video" %}
<video src="{{describe_form.file_path}}" preload="auto" alt="" controls loading="lazy"></video>
{% elif describe_form.type == "audio" %}
<audio src="{{describe_form.file_path}}" preload="auto" alt="" controls loading="lazy"> </audio>
{% else %}
<a href="{{describe_form.file_path}}">file: {{describe_form.file_path}}</a>
{% endif %}
<form id={{id}} method="POST" enctype="multipart/form-data" action="{{ url_for('describer.describe_file', file_id=id) }}">
{{ describe_form.csrf_token }}
<p>File path: {{describe_form.file_path}}</p>
<fieldset class="description">
{{ describe_form.description.label }}
{{ describe_form.description }}
{% for message in describe_form.description.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="">
{{ describe_form.searchtags.label }}
{{ describe_form.searchtags }}
{% for message in describe_form.searchtags.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="">
{{ describe_form.alttext.label }}
{{ describe_form.alttext }}
{% for message in describe_form.alttext.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="button">
{{ describe_form.save }}
</fieldset>
</form>
</div>
{% endfor%}
</div>
{% block redistribusi %}
{% include "redistribusi.html" %}
{% endblock redistribusi%}
</div>
<link rel="stylesheet" type="text/css" href="{{ url_for('describer.static', filename='css/describer.css')}}">
{% endblock %}

13
verse/describer/templates/describe_files/redistribusi.html

@ -0,0 +1,13 @@
<div class="redistribusi">
<p class="tooltip">Run distribusi again after describing your files.<span class="tooltiptext">Distribusi will run again and add your alttext and descriptions.
</span></p>
<form id={{distribusiname}} method="POST" enctype="multipart/form-data" action="{{ url_for('describer.re_distribusi_files', distribusiname=distribusiname) }}">
{{ redistribusi_form.csrf_token }}
<fieldset class="">
{{ redistribusi_form.submit }}
{% for message in redistribusi_form.submit.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
</form>
</div>

82
verse/distribusikan/add_files_to_describer.py

@ -0,0 +1,82 @@
import os
import magic
from distribusi.mappings import FILE_TYPES
from models.distribusi_model import Distribusis
from models.distribusi_file_model import DistribusiFiles
from app import create_app, get_app, db
from sqlalchemy.exc import (
DatabaseError,
DataError,
IntegrityError,
InterfaceError,
InvalidRequestError,
)
MIME_TYPE = magic.Magic(mime=True)
def _distribusi_file_with_type(distribusi, full_path):
mime = MIME_TYPE.from_file(full_path)
type_, subtype = mime.split("/")
if type_ in FILE_TYPES:
_add_distribusi_file_to_db(distribusi, full_path, type_)
def _get_distribusi_from_path(path):
distribusi = Distribusis.query.filter_by(distribusiname=path).first()
return distribusi
def _add_distribusi_file_to_db(distribusi, full_path, type):
app = get_app()
app.logger.info(f"adding file to database: {full_path} type: {type}")
distribusi_file = DistribusiFiles.query.filter_by(path=full_path).first()
if distribusi_file is not None:
app.logger.error(f"File already in database: {full_path}")
return
try:
new_distribusi_file = DistribusiFiles(
path=full_path,
type=type,
distribusi=distribusi.id,
)
db.session.add(new_distribusi_file)
db.session.commit()
return
except InvalidRequestError:
db.session.rollback()
app.logger.error("Something went wrong!")
except IntegrityError:
db.session.rollback()
app.logger.error("File %s already exists!", full_path)
except DataError:
db.session.rollback()
app.logger.error("%s Invalid Entry", full_path)
except InterfaceError:
db.session.rollback()
app.logger.error("Error connecting to the database")
except DatabaseError:
db.session.rollback()
app.logger.error("Error connecting to the database")
def add_distribusi_files_to_db(path):
distribusi = _get_distribusi_from_path(path)
path = os.path.join("stash", path)
for root, dirs, files in os.walk(path, topdown=True):
files = list(filter(lambda f: not f.startswith("."), files))
files = list(filter(lambda f: not f.endswith(".html"), files))
files = list(filter(lambda f: not f.endswith("_thumbnail.jpg"), files))
files = list(filter(lambda f: not f.endswith("_alttext.txt"), files))
files = list(
filter(lambda f: not f.endswith("_dv_description.txt"), files)
)
for file in files:
full_path = os.path.join(root, file)
distribusi_file = DistribusiFiles.query.filter_by(
path=full_path
).first()
if distribusi_file is None:
_distribusi_file_with_type(distribusi, full_path)

84
verse/distribusikan/editor.py → verse/distribusikan/css_editor_page.py

@ -13,28 +13,28 @@ from sqlalchemy.exc import (
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from app import db from app import db
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from forms.distribusiform import DistribusiForm from distribusikan.forms.distribusiform import DistribusiForm
from forms.editorform import EditorForm from distribusikan.forms.editorform import EditorForm
from forms.publicthemeform import PublicThemeForm from distribusikan.forms.publicthemeform import PublicThemeForm
from forms.selectorform import SelectorForm from distribusikan.forms.selectorform import SelectorForm
from forms.themeform import ThemeForm from distribusikan.forms.themeform import ThemeForm
from forms.uploadform import UploadForm from distribusikan.forms.uploadform import UploadForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def Editor(): def css_editor_page():
editorform = EditorForm() editorform = EditorForm()
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
if editorform.validate_on_submit(): if editorform.validate_on_submit():
ValidateEditCssForm(editorform, current_distribusi) validate_edit_css_form(editorform, current_distribusi)
return RenderDistribusiTemplate(current_distribusi) return render_distribusi_template(current_distribusi)
return RenderEditorTemplate(editorform, current_distribusi) return render_editor_template(editorform, current_distribusi)
def ValidateEditCssForm(editorform, current_distribusi): def validate_edit_css_form(editorform, current_distribusi):
newcssfolder = os.path.join("themes/userthemes", current_distribusi) newcssfolder = os.path.join("themes/userthemes", current_distribusi)
if os.path.exists(newcssfolder): if os.path.exists(newcssfolder):
shutil.rmtree(newcssfolder) shutil.rmtree(newcssfolder)
@ -44,22 +44,22 @@ def ValidateEditCssForm(editorform, current_distribusi):
shutil.rmtree(publicfolder) shutil.rmtree(publicfolder)
if editorform.public.data: if editorform.public.data:
MakePublicTheme(editorform, current_distribusi) make_public_theme(editorform, current_distribusi)
if editorform.cssfile.data: if editorform.cssfile.data:
SaveUploadCssFile(editorform, publicfolder) save_upload_css_file(editorform, publicfolder)
CopyPublicToUserFolder(editorform, publicfolder, newcssfolder) copy_public_to_user_folder(editorform, publicfolder, newcssfolder)
return return
else: else:
WriteCssToFile(editorform, publicfolder) write_css_to_file(editorform, publicfolder)
if editorform.cssfile.data: if editorform.cssfile.data:
SaveUploadCssFile(editorform, newcssfolder) save_upload_css_file(editorform, newcssfolder)
return return
if editorform.cssname.data: if editorform.cssname.data:
WriteCssToFile(editorform, newcssfolder) write_css_to_file(editorform, newcssfolder)
def SaveUploadCssFile(editorform, newcssfolder): def save_upload_css_file(editorform, newcssfolder):
if not os.path.exists(newcssfolder): if not os.path.exists(newcssfolder):
os.mkdir(newcssfolder) os.mkdir(newcssfolder)
cssfile = editorform.cssfile.data cssfile = editorform.cssfile.data
@ -74,7 +74,7 @@ def SaveUploadCssFile(editorform, newcssfolder):
cleanfile.close() cleanfile.close()
def WriteCssToFile(editorform, newcssfolder): def write_css_to_file(editorform, newcssfolder):
if not os.path.exists(newcssfolder): if not os.path.exists(newcssfolder):
os.mkdir(newcssfolder) os.mkdir(newcssfolder)
@ -86,7 +86,7 @@ def WriteCssToFile(editorform, newcssfolder):
cssfile.close cssfile.close
def CopyPublicToUserFolder(editorform, publicfolder, newcssfolder): def copy_public_to_user_folder(editorform, publicfolder, newcssfolder):
if not os.path.exists(newcssfolder): if not os.path.exists(newcssfolder):
os.mkdir(newcssfolder) os.mkdir(newcssfolder)
copycssfile = os.path.join( copycssfile = os.path.join(
@ -97,7 +97,7 @@ def CopyPublicToUserFolder(editorform, publicfolder, newcssfolder):
shutil.copy(copycssfile, newcssfolder) shutil.copy(copycssfile, newcssfolder)
def MakePublicTheme(editorform, current_distribusi): def make_public_theme(editorform, current_distribusi):
try: try:
distribusi = Distribusis.query.filter_by( distribusi = Distribusis.query.filter_by(
distribusiname=current_distribusi distribusiname=current_distribusi
@ -119,12 +119,12 @@ def MakePublicTheme(editorform, current_distribusi):
editorform.public.errors.append("Error connecting to the database") editorform.public.errors.append("Error connecting to the database")
def RenderDistribusiTemplate(current_distribusi): def render_distribusi_template(current_distribusi):
uploadform = UploadForm() uploadform = UploadForm()
distribusiform = DistribusiForm() distribusiform = DistribusiForm()
themeform = ThemeForm() themeform = ThemeForm()
publicthemeform = PublicThemeForm() publicthemeform = PublicThemeForm()
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes() publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
selectorform = SelectorForm() selectorform = SelectorForm()
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi) files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
@ -148,11 +148,11 @@ def RenderDistribusiTemplate(current_distribusi):
return template return template
def RenderEditorTemplate(editorform, current_distribusi): def render_editor_template(editorform, current_distribusi):
htmlplaceholder = HtmlPlaceholder() html_placeholder = get_html_placeholder()
cssplaceholder = CssPlaceholder(current_distribusi) css_placeholder = get_css_placeholder(current_distribusi)
editorform.css.data = cssplaceholder editorform.css.data = css_placeholder
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi) files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
distribusi_live = UserHelper.is_distribusi_live(current_distribusi) distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
@ -161,33 +161,33 @@ def RenderEditorTemplate(editorform, current_distribusi):
files_uploaded=files_uploaded, files_uploaded=files_uploaded,
distribusi_live=distribusi_live, distribusi_live=distribusi_live,
editorform=editorform, editorform=editorform,
htmlplaceholder=htmlplaceholder, html_placeholder=html_placeholder,
) )
return template return template
def CssPlaceholder(current_distribusi): def get_css_placeholder(current_distribusi):
cssplaceholder = "Try out your CSS here" css_placeholder = "Try out your CSS here"
distribusi = Distribusis.query.filter_by( distribusi = Distribusis.query.filter_by(
distribusiname=current_distribusi distribusiname=current_distribusi
).first() ).first()
if distribusi is not None and distribusi.publictheme is not None: if distribusi is not None and distribusi.publictheme is not None:
cssplaceholder = GetPublicCssFile(distribusi) css_placeholder = get_public_css_file(distribusi)
else: else:
with open("themes/editor/placeholder.css") as f: with open("themes/editor/placeholder.css") as f:
cssplaceholder = f.read() css_placeholder = f.read()
return cssplaceholder return css_placeholder
def HtmlPlaceholder(): def get_html_placeholder():
htmlplaceholder = "Write some test HTML here" html_placeholder = "Write some test HTML here"
with open("themes/editor/placeholder.html") as f: with open("themes/editor/placeholder.html") as f:
htmlplaceholder = f.read() html_placeholder = f.read()
return htmlplaceholder return html_placeholder
def GetPublicCssFile(distribusi): def get_public_css_file(distribusi):
cssplaceholder = "" css_placeholder = ""
publicthemefolder = os.path.join( publicthemefolder = os.path.join(
"themes/publicthemes", distribusi.distribusiname "themes/publicthemes", distribusi.distribusiname
) )
@ -195,5 +195,5 @@ def GetPublicCssFile(distribusi):
if filename.endswith(".css"): if filename.endswith(".css"):
cssfile = os.path.join(publicthemefolder, filename) cssfile = os.path.join(publicthemefolder, filename)
with open(cssfile) as f: with open(cssfile) as f:
cssplaceholder = f.read() css_placeholder = f.read()
return cssplaceholder return css_placeholder

72
verse/distribusikan/distribusiselector.py → verse/distribusikan/distribusi_selector.py

@ -1,7 +1,7 @@
import os import os
import shutil import shutil
from flask import flash, render_template from flask import flash, render_template, redirect, url_for
from flask_login import current_user from flask_login import current_user
from sqlalchemy.exc import ( from sqlalchemy.exc import (
DatabaseError, DatabaseError,
@ -11,12 +11,12 @@ from sqlalchemy.exc import (
) )
from app import db from app import db
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from forms.distribusiform import DistribusiForm from distribusikan.forms.distribusiform import DistribusiForm
from forms.publicthemeform import PublicThemeForm from distribusikan.forms.publicthemeform import PublicThemeForm
from forms.selectorform import SelectorForm from distribusikan.forms.selectorform import SelectorForm
from forms.themeform import ThemeForm from distribusikan.forms.themeform import ThemeForm
from forms.uploadform import UploadForm from distribusikan.forms.uploadform import UploadForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
@ -24,52 +24,66 @@ from models.user_model import User
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def DistribusiSelector(): def distribusi_selector():
uploadform = UploadForm() uploadform = UploadForm()
selectorform = SelectorForm() selectorform = SelectorForm()
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames() selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
if selectorform.validate_on_submit(): if selectorform.validate_on_submit():
if selectorform.new.data: if selectorform.new.data:
SelectNewDistribusi() select_new_distribusi()
if selectorform.describe.data:
return select_describe_distribusi(selectorform.distribusis.data)
if selectorform.delete.data: if selectorform.delete.data:
selectorform = DeleteDistribusi(selectorform.distribusis.data) selectorform = delete_distribusi(selectorform.distribusis.data)
selectorform.distribusis.choices = ( selectorform.distribusis.choices = (
DistribusisInfo.userdistribusinames() DistribusisInfo.user_distribusinames()
) )
if selectorform.update.data: if selectorform.update.data:
SelectUpdateDistribusi(selectorform.distribusis.data) select_update_distribusi(selectorform.distribusis.data)
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
uploadform = AutoFillInUploadForm(uploadform, current_distribusi) uploadform = auto_fill_in_upload_form(
uploadform, current_distribusi
)
return RenderDistribusiTemplate( return render_distribusi_template(
selectorform, uploadform, current_distribusi selectorform, uploadform, current_distribusi
) )
def AutoFillInUploadForm(uploadform, current_distribusi): def auto_fill_in_upload_form(uploadform, current_distribusi):
distribusi = Distribusis.query.filter_by( distribusi = Distribusis.query.filter_by(
distribusiname=current_distribusi distribusiname=current_distribusi
).first() ).first()
uploadform.sitename.data = distribusi.distribusiname uploadform.sitename.data = distribusi.distribusiname
uploadform.sitename.render_kw = {"readonly": True} uploadform.sitename.render_kw = {"readonly": True}
uploadform.description.data = distribusi.description
uploadform.category.data = distribusi.category uploadform.category.data = distribusi.category
uploadform.year.data = distribusi.year uploadform.year.data = distribusi.year
uploadform.tags.data = distribusi.tags uploadform.tags.data = distribusi.tags
return uploadform return uploadform
def SelectNewDistribusi(): def select_new_distribusi():
print("make a new distribusi") print("make a new distribusi")
SelectCurrentDistribusi("new") select_current_distribusi("new")
def select_describe_distribusi(distribusiname):
return redirect(
url_for(
"describer.show_distribusi_files",
distribusiname=distribusiname,
)
)
def SelectUpdateDistribusi(distribusiname): def select_update_distribusi(distribusiname):
print(f"Update this distribusi {distribusiname}") print(f"Update this distribusi {distribusiname}")
SelectCurrentDistribusi(distribusiname) select_current_distribusi(distribusiname)
def DeleteDistribusi(distribusiname): def delete_distribusi(distribusiname):
print(f"delete this distribusi {distribusiname}") print(f"delete this distribusi {distribusiname}")
selectorform = SelectorForm() selectorform = SelectorForm()
try: try:
@ -103,7 +117,7 @@ def DeleteDistribusi(distribusiname):
return selectorform return selectorform
def SelectCurrentDistribusi(distribusiname): def select_current_distribusi(distribusiname):
if not current_user.is_authenticated: if not current_user.is_authenticated:
return return
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
@ -115,35 +129,35 @@ def SelectCurrentDistribusi(distribusiname):
flash("An error occured !", "danger") flash("An error occured !", "danger")
def DistribusiSelected(): def distribusi_selected():
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
if user.currentdistribusi is None: if user.currentdistribusi is None:
return False return False
return True return True
def SelectorVisible(): def selector_visible():
has_distribusi = UserHelper.has_distribusi() has_distribusi = UserHelper.has_distribusi()
distribusi_selected = DistribusiSelected() is_distribusi_selected = distribusi_selected()
if distribusi_selected: if is_distribusi_selected:
return False return False
if not has_distribusi: if not has_distribusi:
return False return False
return True return True
def RenderDistribusiTemplate(selectorform, uploadform, current_distribusi): def render_distribusi_template(selectorform, uploadform, current_distribusi):
distribusiform = DistribusiForm() distribusiform = DistribusiForm()
themeform = ThemeForm() themeform = ThemeForm()
publicthemeform = PublicThemeForm() publicthemeform = PublicThemeForm()
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes() publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi) files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
distribusi_live = UserHelper.is_distribusi_live(current_distribusi) distribusi_live = UserHelper.is_distribusi_live(current_distribusi)
# because the user has chosen to update his distribusi, we assume # because the user has chosen to update his distribusi, we assume
# no selected css. # no selected css.
css_selected = False css_selected = False
selectorvisible = SelectorVisible() selectorvisible = selector_visible()
limit_reached = UserHelper.distribusi_limit_reached() limit_reached = UserHelper.distribusi_limit_reached()
template = render_template( template = render_template(
"distribusi.html", "distribusi.html",

61
verse/distribusikan/distribusiworkflow.py → verse/distribusikan/distribusi_workflow.py

@ -15,15 +15,16 @@ from sqlalchemy.exc import (
) )
from app import db from app import db
from distribusikan.distribusiselector import SelectorVisible from distribusikan.add_files_to_describer import add_distribusi_files_to_db
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusi_selector import selector_visible
from forms.distribusiform import DistribusiForm from distribusikan.distribusis_info import DistribusisInfo
from forms.publicthemeform import PublicThemeForm from distribusikan.forms.distribusiform import DistribusiForm
from forms.selectorform import SelectorForm from distribusikan.forms.publicthemeform import PublicThemeForm
from forms.themeform import ThemeForm from distribusikan.forms.selectorform import SelectorForm
from distribusikan.forms.themeform import ThemeForm
# Forms! # Forms!
from forms.uploadform import UploadForm from distribusikan.forms.uploadform import UploadForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
@ -31,7 +32,7 @@ from models.user_model import User
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def DistribusiWorkflow(): def distribusi_workflow():
distribusiform = DistribusiForm() distribusiform = DistribusiForm()
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
@ -41,18 +42,19 @@ def DistribusiWorkflow():
if distribusiform.validate_on_submit(): if distribusiform.validate_on_submit():
userfolder = os.path.join("stash", distribusi.distribusiname) userfolder = os.path.join("stash", distribusi.distribusiname)
cssfile = GetCssFile(distribusi) cssfile = get_css_file(distribusi)
UnzipDistribusiFiles(distribusi, userfolder) unzip_distribusi_files(distribusi, userfolder)
CleanUpDistribusiFiles(userfolder) clean_up_distribusi_files(userfolder)
RunDistribusi(userfolder, cssfile) add_distribusi_files_to_db(distribusi.distribusiname)
SetDistribusiToVisible(distribusi, user) run_distribusi(userfolder, cssfile)
DeleteCssFile(cssfile) set_distribusi_to_visible(distribusi, user)
delete_css_file(cssfile)
return redirect(url_for("index")) return redirect(url_for("index"))
return RenderDistribusiTemplate(distribusiform, current_distribusi) return render_distribusi_template(distribusiform, current_distribusi)
def UnzipDistribusiFiles(distribusi, userfolder): def unzip_distribusi_files(distribusi, userfolder):
zipfilename = "{}.zip".format(distribusi.distribusiname) zipfilename = "{}.zip".format(distribusi.distribusiname)
unzipfile = os.path.join(userfolder, zipfilename) unzipfile = os.path.join(userfolder, zipfilename)
@ -64,12 +66,12 @@ def UnzipDistribusiFiles(distribusi, userfolder):
os.remove(os.path.join(userfolder, zipfilename)) os.remove(os.path.join(userfolder, zipfilename))
def CleanUpDistribusiFiles(userfolder): def clean_up_distribusi_files(userfolder):
if os.path.exists(userfolder): if os.path.exists(userfolder):
RemoveMacFolders(userfolder) remove_mac_folders(userfolder)
def RemoveMacFolders(path): def remove_mac_folders(path):
for filename in os.listdir(path): for filename in os.listdir(path):
fullpath = os.path.join(path, filename) fullpath = os.path.join(path, filename)
if filename.startswith("."): if filename.startswith("."):
@ -80,10 +82,10 @@ def RemoveMacFolders(path):
if filename == "__MACOSX": if filename == "__MACOSX":
shutil.rmtree(fullpath) shutil.rmtree(fullpath)
if os.path.isdir(fullpath): if os.path.isdir(fullpath):
RemoveMacFolders(fullpath) remove_mac_folders(fullpath)
def GetCssFile(distribusi): def get_css_file(distribusi):
cssfile = "" cssfile = ""
cssfolder = os.path.join("themes/userthemes", distribusi.distribusiname) cssfolder = os.path.join("themes/userthemes", distribusi.distribusiname)
if os.path.exists(cssfolder): if os.path.exists(cssfolder):
@ -93,13 +95,14 @@ def GetCssFile(distribusi):
return cssfile return cssfile
def RunDistribusi(userfolder, cssfile): def run_distribusi(userfolder, cssfile):
print(f"Run distribusi on this folder: {userfolder} with css:{cssfile}")
parser = build_argparser() parser = build_argparser()
args = parser.parse_args(["-t", "--menu-with-index", "-s", cssfile]) args = parser.parse_args(["-t", "-a", "--menu-with-index", "-s", cssfile])
distribusify(args, userfolder) distribusify(args, userfolder)
def SetDistribusiToVisible(distribusi, user): def set_distribusi_to_visible(distribusi, user):
try: try:
distribusi.visible = True distribusi.visible = True
user.currentdistribusi = None user.currentdistribusi = None
@ -109,19 +112,19 @@ def SetDistribusiToVisible(distribusi, user):
flash("Unknown error occured!") flash("Unknown error occured!")
def DeleteCssFile(cssfile): def delete_css_file(cssfile):
if os.path.exists(cssfile): if os.path.exists(cssfile):
os.remove(cssfile) os.remove(cssfile)
def RenderDistribusiTemplate(distribusiform, current_distribusi): def render_distribusi_template(distribusiform, current_distribusi):
uploadform = UploadForm() uploadform = UploadForm()
themeform = ThemeForm() themeform = ThemeForm()
publicthemeform = PublicThemeForm() publicthemeform = PublicThemeForm()
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes() publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
selectorform = SelectorForm() selectorform = SelectorForm()
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames() selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
selectorvisible = SelectorVisible() selectorvisible = selector_visible()
files_uploaded = UserHelper.is_zip_uploaded(current_distribusi) files_uploaded = UserHelper.is_zip_uploaded(current_distribusi)
distribusi_live = UserHelper.is_distribusi_live(current_distribusi) distribusi_live = UserHelper.is_distribusi_live(current_distribusi)

21
verse/distribusikan/distribusikan.py

@ -1,14 +1,13 @@
from flask import Blueprint from flask import Blueprint
from flask_login import login_required from flask_login import login_required
from distribusikan.distribusiselector import DistribusiSelector from distribusikan.distribusi_selector import distribusi_selector
# Distribusi Information # Distribusi Information
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusi_workflow import distribusi_workflow
from distribusikan.distribusiworkflow import DistribusiWorkflow from distribusikan.css_editor_page import css_editor_page
from distribusikan.editor import Editor from distribusikan.theme_selector import theme_selector
from distribusikan.themeselector import ThemeSelector from distribusikan.upload_page import upload_page
from distribusikan.uploadpage import UploadPage
distribusikan = Blueprint( distribusikan = Blueprint(
"distribusikan", "distribusikan",
@ -21,28 +20,28 @@ distribusikan = Blueprint(
@distribusikan.route("/distribusi", methods=["GET", "POST"]) @distribusikan.route("/distribusi", methods=["GET", "POST"])
@login_required @login_required
def distribusi(): def distribusi():
return DistribusiWorkflow() return distribusi_workflow()
@distribusikan.route("/upload", methods=["POST"]) @distribusikan.route("/upload", methods=["POST"])
@login_required @login_required
def upload(): def upload():
return UploadPage() return upload_page()
@distribusikan.route("/theme", methods=["GET", "POST"]) @distribusikan.route("/theme", methods=["GET", "POST"])
@login_required @login_required
def theme(): def theme():
return ThemeSelector() return theme_selector()
@distribusikan.route("/editor", methods=["GET", "POST"]) @distribusikan.route("/editor", methods=["GET", "POST"])
@login_required @login_required
def editor(): def editor():
return Editor() return css_editor_page()
@distribusikan.route("/selector", methods=["GET", "POST"]) @distribusikan.route("/selector", methods=["GET", "POST"])
@login_required @login_required
def selector(): def selector():
return DistribusiSelector() return distribusi_selector()

8
verse/distribusikan/distribusisinfo.py → verse/distribusikan/distribusis_info.py

@ -5,14 +5,14 @@ from models.user_model import User
class DistribusisInfo: class DistribusisInfo:
def userdistribusinames(): def user_distribusinames():
distribusinames = [] distribusinames = []
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
for distribusi in Distribusis.query.filter_by(userid=user.id).all(): for distribusi in Distribusis.query.filter_by(userid=user.id).all():
distribusinames.append(distribusi.distribusiname) distribusinames.append(distribusi.distribusiname)
return distribusinames return distribusinames
def publicthemes(): def public_themes():
publicthemes = [] publicthemes = []
distribusis = Distribusis.query.filter( distribusis = Distribusis.query.filter(
Distribusis.publictheme.isnot(None) Distribusis.publictheme.isnot(None)
@ -27,12 +27,12 @@ made by {user.username}""",
publicthemes.append(publictheme) publicthemes.append(publictheme)
return publicthemes return publicthemes
def visibledistribusis(): def visible_distribusis():
distribusis = Distribusis.query.filter( distribusis = Distribusis.query.filter(
Distribusis.visible.isnot(False) Distribusis.visible.isnot(False)
).all() ).all()
return distribusis return distribusis
def getuserdistribusis(useremail): def get_user_distribusis(useremail):
user = User.query.filter_by(email=useremail).first() user = User.query.filter_by(email=useremail).first()
return Distribusis.query.filter_by(userid=user.id).all() return Distribusis.query.filter_by(userid=user.id).all()

0
verse/forms/distribusiform.py → verse/distribusikan/forms/distribusiform.py

0
verse/forms/editorform.py → verse/distribusikan/forms/editorform.py

0
verse/forms/publicthemeform.py → verse/distribusikan/forms/publicthemeform.py

2
verse/forms/selectorform.py → verse/distribusikan/forms/selectorform.py

@ -9,4 +9,6 @@ class SelectorForm(FlaskForm):
update = SubmitField("update") update = SubmitField("update")
describe = SubmitField("describe")
delete = SubmitField("delete") delete = SubmitField("delete")

0
verse/forms/themeform.py → verse/distribusikan/forms/themeform.py

17
verse/forms/uploadform.py → verse/distribusikan/forms/uploadform.py

@ -1,18 +1,18 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize from flask_wtf.file import FileAllowed, FileField, FileRequired, FileSize
from wtforms import ( from wtforms import (
IntegerField,
SelectField, SelectField,
StringField, StringField,
SubmitField, SubmitField,
TextAreaField,
validators, validators,
) )
from wtforms.validators import ( from wtforms.validators import (
DataRequired, DataRequired,
Length, Length,
NumberRange,
ValidationError, ValidationError,
) )
from wtforms.widgets import TextArea
from app import settings from app import settings
@ -50,6 +50,13 @@ class UploadForm(FlaskForm):
distribusiname, distribusiname,
], ],
) )
description = StringField(
"Description of this distribusi:",
validators=[
Length(10, 32000),
],
widget=TextArea(),
)
year = SelectField( year = SelectField(
"Year:", "Year:",
validate_choice=True, validate_choice=True,
@ -68,8 +75,8 @@ class UploadForm(FlaskForm):
) )
tags = StringField( tags = StringField(
"Add tags, seperated by commas. No need for the '#' sign:", "Add search tags, seperated by commas. No need for the '#' sign:",
validators=[validators.InputRequired(), Length(2, 500)], validators=[validators.InputRequired(), Length(3, 500)],
) )
zipfile = FileField( zipfile = FileField(
@ -79,7 +86,7 @@ class UploadForm(FlaskForm):
FileRequired(), FileRequired(),
FileSize( FileSize(
max_size=1073741824, max_size=1073741824,
message="Zipfile size must be smaller than 100MB", message="Zipfile size must be smaller than 1024MB",
), ),
], ],
) )

32
verse/distribusikan/static/css/distribusikan.css

@ -0,0 +1,32 @@
#publicthemes > ul {
max-height: 20em;
overflow: auto;
}
#publicthemes > ul > li{
word-break: break-all;
}
.workflow{
margin-top: 1em;
padding: 0.5em;
background-color:#fdfdfd;
text-decoration: none;
scroll-behavior: smooth;
border-style: outset;
}
.workflow > p {
padding-left: 1em;
}
.workflow > h2 {
padding-left: 0.4em;;
}
.workflow input{
max-width: 20em;
}
.new_divider {
height: 5em;
}

8
verse/static/css/editor.css → verse/distribusikan/static/css/editor.css

@ -5,7 +5,7 @@
} }
.editarea { .editarea {
width: 30%; width: 30%;
border: 3px solid #E0B0FF; border: 3px solid #9de457;
border-style: outset; border-style: outset;
margin-right: 1em; margin-right: 1em;
margin-left: 0; margin-left: 0;
@ -31,7 +31,7 @@ textarea {
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
min-height: 250px; min-height: 250px;
background: #E0B0FF; background: #9de457;
outline: none; outline: none;
font-family: Courier, sans-serif; font-family: Courier, sans-serif;
font-size: 16px; font-size: 16px;
@ -45,6 +45,6 @@ iframe {
height: 30em; height: 30em;
} }
#html { #html {
background-color: #60337F; background-color: #9de457;
color: lightgrey; color: black;
} }

11
verse/distribusikan/templates/distribusikan/distribusi.html

@ -20,27 +20,28 @@
<div id="mainworkflow"> <div id="mainworkflow">
{% if selectorvisible %} {% if selectorvisible %}
{% block selector %} {% block selector %}
{% include "distribusiworkflow/selector.html" %} {% include "distribusi_workflow/selector.html" %}
{% endblock selector%} {% endblock selector%}
{% else %} {% else %}
{% block upload %} {% block upload %}
{% include "distribusiworkflow/upload.html" %} {% include "distribusi_workflow/upload.html" %}
{% endblock upload%} {% endblock upload%}
<img src="{{ url_for('static', filename='svg/arrow_1.svg')}}" /> <img src="{{ url_for('static', filename='svg/arrow_1.svg')}}" />
{% block theme %} {% block theme %}
{% include "distribusiworkflow/theme.html" %} {% include "distribusi_workflow/theme.html" %}
{% endblock theme%} {% endblock theme%}
<img src="{{ url_for('static', filename='svg/arrow_2.svg')}}" /> <img src="{{ url_for('static', filename='svg/arrow_2.svg')}}" />
{% block editcss %} {% block editcss %}
{% include "distribusiworkflow/editcss.html" %} {% include "distribusi_workflow/editcss.html" %}
{% endblock editcss%} {% endblock editcss%}
<img src="{{ url_for('static', filename='svg/arrow_3.svg')}}" /> <img src="{{ url_for('static', filename='svg/arrow_3.svg')}}" />
{% block launch %} {% block launch %}
{% include "distribusiworkflow/launch.html" %} {% include "distribusi_workflow/launch.html" %}
{% endblock launch%} {% endblock launch%}
{%endif%} {%endif%}
</div> </div>
{% if css_selected %} {% if css_selected %}
<p id="cssSelected" hidden>css selected</p> <p id="cssSelected" hidden>css selected</p>
{% endif %} {% endif %}
<link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.static', filename='css/distribusikan.css')}}">
{% endblock main %} {% endblock main %}

2
verse/distribusikan/templates/distribusikan/distribusiworkflow/editcss.html → verse/distribusikan/templates/distribusikan/distribusi_workflow/editcss.html

@ -1,7 +1,7 @@
<div id="edit" class="workflow"> <div id="edit" class="workflow">
<h2>Step 3: Edit Custom CSS (Optional)</h2> <h2>Step 3: Edit Custom CSS (Optional)</h2>
{% if files_uploaded or distribusi_live %} {% if files_uploaded or distribusi_live %}
<p><a href="/editor">Go to CSS editor</a></p> <p><a href="/distribusikan/editor">Go to CSS editor</a></p>
{% else %} {% else %}
<p> <p>
You need to upload your files first before you can select a css theme You need to upload your files first before you can select a css theme

0
verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html → verse/distribusikan/templates/distribusikan/distribusi_workflow/launch.html

8
verse/distribusikan/templates/distribusikan/distribusiworkflow/selector.html → verse/distribusikan/templates/distribusikan/distribusi_workflow/selector.html

@ -18,6 +18,13 @@
{{ selectorform.update }} {{ selectorform.update }}
</fieldset> </fieldset>
<hr> <hr>
<p>
Describe your distribusi files. Add description texts, tags for search
or add alt-text for images</p>
<fieldset class="button required multiselect">
{{ selectorform.describe }}
</fieldset>
<hr>
<p> <p>
This will delete your distribusi site. This will delete your distribusi site.
<strong> This action cannot be undone! </strong> <strong> This action cannot be undone! </strong>
@ -29,6 +36,7 @@
{% endfor %} {% endfor %}
</fieldset> </fieldset>
<hr> <hr>
<div class="new_divider"></div>
{% if limit_reached %} {% if limit_reached %}
<p>You have reached your limit of distribusi websites</p> <p>You have reached your limit of distribusi websites</p>
{% else %} {% else %}

0
verse/distribusikan/templates/distribusikan/distribusiworkflow/theme.html → verse/distribusikan/templates/distribusikan/distribusi_workflow/theme.html

7
verse/distribusikan/templates/distribusikan/distribusiworkflow/upload.html → verse/distribusikan/templates/distribusikan/distribusi_workflow/upload.html

@ -10,6 +10,13 @@
<div class="error">{{ message }}</div> <div class="error">{{ message }}</div>
{% endfor %} {% endfor %}
</fieldset> </fieldset>
<fieldset class="required">
{{ uploadform.description.label }}
{{ uploadform.description }}
{% for message in uploadform.description.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="required"> <fieldset class="required">
{{ uploadform.year.label }} {{ uploadform.year.label }}
<div class="selector-style"> <div class="selector-style">

4
verse/distribusikan/templates/distribusikan/editor.html

@ -5,7 +5,7 @@
<div class="editarea editor"> <div class="editarea editor">
<fieldset class="required"> <fieldset class="required">
<textarea id="html" placeholder="Write some test HTML here" readonly> <textarea id="html" placeholder="Write some test HTML here" readonly>
{{htmlplaceholder}} {{html_placeholder}}
</textarea> </textarea>
</fieldset> </fieldset>
</div> </div>
@ -54,6 +54,6 @@
</form> </form>
<iframe id="code"></iframe> <iframe id="code"></iframe>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/editor.css')}}"> <link rel="stylesheet" type="text/css" href="{{ url_for('distribusikan.static', filename='css/editor.css')}}">
<script src="{{ url_for('static', filename='js/editorupdate.js')}}"></script> <script src="{{ url_for('static', filename='js/editorupdate.js')}}"></script>
{% endblock main %} {% endblock main %}

26
verse/distribusikan/themeselector.py → verse/distribusikan/theme_selector.py

@ -3,45 +3,45 @@ import shutil
from flask import render_template from flask import render_template
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from forms.distribusiform import DistribusiForm from distribusikan.forms.distribusiform import DistribusiForm
from forms.publicthemeform import PublicThemeForm from distribusikan.forms.publicthemeform import PublicThemeForm
from forms.selectorform import SelectorForm from distribusikan.forms.selectorform import SelectorForm
from forms.themeform import ThemeForm from distribusikan.forms.themeform import ThemeForm
from forms.uploadform import UploadForm from distribusikan.forms.uploadform import UploadForm
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def ThemeSelector(): def theme_selector():
themeform = ThemeForm() themeform = ThemeForm()
publicthemeform = PublicThemeForm() publicthemeform = PublicThemeForm()
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes() publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
if themeform.validate_on_submit(): if themeform.validate_on_submit():
copycssfile = os.path.join( copycssfile = os.path.join(
"themes", "themes",
f"{themeform.theme.data}.css", f"{themeform.theme.data}.css",
) )
MoveCssToUserFolder(current_distribusi, copycssfile) move_css_to_user_folder(current_distribusi, copycssfile)
if publicthemeform.validate_on_submit(): if publicthemeform.validate_on_submit():
copycssfile = os.path.join( copycssfile = os.path.join(
"themes/publicthemes/", "themes/publicthemes/",
f"{publicthemeform.publicthemes.data}.css", f"{publicthemeform.publicthemes.data}.css",
) )
MoveCssToUserFolder(current_distribusi, copycssfile) move_css_to_user_folder(current_distribusi, copycssfile)
return RenderDistribusiTemplate( return render_distribusi_template(
themeform, publicthemeform, current_distribusi themeform, publicthemeform, current_distribusi
) )
def MoveCssToUserFolder(current_distribusi, copycssfile): def move_css_to_user_folder(current_distribusi, copycssfile):
newcssfolder = os.path.join("themes/userthemes", current_distribusi) newcssfolder = os.path.join("themes/userthemes", current_distribusi)
if not os.path.exists(newcssfolder): if not os.path.exists(newcssfolder):
os.mkdir(newcssfolder) os.mkdir(newcssfolder)
shutil.copy(copycssfile, newcssfolder) shutil.copy(copycssfile, newcssfolder)
def RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi): def render_distribusi_template(themeform, publicthemeform, current_distribusi):
uploadform = UploadForm() uploadform = UploadForm()
distribusiform = DistribusiForm() distribusiform = DistribusiForm()
selectorform = SelectorForm() selectorform = SelectorForm()

12
verse/distribusikan/upload.py

@ -12,14 +12,14 @@ from sqlalchemy.exc import (
) )
from app import db from app import db
from distribusikan.distribusiselector import SelectCurrentDistribusi from distribusikan.distribusi_selector import select_current_distribusi
from forms.uploadform import UploadForm from distribusikan.forms.uploadform import UploadForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def UploadNewDistribusi(uploadfolder): def upload_new_distribusi(uploadfolder):
uploadform = UploadForm() uploadform = UploadForm()
if uploadform.validate_on_submit(): if uploadform.validate_on_submit():
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
@ -28,6 +28,7 @@ def UploadNewDistribusi(uploadfolder):
distribusiname=uploadform.sitename.data, distribusiname=uploadform.sitename.data,
userid=user.id, userid=user.id,
category=uploadform.category.data, category=uploadform.category.data,
description=uploadform.description.data,
year=uploadform.year.data, year=uploadform.year.data,
tags=uploadform.tags.data, tags=uploadform.tags.data,
) )
@ -46,7 +47,7 @@ def UploadNewDistribusi(uploadfolder):
uploadform.sitename.errors.append("Something went wrong!") uploadform.sitename.errors.append("Something went wrong!")
flash("Something went wrong!", "danger") flash("Something went wrong!", "danger")
return uploadform return uploadform
SelectCurrentDistribusi(newdistribusi.distribusiname) select_current_distribusi(newdistribusi.distribusiname)
zipfilename = "{}.zip".format(newdistribusi.distribusiname) zipfilename = "{}.zip".format(newdistribusi.distribusiname)
zipfile = uploadform.zipfile.data zipfile = uploadform.zipfile.data
zipfile.save(os.path.join(uploadfolder, zipfilename)) zipfile.save(os.path.join(uploadfolder, zipfilename))
@ -62,7 +63,7 @@ def UploadNewDistribusi(uploadfolder):
return uploadform return uploadform
def UploadUpdatedFiles(uploadfolder): def upload_updates_files(uploadfolder):
uploadform = UploadForm() uploadform = UploadForm()
if uploadform.validate_on_submit(): if uploadform.validate_on_submit():
try: try:
@ -71,6 +72,7 @@ def UploadUpdatedFiles(uploadfolder):
distribusiname=current_distribusi distribusiname=current_distribusi
).first() ).first()
distribusi.category = uploadform.category.data distribusi.category = uploadform.category.data
distribusi.description = (uploadform.description.data,)
distribusi.year = uploadform.year.data distribusi.year = uploadform.year.data
distribusi.tags = uploadform.tags.data distribusi.tags = uploadform.tags.data
distribusi.visible = False distribusi.visible = False

26
verse/distribusikan/uploadpage.py → verse/distribusikan/upload_page.py

@ -1,34 +1,34 @@
from flask import render_template from flask import render_template
from app import APP from app import APP
from distribusikan.distribusiselector import SelectorVisible from distribusikan.distribusi_selector import selector_visible
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from distribusikan.upload import UploadNewDistribusi, UploadUpdatedFiles from distribusikan.upload import upload_new_distribusi, upload_updates_files
from forms.distribusiform import DistribusiForm from distribusikan.forms.distribusiform import DistribusiForm
from forms.publicthemeform import PublicThemeForm from distribusikan.forms.publicthemeform import PublicThemeForm
from forms.selectorform import SelectorForm from distribusikan.forms.selectorform import SelectorForm
from forms.themeform import ThemeForm from distribusikan.forms.themeform import ThemeForm
# UserPengguna # UserPengguna
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
def UploadPage(): def upload_page():
"render upload page section of distribusi workflow" "render upload page section of distribusi workflow"
uploadfolder = APP.config["UPLOAD_FOLDER"] uploadfolder = APP.config["UPLOAD_FOLDER"]
distribusiform = DistribusiForm() distribusiform = DistribusiForm()
themeform = ThemeForm() themeform = ThemeForm()
publicthemeform = PublicThemeForm() publicthemeform = PublicThemeForm()
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes() publicthemeform.publicthemes.choices = DistribusisInfo.public_themes()
selectorform = SelectorForm() selectorform = SelectorForm()
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames() selectorform.distribusis.choices = DistribusisInfo.user_distribusinames()
selectorvisible = SelectorVisible() selectorvisible = selector_visible()
current_distribusi = UserHelper.current_distribusi() current_distribusi = UserHelper.current_distribusi()
if current_distribusi == "new" or UserHelper.has_distribusi() is False: if current_distribusi == "new" or UserHelper.has_distribusi() is False:
uploadform = UploadNewDistribusi(uploadfolder) uploadform = upload_new_distribusi(uploadfolder)
else: else:
uploadform = UploadUpdatedFiles(uploadfolder) uploadform = upload_updates_files(uploadfolder)
files_uploaded = UserHelper.is_zip_uploaded(uploadform.sitename.data) files_uploaded = UserHelper.is_zip_uploaded(uploadform.sitename.data)
distribusi_live = UserHelper.is_distribusi_live(current_distribusi) distribusi_live = UserHelper.is_distribusi_live(current_distribusi)

3
verse/migrations/env.py

@ -3,9 +3,10 @@ from __future__ import with_statement
import logging import logging
from logging.config import fileConfig from logging.config import fileConfig
from alembic import context
from flask import current_app from flask import current_app
from alembic import context
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
# access to the values within the .ini file in use. # access to the values within the .ini file in use.
config = context.config config = context.config

9
verse/models/distribusi_file_model.py

@ -7,11 +7,12 @@ class DistribusiFiles(db.Model):
__tablename__ = "distribusi_files" __tablename__ = "distribusi_files"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String(300), nullable=False, unique=True) type = db.Column(db.String(100), nullable=True, unique=False)
distribusi = db.Column(db.Integer, db.ForeignKey("distribusis.id")) distribusi = db.Column(db.Integer, db.ForeignKey("distribusis.id"))
path = db.Column(db.String(4096), nullable=True, unique=False) path = db.Column(db.String(4096), nullable=True, unique=True)
description = db.Column(db.String(9), nullable=True, unique=False) alttext = db.Column(db.String(255), nullable=True, unique=False)
tags = db.Column(db.String(500), nullable=True, unique=False) tags = db.Column(db.String(500), nullable=True, unique=False)
description = db.Column(db.String(4096), nullable=True, unique=False)
def __repr__(self): def __repr__(self):
return "<Distribusi_File %r>" % self.distribusiname return "<Distribusi_File %r>" % self.path

1
verse/models/distribusi_model.py

@ -11,6 +11,7 @@ class Distribusis(db.Model):
userid = db.Column(db.Integer, db.ForeignKey("users.id")) userid = db.Column(db.Integer, db.ForeignKey("users.id"))
category = db.Column(db.String(500), nullable=True, unique=False) category = db.Column(db.String(500), nullable=True, unique=False)
year = db.Column(db.String(9), nullable=True, unique=False) year = db.Column(db.String(9), nullable=True, unique=False)
description = db.Column(db.String(32000), nullable=True, unique=False)
tags = db.Column(db.String(500), nullable=True, unique=False) tags = db.Column(db.String(500), nullable=True, unique=False)
publictheme = db.Column(db.String(300), unique=True, nullable=True) publictheme = db.Column(db.String(300), unique=True, nullable=True)
visible = db.Column(db.Boolean, default=False) visible = db.Column(db.Boolean, default=False)

11
verse/search/forms/searchform.py

@ -0,0 +1,11 @@
"""SearchForm to search files and distribusis in the distribusi archive"""
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, validators
from wtforms.validators import Length
from wtforms.widgets import TextArea
class SearchForm(FlaskForm):
searchfield = StringField("Search distribusi-verse archive")
submit = SubmitField("Search")

42
verse/search/search.py

@ -0,0 +1,42 @@
import os
from flask import Blueprint, render_template
from whoosh.fields import *
from whoosh.index import open_dir
from whoosh.qparser import QueryParser
from search.forms.searchform import SearchForm
searchpages = Blueprint(
"search",
__name__,
template_folder="templates/search",
static_folder="static",
)
SCRIPT_DIR = os.path.dirname(__file__)
SEARCH_DATA_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "searchdata"))
@searchpages.route("/", methods=["GET", "POST"])
def searchpage():
searchform = SearchForm()
found_distribusis = []
if searchform.validate_on_submit():
found_distribusis = search(searchform.searchfield.data)
template = render_template(
"search.html",
searchform=searchform,
found_distribusis=found_distribusis,
)
return template
def search(searchinput):
"""search and get search result titles and return them as distribusi ids"""
ix = open_dir(SEARCH_DATA_DIR)
with ix.searcher() as searcher:
query = QueryParser("content", ix.schema).parse(searchinput)
search_results = searcher.search(query)
found_distribusis = []
for result in search_results:
found_distribusis.append(result["title"])
return found_distribusis

52
verse/search/search_index.py

@ -0,0 +1,52 @@
import os
from whoosh.fields import *
from whoosh.index import create_in
from whoosh.qparser import QueryParser
from models.distribusi_model import Distribusis
import flask_apscheduler
SCRIPT_DIR = os.path.dirname(__file__)
SEARCH_DATA_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "searchdata"))
def init_search_index(APP):
scheduler = flask_apscheduler.APScheduler()
scheduler.api_enabled = False
scheduler.init_app(APP)
scheduler.start()
index_distribusis(APP)
index_distribusi_files(APP)
@scheduler.task("interval", id="update", minutes=60)
def update_search_index():
index_distribusis(APP)
index_distribusi_files(APP)
def index_distribusis(APP):
schema = Schema(
title=TEXT(stored=True), path=ID(stored=True), content=TEXT
)
ix = create_in(SEARCH_DATA_DIR, schema)
writer = ix.writer()
distribusis = _visible_distribusis(APP)
for distribusi in distribusis:
writer.add_document(
title=distribusi.distribusiname,
path="/a",
content=distribusi.description,
)
writer.commit()
def index_distribusi_files(APP):
APP.logger.info("searching distribusi files not implemented yet.")
def _visible_distribusis(APP):
with APP.app_context():
distribusis = Distribusis.query.filter(
Distribusis.visible.isnot(False)
).all()
return distribusis

23
verse/search/templates/search/search.html

@ -0,0 +1,23 @@
{% extends "base/base.html" %}
{% block main %}
<div id="distribusiverse" class="maincontent">
<form method="POST" enctype="multipart/form-data" action="{{ url_for('search.searchpage') }}">
{{ searchform.csrf_token }}
<fieldset class="required">
{{ searchform.searchfield.label }}
{{ searchform.searchfield }}
{% for message in searchform.searchfield.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="button required">
{{ searchform.submit }}
</fieldset>
</form>
<div class="searchresults">
{% for found_distribusi in found_distribusis %}
<a href='{{ url_for('shortstashurl')}}/{{found_distribusi}}/index.html'>{{found_distribusi}}</a>
{% endfor %}
</div>
</div>
{% endblock %}

5
verse/settings.toml

@ -1,4 +1,7 @@
title = "Varia Archive X Distribusi-Verse" title = "Varia Archive X Distribusi-Verse"
categories = ["event","gathering","work session","workgroup","performance","music event"] categories = [ "article", "booklaunch", "broadcast", "curriculum", "game",
"gathering", "lecture", "opencall", "party", "performance", "presentation",
"publication", "report", "screening", "statement", "workgroup", "worksession",
"workshop"]
start_time = 2017-11-03 start_time = 2017-11-03
end_time = 2025-12-31 end_time = 2025-12-31

46
verse/start.py

@ -11,31 +11,35 @@ from flask import (
url_for, url_for,
) )
from flask_login import current_user, login_required, logout_user from flask_login import current_user, login_required, logout_user
from flask_mail import Mail
from flask_wtf.csrf import CSRFError from flask_wtf.csrf import CSRFError
# Interface! these are seperate files in main folder from admin import is_adminuser
from adminpage import AdminPage from admin_page.admin_page import admin_page
from app import create_app, login_manager from app import create_app, login_manager, db
from describer.describe_files import describer
from distribusikan.distribusikan import distribusikan from distribusikan.distribusikan import distribusikan
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from distribusikan.forms.uploadform import UploadForm
# Use upload form to populate filters
from forms.uploadform import UploadForm
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
from statuspengguna.forgotpassword import forgot_password from statuspengguna.forgotpassword import forgot_password
from statuspengguna.helper import UserHelper from statuspengguna.helper import UserHelper
from statuspengguna.loginuser import login_section from statuspengguna.loginuser import login_section
from statuspengguna.registeruser import register_user from statuspengguna.registeruser import register_user
from search.search import searchpages
from search.search_index import init_search_index
APP = create_app() APP = create_app()
stash_page = Blueprint("stash_page", __name__, static_folder="stash") stash_page = Blueprint("stash_page", __name__, static_folder="stash")
APP.register_blueprint(stash_page)
APP.register_blueprint(describer, url_prefix="/describer")
APP.register_blueprint(login_section, url_prefix="/login") APP.register_blueprint(login_section, url_prefix="/login")
APP.register_blueprint(register_user, url_prefix="/register") APP.register_blueprint(register_user, url_prefix="/register")
APP.register_blueprint(forgot_password, url_prefix="/login/forgotpassword") APP.register_blueprint(forgot_password, url_prefix="/login/forgotpassword")
APP.register_blueprint(distribusikan) APP.register_blueprint(admin_page, url_prefix="/admin")
APP.register_blueprint(stash_page) APP.register_blueprint(distribusikan, url_prefix="/distribusikan")
APP.register_blueprint(searchpages, url_prefix="/search")
init_search_index(APP)
@APP.before_request @APP.before_request
@ -48,7 +52,7 @@ def session_handler():
def index(): def index():
UserHelper.reset_user_state() UserHelper.reset_user_state()
uploadform = UploadForm() uploadform = UploadForm()
distribusis = DistribusisInfo.visibledistribusis() distribusis = DistribusisInfo.visible_distribusis()
distribusisindex = {} distribusisindex = {}
for distribusi in distribusis: for distribusi in distribusis:
user = User.query.filter_by(id=distribusi.userid).first() user = User.query.filter_by(id=distribusi.userid).first()
@ -60,9 +64,10 @@ def index():
"tags": distribusi.tags.split(","), "tags": distribusi.tags.split(","),
} }
distribusisindex[distribusi.distribusiname] = singledistribusi distribusisindex[distribusi.distribusiname] = singledistribusi
years = uploadform.year.choices years = uploadform.year.choices
categories = uploadform.category.choices categories = uploadform.category.choices
adminuser = isadminuser() adminuser = is_adminuser()
template = render_template( template = render_template(
"base/index.html", "base/index.html",
distribusisindex=distribusisindex, distribusisindex=distribusisindex,
@ -91,14 +96,6 @@ def shortstashurl():
return redirect(url_for("index")) return redirect(url_for("index"))
@APP.route("/admin", methods=["GET", "POST"])
@login_required
def admin():
if not isadminuser():
return redirect(url_for("index"))
return AdminPage()
@APP.route("/logout") @APP.route("/logout")
@login_required @login_required
def logout(): def logout():
@ -113,14 +110,7 @@ def handle_csrf_error(e):
@login_manager.user_loader @login_manager.user_loader
def load_user(user_id): def load_user(user_id):
return User.query.get(int(user_id)) return db.session.get(User, int(user_id))
def isadminuser():
if not current_user.is_authenticated:
return False
user = User.query.filter_by(email=current_user.email).first()
return user.admin
if __name__ == "__main__": if __name__ == "__main__":

4
verse/static/css/dropdown.css

@ -2,7 +2,7 @@
/* for sorting on year and category /* for sorting on year and category
*/ */
button { button {
background-color: #E0B0FF; background-color: #9de457;
text-decoration: none; text-decoration: none;
border: none; border: none;
} }
@ -26,7 +26,7 @@ button {
.dropdown-content { .dropdown-content {
display: none; display: none;
position: absolute; position: absolute;
background-color: #E0B0FF; background-color: #9de457;
min-width: 120px; min-width: 120px;
border: 2px solid; border: 2px solid;
z-index: 1; z-index: 1;

8
verse/static/css/selector.css

@ -4,7 +4,7 @@
max-width: 20em; max-width: 20em;
position: relative; position: relative;
border: none; border: none;
background: #E0B0FF; background: #9de457;
text-decoration: none; text-decoration: none;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -18,7 +18,7 @@
max-width: 20em; max-width: 20em;
border: none; border: none;
box-shadow: none; box-shadow: none;
background-color: #E0B0FF; background-color: #9de457;
background-image: none; background-image: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
@ -41,7 +41,7 @@
} }
select.selector option{ select.selector option{
color: white; color: white;
background-color: #60337F; background-color: #9de457;
padding: 0 10px; padding: 0 10px;
} }
@ -49,5 +49,5 @@ select.selector option{
outline: none; outline: none;
} }
.selector-style select option:hover { .selector-style select option:hover {
background: #60337F; background: #9de457;
} }

116
verse/static/css/style.css

@ -3,63 +3,16 @@ body
font-family: monospace, monospace; font-family: monospace, monospace;
font-size: 15px; font-size: 15px;
background-color: #fdfdfd; background-color: #fdfdfd;
color:#29d148; color:#091411;
word-wrap: break-word; word-wrap: break-word;
line-height: 1.1; line-height: 1.1;
} }
div#login{
width: 30%;
margin-left: auto;
margin-right: auto;
background-color:#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 #mainworkflow
{ {
width: 30em; width: 40em;
margin:0 auto; margin:0 auto;
} }
@ -69,7 +22,16 @@ div#upload form {
#distribusi-index { #distribusi-index {
padding-left: 1em; padding-left: 1em;
} }
.description > textarea {
width: 100%;
height: 10em;
resize: none;
}
textarea#description {
width: 100%;
height: 20em;
resize: none;
}
div#buttons{ div#buttons{
position: fixed; position: fixed;
top: 0.5em; top: 0.5em;
@ -82,13 +44,13 @@ div#buttons{
div#buttons .distribusi input{ div#buttons .distribusi input{
border: none; border: none;
background: #fff600; background: #9de457;
text-decoration: none; text-decoration: none;
margin: 0.2em; margin: 0.2em;
} }
div#buttons .distribusi input:hover{ div#buttons .distribusi input:hover{
background: #ffbf00; background: #091411;
color: #6df2cc;
} }
fieldset.required { fieldset.required {
border: none; border: none;
@ -101,21 +63,15 @@ fieldset.required > ul {
fieldset.required > ul > li{ fieldset.required > ul > li{
list-style-type: none; list-style-type: none;
} }
fieldset.tagfield > input { fieldset.tagfield > input {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
} }
#publicthemes > ul {
max-height: 20em;
overflow: auto;
}
#publicthemes > ul > li{
word-break: break-all;
}
input { input {
border: none; border: none;
background: #E0B0FF; background: #9de457;
text-decoration: none; text-decoration: none;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -124,7 +80,9 @@ input {
} }
input:hover { input:hover {
background: #60337F; background: #091411;
color: #6df2cc;
cursor: pointer;
} }
input[type="submit"]:disabled:hover, input[type="submit"]:disabled:hover,
@ -144,31 +102,11 @@ input[type="submit"]:disabled:focus {
background-color: #F92020; background-color: #F92020;
} }
#update { #update, #describe {
color: black; color: black;
background-color: #62b264; background-color: #62b264;
} }
/* unvisited link */
a:link {
color: #fff600;
}
/* visited link */
a:visited {
color: #d28cff;
}
/* mouse over link */
a:hover {
color: #60337F;
}
/* selected link */
a:active {
color: white;
}
/* STOLEN GOODS */ /* STOLEN GOODS */
#fancyboi::before { #fancyboi::before {
content: "$ "; content: "$ ";
@ -189,7 +127,7 @@ a:active {
white-space: nowrap; white-space: nowrap;
animation: reveal 4s linear; animation: reveal 4s linear;
text-overflow: "█"; text-overflow: "█";
background-color: #2D3039; background-color: #9de457;
} }
#fancyboi::after { #fancyboi::after {
content: "█"; content: "█";
@ -199,7 +137,7 @@ a:active {
div.maincontent{ div.maincontent{
width: 55%; width: 55%;
border: 3px #E0B0FF; border: 3px #9de457;
margin-top: 0.5em; margin-top: 0.5em;
padding: 0.5em; padding: 0.5em;
border-style: outset; border-style: outset;
@ -238,8 +176,6 @@ div.maincontent{
bottom: 100%; bottom: 100%;
left: 50%; left: 50%;
margin-left: -60px; margin-left: -60px;
/* Fade in tooltip - takes 1 second to go from 0% to 100% opac: */
opacity: 0; opacity: 0;
transition: opacity 2s; transition: opacity 2s;
} }
@ -254,7 +190,7 @@ div.maincontent{
color: black; color: black;
padding: 1em; padding: 1em;
box-sizing: border-box; box-sizing: border-box;
background: #E0B0FF; background: #9de457;
outline: none; outline: none;
font-family: Courier, sans-serif; font-family: Courier, sans-serif;
font-size: 16px; font-size: 16px;
@ -262,11 +198,11 @@ div.maincontent{
/* /*
Project colors so far. Project colors so far.
light light
#E0B0FF #9de457
medium medium
#d28cff #d28cff
dark dark
#60337F #9de457
background dark background dark
#2D3039 #2D3039

2
verse/statuspengguna/forgotpassword.py

@ -11,7 +11,7 @@ from sqlalchemy.exc import (
) )
from app import db, get_app from app import db, get_app
from forms.forgotpasswordform import ForgotPasswordForm from statuspengguna.forms.forgotpasswordform import ForgotPasswordForm
from models.user_model import User from models.user_model import User
mail = Mail(get_app()) mail = Mail(get_app())

0
verse/forms/forgotpasswordform.py → verse/statuspengguna/forms/forgotpasswordform.py

0
verse/forms/loginform.py → verse/statuspengguna/forms/loginform.py

2
verse/forms/registerform.py → verse/statuspengguna/forms/registerform.py

@ -2,7 +2,7 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, SubmitField, validators from wtforms import PasswordField, StringField, SubmitField, validators
from wtforms.validators import Email, EqualTo, Length, ValidationError from wtforms.validators import Email, EqualTo, Length
class RegisterForm(FlaskForm): class RegisterForm(FlaskForm):

0
verse/forms/resetpasswordform.py → verse/statuspengguna/forms/resetpasswordform.py

6
verse/statuspengguna/helper.py

@ -10,7 +10,7 @@ from sqlalchemy.exc import (
) )
from app import db from app import db
from distribusikan.distribusisinfo import DistribusisInfo from distribusikan.distribusis_info import DistribusisInfo
from models.distribusi_model import Distribusis from models.distribusi_model import Distribusis
from models.user_model import User from models.user_model import User
@ -76,7 +76,9 @@ class UserHelper:
def distribusi_limit_reached(): def distribusi_limit_reached():
user = User.query.filter_by(email=current_user.email).first() user = User.query.filter_by(email=current_user.email).first()
distribusiamount = len(DistribusisInfo.getuserdistribusis(user.email)) distribusiamount = len(
DistribusisInfo.get_user_distribusis(user.email)
)
if distribusiamount > 19: if distribusiamount > 19:
print("user already has 20 distribusis") print("user already has 20 distribusis")
return True return True

5
verse/statuspengguna/loginuser.py

@ -5,14 +5,12 @@ from flask import (
redirect, redirect,
render_template, render_template,
request, request,
send_from_directory,
session,
url_for, url_for,
) )
from flask_bcrypt import check_password_hash from flask_bcrypt import check_password_hash
from flask_login import login_user from flask_login import login_user
from forms.loginform import LoginForm from statuspengguna.forms.loginform import LoginForm
from models.user_model import User from models.user_model import User
login_section = Blueprint( login_section = Blueprint(
@ -37,7 +35,6 @@ def LoginUser():
loginform.password.errors.append("Invalid email or password!") loginform.password.errors.append("Invalid email or password!")
return render_template("login.html", loginform=loginform) return render_template("login.html", loginform=loginform)
if check_password_hash(user.password, loginform.password.data): if check_password_hash(user.password, loginform.password.data):
print(type(user))
login_user(user) login_user(user)
flash("Logged in successfully.", "success") flash("Logged in successfully.", "success")
next = request.args.get("next") next = request.args.get("next")

2
verse/statuspengguna/registeruser.py

@ -11,7 +11,7 @@ from sqlalchemy.exc import (
from werkzeug.routing import BuildError from werkzeug.routing import BuildError
from app import db from app import db
from forms.registerform import RegisterForm from statuspengguna.forms.registerform import RegisterForm
from models.user_model import User from models.user_model import User
register_user = Blueprint( register_user = Blueprint(

5
verse/statuspengguna/resetpassword.py

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
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_bcrypt import generate_password_hash
from flask_login import login_user from flask_login import login_user
from sqlalchemy.exc import ( from sqlalchemy.exc import (
@ -13,9 +13,8 @@ from sqlalchemy.exc import (
from werkzeug.routing import BuildError from werkzeug.routing import BuildError
from app import db from app import db
from forms.resetpasswordform import ResetPasswordForm from statuspengguna.forms.resetpasswordform import ResetPasswordForm
from models.user_model import User from models.user_model import User
from statuspengguna import statuspengguna
reset_password = Blueprint( reset_password = Blueprint(
"reset_password", "reset_password",

69
verse/statuspengguna/static/css/dropdown.css

@ -1,69 +0,0 @@
/* 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;
}
}

50
verse/statuspengguna/static/css/editor.css

@ -1,50 +0,0 @@
.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;
}

22
verse/statuspengguna/static/css/login.css

@ -0,0 +1,22 @@
div#login{
width: 30%;
margin-left: auto;
margin-right: auto;
background-color:#fdfdfd;
text-decoration: none;
}
div#login form {
width: 24em;
margin: 0 auto;
padding-left: 15%;
padding-right: 15%;
}
input[type=text], input[type=password], input[type=file] {
color: #091411;
width: 18em;
max-width: 18em;
background-color: #fdfdfd;
border: 1px solid #9de457;
}

53
verse/statuspengguna/static/css/selector.css

@ -1,53 +0,0 @@
.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;
}

281
verse/statuspengguna/static/css/style.css

@ -1,281 +0,0 @@
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;
}
/* 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
*/

6
verse/statuspengguna/static/icons/about.txt

@ -1,6 +0,0 @@
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))

BIN
verse/statuspengguna/static/icons/android-chrome-192x192.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
verse/statuspengguna/static/icons/android-chrome-512x512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
verse/statuspengguna/static/icons/apple-touch-icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

BIN
verse/statuspengguna/static/icons/favicon-16x16.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

BIN
verse/statuspengguna/static/icons/favicon-32x32.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

BIN
verse/statuspengguna/static/icons/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

1
verse/statuspengguna/static/icons/site.webmanifest

@ -1 +0,0 @@
{"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"}

92
verse/statuspengguna/static/js/dropdown.js

@ -1,92 +0,0 @@
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");
}
}

19
verse/statuspengguna/static/js/editorupdate.js

@ -1,19 +0,0 @@
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+"<style>"+css.value+"</style>");
code.close();
};
document.addEventListener("DOMContentLoaded", function(){
code.open();
code.writeln(html.value+"<style>"+css.value+"</style>");
code.close();
});
};
update();

25
verse/statuspengguna/static/js/script.js

@ -1,25 +0,0 @@
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")
// }

75
verse/statuspengguna/static/svg/arrow_1.svg

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="240"
height="320"
viewBox="0 0 63.5 84.666667"
version="1.1"
id="svgArrowOne"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="arrow_1.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="px"
showgrid="false"
units="px"
inkscape:zoom="1"
inkscape:cx="236"
inkscape:cy="70"
inkscape:window-width="1916"
inkscape:window-height="1041"
inkscape:window-x="1366"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
id="path865"
style="fill:#060000;stroke:#fcfffe;stroke-width:0.264583"
cx="167.50412"
cy="23.866825"
r="0.211455" />
<circle
id="path867"
style="fill:#e0b0ff;stroke:#fcfffe;stroke-width:0.264583;fill-opacity:1"
cx="167.50412"
cy="23.866825"
r="0.211455" />
<circle
id="path2716"
style="fill:#d28cff;stroke:#fcfffe;stroke-width:0.264583"
cx="31.623327"
cy="-54.739456"
r="0.1057275" />
<circle
id="path2718"
style="fill:#d28cff;stroke:#fcfffe;stroke-width:0.264583"
cx="31.623327"
cy="-54.739456"
r="0.1057275" />
<path
style="fill:#d28cff;fill-opacity:1;stroke:none;stroke-width:0.799999"
id="path2732"
d="m 19.948434,3.1817159 c 1.290131,0.6550689 2.512225,1.4411814 3.755485,2.1807733 1.793529,1.0335667 3.565214,2.1021852 5.308482,3.2186226 0.483624,0.3097273 0.976024,0.6070071 1.443413,0.9407313 0.457224,0.3264641 0.885243,0.6920049 1.327862,1.0380079 2.123546,1.795334 3.693565,4.041838 4.640125,6.658655 0.242448,0.67026 0.417224,1.3631 0.625834,2.04465 0.468096,1.928539 0.850726,3.889399 1.025174,5.869019 0.146409,1.661472 0.09044,3.337613 0.06891,5.002416 -0.136327,2.53704 -0.886677,4.958631 -1.507659,7.401952 -0.562415,2.08257 -2.195008,3.525988 -3.642546,5.02172 -1.668862,1.796187 -3.430227,3.511629 -4.96204,5.428684 -1.232474,1.501685 -2.187443,3.200289 -3.147382,4.881737 -1.04435,1.719871 -1.86704,3.57665 -2.548164,5.467477 -0.710967,2.173634 -0.907385,4.460639 -1.024063,6.730664 0.02091,1.346742 0.05687,2.707192 0.493364,3.995719 0.26353,0.777933 0.396191,0.938664 0.795115,1.653278 0.654367,1.054661 1.365786,2.066507 2.102377,3.064017 0,0 3.841737,-0.248322 3.841737,-0.248322 v 0 c -0.75511,-0.983935 -1.542788,-1.939549 -2.17802,-3.011543 -0.382704,-0.652497 -0.537692,-0.849823 -0.799641,-1.562883 -0.455204,-1.239128 -0.493109,-2.567154 -0.52219,-3.871724 0.107265,-2.234121 0.278343,-4.485997 1.012129,-6.617327 0.708365,-1.865136 1.556754,-3.687384 2.575162,-5.404406 0.953056,-1.662009 1.928715,-3.32604 3.127534,-4.825259 1.51579,-1.902804 3.233504,-3.631578 4.905512,-5.395754 1.510255,-1.536197 3.212571,-3.04624 3.747334,-5.218498 0.540033,-2.47709 1.199993,-4.93743 1.330515,-7.483557 0.02558,-1.263529 0.06192,-2.073423 0.0096,-3.332359 C 41.644752,24.222411 41.147902,21.675409 40.546318,19.16389 40.076453,17.470971 39.976922,16.848842 39.271149,15.277213 38.380408,13.293695 37.001362,11.650365 35.365848,10.236089 34.920391,9.8793346 34.491789,9.5004423 34.02947,9.1658256 33.563896,8.8288536 33.065526,8.5396762 32.585453,8.223701 30.879732,7.1010218 29.144655,6.0327407 27.391958,4.9847267 26.212505,4.238663 25.04853,3.4635151 23.848505,2.7523961 c 0,0 -3.900058,0.4293198 -3.900058,0.4293198 z" />
<path
style="fill:#d28cff;fill-opacity:1;stroke:none;stroke-width:0.799999"
id="path2736"
d="m 17.143337,69.050471 c 1.633713,0.894598 3.185906,1.900385 4.678149,3.014075 1.115423,0.934426 2.160169,1.94547 3.113059,3.044203 1.826876,2.397014 -1.146831,1.665475 4.779041,1.263248 0.383141,-1.686153 0.758291,-3.374861 1.165924,-5.055598 0.206131,-0.876517 0.390895,-1.75066 0.653066,-2.611993 0,0 -3.799028,-0.103103 -3.799028,-0.103103 v 0 c -0.220279,0.87104 -0.384717,1.750875 -0.582906,2.627255 -0.396558,1.670132 -0.773763,3.348908 -1.349638,4.968864 1.273906,0.05246 2.548034,0.09977 3.821714,0.157393 0.02885,0.0013 -0.07003,0.03579 -0.08584,0.01161 -0.06107,-0.09337 -0.07566,-0.209923 -0.112684,-0.315174 -0.133537,-0.379648 -0.146335,-0.493044 -0.361696,-0.837912 -0.08452,-0.135356 -0.188793,-0.257328 -0.283188,-0.385993 -1.017286,-1.099923 -2.077482,-2.15043 -3.232635,-3.10924 -1.452916,-1.095015 -2.937697,-2.134709 -4.528692,-3.02179 0,0 -3.87465,0.354142 -3.87465,0.354142 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

12
verse/statuspengguna/static/svg/arrow_2.svg

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="240" height="320" version="1.1" viewBox="0 0 63.5 84.667" xmlns="http://www.w3.org/2000/svg">
<circle cx="167.5" cy="23.867" r=".21146" fill="#060000" stroke="#fcfffe" stroke-width=".26458"/>
<circle cx="167.5" cy="23.867" r=".21146" fill="#e0b0ff" stroke="#fcfffe" stroke-width=".26458"/>
<g fill="#d28cff">
<circle cx="31.623" cy="-54.739" r=".10573" stroke="#fcfffe" stroke-width=".26458"/>
<circle cx="31.623" cy="-54.739" r=".10573" stroke="#fcfffe" stroke-width=".26458"/>
<path d="m29.598 1.3629c0.29231 0.93057 0.37445 1.9094 0.44145 2.8774 0.01955 0.99076 0.29893 1.9483 0.43855 2.9253 0.04901 1.1628 0.31687 2.2714 0.7734 3.3386 0.60807 1.244 1.6241 2.1846 2.6231 3.1104 0.76132 0.7147 1.5224 1.4053 2.414 1.9528 0.85013 0.49091 1.8227 0.71794 2.7627 0.97569 1.4036 0.22312 2.1015 0.68412 3.1203 1.648 1.1101 1.0733 1.7779 2.4463 2.4311 3.8184 0.5827 1.142 0.76303 2.3828 0.73638 3.6503-0.08765 1.5129-0.67764 2.6923-1.5432 3.8976-0.84272 1.225-2.1589 1.8621-3.4214 2.5577-1.3039 0.74177-2.5458 1.586-3.8965 2.2408-1.5184 0.78493-3.077 1.4779-4.6378 2.1709-1.2968 0.70155-2.6371 1.3714-3.7845 2.3055-1.5755 1.3431-2.9301 2.8922-4.2407 4.4866-1.0773 1.3646-2.2517 2.7443-2.9262 4.3683-0.12219 0.2942-0.20988 0.60157-0.31482 0.90236-0.3176 1.1367-0.50828 2.2967-0.36852 3.4764 0.04693 0.39614 0.19687 1.057 0.27962 1.4461 0.30758 1.2289 0.84547 2.3725 1.4798 3.4625 0.70009 1.3121 1.6509 2.4545 2.6734 3.5243 1.0661 1.0736 2.1636 2.1225 3.154 3.2675 0.78168 0.87789 1.0179 1.9433 1.1372 3.0756 0.03544 0.92388 0.11577 1.8587 0.07073 2.7841-0.01773 0.36408-0.1093 0.87867-0.16701 1.2409-0.09034 1.015-0.40258 1.9744-0.65202 2.9542-0.22907 0.93923-0.24592 1.9133-0.26994 2.875 0.01627 0.16165 0.03253 0.3233 0.04879 0.48495 0 0 3.8677-0.04599 3.8677-0.04599l-0.06033-0.42856c0.0271-0.93446 0.05799-1.8763 0.28398-2.7884 0.25461-0.98959 0.52171-1.9723 0.61567-2.9949 0.18691-1.3678 0.16751-2.7404 0.09837-4.1178-0.10464-1.2084-0.33697-2.37-1.1681-3.3092-0.98696-1.161-2.0984-2.2119-3.1752-3.2885-1.0093-1.0344-1.9609-2.1448-2.6312-3.4346-0.61359-1.0586-1.166-2.1573-1.4984-3.3403-0.02657-0.1263-0.26629-1.2542-0.28656-1.3963-0.16054-1.1256 0.0079-2.2354 0.32978-3.3186 0.10325-0.28669 0.1907-0.57958 0.30976-0.86007 0.67556-1.5915 1.8191-2.9377 2.8755-4.2834 1.3254-1.6128 2.6979-3.0863 4.332-4.3948 1.1702-0.86077 2.4531-1.5682 3.751-2.2163 1.5673-0.6912 3.1104-1.4304 4.6479-2.1857 1.3437-0.6747 2.5823-1.5317 3.8988-2.2576 1.3084-0.71914 2.6043-1.447 3.4487-2.7293 0.89598-1.305 1.4437-2.5132 1.5458-4.1199 0.04656-1.326-0.09489-2.646-0.72638-3.8363-0.70909-1.3836-1.3422-2.8175-2.4524-3.9439-0.95909-0.99062-1.6743-1.6678-3.0935-1.8533-0.92413-0.22693-1.8609-0.47835-2.7296-0.86415-0.37733-0.23195-0.7701-0.43303-1.1225-0.70362-0.56026-0.43018-0.30161-0.27292-0.85155-0.76539-0.15495-0.13876-0.31951-0.26638-0.47927-0.39957-0.94654-0.91944-2.0219-1.7437-2.6195-2.9576-0.46026-1.0278-0.6805-2.1099-0.72907-3.2345-0.13163-0.97099-0.41801-1.9171-0.4545-2.8972-0.062288-0.99479-0.16352-1.9871-0.37122-2.9636l-3.9178 0.11147z"/>
<path d="m22.025 71.064c1.1076 0.81571 1.8963 1.9751 2.6453 3.1096 0.63322 0.93788 1.2476 1.8855 1.6118 2.9617 0.28693 0.82149 0.62284 1.6252 1.0669 2.3743 1.0301 1.4304 1.5291 0.58576 4.4538 0.16545 0.14811-0.02127 0.42422-1.2095 0.46069-1.36 0.25411-0.96357 0.61812-1.6049 1.3587-2.2583 0.75053-0.69358 1.7635-0.9666 2.6863-1.3526 1.0408-0.39579 2.1353-0.68763 3.1287-1.1962 0.12805-0.06035 0.2561-0.12069 0.38414-0.18103 0 0-3.8517-0.43584-3.8517-0.43584-0.12618 0.06059-0.25236 0.12119-0.37854 0.18179-1.0095 0.4429-2.0552 0.79223-3.0867 1.1786-0.97696 0.38612-1.9942 0.73078-2.7537 1.4922-0.6784 0.69992-1.1041 1.5244-1.3418 2.4736-0.08563 0.30808-0.14275 0.60119-0.30832 0.87972-0.05051 0.08497-0.29376 0.21564-0.1952 0.22324 1.2279 0.09468 2.4644-0.01566 3.6945 0.04656 0.07116 0.0036-0.09855 0.1467-0.16824 0.13185-0.0954-0.02032-0.13522-0.1406-0.20282-0.2109-0.48406-0.68952-0.78002-1.4975-1.0935-2.2751-0.36094-1.1214-0.97173-2.1151-1.6698-3.0606-0.6571-0.99538-1.3971-2.1349-2.2397-2.9817-0.09081-0.09127-0.19994-0.16228-0.29991-0.24342l-3.9009 0.33725z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

12
verse/statuspengguna/static/svg/arrow_3.svg

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="240" height="320" version="1.1" viewBox="0 0 63.5 84.667" xmlns="http://www.w3.org/2000/svg">
<circle cx="167.5" cy="23.867" r=".21146" fill="#060000" stroke="#fcfffe" stroke-width=".26458"/>
<circle cx="167.5" cy="23.867" r=".21146" fill="#e0b0ff" stroke="#fcfffe" stroke-width=".26458"/>
<g fill="#d28cff">
<circle cx="31.623" cy="-54.739" r=".10573" stroke="#fcfffe" stroke-width=".26458"/>
<circle cx="31.623" cy="-54.739" r=".10573" stroke="#fcfffe" stroke-width=".26458"/>
<path d="m56.965 3.4219c0.12058 1.1521-0.2318 2.054-0.81004 3.0337-1.0875 1.5907-2.5336 2.8659-4.0843 3.9867-2.1505 1.3863-4.4585 2.4853-6.8625 3.3491-2.7815 0.78715-5.6335 1.3087-8.4879 1.7525-3.134 0.44066-6.2863 0.71397-9.4313 1.0632-3.4224 0.40837-6.8733 1.0287-10.07 2.3675-0.54185 0.22694-1.0603 0.5062-1.5904 0.75929-0.89954 0.54647-1.6469 0.92677-2.4068 1.6606-1.3388 1.2929-2.0863 2.9786-2.778 4.6654-0.87449 2.3542-1.198 4.6214-1.1672 7.1174 0.18717 1.5284 0.31332 3.0753 0.6288 4.5852 0.091869 0.43967 0.3934 1.5785 0.51155 2.0327 0.39771 1.1363 0.63254 2.4445 1.5286 3.3253 0.14764 0.14511 0.3231 0.25888 0.48465 0.38832 0.20368 0.10006 0.39798 0.22205 0.61104 0.30019 0.42615 0.15629 1.2969 0.32297 1.7205 0.41193 1.1822 0.24832 2.3631 0.46842 3.5556 0.66151 1.9566 0.35044 3.9445 0.66205 5.8228 1.331 1.3844 0.50374 2.5213 1.4098 3.6121 2.3711 1.0386 0.99728 2.1001 1.9882 2.7426 3.3026 0.16634 0.3403 0.28751 0.70088 0.43126 1.0513 0.86215 2.4558 1.0225 5.0522 0.9327 7.631-0.07031 1.5349-0.29391 3.1185-1.156 4.4316-0.12638 0.19249-0.28466 0.36202-0.42699 0.54304-0.81011 0.9275-1.7382 1.735-2.5931 2.6178-0.73658 0.82062-1.6053 0.85394-2.5586 1.1927-0.94832 0.22808-1.8262 0.65178-2.7421 0.97327-1.1741 0.61913-2.4826 0.76669-3.7501 1.0714-1.2105 0.21338-2.3976 0.48893-3.5183 1.0015-0.67364 0.34917-1.2076 0.81882-1.6407 1.4361l3.8658 0.2497c0.43909-0.54046 0.98065-0.94428 1.6142-1.2389 1.1138-0.46371 2.2851-0.70648 3.4664-0.93518 1.2893-0.27722 2.5716-0.54674 3.7736-1.1162 0.91684-0.34231 1.829-0.69308 2.7728-0.9567 1.0196-0.30093 1.8099-0.54985 2.5486-1.3801 0.87014-0.87766 1.7909-1.7075 2.5674-2.6739 0.13769-0.20005 0.28985-0.39088 0.41308-0.60015 0.82211-1.3961 1.0263-3.0131 1.0537-4.6056 0.05629-2.6169-0.0783-5.2743-0.90909-7.7781-0.13345-0.36853-0.2428-0.74671-0.40035-1.1056-0.60136-1.3698-1.6464-2.4153-2.7131-3.4287-1.0905-1.0008-2.2045-1.983-3.6142-2.5099-0.24452-0.10125-0.48125-0.22386-0.73356-0.30376-0.26822-0.08493-0.54885-0.12408-0.82261-0.18901-1.4524-0.34445-2.9022-0.68462-4.3809-0.90277-1.1723-0.18185-2.3227-0.40058-3.4815-0.65277-0.54954-0.11959-1.1101-0.19272-1.6518-0.34365-0.20174-0.05621-0.39234-0.14664-0.58852-0.21996-0.15779-0.10023-0.32954-0.18128-0.47337-0.30068-0.94359-0.78332-1.2089-2.059-1.6142-3.1487-0.16754-0.59766-0.40066-1.3998-0.53448-2.0013-0.33082-1.487-0.45328-3.013-0.62991-4.523-0.03942-2.3761 0.26799-4.7319 1.1144-6.9675 0.36685-0.8728 0.62759-1.5813 1.1166-2.3961 0.98125-1.6348 2.2842-2.7836 3.9639-3.6713 0.51139-0.24392 1.0106-0.5151 1.5342-0.73175 3.1926-1.3212 6.6474-1.892 10.059-2.2825 3.192-0.33755 6.3934-0.58867 9.5696-1.062 2.856-0.4763 5.7214-0.9862 8.4967-1.8227 2.4241-0.91126 4.7643-2.0293 6.9388-3.445 1.5814-1.1701 3.0403-2.506 4.1596-4.1381 0.60624-1.0679 0.95747-1.9925 0.94037-3.2331h-3.9289z"/>
<path d="m12.023 67.543c0.49829 0.89935 0.97249 1.783 1.2618 2.7744 0.05193 0.50463 0.27477 0.97017 0.38441 1.4597 0.09562 0.42695 0.06722 0.87236 0.05564 1.3052-0.02684 0.65007-0.04547 1.3125-0.23635 1.9412-0.02066 0.06805-0.31569 0.75033-0.34246 0.81269-1.0192 2.7097-0.33194 1.7204 3.7574 1.4613 0.62653-0.65486 1.2245-1.3292 1.8921-1.9454 0.77212-0.76506 1.7378-1.0867 2.7527-1.378 1.0883-0.39202 2.2359-0.50662 3.3814-0.5819 0.64057-0.03615 1.2822-0.04273 1.9235-0.05158 0 0-3.7766-0.59155-3.7766-0.59155-0.64632 0.01315-1.2932 0.02268-1.9381 0.07203-1.1598 0.09954-2.3084 0.26952-3.4108 0.66115-1.0413 0.32357-1.9937 0.77464-2.7811 1.553-0.66771 0.6262-1.2491 1.3374-1.9404 1.9381 1.2373 0.03262 2.4793-0.01378 3.712 0.09787 0.08161 0.0074-0.02469 0.17515-0.08494 0.23068-0.03731 0.03439-0.0096-0.10235-1.85e-4 -0.15222 0.07511-0.39908 0.24315-0.78076 0.38022-1.1601 0.37962-0.92942 0.58939-1.8859 0.57777-2.896 0.01506-0.47523 0.04582-0.96424-0.0577-1.4334-0.10794-0.48914-0.37419-0.93937-0.40173-1.4497-0.15402-0.55207-0.32508-1.0754-0.57723-1.592-0.14541-0.29797-0.33208-0.5746-0.4773-0.87265-0.05226-0.10725-0.08657-0.22235-0.12986-0.33352l-3.9242 0.13069z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

5
verse/statuspengguna/templates/statuspengguna/forgotpassword.html

@ -1,7 +1,7 @@
{% extends "base/base.html" %} {% extends "base/base.html" %}
{% block main %} {% block main %}
<div id="mainworkflow"> <div id="mainworkflow">
<div class="workflow"> <div class="login">
<h2>Forgot your password?</h2> <h2>Forgot your password?</h2>
<p> <p>
Enter the email address that was used to register with Distribusiverse. Enter the email address that was used to register with Distribusiverse.
@ -26,5 +26,6 @@
</fieldset> </fieldset>
</form> </form>
</div> </div>
</div </div>
<link rel="stylesheet" type="text/css" href="{{ url_for('forgotpassword.static', filename='css/login.css')}}">
{% endblock main %} {% endblock main %}

1
verse/statuspengguna/templates/statuspengguna/login.html

@ -23,4 +23,5 @@
</fieldset> </fieldset>
</form> </form>
</div> </div>
<link rel="stylesheet" type="text/css" href="{{ url_for('login.static', filename='css/login.css')}}">
{% endblock main %} {% endblock main %}

1
verse/statuspengguna/templates/statuspengguna/register.html

@ -36,4 +36,5 @@
</fieldset> </fieldset>
</form> </form>
</div> </div>
<link rel="stylesheet" type="text/css" href="{{ url_for('register.static', filename='css/login.css')}}">
{% endblock main %} {% endblock main %}

1
verse/statuspengguna/templates/statuspengguna/resetpassword.html

@ -26,4 +26,5 @@
<h3>Password reset link no longer valid.</h3> <h3>Password reset link no longer valid.</h3>
{% endif %} {% endif %}
</div> </div>
<link rel="stylesheet" type="text/css" href="{{ url_for('reset_password.static', filename='css/login.css')}}">
{% endblock main %} {% endblock main %}

2
verse/templates/base/help.html

@ -19,7 +19,7 @@
</div> </div>
{% else %} {% else %}
<div class="distribusi"> <div class="distribusi">
<a href="/distribusi"> <a href="distribusikan/distribusi">
<input type="button" name="button" value="Distribusi"></input> <input type="button" name="button" value="Distribusi"></input>
</a> </a>
</div> </div>

2
verse/templates/base/index.html

@ -15,7 +15,7 @@
</div> </div>
{% else %} {% else %}
<div class="distribusi"> <div class="distribusi">
<a href="/distribusi"> <a href="distribusikan/distribusi">
<input type="button" name="button" value="Distribusi"></input> <input type="button" name="button" value="Distribusi"></input>
</a> </a>
</div> </div>

4
verse/themes/editor/placeholder.html

@ -6,14 +6,14 @@
<div id="willemdekooning-logo" class="png"> <div id="willemdekooning-logo" class="png">
<figure> <figure>
<img class="image" src="https://www.wdka.nl/build/img/willemdekooning-logo.png"> <img class="image" src="https://varia.zone/archive/00-varia-server/welcome.png">
<figcaption>logo.png</figcaption> <figcaption>logo.png</figcaption>
</figure> </figure>
</div> </div>
<div id="example_video" class="mp4"> <div id="example_video" class="mp4">
<video controls> <video controls>
<source src="example_video.mp4"> <source src="https://vvvvvvaria.org/~crunk/box/tomb_of_doom-2021-02-13_21.41.03.mp4">
</video> </video>
<span class="filename">example_video.mp4</span> <span class="filename">example_video.mp4</span>
</div> </div>

Loading…
Cancel
Save