should work
This commit is contained in:
4
run.py
Executable file
4
run.py
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
from shop import app
|
||||||
|
print('http://127.0.0.1:5000/')
|
||||||
|
app.run(debug=True,host="0.0.0.0")
|
||||||
19
schema.sql
Normal file
19
schema.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
drop table if exists users;
|
||||||
|
drop table if exists shops;
|
||||||
|
drop table if exists shares;
|
||||||
|
create table users (
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
user text not null,
|
||||||
|
pass text not null
|
||||||
|
);
|
||||||
|
create table shops (
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
shop text not null,
|
||||||
|
owner integer not null
|
||||||
|
);
|
||||||
|
create table shares (
|
||||||
|
shopid integer not null,
|
||||||
|
userid integer not null
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
413
shop.py
Normal file
413
shop.py
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# all the imports
|
||||||
|
import sqlite3, time, datetime, hashlib, os
|
||||||
|
from shutil import copyfile
|
||||||
|
from flask import Flask, request, session, g, redirect, url_for, \
|
||||||
|
abort, render_template, flash
|
||||||
|
|
||||||
|
# configuration
|
||||||
|
DATABASE = 'shop.db'
|
||||||
|
DATADIR = 'data'
|
||||||
|
DEBUG = False
|
||||||
|
SECRET_KEY = 'development key'
|
||||||
|
USERNAME = 'admin'
|
||||||
|
PASSWORD = 'default'
|
||||||
|
|
||||||
|
# create our little application :)
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(__name__)
|
||||||
|
|
||||||
|
def connect_db():
|
||||||
|
if not os.path.exists(app.config['DATABASE']):
|
||||||
|
db=sqlite3.connect(app.config['DATABASE'])
|
||||||
|
for command in open('schema.sql','rt').read().split(';'):
|
||||||
|
db.execute(command)
|
||||||
|
db.commit()
|
||||||
|
return sqlite3.connect(app.config['DATABASE'])
|
||||||
|
|
||||||
|
def password_hash(s):
|
||||||
|
return hashlib.sha224(s).hexdigest()
|
||||||
|
|
||||||
|
def get_username(id):
|
||||||
|
cur = g.db.execute('select * from users')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if id==row[0]:
|
||||||
|
return row[1]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_userid(name):
|
||||||
|
cur = g.db.execute('select * from users')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if name==row[1]:
|
||||||
|
return row[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_shares(id):
|
||||||
|
cur = g.db.execute('select * from shares')
|
||||||
|
shares=[]
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if id==row[1]:
|
||||||
|
shares.append(row[0])
|
||||||
|
return shares
|
||||||
|
|
||||||
|
def get_shop_date(id):
|
||||||
|
date=""
|
||||||
|
cur = g.db.execute('select * from shops')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if id==row[0]:
|
||||||
|
data_dir=os.path.join(DATADIR, get_username(row[2]))
|
||||||
|
data_file=os.path.join(data_dir, row[1]+".md")
|
||||||
|
if os.path.exists(data_file):
|
||||||
|
date=datetime.datetime.fromtimestamp(
|
||||||
|
os.path.getmtime(data_file)).strftime('%m/%d %H:%M')
|
||||||
|
return date
|
||||||
|
|
||||||
|
def scan_for_new_documents(id):
|
||||||
|
user=get_username(id)
|
||||||
|
data_dir=os.path.join(DATADIR, user)
|
||||||
|
if not os.path.exists(data_dir):
|
||||||
|
return
|
||||||
|
cur = g.db.execute('select * from shops')
|
||||||
|
existing=[]
|
||||||
|
non_existing=[]
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if row[2]!=id:
|
||||||
|
continue
|
||||||
|
existing.append(row[1])
|
||||||
|
for row in os.listdir(data_dir):
|
||||||
|
if row.endswith(".md"):
|
||||||
|
if row[:-3] not in existing:
|
||||||
|
non_existing.append(row[:-3])
|
||||||
|
for shop in non_existing:
|
||||||
|
g.db.execute('insert into shops (shop,owner) values (?, ?)',
|
||||||
|
[shop, id])
|
||||||
|
g.db.commit()
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def before_request():
|
||||||
|
g.db = connect_db()
|
||||||
|
@app.teardown_request
|
||||||
|
def teardown_request(exception):
|
||||||
|
db = getattr(g, 'db', None)
|
||||||
|
if db is not None:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
@app.route('/shop/<shopid>')
|
||||||
|
def show_shop(shopid):
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
return redirect(url_for('login',error=None))
|
||||||
|
try:
|
||||||
|
shopid=int(shopid)
|
||||||
|
except ValueError:
|
||||||
|
return redirect(url_for('login',error=None))
|
||||||
|
has_access=False
|
||||||
|
cur = g.db.execute('select * from shops')
|
||||||
|
shared=get_shares(session.get('user'))
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if row[0]==shopid:
|
||||||
|
if row[2]==session.get('user') or row[0] in shared:
|
||||||
|
has_access=True
|
||||||
|
shopname=row[1]
|
||||||
|
break
|
||||||
|
if not has_access:
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
data_dir=os.path.join(DATADIR, get_username(row[2]))
|
||||||
|
data_file=os.path.join(data_dir, row[1]+".md")
|
||||||
|
entries=[]
|
||||||
|
content=open(data_file, 'rt').read()
|
||||||
|
for i,row in enumerate(open( data_file, 'rt').read().split("\n")):
|
||||||
|
# any parsing magick would be here
|
||||||
|
if row=="":
|
||||||
|
continue
|
||||||
|
icon=" "
|
||||||
|
extra_class="noitem"
|
||||||
|
if "[ ]" in row or "[x]" in row:
|
||||||
|
icon=u"\u2714"
|
||||||
|
extra_class=""
|
||||||
|
entries.append( dict(row=i, text=row, icon=icon, extra_class=extra_class) )
|
||||||
|
shared_to=[]
|
||||||
|
cur = g.db.execute('select * from shares')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if row[0]==shopid:
|
||||||
|
shared_to.append(get_username(row[1]))
|
||||||
|
return render_template('show_shop.html', entries=entries, shop=shopname, shopid=shopid, content=content,shares=shared_to)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def list_shops():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
return redirect(url_for('login',error=None))
|
||||||
|
scan_for_new_documents(session.get('user'))
|
||||||
|
cur = g.db.execute('select * from shops order by shop')
|
||||||
|
entries=[]
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if session.get('user')==row[2]: # owner
|
||||||
|
date=get_shop_date(row[0])
|
||||||
|
entries.append( dict(shop=row[1], shopid=row[0], owner=get_username(row[2]),date=date) )
|
||||||
|
cur = g.db.execute('select * from shops order by shop')
|
||||||
|
shares=get_shares(session.get('user'))
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if row[0] in shares: # Has been shared to
|
||||||
|
date=get_shop_date(row[0])
|
||||||
|
entries.append( dict(shop=row[1], shopid=row[0], owner=get_username(row[2]),date=date) )
|
||||||
|
|
||||||
|
return render_template('list_shops.html', entries=entries)
|
||||||
|
|
||||||
|
@app.route('/add', methods=['POST'])
|
||||||
|
def add_items():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
shopid=int(request.form['shopid'])
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
shopname=g.db.execute('select shop from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
ownername=get_username(ownerid)
|
||||||
|
data_dir=os.path.join(DATADIR, ownername)
|
||||||
|
data_file=os.path.join(data_dir, shopname+".md")
|
||||||
|
count=0
|
||||||
|
contents_file=open(data_file,'at')
|
||||||
|
for row in request.form['add_md'].split("\n"):
|
||||||
|
if row.strip()=="":
|
||||||
|
continue
|
||||||
|
count+=1
|
||||||
|
contents_file.write("[ ] %s\n"%row.strip())
|
||||||
|
contents_file.close()
|
||||||
|
flash('Added %d items'%(count))
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
@app.route('/edit', methods=['POST'])
|
||||||
|
def edit_md():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
shopid=int(request.form['shopid'])
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
shopname=g.db.execute('select shop from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
ownername=get_username(ownerid)
|
||||||
|
data_dir=os.path.join(DATADIR, ownername)
|
||||||
|
data_file=os.path.join(data_dir, shopname+".md")
|
||||||
|
backup=data_file+".bkp"
|
||||||
|
copyfile(data_file, backup)
|
||||||
|
contents_file=open(data_file,'wt')
|
||||||
|
for row in request.form['edit_md'].split("\n"):
|
||||||
|
contents_file.write("%s\n"%row)
|
||||||
|
contents_file.close()
|
||||||
|
flash('Saved new file.')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/toggle', methods=['POST'])
|
||||||
|
def toggle_item():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
shopid=int(request.form['shopid'])
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
shopname=g.db.execute('select shop from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
ownername=get_username(ownerid)
|
||||||
|
req_row=None
|
||||||
|
for key in request.form:
|
||||||
|
if key.startswith('item'):
|
||||||
|
req_row=int(key[4:])
|
||||||
|
if req_row==None:
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
data_dir=os.path.join(DATADIR, ownername)
|
||||||
|
data_file=os.path.join(data_dir, shopname+".md")
|
||||||
|
contents_file=open(data_file,'rt')
|
||||||
|
contents=contents_file.read().split("\n")
|
||||||
|
contents_file.close()
|
||||||
|
changed=False
|
||||||
|
for i,row in enumerate(contents):
|
||||||
|
if i==req_row:
|
||||||
|
if '[ ]' in row:
|
||||||
|
contents[i]=row.replace('[ ]','[x]')
|
||||||
|
if '[x]' in row:
|
||||||
|
contents[i]=row.replace('[x]','[ ]')
|
||||||
|
if row!=contents[i]:
|
||||||
|
changed=True
|
||||||
|
break
|
||||||
|
if changed:
|
||||||
|
contents_file=open(data_file,'wt')
|
||||||
|
contents_file.write("\n".join(contents))
|
||||||
|
contents_file.close()
|
||||||
|
#~ flash('successfully posted %s (%d)'%(row,req_row))
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
@app.route('/remove_toggled', methods=['POST'])
|
||||||
|
def remove_toggled():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
shopid=int(request.form['shopid'])
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
shopname=g.db.execute('select shop from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
ownername=get_username(ownerid)
|
||||||
|
data_dir=os.path.join(DATADIR, ownername)
|
||||||
|
data_file=os.path.join(data_dir, shopname+".md")
|
||||||
|
contents_file=open(data_file,'rt')
|
||||||
|
contents=[]
|
||||||
|
changed=False
|
||||||
|
for i,row in enumerate(contents_file.read().split("\n")):
|
||||||
|
if '[x]' not in row:
|
||||||
|
contents.append(row)
|
||||||
|
changed=True
|
||||||
|
contents_file.close()
|
||||||
|
if changed:
|
||||||
|
contents_file=open(data_file,'wt')
|
||||||
|
contents_file.write("\n".join(contents))
|
||||||
|
contents_file.close()
|
||||||
|
#~ flash('successfully posted %s (%d)'%(row,req_row))
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
@app.route('/add_shop', methods=['POST'])
|
||||||
|
def add_shop():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
import re, string
|
||||||
|
pattern = re.compile('[\W_]+')
|
||||||
|
shopname=pattern.sub('', request.form['shop'])
|
||||||
|
if shopname=="":
|
||||||
|
flash('Shop name empty!')
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
cur = g.db.execute('select * from shops order by shop')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if shopname==row[1]:
|
||||||
|
flash('Shop already exists! '+shopname)
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
g.db.execute('insert into shops (shop,owner) values (?, ?)',
|
||||||
|
[shopname, session['user']])
|
||||||
|
g.db.commit()
|
||||||
|
new_dir=os.path.join(DATADIR, get_username(session['user']))
|
||||||
|
new_file=os.path.join(new_dir, shopname+".md")
|
||||||
|
if not os.path.exists(new_dir):
|
||||||
|
os.mkdir(new_dir)
|
||||||
|
open( new_file, 'at')
|
||||||
|
flash('successfully created new shop: '+shopname)
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
|
||||||
|
@app.route('/add_share', methods=['POST'])
|
||||||
|
def add_share():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
import re, string
|
||||||
|
pattern = re.compile('[\W_]+')
|
||||||
|
username=pattern.sub('', request.form['share'])
|
||||||
|
shopid=pattern.sub('', request.form['shopid'])
|
||||||
|
if username=="":
|
||||||
|
flash('User name empty!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
userid=get_userid(username)
|
||||||
|
if userid==None:
|
||||||
|
flash('No such user!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
if session.get('user')!=ownerid:
|
||||||
|
flash('Not your shop!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
g.db.execute('insert into shares (shopid,userid) values (?, ?)',
|
||||||
|
[shopid, userid])
|
||||||
|
g.db.commit()
|
||||||
|
flash('Shared to %s'%(username))
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
@app.route('/remove_share', methods=['POST'])
|
||||||
|
def remove_share():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
import re, string
|
||||||
|
pattern = re.compile('[\W_]+')
|
||||||
|
username=pattern.sub('', request.form['user'])
|
||||||
|
shopid=pattern.sub('', request.form['shopid'])
|
||||||
|
if username=="":
|
||||||
|
flash('User name empty!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
userid=get_userid(username)
|
||||||
|
if userid==None:
|
||||||
|
flash('No such user!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
if session.get('user')!=ownerid:
|
||||||
|
flash('Not your shop!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
g.db.execute('delete from shares where shopid=? and userid=?',
|
||||||
|
[shopid, userid])
|
||||||
|
g.db.commit()
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
|
||||||
|
@app.route('/remove_shop', methods=['POST'])
|
||||||
|
def remove_shop():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
abort(401)
|
||||||
|
shopid=int(request.form['shopid'])
|
||||||
|
ownerid=g.db.execute('select owner from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
shopname=g.db.execute('select shop from shops where id=?',request.form['shopid']).fetchall()[0][0]
|
||||||
|
ownername=get_username(ownerid)
|
||||||
|
data_dir=os.path.join(DATADIR, ownername)
|
||||||
|
data_file=os.path.join(data_dir, shopname+".md")
|
||||||
|
if session.get('user')!=ownerid:
|
||||||
|
flash('Not your shop!')
|
||||||
|
return redirect(url_for('show_shop',shopid=shopid))
|
||||||
|
# remove shop DB
|
||||||
|
g.db.execute('delete from shops where id=?',
|
||||||
|
[shopid])
|
||||||
|
g.db.commit()
|
||||||
|
# backup data, and remove
|
||||||
|
backup=data_file+".bkp"
|
||||||
|
copyfile(data_file, backup)
|
||||||
|
os.remove(data_file)
|
||||||
|
# remove shares
|
||||||
|
g.db.execute('delete from shares where shopid=?',
|
||||||
|
[shopid])
|
||||||
|
g.db.commit()
|
||||||
|
flash('successfully deleted shop %s'%(shopname))
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
error = None
|
||||||
|
if request.method == 'POST':
|
||||||
|
cur = g.db.execute('select * from users')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if request.form['username'] == row[1]:
|
||||||
|
if password_hash(request.form['password']) == row[2]:
|
||||||
|
session['logged_in'] = True
|
||||||
|
session['user'] = row[0]
|
||||||
|
# scan_for_new_documents(row[0])
|
||||||
|
return redirect(url_for('list_shops'))
|
||||||
|
error='Invalid user/pass'
|
||||||
|
return render_template('login.html', error=error)
|
||||||
|
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
error = None
|
||||||
|
if request.method == 'POST':
|
||||||
|
import re, string
|
||||||
|
pattern = re.compile('[\W_]+')
|
||||||
|
username=pattern.sub('', request.form['username'])
|
||||||
|
password=password_hash(request.form['password'])
|
||||||
|
if len(username)==0:
|
||||||
|
error="No username given"
|
||||||
|
return render_template('register.html', error=error)
|
||||||
|
if len(request.form['password'])<5:
|
||||||
|
error="Password too short"
|
||||||
|
return render_template('register.html', error=error)
|
||||||
|
cur = g.db.execute('select * from users')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
if username == row[1]:
|
||||||
|
error="Username already exists"
|
||||||
|
return render_template('register.html', error=error)
|
||||||
|
g.db.execute('insert into users (user,pass) values (?, ?)',
|
||||||
|
[username,password])
|
||||||
|
g.db.commit()
|
||||||
|
flash('successfully registered user "%s". Now login.'%username)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
return render_template('register.html', error=error)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
session.pop('logged_in', None)
|
||||||
|
session.pop('user', None)
|
||||||
|
flash('You were logged out')
|
||||||
|
return render_template('login.html', error=None)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
||||||
24
static/script.js
Normal file
24
static/script.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
function growTextarea(name) {
|
||||||
|
var el=document.getElementById(name);
|
||||||
|
var rows=el.value.split(/\r?\n|\r/);
|
||||||
|
el.rows=rows.length+1;
|
||||||
|
var cols=40;
|
||||||
|
for (var i = 0, row; row = rows[i]; i++) {
|
||||||
|
cols=Math.max(cols, row.length);
|
||||||
|
}
|
||||||
|
el.cols=cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
location.href=window.location.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidetoggle(name,button) {
|
||||||
|
document.getElementById(name).style.display='none';
|
||||||
|
document.getElementById(button).onclick=function(){showtoggle(name,button);};
|
||||||
|
}
|
||||||
|
function showtoggle(name,button) {
|
||||||
|
document.getElementById(name).style.display='inline-block';
|
||||||
|
document.getElementById(button).onclick=function(){hidetoggle(name,button);};
|
||||||
|
}
|
||||||
|
|
||||||
24
static/style.css
Normal file
24
static/style.css
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
body { font-family: monospace; background: #eee; }
|
||||||
|
a, h1, h2 { color: #377ba8; }
|
||||||
|
h1, h2 { margin: 0; }
|
||||||
|
h1 { border-bottom: 2px solid #eee; }
|
||||||
|
h2 { font-size: 1.2em; border-top: 2px solid #eee; margin-top: 0.25em; margin-bottom: 0.25em;}
|
||||||
|
td { height: 1.75em; }
|
||||||
|
|
||||||
|
.submit { font-family: monospace; }
|
||||||
|
.tickbox { margin-right: 1em; }
|
||||||
|
.noitem { background-color: transparent; border-color:transparent; }
|
||||||
|
.page { border: 5px solid #ccc; padding: 0.5em;
|
||||||
|
background: white; }
|
||||||
|
.entries { margin-top: 1em; margin-bottom: 1em; }
|
||||||
|
.entries input { margin-top: 0.125em; margin-bottom: 0.125em; }
|
||||||
|
.entries li { margin: 0.8em 1.2em; }
|
||||||
|
.entries h2 { margin-left: -1em; }
|
||||||
|
.add-entry { font-size: 0.9em; }
|
||||||
|
.add-entry dl { font-weight: bold; }
|
||||||
|
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
|
||||||
|
margin-bottom: 1em; background: #fafafa; }
|
||||||
|
.flash { background: #cee5F5; padding: 0.5em;
|
||||||
|
border: 1px solid #aacbe2; }
|
||||||
|
.error { background: #f0d6d6; padding: 0.5em; }
|
||||||
|
.hidden { display: none; }
|
||||||
320
static/table.js
Executable file
320
static/table.js
Executable file
@@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
Table sorting script by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
|
||||||
|
Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
|
||||||
|
Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
|
||||||
|
Edited, removed image support and a tags from headers, Ville Rantanen 2015
|
||||||
|
|
||||||
|
Copyright (c) 1997-2007 Stuart Langridge, Joost de Valk.
|
||||||
|
|
||||||
|
Version 1.5.7
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* You can change these values */
|
||||||
|
var europeandate = true;
|
||||||
|
var alternate_row_colors = false;
|
||||||
|
|
||||||
|
/* Don't change anything below this unless you know what you're doing */
|
||||||
|
addEvent(window, "load", sortables_init);
|
||||||
|
|
||||||
|
var SORT_COLUMN_INDEX;
|
||||||
|
var thead = false;
|
||||||
|
|
||||||
|
function sortables_init() {
|
||||||
|
// Find all tables with class sortable and make them sortable
|
||||||
|
if (!document.getElementsByTagName) return;
|
||||||
|
tbls = document.getElementsByTagName("table");
|
||||||
|
for (ti=0;ti<tbls.length;ti++) {
|
||||||
|
thisTbl = tbls[ti];
|
||||||
|
if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
|
||||||
|
ts_makeSortable(thisTbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ts_makeSortable(t) {
|
||||||
|
if (t.rows && t.rows.length > 0) {
|
||||||
|
if (t.tHead && t.tHead.rows.length > 0) {
|
||||||
|
var firstRow = t.tHead.rows[t.tHead.rows.length-1];
|
||||||
|
thead = true;
|
||||||
|
} else {
|
||||||
|
var firstRow = t.rows[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!firstRow) return;
|
||||||
|
|
||||||
|
// We have a first row: assume it's the header, and make its contents clickable links
|
||||||
|
for (var i=0;i<firstRow.cells.length;i++) {
|
||||||
|
var cell = firstRow.cells[i];
|
||||||
|
var txt = ts_getInnerText(cell);
|
||||||
|
if (cell.className != "unsortable" && cell.className.indexOf("unsortable") == -1) {
|
||||||
|
cell.innerHTML = '<span class="sortheader" onclick="ts_resortTable(this, '+i+');return false;">'+txt+'<span class="sortarrow"> ↕</span></span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alternate_row_colors) {
|
||||||
|
alternate(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ts_getInnerText(el) {
|
||||||
|
if (typeof el == "string") return el;
|
||||||
|
if (typeof el == "undefined") { return el };
|
||||||
|
if (el.innerText) return el.innerText; //Not needed but it is faster
|
||||||
|
var str = "";
|
||||||
|
|
||||||
|
var cs = el.childNodes;
|
||||||
|
var l = cs.length;
|
||||||
|
for (var i = 0; i < l; i++) {
|
||||||
|
switch (cs[i].nodeType) {
|
||||||
|
case 1: //ELEMENT_NODE
|
||||||
|
str += ts_getInnerText(cs[i]);
|
||||||
|
break;
|
||||||
|
case 3: //TEXT_NODE
|
||||||
|
str += cs[i].nodeValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ts_resortTable(lnk, clid) {
|
||||||
|
var span;
|
||||||
|
for (var ci=0;ci<lnk.childNodes.length;ci++) {
|
||||||
|
if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
|
||||||
|
}
|
||||||
|
var spantext = ts_getInnerText(span);
|
||||||
|
var td = lnk.parentNode;
|
||||||
|
var column = clid || td.cellIndex;
|
||||||
|
var t = getParent(td,'TABLE');
|
||||||
|
// Work out a type for the column
|
||||||
|
if (t.rows.length <= 1) return;
|
||||||
|
var itm = "";
|
||||||
|
var i = 0;
|
||||||
|
while (itm == "" && i < t.tBodies[0].rows.length) {
|
||||||
|
var itm = ts_getInnerText(t.tBodies[0].rows[i].cells[column]);
|
||||||
|
itm = trim(itm);
|
||||||
|
if (itm.substr(0,4) == "<!--" || itm.length == 0) {
|
||||||
|
itm = "";
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (itm == "") return;
|
||||||
|
sortfn = ts_sort_caseinsensitive;
|
||||||
|
if (itm.match(/^\d\d[\/\.-][a-zA-z][a-zA-Z][a-zA-Z][\/\.-]\d\d\d\d$/)) sortfn = ts_sort_date;
|
||||||
|
if (itm.match(/^\d\d[\/\.-]\d\d[\/\.-]\d\d\d{2}?$/)) sortfn = ts_sort_date;
|
||||||
|
if (itm.match(/^-?[£$Û¢´]\d/)) sortfn = ts_sort_numeric;
|
||||||
|
if (itm.match(/^-?(\d+[,\.]?)+(E[-+][\d]+)?%?$/)) sortfn = ts_sort_numeric;
|
||||||
|
SORT_COLUMN_INDEX = column;
|
||||||
|
var firstRow = new Array();
|
||||||
|
var newRows = new Array();
|
||||||
|
for (k=0;k<t.tBodies.length;k++) {
|
||||||
|
for (i=0;i<t.tBodies[k].rows[0].length;i++) {
|
||||||
|
firstRow[i] = t.tBodies[k].rows[0][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (k=0;k<t.tBodies.length;k++) {
|
||||||
|
if (!thead) {
|
||||||
|
// Skip the first row
|
||||||
|
for (j=1;j<t.tBodies[k].rows.length;j++) {
|
||||||
|
newRows[j-1] = t.tBodies[k].rows[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Do NOT skip the first row
|
||||||
|
for (j=0;j<t.tBodies[k].rows.length;j++) {
|
||||||
|
newRows[j] = t.tBodies[k].rows[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRows.sort(sortfn);
|
||||||
|
if (span.getAttribute("sortdir") == 'down') {
|
||||||
|
ARROW = ' ↑';
|
||||||
|
newRows.reverse();
|
||||||
|
span.setAttribute('sortdir','up');
|
||||||
|
} else {
|
||||||
|
ARROW = ' ↓';
|
||||||
|
span.setAttribute('sortdir','down');
|
||||||
|
}
|
||||||
|
// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
|
||||||
|
// don't do sortbottom rows
|
||||||
|
for (i=0; i<newRows.length; i++) {
|
||||||
|
if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) {
|
||||||
|
t.tBodies[0].appendChild(newRows[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do sortbottom rows only
|
||||||
|
for (i=0; i<newRows.length; i++) {
|
||||||
|
if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1))
|
||||||
|
t.tBodies[0].appendChild(newRows[i]);
|
||||||
|
}
|
||||||
|
// Delete any other arrows there may be showing
|
||||||
|
var allspans = document.getElementsByTagName("span");
|
||||||
|
for (var ci=0;ci<allspans.length;ci++) {
|
||||||
|
if (allspans[ci].className == 'sortarrow') {
|
||||||
|
if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
|
||||||
|
allspans[ci].innerHTML = ' ↕';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span.innerHTML = ARROW;
|
||||||
|
alternate(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParent(el, pTagName) {
|
||||||
|
if (el == null) {
|
||||||
|
return null;
|
||||||
|
} else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {
|
||||||
|
return el;
|
||||||
|
} else {
|
||||||
|
return getParent(el.parentNode, pTagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sort_date(date) {
|
||||||
|
// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
|
||||||
|
dt = "00000000";
|
||||||
|
if (date.length == 11) {
|
||||||
|
mtstr = date.substr(3,3);
|
||||||
|
mtstr = mtstr.toLowerCase();
|
||||||
|
switch(mtstr) {
|
||||||
|
case "jan": var mt = "01"; break;
|
||||||
|
case "feb": var mt = "02"; break;
|
||||||
|
case "mar": var mt = "03"; break;
|
||||||
|
case "apr": var mt = "04"; break;
|
||||||
|
case "may": var mt = "05"; break;
|
||||||
|
case "jun": var mt = "06"; break;
|
||||||
|
case "jul": var mt = "07"; break;
|
||||||
|
case "aug": var mt = "08"; break;
|
||||||
|
case "sep": var mt = "09"; break;
|
||||||
|
case "oct": var mt = "10"; break;
|
||||||
|
case "nov": var mt = "11"; break;
|
||||||
|
case "dec": var mt = "12"; break;
|
||||||
|
// default: var mt = "00";
|
||||||
|
}
|
||||||
|
dt = date.substr(7,4)+mt+date.substr(0,2);
|
||||||
|
return dt;
|
||||||
|
} else if (date.length == 10) {
|
||||||
|
if (europeandate == false) {
|
||||||
|
dt = date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
|
||||||
|
return dt;
|
||||||
|
} else {
|
||||||
|
dt = date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
} else if (date.length == 8) {
|
||||||
|
yr = date.substr(6,2);
|
||||||
|
if (parseInt(yr) < 50) {
|
||||||
|
yr = '20'+yr;
|
||||||
|
} else {
|
||||||
|
yr = '19'+yr;
|
||||||
|
}
|
||||||
|
if (europeandate == true) {
|
||||||
|
dt = yr+date.substr(3,2)+date.substr(0,2);
|
||||||
|
return dt;
|
||||||
|
} else {
|
||||||
|
dt = yr+date.substr(0,2)+date.substr(3,2);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ts_sort_date(a,b) {
|
||||||
|
dt1 = sort_date(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
|
||||||
|
dt2 = sort_date(ts_getInnerText(b.cells[SORT_COLUMN_INDEX]));
|
||||||
|
|
||||||
|
if (dt1==dt2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (dt1<dt2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
function ts_sort_numeric(a,b) {
|
||||||
|
var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
|
||||||
|
aa = clean_num(aa);
|
||||||
|
var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
|
||||||
|
bb = clean_num(bb);
|
||||||
|
return compare_numeric(aa,bb);
|
||||||
|
}
|
||||||
|
function compare_numeric(a,b) {
|
||||||
|
var a = parseFloat(a);
|
||||||
|
a = (isNaN(a) ? 0 : a);
|
||||||
|
var b = parseFloat(b);
|
||||||
|
b = (isNaN(b) ? 0 : b);
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
function ts_sort_caseinsensitive(a,b) {
|
||||||
|
aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
|
||||||
|
bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
|
||||||
|
if (aa==bb) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (aa<bb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
function ts_sort_default(a,b) {
|
||||||
|
aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
|
||||||
|
bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
|
||||||
|
if (aa==bb) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (aa<bb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
function addEvent(elm, evType, fn, useCapture)
|
||||||
|
// addEvent and removeEvent
|
||||||
|
// cross-browser event handling for IE5+, NS6 and Mozilla
|
||||||
|
// By Scott Andrew
|
||||||
|
{
|
||||||
|
if (elm.addEventListener){
|
||||||
|
elm.addEventListener(evType, fn, useCapture);
|
||||||
|
return true;
|
||||||
|
} else if (elm.attachEvent){
|
||||||
|
var r = elm.attachEvent("on"+evType, fn);
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
alert("Handler could not be removed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function clean_num(str) {
|
||||||
|
str = str.replace(new RegExp(/[^-?0-9.]/g),"");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
function trim(s) {
|
||||||
|
return s.replace(/^\s+|\s+$/g, "");
|
||||||
|
}
|
||||||
|
function alternate(table) {
|
||||||
|
// Take object table and get all it's tbodies.
|
||||||
|
var tableBodies = table.getElementsByTagName("tbody");
|
||||||
|
// Loop through these tbodies
|
||||||
|
for (var i = 0; i < tableBodies.length; i++) {
|
||||||
|
// Take the tbody, and get all it's rows
|
||||||
|
var tableRows = tableBodies[i].getElementsByTagName("tr");
|
||||||
|
// Loop through these rows
|
||||||
|
// Start at 1 because we want to leave the heading row untouched
|
||||||
|
for (var j = 0; j < tableRows.length; j++) {
|
||||||
|
// Check if j is even, and apply classes for both possible results
|
||||||
|
if ( (j % 2) == 0 ) {
|
||||||
|
if ( !(tableRows[j].className.indexOf('odd') == -1) ) {
|
||||||
|
tableRows[j].className = tableRows[j].className.replace('odd', 'even');
|
||||||
|
} else {
|
||||||
|
if ( tableRows[j].className.indexOf('even') == -1 ) {
|
||||||
|
tableRows[j].className += " even";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( !(tableRows[j].className.indexOf('even') == -1) ) {
|
||||||
|
tableRows[j].className = tableRows[j].className.replace('even', 'odd');
|
||||||
|
} else {
|
||||||
|
if ( tableRows[j].className.indexOf('odd') == -1 ) {
|
||||||
|
tableRows[j].className += " odd";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
templates/layout.html
Normal file
29
templates/layout.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8/>
|
||||||
|
<title>Shop</title>
|
||||||
|
<script language="javascript" src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
|
<script language="javascript" src="{{ url_for('static', filename='table.js') }}"></script>
|
||||||
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="HandheldFriendly" content="true" />
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=page>
|
||||||
|
<a href="{{ url_for('list_shops') }}">List shops</a>
|
||||||
|
<a href="#" onclick="reload();">Reload page</a>
|
||||||
|
<a href="{{ url_for('logout') }}">Logout</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div class=metanav>
|
||||||
|
</div>
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<div class=flash>{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
17
templates/list_shops.html
Normal file
17
templates/list_shops.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<table class="sortable" id="shoplist">
|
||||||
|
<tr><th>Shop</th><th>Last edited</th><th>Owner</th></tr>
|
||||||
|
{% for entry in entries %}
|
||||||
|
<tr><td><a href="{{ url_for('show_shop',shopid=entry.shopid) }}">{{ entry.shop }}</a></td><td>{{ entry.date }}</td><td>{{ entry.owner }}</td>
|
||||||
|
{% else %}
|
||||||
|
<tr><td><em>Unbelievable. You dont have any shops</em>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<form action="{{ url_for('add_shop') }}" method=post class=add-entry>
|
||||||
|
<h2>Add new shop:</h2>
|
||||||
|
<input class=submit type=text size=30 name=shop> <input class=submit type=submit>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
15
templates/login.html
Normal file
15
templates/login.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<h2>Login</h2>
|
||||||
|
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
|
||||||
|
<form action="{{ url_for('login') }}" method=post>
|
||||||
|
<dl>
|
||||||
|
<dt>Username:
|
||||||
|
<dd><input type=text name=username>
|
||||||
|
<dt>Password:
|
||||||
|
<dd><input type=password name=password>
|
||||||
|
<dd><input class=submit type=submit value=Login>
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
<!--a href="{{ url_for('register') }}">Register</a-->
|
||||||
|
{% endblock %}
|
||||||
14
templates/register.html
Normal file
14
templates/register.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<h2>Register</h2>
|
||||||
|
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
|
||||||
|
<form action="{{ url_for('register') }}" method=post>
|
||||||
|
<dl>
|
||||||
|
<dt>Username:
|
||||||
|
<dd><input type=text name=username>
|
||||||
|
<dt>Password:
|
||||||
|
<dd><input type=password name=password>
|
||||||
|
<dd><input type=submit value=Login>
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
51
templates/show_shop.html
Normal file
51
templates/show_shop.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<H2># {{ shop }}</H2>
|
||||||
|
<div class=entries>
|
||||||
|
<form action="{{ url_for('toggle_item') }}" method=post class=toggle-item>
|
||||||
|
<input type=hidden name=shopid value="{{ shopid }}" >
|
||||||
|
{% for entry in entries %}
|
||||||
|
<input class="submit tickbox {{ entry.extra_class }}" type=submit name=item{{ entry.row }} value="{{ entry.icon }}" >{{ entry.text }}<br>
|
||||||
|
{% endfor %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Add items:</h2>
|
||||||
|
<form name="area_add" action="{{ url_for('add_items') }}" method=post class=add-entry>
|
||||||
|
<dl>
|
||||||
|
<dt><textarea cols=40 rows=1 id=add_md name=add_md onkeyup="growTextarea('add_md')"></textarea><br><input type=hidden name=shopid value={{ shopid }}><input class=submit type=submit value=Add>
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
<h2 id="h2_remove_toggled" onclick="showtoggle('disp_remove_toggled','h2_remove_toggled')">Remove ticked:</h2>
|
||||||
|
<form id=disp_remove_toggled class=hidden action="{{ url_for('remove_toggled') }}" method=post class=add-entry>
|
||||||
|
<dl><input type=hidden name=shopid value={{ shopid }}><input class=submit type=submit value=Remove>
|
||||||
|
</dl></form>
|
||||||
|
<h2 id="h2_edit" onclick="showtoggle('disp_edit','h2_edit');growTextarea('edit_md');">Edit items:</h2>
|
||||||
|
<form id="disp_edit" class=hidden name="area_edit" action="{{ url_for('edit_md') }}" method=post class=add-entry>
|
||||||
|
<dl>
|
||||||
|
<dt><textarea cols=40 rows=5 id=edit_md name=edit_md onkeyup="growTextarea('edit_md')">{{ content }}</textarea><br>
|
||||||
|
<input type=hidden name=shopid value={{ shopid }}><input class=submit type=submit value=Edit onclick="return confirm('Do you really want to replace content?');" >
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
<h2 id="h2_share" onclick="showtoggle('disp_share','h2_share')">Share shop:</h2>
|
||||||
|
<div class=hidden id=disp_share>
|
||||||
|
<form action="{{ url_for('remove_share') }}" method=post class=toggle-item>
|
||||||
|
<input type=hidden name=shopid value="{{ shopid }}" >
|
||||||
|
<ul>
|
||||||
|
{% for user in shares %}
|
||||||
|
<li> Remove: <input class="submit" type=submit name=user value="{{ user }}" onclick="return confirm('Do you really want to remove {{ user }}?');" ><br>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
<form name="area_share" action="{{ url_for('add_share') }}" method=post class=add-entry>
|
||||||
|
<dl>
|
||||||
|
<dt><input class=submit type=text length=10 name=share><input type=hidden name=shopid value={{ shopid }}><input class=submit type=submit value=Share>
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<h2 id="h2_delete" onclick="showtoggle('disp_delete','h2_delete')">Delete shop:</h2>
|
||||||
|
<form id="disp_delete" class=hidden action="{{ url_for('remove_shop') }}" method=post class=add-entry>
|
||||||
|
<dl><input type=hidden name=shopid value={{ shopid }}><input class=submit type=submit value=Remove onclick="return confirm('Do you really want to remove shop {{ shop }}?');">
|
||||||
|
</dl></form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user