regular users can have 5 distribusi sites, tutors can have 15
This commit is contained in:
parent
2b082165c3
commit
bcfa90a08a
@ -49,9 +49,10 @@ def DeleteDistribusi(distribusiname):
|
|||||||
"themes/userthemes", distribusi.distribusiname
|
"themes/userthemes", distribusi.distribusiname
|
||||||
)
|
)
|
||||||
shutil.rmtree(cssfolder)
|
shutil.rmtree(cssfolder)
|
||||||
|
selectorform.delete.errors.append("Distribusi deleted!")
|
||||||
except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
|
except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
selectorform.distribusis.errors.append("Unknown error occured!")
|
selectorform.delete.errors.append("Unknown error occured!")
|
||||||
flash("An error occured !", "danger")
|
flash("An error occured !", "danger")
|
||||||
return selectorform
|
return selectorform
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from distribusimodel import Distribusis
|
|||||||
|
|
||||||
|
|
||||||
class DistribusisInfo:
|
class DistribusisInfo:
|
||||||
def userdistribusis():
|
def userdistribusinames():
|
||||||
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():
|
||||||
|
136
verse/distribusiworkflow.py
Normal file
136
verse/distribusiworkflow.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
from flask_login import current_user
|
||||||
|
from flask import (
|
||||||
|
render_template,
|
||||||
|
redirect,
|
||||||
|
url_for,
|
||||||
|
)
|
||||||
|
from sqlalchemy.exc import (
|
||||||
|
IntegrityError,
|
||||||
|
InvalidRequestError,
|
||||||
|
DataError,
|
||||||
|
InterfaceError,
|
||||||
|
DatabaseError,
|
||||||
|
)
|
||||||
|
from app import db
|
||||||
|
|
||||||
|
from usermodel import User
|
||||||
|
from distribusimodel import Distribusis
|
||||||
|
|
||||||
|
# UserPengguna
|
||||||
|
from statuspengguna.helper import (
|
||||||
|
IsZipUploaded,
|
||||||
|
IsCssSelected,
|
||||||
|
CurrentDistribusi,
|
||||||
|
IsDistribusiLive,
|
||||||
|
DistribusiLimitReached,
|
||||||
|
)
|
||||||
|
|
||||||
|
from distribusiselector import SelectorVisible
|
||||||
|
|
||||||
|
# Forms!
|
||||||
|
from forms.uploadform import UploadForm
|
||||||
|
from forms.distribusiform import DistribusiForm
|
||||||
|
from forms.themeform import ThemeForm
|
||||||
|
from forms.publicthemeform import PublicThemeForm
|
||||||
|
from forms.selectorform import SelectorForm
|
||||||
|
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
|
# Tada!
|
||||||
|
from distribusi.cli import build_argparser
|
||||||
|
from distribusi.distribusi import distribusify
|
||||||
|
|
||||||
|
|
||||||
|
def DistribusiWorkflow():
|
||||||
|
distribusiform = DistribusiForm()
|
||||||
|
current_distribusi = CurrentDistribusi()
|
||||||
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
|
distribusi = Distribusis.query.filter_by(
|
||||||
|
distribusiname=current_distribusi
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if distribusiform.validate_on_submit():
|
||||||
|
userfolder = os.path.join("stash", distribusi.distribusiname)
|
||||||
|
cssfile = GetCssFile(distribusi)
|
||||||
|
UnzipDistribusiFiles(distribusi, userfolder)
|
||||||
|
RunDistribusi(userfolder, cssfile)
|
||||||
|
SetDistribusiToVisible(distribusi, user)
|
||||||
|
DeleteCssFile(cssfile)
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
return RenderDistribusiTemplate(distribusiform, current_distribusi)
|
||||||
|
|
||||||
|
|
||||||
|
def UnzipDistribusiFiles(distribusi, userfolder):
|
||||||
|
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
||||||
|
unzipfile = os.path.join(userfolder, zipfilename)
|
||||||
|
|
||||||
|
if os.path.exists(unzipfile):
|
||||||
|
with zipfile.ZipFile(unzipfile, "r") as zip_ref:
|
||||||
|
zip_ref.extractall(userfolder)
|
||||||
|
# after extracting all files remove zip file.
|
||||||
|
if os.path.exists(unzipfile):
|
||||||
|
os.remove(os.path.join(userfolder, zipfilename))
|
||||||
|
|
||||||
|
|
||||||
|
def GetCssFile(distribusi):
|
||||||
|
cssfile = ""
|
||||||
|
cssfolder = os.path.join(
|
||||||
|
"themes/userthemes", distribusi.distribusiname
|
||||||
|
)
|
||||||
|
for filename in os.listdir(cssfolder):
|
||||||
|
if filename.endswith(".css"):
|
||||||
|
cssfile = os.path.join(cssfolder, filename)
|
||||||
|
return cssfile
|
||||||
|
|
||||||
|
|
||||||
|
def RunDistribusi(userfolder, cssfile):
|
||||||
|
parser = build_argparser()
|
||||||
|
args = parser.parse_args(["-s", cssfile])
|
||||||
|
distribusify(args, userfolder)
|
||||||
|
|
||||||
|
|
||||||
|
def SetDistribusiToVisible(distribusi, user):
|
||||||
|
try:
|
||||||
|
distribusi.visible = True
|
||||||
|
user.currentdistribusi = None
|
||||||
|
db.session.commit()
|
||||||
|
except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
|
||||||
|
db.session.rollback()
|
||||||
|
distribusiform.submit.errors.append("Unknown error occured!")
|
||||||
|
|
||||||
|
|
||||||
|
def DeleteCssFile(cssfile):
|
||||||
|
if os.path.exists(cssfile):
|
||||||
|
os.remove(cssfile)
|
||||||
|
|
||||||
|
|
||||||
|
def RenderDistribusiTemplate(distribusiform, current_distribusi):
|
||||||
|
uploadform = UploadForm()
|
||||||
|
themeform = ThemeForm()
|
||||||
|
publicthemeform = PublicThemeForm()
|
||||||
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
|
selectorform = SelectorForm()
|
||||||
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
|
selectorvisible = SelectorVisible()
|
||||||
|
|
||||||
|
files_uploaded = IsZipUploaded(current_distribusi)
|
||||||
|
distribusi_live = IsDistribusiLive(current_distribusi)
|
||||||
|
css_selected = IsCssSelected(current_distribusi)
|
||||||
|
limit_reached = DistribusiLimitReached()
|
||||||
|
template = render_template(
|
||||||
|
"distribusi.html",
|
||||||
|
uploadform=uploadform,
|
||||||
|
distribusiform=distribusiform,
|
||||||
|
themeform=themeform,
|
||||||
|
publicthemeform=publicthemeform,
|
||||||
|
selectorform=selectorform,
|
||||||
|
files_uploaded=files_uploaded,
|
||||||
|
distribusi_live=distribusi_live,
|
||||||
|
css_selected=css_selected,
|
||||||
|
selectorvisible=selectorvisible,
|
||||||
|
limit_reached=limit_reached,
|
||||||
|
)
|
||||||
|
return template
|
@ -16,6 +16,7 @@ from statuspengguna.helper import (
|
|||||||
IsZipUploaded,
|
IsZipUploaded,
|
||||||
CurrentDistribusi,
|
CurrentDistribusi,
|
||||||
IsDistribusiLive,
|
IsDistribusiLive,
|
||||||
|
DistribusiLimitReached,
|
||||||
)
|
)
|
||||||
from distribusimodel import Distribusis
|
from distribusimodel import Distribusis
|
||||||
from distribusisinfo import DistribusisInfo
|
from distribusisinfo import DistribusisInfo
|
||||||
@ -115,7 +116,7 @@ def RenderDistribusiTemplate(current_distribusi):
|
|||||||
distribusi_live = IsDistribusiLive(current_distribusi)
|
distribusi_live = IsDistribusiLive(current_distribusi)
|
||||||
css_selected = True
|
css_selected = True
|
||||||
selectorvisible = False
|
selectorvisible = False
|
||||||
|
limit_reached = DistribusiLimitReached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
||||||
uploadform=uploadform,
|
uploadform=uploadform,
|
||||||
@ -127,6 +128,7 @@ def RenderDistribusiTemplate(current_distribusi):
|
|||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
css_selected=css_selected,
|
css_selected=css_selected,
|
||||||
selectorvisible=selectorvisible,
|
selectorvisible=selectorvisible,
|
||||||
|
limit_reached=limit_reached,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""This is the main flask distribusi page"""
|
"""This is the main flask distribusi page"""
|
||||||
import os
|
|
||||||
import zipfile
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
@ -18,7 +16,7 @@ from flask_login import (
|
|||||||
|
|
||||||
from flask_wtf.csrf import CSRFError
|
from flask_wtf.csrf import CSRFError
|
||||||
|
|
||||||
from app import create_app, db, login_manager
|
from app import create_app, login_manager
|
||||||
from usermodel import User
|
from usermodel import User
|
||||||
from distribusimodel import Distribusis
|
from distribusimodel import Distribusis
|
||||||
|
|
||||||
@ -42,6 +40,7 @@ from editor import Editor
|
|||||||
from themeselector import ThemeSelector
|
from themeselector import ThemeSelector
|
||||||
from upload import UploadNewDistribusi, UploadUpdatedFiles
|
from upload import UploadNewDistribusi, UploadUpdatedFiles
|
||||||
from adminpage import AdminPage
|
from adminpage import AdminPage
|
||||||
|
from distribusiworkflow import DistribusiWorkflow
|
||||||
|
|
||||||
# UserPengguna
|
# UserPengguna
|
||||||
from statuspengguna.helper import (
|
from statuspengguna.helper import (
|
||||||
@ -51,15 +50,12 @@ from statuspengguna.helper import (
|
|||||||
HasDistribusi,
|
HasDistribusi,
|
||||||
IsDistribusiLive,
|
IsDistribusiLive,
|
||||||
ResetUserState,
|
ResetUserState,
|
||||||
|
DistribusiLimitReached,
|
||||||
)
|
)
|
||||||
from statuspengguna.loginuser import LoginUser
|
from statuspengguna.loginuser import LoginUser
|
||||||
from statuspengguna.registeruser import RegisterUser
|
from statuspengguna.registeruser import RegisterUser
|
||||||
from distribusisinfo import DistribusisInfo
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
# Tada!
|
|
||||||
from distribusi.cli import build_argparser
|
|
||||||
from distribusi.distribusi import distribusify
|
|
||||||
|
|
||||||
APP = create_app()
|
APP = create_app()
|
||||||
|
|
||||||
|
|
||||||
@ -104,66 +100,7 @@ def index():
|
|||||||
@APP.route("/distribusi", methods=["GET", "POST"])
|
@APP.route("/distribusi", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def distribusi():
|
def distribusi():
|
||||||
uploadform = UploadForm()
|
return DistribusiWorkflow()
|
||||||
distribusiform = DistribusiForm()
|
|
||||||
themeform = ThemeForm()
|
|
||||||
publicthemeform = PublicThemeForm()
|
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
|
||||||
selectorform = SelectorForm()
|
|
||||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusis()
|
|
||||||
|
|
||||||
selectorvisible = SelectorVisible()
|
|
||||||
current_distribusi = CurrentDistribusi()
|
|
||||||
files_uploaded = IsZipUploaded(current_distribusi)
|
|
||||||
distribusi_live = IsDistribusiLive(current_distribusi)
|
|
||||||
css_selected = IsCssSelected(current_distribusi)
|
|
||||||
user = User.query.filter_by(email=current_user.email).first()
|
|
||||||
distribusi = Distribusis.query.filter_by(
|
|
||||||
distribusiname=current_distribusi
|
|
||||||
).first()
|
|
||||||
|
|
||||||
if distribusiform.validate_on_submit():
|
|
||||||
zipfilename = "{}.zip".format(distribusi.distribusiname)
|
|
||||||
userfolder = os.path.join("stash", distribusi.distribusiname)
|
|
||||||
unzipfile = os.path.join(userfolder, zipfilename)
|
|
||||||
|
|
||||||
if os.path.exists(unzipfile):
|
|
||||||
with zipfile.ZipFile(unzipfile, "r") as zip_ref:
|
|
||||||
# To do, replace extractall with inspection and extract
|
|
||||||
zip_ref.extractall(userfolder)
|
|
||||||
if os.path.exists(unzipfile):
|
|
||||||
os.remove(os.path.join(userfolder, zipfilename))
|
|
||||||
# To Do: Make sure nothing can be executed from the upload folder
|
|
||||||
cssfile = ""
|
|
||||||
cssfolder = os.path.join(
|
|
||||||
"themes/userthemes", distribusi.distribusiname
|
|
||||||
)
|
|
||||||
for filename in os.listdir(cssfolder):
|
|
||||||
if filename.endswith(".css"):
|
|
||||||
cssfile = os.path.join(cssfolder, filename)
|
|
||||||
|
|
||||||
parser = build_argparser()
|
|
||||||
args = parser.parse_args(["-s", cssfile])
|
|
||||||
distribusify(args, userfolder)
|
|
||||||
distribusi.visible = True
|
|
||||||
user.currentdistribusi = None
|
|
||||||
if os.path.exists(cssfile):
|
|
||||||
os.remove(cssfile)
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(url_for("index"))
|
|
||||||
template = render_template(
|
|
||||||
"distribusi.html",
|
|
||||||
uploadform=uploadform,
|
|
||||||
distribusiform=distribusiform,
|
|
||||||
themeform=themeform,
|
|
||||||
publicthemeform=publicthemeform,
|
|
||||||
selectorform=selectorform,
|
|
||||||
files_uploaded=files_uploaded,
|
|
||||||
distribusi_live=distribusi_live,
|
|
||||||
css_selected=css_selected,
|
|
||||||
selectorvisible=selectorvisible,
|
|
||||||
)
|
|
||||||
return template
|
|
||||||
|
|
||||||
|
|
||||||
@APP.route("/upload", methods=["POST"])
|
@APP.route("/upload", methods=["POST"])
|
||||||
@ -174,7 +111,7 @@ def upload():
|
|||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusis()
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
selectorvisible = SelectorVisible()
|
selectorvisible = SelectorVisible()
|
||||||
current_distribusi = CurrentDistribusi()
|
current_distribusi = CurrentDistribusi()
|
||||||
if current_distribusi == "new" or HasDistribusi() is False:
|
if current_distribusi == "new" or HasDistribusi() is False:
|
||||||
@ -184,6 +121,7 @@ def upload():
|
|||||||
files_uploaded = IsZipUploaded(uploadform.sitename.data)
|
files_uploaded = IsZipUploaded(uploadform.sitename.data)
|
||||||
distribusi_live = IsDistribusiLive(current_distribusi)
|
distribusi_live = IsDistribusiLive(current_distribusi)
|
||||||
css_selected = IsCssSelected(current_distribusi)
|
css_selected = IsCssSelected(current_distribusi)
|
||||||
|
limit_reached = DistribusiLimitReached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
||||||
uploadform=uploadform,
|
uploadform=uploadform,
|
||||||
@ -195,6 +133,7 @@ def upload():
|
|||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
css_selected=css_selected,
|
css_selected=css_selected,
|
||||||
selectorvisible=selectorvisible,
|
selectorvisible=selectorvisible,
|
||||||
|
limit_reached=limit_reached,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
@ -221,7 +160,7 @@ def selector():
|
|||||||
publicthemeform = PublicThemeForm()
|
publicthemeform = PublicThemeForm()
|
||||||
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
publicthemeform.publicthemes.choices = DistribusisInfo.publicthemes()
|
||||||
selectorform = SelectorForm()
|
selectorform = SelectorForm()
|
||||||
selectorform.distribusis.choices = DistribusisInfo.userdistribusis()
|
selectorform.distribusis.choices = DistribusisInfo.userdistribusinames()
|
||||||
current_distribusi = CurrentDistribusi()
|
current_distribusi = CurrentDistribusi()
|
||||||
if selectorform.validate_on_submit():
|
if selectorform.validate_on_submit():
|
||||||
if selectorform.new.data:
|
if selectorform.new.data:
|
||||||
@ -229,7 +168,7 @@ def selector():
|
|||||||
if selectorform.delete.data:
|
if selectorform.delete.data:
|
||||||
selectorform = DeleteDistribusi(selectorform.distribusis.data)
|
selectorform = DeleteDistribusi(selectorform.distribusis.data)
|
||||||
selectorform.distribusis.choices = (
|
selectorform.distribusis.choices = (
|
||||||
DistribusisInfo.userdistribusis()
|
DistribusisInfo.userdistribusinames()
|
||||||
)
|
)
|
||||||
if selectorform.update.data:
|
if selectorform.update.data:
|
||||||
selectorform = SelectUpdateDistribusi(
|
selectorform = SelectUpdateDistribusi(
|
||||||
@ -252,7 +191,7 @@ def selector():
|
|||||||
# no selected css.
|
# no selected css.
|
||||||
css_selected = False
|
css_selected = False
|
||||||
selectorvisible = SelectorVisible()
|
selectorvisible = SelectorVisible()
|
||||||
|
limit_reached = DistribusiLimitReached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
||||||
uploadform=uploadform,
|
uploadform=uploadform,
|
||||||
@ -264,6 +203,7 @@ def selector():
|
|||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
css_selected=css_selected,
|
css_selected=css_selected,
|
||||||
selectorvisible=selectorvisible,
|
selectorvisible=selectorvisible,
|
||||||
|
limit_reached=limit_reached,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ from sqlalchemy.exc import (
|
|||||||
|
|
||||||
from usermodel import User
|
from usermodel import User
|
||||||
from distribusimodel import Distribusis
|
from distribusimodel import Distribusis
|
||||||
|
from distribusisinfo import DistribusisInfo
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +78,18 @@ def ResetUserState():
|
|||||||
flash("An error occured !", "danger")
|
flash("An error occured !", "danger")
|
||||||
|
|
||||||
|
|
||||||
|
def DistribusiLimitReached():
|
||||||
|
user = User.query.filter_by(email=current_user.email).first()
|
||||||
|
distribusiamount = len(
|
||||||
|
DistribusisInfo.getuserdistribusis(user.email)
|
||||||
|
)
|
||||||
|
if user.tutor and distribusiamount > 14:
|
||||||
|
print("tutor already has 15 distribusis")
|
||||||
|
return True
|
||||||
|
if distribusiamount > 4:
|
||||||
|
print("user already has 5 distribusis")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,11 +24,18 @@
|
|||||||
</p>
|
</p>
|
||||||
<fieldset class="button required multiselect delete">
|
<fieldset class="button required multiselect delete">
|
||||||
{{ selectorform.delete }}
|
{{ selectorform.delete }}
|
||||||
|
{% for message in selectorform.delete.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<hr>
|
<hr>
|
||||||
|
{% if limit_reached %}
|
||||||
|
<p>You have reached your limit of distribusi websites</p>
|
||||||
|
{% else %}
|
||||||
<p>Alternatively you can make a new site</p>
|
<p>Alternatively you can make a new site</p>
|
||||||
<fieldset class="button required multiselect">
|
<fieldset class="button required multiselect">
|
||||||
{{ selectorform.new }}
|
{{ selectorform.new }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,7 @@ from statuspengguna.helper import (
|
|||||||
IsCssSelected,
|
IsCssSelected,
|
||||||
CurrentDistribusi,
|
CurrentDistribusi,
|
||||||
IsDistribusiLive,
|
IsDistribusiLive,
|
||||||
|
DistribusiLimitReached,
|
||||||
)
|
)
|
||||||
from distribusisinfo import DistribusisInfo
|
from distribusisinfo import DistribusisInfo
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ def RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi):
|
|||||||
distribusi_live = IsDistribusiLive(current_distribusi)
|
distribusi_live = IsDistribusiLive(current_distribusi)
|
||||||
css_selected = IsCssSelected(current_distribusi)
|
css_selected = IsCssSelected(current_distribusi)
|
||||||
selectorvisible = False
|
selectorvisible = False
|
||||||
|
limit_reached = DistribusiLimitReached()
|
||||||
template = render_template(
|
template = render_template(
|
||||||
"distribusi.html",
|
"distribusi.html",
|
||||||
uploadform=uploadform,
|
uploadform=uploadform,
|
||||||
@ -68,5 +69,6 @@ def RenderDistribusiTemplate(themeform, publicthemeform, current_distribusi):
|
|||||||
distribusi_live=distribusi_live,
|
distribusi_live=distribusi_live,
|
||||||
css_selected=css_selected,
|
css_selected=css_selected,
|
||||||
selectorvisible=selectorvisible,
|
selectorvisible=selectorvisible,
|
||||||
|
limit_reached=limit_reached,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
Loading…
Reference in New Issue
Block a user