Adding archives
Added functionality to add archives.
This commit is contained in:
parent
978c634067
commit
c3bd73a068
|
@ -36,10 +36,12 @@
|
|||
<yjs:SolidColorFill x:Key="1" color="#FFE8EEF7"/>
|
||||
<yjs:SolidColorFill x:Key="2" color="#FFB7C9E3"/>
|
||||
<y:FreeNodePortLocationModelParameter x:Key="3" Ratio="0.007575757575757569,0.5"/>
|
||||
<yjs:SolidColorFill x:Key="4" color="#FF663800"/>
|
||||
<yjs:Stroke x:Key="5" fill="{y:GraphMLReference 4}"/>
|
||||
<yjs:Arrow x:Key="6" type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}" cropLength="1"/>
|
||||
<yjs:PolylineEdgeStyle x:Key="7" stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}"/>
|
||||
<yjs:SolidColorFill x:Key="4" color="#FFCEDED3"/>
|
||||
<yjs:SolidColorFill x:Key="5" color="#FFB7E3C5"/>
|
||||
<yjs:SolidColorFill x:Key="6" color="#FF663800"/>
|
||||
<yjs:Stroke x:Key="7" fill="{y:GraphMLReference 6}"/>
|
||||
<yjs:Arrow x:Key="8" type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
|
||||
<yjs:PolylineEdgeStyle x:Key="9" stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}"/>
|
||||
</y:SharedData>
|
||||
</data>
|
||||
<graph id="G" edgedefault="directed">
|
||||
|
@ -135,7 +137,6 @@
|
|||
<x:Array Type="sys:Object">
|
||||
<sys:String>ID</sys:String>
|
||||
<sys:String>LABEL</sys:String>
|
||||
<sys:String>CATEGORY</sys:String>
|
||||
<sys:String>TYPE</sys:String>
|
||||
</x:Array>
|
||||
</x0:EntityRelationshipModel.attributes>
|
||||
|
@ -165,7 +166,7 @@
|
|||
<y:RectD X="-27.5" Y="-250" Width="130" Height="130"/>
|
||||
</data>
|
||||
<data key="d7">
|
||||
<x0:EntityNodeStyle fill="#FFCEDED3" insetFill="#FFB7E3C5" stroke="BLACK">
|
||||
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
|
||||
<x0:EntityNodeStyle.model>
|
||||
<x0:EntityRelationshipModel>
|
||||
<x0:EntityRelationshipModel.title>ArchLab</x0:EntityRelationshipModel.title>
|
||||
|
@ -253,7 +254,7 @@
|
|||
<node id="n6">
|
||||
<data key="d0">8</data>
|
||||
<data key="d5">
|
||||
<y:RectD X="210.5" Y="-453" Width="130" Height="130"/>
|
||||
<y:RectD X="210.5" Y="-48" Width="130" Height="130"/>
|
||||
</data>
|
||||
<data key="d7">
|
||||
<x0:EntityNodeStyle fill="{y:GraphMLReference 1}" insetFill="{y:GraphMLReference 2}" stroke="BLACK">
|
||||
|
@ -281,53 +282,105 @@
|
|||
<y:FreeNodePortLocationModelParameter Ratio="0.5,0.9924242424242424"/>
|
||||
</data>
|
||||
</port>
|
||||
<port name="p2">
|
||||
<data key="d16">
|
||||
<y:FreeNodePortLocationModelParameter Ratio="0.007575757575757569,0.5"/>
|
||||
</data>
|
||||
</port>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d0">9</data>
|
||||
<data key="d5">
|
||||
<y:RectD X="-27.5" Y="-48" Width="130" Height="130"/>
|
||||
</data>
|
||||
<data key="d7">
|
||||
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
|
||||
<x0:EntityNodeStyle.model>
|
||||
<x0:EntityRelationshipModel>
|
||||
<x0:EntityRelationshipModel.title>CatLabType</x0:EntityRelationshipModel.title>
|
||||
<x0:EntityRelationshipModel.attributes>
|
||||
<x:Array Type="sys:Object">
|
||||
<sys:String>ID</sys:String>
|
||||
<sys:String>CATID</sys:String>
|
||||
<sys:String>LABID</sys:String>
|
||||
</x:Array>
|
||||
</x0:EntityRelationshipModel.attributes>
|
||||
</x0:EntityRelationshipModel>
|
||||
</x0:EntityNodeStyle.model>
|
||||
</x0:EntityNodeStyle>
|
||||
</data>
|
||||
<data key="d8">
|
||||
<y:ViewState>
|
||||
<y:FolderNodeState x:Key="c|view" Layout="-12.5,-235,130,130">
|
||||
<y:FolderNodeState.Style>
|
||||
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
|
||||
<x0:EntityNodeStyle.model>
|
||||
<x0:EntityRelationshipModel>
|
||||
<x0:EntityRelationshipModel.title>ArchLab</x0:EntityRelationshipModel.title>
|
||||
<x0:EntityRelationshipModel.attributes>
|
||||
<x:Array Type="sys:Object">
|
||||
<sys:String>ID</sys:String>
|
||||
<sys:String>ARCHID</sys:String>
|
||||
<sys:String>LABID</sys:String>
|
||||
</x:Array>
|
||||
</x0:EntityRelationshipModel.attributes>
|
||||
</x0:EntityRelationshipModel>
|
||||
</x0:EntityNodeStyle.model>
|
||||
</x0:EntityNodeStyle>
|
||||
</y:FolderNodeState.Style>
|
||||
</y:FolderNodeState>
|
||||
</y:ViewState>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n0" target="n2" sourceport="p0" targetport="p0">
|
||||
<data key="d10"><![CDATA[]]></data>
|
||||
<data key="d13">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}">
|
||||
<yjs:PolylineEdgeStyle.sourceArrow>
|
||||
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}" cropLength="1"/>
|
||||
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
|
||||
</yjs:PolylineEdgeStyle.sourceArrow>
|
||||
</yjs:PolylineEdgeStyle>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n0" target="n1" sourceport="p1" targetport="p0">
|
||||
<data key="d13">
|
||||
<y:GraphMLReference ResourceKey="7"/>
|
||||
<y:GraphMLReference ResourceKey="9"/>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n4" sourceport="p2" targetport="p0">
|
||||
<data key="d13">
|
||||
<y:GraphMLReference ResourceKey="7"/>
|
||||
<y:GraphMLReference ResourceKey="9"/>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n4" target="n5" sourceport="p1" targetport="p0">
|
||||
<data key="d13">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}">
|
||||
<yjs:PolylineEdgeStyle.targetArrow>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
|
||||
</yjs:PolylineEdgeStyle.targetArrow>
|
||||
<yjs:PolylineEdgeStyle.sourceArrow>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
|
||||
</yjs:PolylineEdgeStyle.sourceArrow>
|
||||
</yjs:PolylineEdgeStyle>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n6" target="n2" sourceport="p1" targetport="p1">
|
||||
<data key="d13">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}">
|
||||
<yjs:PolylineEdgeStyle.targetArrow>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
|
||||
</yjs:PolylineEdgeStyle.targetArrow>
|
||||
<yjs:PolylineEdgeStyle.sourceArrow>
|
||||
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
|
||||
</yjs:PolylineEdgeStyle.sourceArrow>
|
||||
</yjs:PolylineEdgeStyle>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n2" target="n1" sourceport="p2" targetport="p1">
|
||||
<edge id="e5" source="n6" target="n1" sourceport="p2" targetport="p1">
|
||||
<data key="d13">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}">
|
||||
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}">
|
||||
<yjs:PolylineEdgeStyle.sourceArrow>
|
||||
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
|
||||
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
|
||||
</yjs:PolylineEdgeStyle.sourceArrow>
|
||||
</yjs:PolylineEdgeStyle>
|
||||
</data>
|
||||
|
|
89
flask/app.py
89
flask/app.py
|
@ -19,7 +19,7 @@ def timectime(s):
|
|||
def convsize(s):
|
||||
sizes=("B","KB","MB","GB","TB")
|
||||
n=0
|
||||
while s > 1000:
|
||||
while s >= 1000:
|
||||
n+=1
|
||||
s=s/1000
|
||||
return str("%.2f" % s)+sizes[n]
|
||||
|
@ -28,31 +28,48 @@ def convsize(s):
|
|||
@app.route('/')
|
||||
def homepage():
|
||||
# try to get userdata, else logout state
|
||||
print(request.base_url)
|
||||
try:
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session'))
|
||||
except Exception as e:
|
||||
logged_in,userdata=False,()
|
||||
#try: # TODO: CLEAN!
|
||||
# # get sesskey and get info about user
|
||||
# sesskey=request.cookies.get('session')
|
||||
# res,userid=db.check_sesskey(sesskey)
|
||||
# if not res:
|
||||
# return 500
|
||||
# userdata=db.get_user_info(userid)
|
||||
# logged_in=True
|
||||
#except Exception as e:
|
||||
# logged_in=False
|
||||
# userdata=()
|
||||
archives=db.get_n_archives()
|
||||
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
|
||||
|
||||
@app.route('/user')
|
||||
@app.route('/user/<int:userid>')
|
||||
def userpage(userid:int=0):
|
||||
if userid == 0:
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session'))
|
||||
if not logged_in:
|
||||
return make_response(redirect('/'))
|
||||
|
||||
@app.route('/add', methods=['GET','POST'])
|
||||
def addpage():
|
||||
# try to get userdata, else yeet to the homepage
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session'))
|
||||
if not logged_in:
|
||||
return make_response(redirect('/'))
|
||||
# POST: check and add archive, show confirmation/error message at the end
|
||||
if request.method == 'POST':
|
||||
postdict={}
|
||||
# get and save all inputs, error if one doesn't exist or is wrong type
|
||||
for i,itype in [("name",str),("hash",str),("category",int),("size",float)]:
|
||||
try:
|
||||
postdict[i]=itype(request.form[i])
|
||||
except Exception as e:
|
||||
return "<h2>ERROR: All fields need to be filled and don't play with their names!</h2> Go back and try again."
|
||||
|
||||
try:
|
||||
postdict["size"]=postdict["size"]*int(request.form['size_multiplier'])
|
||||
except Exception as e:
|
||||
return "<h2>ERROR: All fields need to be filled and don't play with their names!</h2> Go back and try again."
|
||||
postdict["owner"]=userdata[0]
|
||||
res,archid=db.add_archive(postdict)
|
||||
if res:
|
||||
return make_response(redirect(f"/view/{str(archid)}"))
|
||||
else:
|
||||
return f"<h2>ERROR: {archid}</h2> Go back and try again.", 400
|
||||
|
||||
# GET: return normal page
|
||||
htmlcatlist=get_category_selection(False)
|
||||
return render_template("add.html", title="Add Archive",categories=htmlcatlist)
|
||||
|
||||
@app.route('/login', methods=["GET","POST"])
|
||||
def loginpage():
|
||||
# POST: Process login request
|
||||
|
@ -78,10 +95,7 @@ def loginpage():
|
|||
@app.route('/search')
|
||||
def searchpage():
|
||||
# try to get userdata, else logout state
|
||||
try:
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session'))
|
||||
except Exception as e:
|
||||
logged_in,userdata=False,()
|
||||
|
||||
# try to set all required variables, else defaults
|
||||
try:
|
||||
|
@ -103,26 +117,18 @@ def searchpage():
|
|||
count=20
|
||||
archives=db.get_n_archives(sorttype,category,keywords,count)
|
||||
|
||||
catlist=db.get_all_categories()
|
||||
htmlcatlist=[]
|
||||
# parse all categories and sort them into select box
|
||||
for cat in catlist:
|
||||
if not cat[2]:
|
||||
htmlcatlist.append((cat[0],cat[1]))
|
||||
parent=cat[1]
|
||||
parentid=cat[0]
|
||||
for i in catlist:
|
||||
if i[2] == parentid:
|
||||
htmlcatlist.append((i[0],f"{parent}/{i[1]}"))
|
||||
htmlcatlist=get_category_selection()
|
||||
|
||||
return render_template("search.html", title="Advanced Search",categories=htmlcatlist,userdata=userdata,login=logged_in,archives=archives)
|
||||
|
||||
## FUNCTIONS
|
||||
|
||||
## Checks if given sesskey is valid and returns user data
|
||||
## OUTPUT: (if sesskey valid) logged_in:bool=True, userdata:tup
|
||||
## (if sesskey invalid)
|
||||
## OUTPUT: (if sesskey valid) logged_in:bool=True, userdata:tuple
|
||||
## (if sesskey invalid) logged_in:bool=False, userdata:tuple=()
|
||||
def get_login_info(sesskey:str):
|
||||
if not sesskey:
|
||||
return False,()
|
||||
logged_in,userid=db.check_sesskey(sesskey)
|
||||
if logged_in:
|
||||
userdata=db.get_user_info(userid)
|
||||
|
@ -135,6 +141,23 @@ def setcookie(name:str,value:str,lifetime:int=10000):
|
|||
resp.set_cookie(name, value, max_age=lifetime)
|
||||
return resp
|
||||
|
||||
## Gets all categories and returns them (with or without parents)
|
||||
## OUTPUT: […,(ID:int,NAME:str),…]
|
||||
def get_category_selection(include_parents:bool=True):
|
||||
catlist=db.get_all_categories()
|
||||
htmlcatlist=[]
|
||||
# parse all categories and sort them into list
|
||||
for cat in catlist:
|
||||
if not cat[2]:
|
||||
if include_parents:
|
||||
htmlcatlist.append((cat[0],cat[1]))
|
||||
parent=cat[1]
|
||||
parentid=cat[0]
|
||||
for i in catlist:
|
||||
if i[2] == parentid:
|
||||
htmlcatlist.append((i[0],f"{parent}/{i[1]}"))
|
||||
return htmlcatlist
|
||||
|
||||
## API CALLS (NO THANKS)
|
||||
|
||||
# main driver function
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
## MAIN FUNCTIONS FILE FOR BACK-BACKEND OF FLASK
|
||||
import mariadb as sql
|
||||
from os import environ
|
||||
import time
|
||||
import time,re
|
||||
|
||||
## params populated with environment variables, defaults can be changed for a permanent solution
|
||||
conn_params={
|
||||
|
@ -23,7 +23,7 @@ class db:
|
|||
self.cur.execute("""CREATE TABLE IF NOT EXISTS Archs(
|
||||
ID int PRIMARY KEY AUTO_INCREMENT,
|
||||
NAME text NOT NULL,
|
||||
HASH text NOT NULL,
|
||||
HASH text NOT NULL UNIQUE,
|
||||
SIZE int NOT NULL,
|
||||
IMPORTED int,
|
||||
CATEGORY int,
|
||||
|
@ -55,10 +55,14 @@ class db:
|
|||
ARCHID int NOT NULL,
|
||||
LABID int NOT NULL
|
||||
);""")
|
||||
self.cur.execute("""CREATE TABLE IF NOT EXISTS CatLabType(
|
||||
ID int PRIMARY KEY AUTO_INCREMENT,
|
||||
CATID int NOT NULL,
|
||||
LABID int NOT NULL
|
||||
);""")
|
||||
self.cur.execute("""CREATE TABLE IF NOT EXISTS Labs(
|
||||
ID int PRIMARY KEY AUTO_INCREMENT,
|
||||
LABEL text NOT NULL,
|
||||
CATEGORY text,
|
||||
TYPE int NOT NULL
|
||||
);""")
|
||||
self.cur.execute("""CREATE TABLE IF NOT EXISTS LabType(
|
||||
|
@ -99,8 +103,30 @@ class db:
|
|||
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
|
||||
return self.cur.fetchone()
|
||||
|
||||
## Checks information for errors and adds archive to the DB
|
||||
## OUTPUT: (if successful) res:bool=True, ID:int
|
||||
## (if unsuccessful) res:bool=False, str
|
||||
def add_archive(self, archive:dict):
|
||||
# Check everything for errors or malicious things
|
||||
archive["hash"]=archive["hash"].upper()
|
||||
if not re.match('[A-Z0-9]{40}', archive["hash"]):
|
||||
return False, "Hash needs to be 40 characters in hexadecimal (SHA-1)."
|
||||
if re.match('.*[^A-Za-z0-9\. _-].*', archive["name"]):
|
||||
return False, "The name contains illegal characters. Allowed chars: '[A-Za-z0-9\. _-]'"
|
||||
print(archive["name"])
|
||||
|
||||
curtime=time.time()
|
||||
try:
|
||||
self.cur.execute(f"INSERT INTO Archs(NAME,HASH,SIZE,IMPORTED,CATEGORY,OWNER) VALUES('{archive['name']}','{archive['hash']}',{archive['size']},{curtime},{archive['category']},{archive['owner']})")
|
||||
except Exception as e: # hash needs to be unique
|
||||
return False, e
|
||||
self.cur.execute(f"SELECT ID FROM Archs WHERE HASH='{archive['hash']}'")
|
||||
archid=self.cur.fetchone()
|
||||
return True,archid[0]
|
||||
|
||||
|
||||
## Returns all relevant information about one (1) archive
|
||||
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str)
|
||||
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str),
|
||||
## labels:array=[…,(LABEL:str,CATEGORY:str,CATDESC:str,LABTYPE:str,LABDESC:str),…]
|
||||
def get_archive_info(self, hash:str):
|
||||
self.cur.execute(f"""SELECT Archs.NAME,Archs.HASH,Archs.IMPORTED,Cats.CATEGORY,Cats.DESCRIPTION,Users.UNAME,Users.DNAME FROM Archs
|
||||
|
@ -146,14 +172,14 @@ class db:
|
|||
|
||||
# Get all children of category (if exist) and put into query string
|
||||
categories=self.get_all_categories()
|
||||
catlist=[category]
|
||||
catlist=[str(category)]
|
||||
for i in categories:
|
||||
if i[2] == int(category):
|
||||
catlist.append(str(i[0]))
|
||||
category="(" + ",".join(catlist) + ")"
|
||||
categories="(" + ",".join(catlist) + ")"
|
||||
|
||||
self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs
|
||||
{"WHERE 1=1" if category==0 else " WHERE CATEGORY IN " + category}
|
||||
{"WHERE 1=1" if category==0 else " WHERE CATEGORY IN " + categories}
|
||||
{keyword_string}
|
||||
ORDER BY {sorttype} LIMIT {count};""")
|
||||
archives=self.cur.fetchall()
|
||||
|
|
4
flask/static/add.css
Normal file
4
flask/static/add.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
div.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: max-content max-content;
|
||||
}
|
|
@ -15,6 +15,16 @@ header {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
header > a {
|
||||
margin: auto;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
}
|
||||
a > img {
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
header > div#container {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
|
34
flask/templates/add.html
Normal file
34
flask/templates/add.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block meta %}
|
||||
<link rel="stylesheet" href="/static/add.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/add" method="post">
|
||||
<div class="grid-container">
|
||||
<div><b>Name: </b></div>
|
||||
<div><input type="text" name="name" placeholder="Archive Name"></div>
|
||||
<div><b>Hash: </b></div>
|
||||
<div><input type="text" name="hash" placeholder="Hash"></div>
|
||||
<div><b>Category: </b></div>
|
||||
<div>
|
||||
<select name="category">
|
||||
{% for i in categories %}
|
||||
<option value="{{i[0]}}">{{i[1]}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div><b>Size: </b></div>
|
||||
<div>
|
||||
<input type="number" name="size" step="0.01" min="0" placeholder="Size">
|
||||
<select name="size_multiplier">
|
||||
{% for val,size in ((1,"B"),(1000,"KB"),(1000000,"MB"),(1000000000,"GB"),(1000000000000,"TB")) %}
|
||||
<option value="{{val}}">{{size}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<input type="submit" value="Add Archive">
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/"><img src="/static/favicon.ico" alt="favicon"></a>
|
||||
<span id="title">{{title}}</span>
|
||||
<div id="container">
|
||||
<!-- BUTTONS IF USER LOGGED IN -->
|
||||
|
|
Loading…
Reference in a new issue