From bd519d6925570d556316211462dbf9732f687daf Mon Sep 17 00:00:00 2001 From: crunk Date: Sun, 28 Apr 2024 13:04:07 +0200 Subject: [PATCH] leave no file untouched --- verse/adminpage.py | 9 +- verse/admintool.py | 5 +- verse/app.py | 6 +- verse/deploydb.py | 3 +- verse/distribusikan/distribusikan.py | 46 +++ .../{ => distribusikan}/distribusiselector.py | 9 +- verse/{ => distribusikan}/distribusisinfo.py | 1 + .../{ => distribusikan}/distribusiworkflow.py | 14 +- verse/{ => distribusikan}/editor.py | 11 +- .../templates/distribusikan}/distribusi.html | 2 +- .../distribusiworkflow/editcss.html | 0 .../distribusiworkflow/launch.html | 2 +- .../distribusiworkflow/selector.html | 2 +- .../distribusiworkflow/theme.html | 4 +- .../distribusiworkflow/upload.html | 2 +- .../templates/distribusikan}/editor.html | 2 +- verse/{ => distribusikan}/themeselector.py | 3 +- verse/{ => distribusikan}/upload.py | 9 +- verse/{ => distribusikan}/uploadpage.py | 9 +- verse/forms/uploadform.py | 3 +- verse/models/usermodel.py | 3 +- verse/start.py | 83 +---- verse/statuspengguna/__init__.py | 0 verse/statuspengguna/forgotpassword.py | 24 +- verse/statuspengguna/helper.py | 9 +- verse/statuspengguna/loginuser.py | 19 +- verse/statuspengguna/registeruser.py | 21 +- verse/statuspengguna/resetpassword.py | 20 +- verse/statuspengguna/static/css/dropdown.css | 69 +++++ verse/statuspengguna/static/css/editor.css | 50 +++ verse/statuspengguna/static/css/selector.css | 53 ++++ verse/statuspengguna/static/css/style.css | 286 ++++++++++++++++++ verse/statuspengguna/static/icons/about.txt | 6 + .../static/icons/android-chrome-192x192.png | Bin 0 -> 5479 bytes .../static/icons/android-chrome-512x512.png | Bin 0 -> 12656 bytes .../static/icons/apple-touch-icon.png | Bin 0 -> 4441 bytes .../static/icons/favicon-16x16.png | Bin 0 -> 322 bytes .../static/icons/favicon-32x32.png | Bin 0 -> 666 bytes verse/statuspengguna/static/icons/favicon.ico | Bin 0 -> 15406 bytes .../static/icons/site.webmanifest | 1 + verse/statuspengguna/static/js/dropdown.js | 92 ++++++ .../statuspengguna/static/js/editorupdate.js | 19 ++ verse/statuspengguna/static/js/script.js | 25 ++ verse/statuspengguna/static/svg/arrow_1.svg | 75 +++++ verse/statuspengguna/static/svg/arrow_2.svg | 12 + verse/statuspengguna/static/svg/arrow_3.svg | 12 + .../statuspengguna}/forgotpassword.html | 5 +- .../templates/statuspengguna}/login.html | 6 +- .../templates/statuspengguna}/register.html | 4 +- .../statuspengguna}/resetpassword.html | 2 +- verse/templates/{ => base}/admin.html | 2 +- verse/templates/{ => base}/base.html | 0 verse/templates/{ => base}/filtermenu.html | 0 verse/templates/{ => base}/help.html | 2 +- verse/templates/{ => base}/index.html | 4 +- 55 files changed, 907 insertions(+), 139 deletions(-) create mode 100644 verse/distribusikan/distribusikan.py rename verse/{ => distribusikan}/distribusiselector.py (98%) rename verse/{ => distribusikan}/distribusisinfo.py (99%) rename verse/{ => distribusikan}/distribusiworkflow.py (97%) rename verse/{ => distribusikan}/editor.py (99%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusi.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/editcss.html (100%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/launch.html (95%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/selector.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/theme.html (95%) rename verse/{templates => distribusikan/templates/distribusikan}/distribusiworkflow/upload.html (97%) rename verse/{templates => distribusikan/templates/distribusikan}/editor.html (98%) rename verse/{ => distribusikan}/themeselector.py (97%) rename verse/{ => distribusikan}/upload.py (97%) rename verse/{ => distribusikan}/uploadpage.py (89%) create mode 100644 verse/statuspengguna/__init__.py create mode 100644 verse/statuspengguna/static/css/dropdown.css create mode 100644 verse/statuspengguna/static/css/editor.css create mode 100644 verse/statuspengguna/static/css/selector.css create mode 100644 verse/statuspengguna/static/css/style.css create mode 100644 verse/statuspengguna/static/icons/about.txt create mode 100644 verse/statuspengguna/static/icons/android-chrome-192x192.png create mode 100644 verse/statuspengguna/static/icons/android-chrome-512x512.png create mode 100644 verse/statuspengguna/static/icons/apple-touch-icon.png create mode 100644 verse/statuspengguna/static/icons/favicon-16x16.png create mode 100644 verse/statuspengguna/static/icons/favicon-32x32.png create mode 100644 verse/statuspengguna/static/icons/favicon.ico create mode 100644 verse/statuspengguna/static/icons/site.webmanifest create mode 100644 verse/statuspengguna/static/js/dropdown.js create mode 100644 verse/statuspengguna/static/js/editorupdate.js create mode 100644 verse/statuspengguna/static/js/script.js create mode 100644 verse/statuspengguna/static/svg/arrow_1.svg create mode 100644 verse/statuspengguna/static/svg/arrow_2.svg create mode 100644 verse/statuspengguna/static/svg/arrow_3.svg rename verse/{templates => statuspengguna/templates/statuspengguna}/forgotpassword.html (86%) rename verse/{templates => statuspengguna/templates/statuspengguna}/login.html (80%) rename verse/{templates => statuspengguna/templates/statuspengguna}/register.html (91%) rename verse/{templates => statuspengguna/templates/statuspengguna}/resetpassword.html (96%) rename verse/templates/{ => base}/admin.html (98%) rename verse/templates/{ => base}/base.html (100%) rename verse/templates/{ => base}/filtermenu.html (100%) rename verse/templates/{ => base}/help.html (99%) rename verse/templates/{ => base}/index.html (97%) diff --git a/verse/adminpage.py b/verse/adminpage.py index c12fac3..7504d30 100644 --- a/verse/adminpage.py +++ b/verse/adminpage.py @@ -1,15 +1,16 @@ import os import shutil -from app import db -from distribusisinfo import DistribusisInfo from flask import render_template +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.admindistribusiform import AdminDistribusiForm from forms.adminuserform import AdminUserForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) def AdminPage(): diff --git a/verse/admintool.py b/verse/admintool.py index 8fc9e2e..21aa782 100644 --- a/verse/admintool.py +++ b/verse/admintool.py @@ -1,10 +1,11 @@ import sys +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 from models.usermodel import User # noqa: F401 -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) def admintool(): diff --git a/verse/app.py b/verse/app.py index 8112e94..56dd762 100644 --- a/verse/app.py +++ b/verse/app.py @@ -16,7 +16,7 @@ login_manager = LoginManager() def create_app(): - APP.secret_key = "secret-key" + APP.secret_key = os.urandom(24) APP.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///distribusiverse.db" APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True APP.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024 @@ -59,6 +59,10 @@ def settings(): return APP +def get_app(): + return APP + + def settings_from_file(): settings = {} if os.path.isfile("settings_development.toml"): diff --git a/verse/deploydb.py b/verse/deploydb.py index 6ebd4eb..26c2e5b 100644 --- a/verse/deploydb.py +++ b/verse/deploydb.py @@ -1,7 +1,8 @@ def deploy(): """Run deployment of database.""" - from app import create_app, db from flask_migrate import init, migrate, stamp, upgrade + + from app import create_app, db from models.distribusimodel import Distribusis # noqa: F401 # This model is required for flask_migrate to make the table from models.usermodel import User # noqa: F401 diff --git a/verse/distribusikan/distribusikan.py b/verse/distribusikan/distribusikan.py new file mode 100644 index 0000000..6350228 --- /dev/null +++ b/verse/distribusikan/distribusikan.py @@ -0,0 +1,46 @@ +from flask import Blueprint +from flask_login import login_required + +from distribusikan.distribusiselector import DistribusiSelector +# Distribusi Information +from distribusikan.distribusisinfo import DistribusisInfo +from distribusikan.distribusiworkflow import DistribusiWorkflow +from distribusikan.editor import Editor +from distribusikan.themeselector import ThemeSelector +from distribusikan.uploadpage import UploadPage + +distribusikan = Blueprint( + "distribusikan", + __name__, + template_folder="templates/distribusikan", + static_folder="static", +) + + +@distribusikan.route("/distribusi", methods=["GET", "POST"]) +@login_required +def distribusi(): + return DistribusiWorkflow() + + +@distribusikan.route("/upload", methods=["POST"]) +@login_required +def upload(): + return UploadPage() + + +@distribusikan.route("/theme", methods=["GET", "POST"]) +@login_required +def theme(): + return ThemeSelector() + +@distribusikan.route("/editor", methods=["GET", "POST"]) +@login_required +def editor(): + return Editor() + + +@distribusikan.route("/selector", methods=["GET", "POST"]) +@login_required +def selector(): + return DistribusiSelector() diff --git a/verse/distribusiselector.py b/verse/distribusikan/distribusiselector.py similarity index 98% rename from verse/distribusiselector.py rename to verse/distribusikan/distribusiselector.py index 3a5d72f..64a8ef4 100644 --- a/verse/distribusiselector.py +++ b/verse/distribusikan/distribusiselector.py @@ -1,10 +1,13 @@ import os import shutil -from app import db -from distribusisinfo import DistribusisInfo from flask import flash, render_template from flask_login import current_user +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -12,8 +15,6 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper diff --git a/verse/distribusisinfo.py b/verse/distribusikan/distribusisinfo.py similarity index 99% rename from verse/distribusisinfo.py rename to verse/distribusikan/distribusisinfo.py index 0b40c1b..5a47d5f 100644 --- a/verse/distribusisinfo.py +++ b/verse/distribusikan/distribusisinfo.py @@ -1,4 +1,5 @@ from flask_login import current_user + from models.distribusimodel import Distribusis from models.usermodel import User diff --git a/verse/distribusiworkflow.py b/verse/distribusikan/distribusiworkflow.py similarity index 97% rename from verse/distribusiworkflow.py rename to verse/distribusikan/distribusiworkflow.py index f073232..bd7260f 100644 --- a/verse/distribusiworkflow.py +++ b/verse/distribusikan/distribusiworkflow.py @@ -2,13 +2,17 @@ import os import shutil import zipfile -from app import db # Tada! from distribusi.cli import build_argparser -from distribusiselector import SelectorVisible -from distribusisinfo import DistribusisInfo +from distribusi.distribusi import distribusify from flask import flash, redirect, render_template, url_for from flask_login import current_user +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) + +from app import db +from distribusikan.distribusiselector import SelectorVisible +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.publicthemeform import PublicThemeForm from forms.selectorform import SelectorForm @@ -17,13 +21,9 @@ from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis from models.usermodel import User -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) # UserPengguna from statuspengguna.helper import UserHelper -from distribusi.distribusi import distribusify - def DistribusiWorkflow(): distribusiform = DistribusiForm() diff --git a/verse/editor.py b/verse/distribusikan/editor.py similarity index 99% rename from verse/editor.py rename to verse/distribusikan/editor.py index bfbfaeb..8fb0c9c 100644 --- a/verse/editor.py +++ b/verse/distribusikan/editor.py @@ -2,10 +2,14 @@ import os import shutil import bleach -from app import db from bleach_allowlist import all_styles -from distribusisinfo import DistribusisInfo from flask import render_template +from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, + InvalidRequestError) +from werkzeug.utils import secure_filename + +from app import db +from distribusikan.distribusisinfo import DistribusisInfo from forms.distribusiform import DistribusiForm from forms.editorform import EditorForm from forms.publicthemeform import PublicThemeForm @@ -13,10 +17,7 @@ from forms.selectorform import SelectorForm from forms.themeform import ThemeForm from forms.uploadform import UploadForm from models.distribusimodel import Distribusis -from sqlalchemy.exc import (DatabaseError, DataError, InterfaceError, - InvalidRequestError) from statuspengguna.helper import UserHelper -from werkzeug.utils import secure_filename def Editor(): diff --git a/verse/templates/distribusi.html b/verse/distribusikan/templates/distribusikan/distribusi.html similarity index 97% rename from verse/templates/distribusi.html rename to verse/distribusikan/templates/distribusikan/distribusi.html index 8182e00..e85f04c 100644 --- a/verse/templates/distribusi.html +++ b/verse/distribusikan/templates/distribusikan/distribusi.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% block main %}
diff --git a/verse/templates/distribusiworkflow/editcss.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/editcss.html similarity index 100% rename from verse/templates/distribusiworkflow/editcss.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/editcss.html diff --git a/verse/templates/distribusiworkflow/launch.html b/verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html similarity index 95% rename from verse/templates/distribusiworkflow/launch.html rename to verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html index 3c4685a..562b616 100644 --- a/verse/templates/distribusiworkflow/launch.html +++ b/verse/distribusikan/templates/distribusikan/distribusiworkflow/launch.html @@ -3,7 +3,7 @@

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

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

Welcome back to your Distribusi

You have already uploaded a distribusi website:

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

Don't forget to press Save


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

Step 1: Upload

Upload your files here:

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

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

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

List of distribusis

- {% include 'filtermenu.html' %} + {% include 'base/filtermenu.html' %}
    {% for name, distribusi in distribusisindex.items() %}