inpos 2016-05-15 10:52:06 +03:00
parent 746da62a26
commit 65774a6d04
13 changed files with 4006 additions and 0 deletions

20
lyb 100644
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
from lybmods import lybrary, lybpqconn, lybpqsession
from lybmods.lybformauth import check_auth
from lybmods import lybcfg
from cherrypy.process.plugins import Daemonizer, DropPrivileges, PIDFile
from pwd import getpwnam
import cherrypy
cherrypy.log.screen = None
lybpqconn.create_schema()
cherrypy.lib.sessions.PgsqlSession = lybpqsession.PgsqlSession
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
cherrypy.config.update({
'server.socket_port': lybcfg.webport,
'server.socket_host': '0.0.0.0',
})
DropPrivileges(cherrypy.engine, umask=0o640, uid=getpwnam(lybcfg.user).pw_uid, gid=getpwnam(lybcfg.user).pw_gid).subscribe()
PIDFile(cherrypy.engine, lybcfg.pid).subscribe()
Daemonizer(cherrypy.engine).subscribe()
cherrypy.quickstart(lybrary.Root())

View File

22
lybmods/lybcfg.py 100644
View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
pid = '/var/www/vhosts/ukamnya.ru/tmp/lyb2.pid'
user = 'lyb2'
dbhost = '91.224.22.151'
webport = 80
dbname = 'inposya5_lyb2'
dbuser = 'lyb2'
dbpass = '1lyb20qwe'
sess_table_name = 'session'
sess_data_table_name = 'session_data'
cat_table_name = 'category'
doc_table_name = 'document'
bin_table_name = 'binaries'
docbin_table_name = 'docbinref'
catdoc_table_name = 'catdocref'
pwd_table_name = 'auth_passwd'
grp_table_name = 'auth_group'
mbrsh_table_name = 'auth_membership'
grpcat_access_table = 'grpcat_access'
grpdoc_access_table = 'grpdoc_access'
pwdcat_access_table = 'pwdcat_access'
pwddoc_access_table = 'pwddoc_access'

View File

@ -0,0 +1,624 @@
# -*- coding: utf-8 -*-
import cherrypy
import lybmods.lybcfg as lybcfg
import lybmods.lybtools as lybtools
from hashlib import sha1
class Doc:
def __init__(self, did):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
self.id = did
@property
def text(self):
self.cursor.execute('SELECT body FROM %s WHERE id = %%s;' % lybcfg.doc_table_name, (self.id,))
return self.cursor.fetchone()[0]
@property
def bins(self):
"""D.keys() -> list of D's keys."""
k = []
self.cursor.execute('''SELECT b.hash FROM {bin} AS b, (SELECT binid FROM {docbin}
WHERE docid = %s) AS d WHERE b.id = d.binid;'''.format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name),
(self.id,))
res = self.cursor.fetchall()
for i in res:
k.append(i[0])
return k
@property
def mtime(self):
self.cursor.execute('SELECT mtime FROM %s WHERE id = %%s;' % lybcfg.doc_table_name, (self.id,))
return self.cursor.fetchone()[0]
@property
def muser(self):
self.cursor.execute('SELECT muser FROM %s WHERE id = %%s;' % lybcfg.doc_table_name, (self.id,))
return Usr(self.cursor.fetchone()[0])
@property
def name(self):
self.cursor.execute('SELECT name FROM %s WHERE id = %%s;' % lybcfg.doc_table_name, (self.id,))
return self.cursor.fetchone()[0]
def setname(self, name):
muser = cherrypy.session.get(lybtools.SESSION_KEY, None)
self.cursor.execute('''UPDATE {doc} SET name = %s, muser = %s WHERE id = %s;'''.format(doc = lybcfg.doc_table_name), (name, muser, self.id))
self.db.commit()
return True
def setcatid(self, catid):
muser = cherrypy.session.get(lybtools.SESSION_KEY, None)
self.cursor.execute('''UPDATE {doc} SET catid = %s, muser = %s WHERE id = %s;'''.format(doc = lybcfg.doc_table_name), (catid, muser, self.id))
self.db.commit()
return True
@property
def cat(self):
self.cursor.execute('SELECT catid FROM %s WHERE id = %%s;' % lybcfg.doc_table_name, (self.id,))
catid = self.cursor.fetchone()
if catid: return Cat(catid[0])
else: return None
@property
def path(self):
def np(p, cid):
p.append(cid)
if cid > 0:
p = np(p, Cat(cid).parent.id)
return p
p = []
p = np(p, self.cat.id)
p.reverse()
return p
@property
def users(self):
"""D.keys() -> list of D's keys."""
n = []
self.cursor.execute('''SELECT b.username FROM {pwd} AS b, (SELECT pid FROM {pwddoc_access}
WHERE docid = %s) AS d WHERE b.id = d.pid;'''.format(pwd = lybcfg.pwd_table_name,
pwddoc_access = lybcfg.pwddoc_access_table),
(self.id,))
res = self.cursor.fetchall()
for i in res:
n.append(i[0])
return n
@property
def groups(self):
"""D.keys() -> list of D's keys."""
n = []
self.cursor.execute('''SELECT b.name FROM {grp} AS b, (SELECT gid FROM {grpdoc_access}
WHERE docid = %s) AS d WHERE b.id = d.gid;'''.format(grp = lybcfg.grp_table_name,
grpdoc_access = lybcfg.grpdoc_access_table),
(self.id,))
res = self.cursor.fetchall()
for i in res:
n.append(i[0])
return n
def __getitem__(self, key):
self.cursor.execute("""\
SELECT b.type, b.body FROM {bin} AS b, (SELECT binid FROM {docbin} WHERE docid = %s) AS d
WHERE b.id = d.binid AND hash = %s;""".format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name), (self.id, key))
try:
res = self.cursor.fetchone()
(ctype, body) = res
return {'type': ctype, 'body': body.tobytes()}
except:
raise KeyError(key)
def __setitem__(self, key, value):
self.cursor.execute('SELECT id FROM %s WHERE hash = %%s;' % lybcfg.bin_table_name, (key, ))
binid = self.cursor.fetchone()
if binid:
binid = binid[0]
self.cursor.execute('SELECT COUNT(*) FROM %s WHERE binid = %%s AND docid = %%s;' % lybcfg.docbin_table_name, (binid, self.id))
count = self.cursor.fetchone()[0]
if count:
return
else:
self.cursor.execute('INSERT INTO %s (hash, type, body) VALUES (%%s, %%s, %%s) RETURNING id;' % lybcfg.bin_table_name,
(key, value['type'], value['body']))
binid = self.cursor.fetchone()[0]
self.cursor.execute('INSERT INTO %s (binid, docid) VALUES (%%s, %%s);' % lybcfg.docbin_table_name, (binid, self.id))
self.db.commit()
def __delitem__(self, key):
self.cursor.execute('DELETE FROM {docbin} WHERE binid = (SELECT id FROM {bin} WHERE hash = %s) AND docid = %s;'.format(docbin = lybcfg.docbin_table_name,
bin = lybcfg.bin_table_name), (key, self.id))
self.db.commit()
def pop(self, key, default = None):
"""Remove the specified key and return the corresponding value.
If key is not found, default is returned if given,
otherwise KeyError is raised.
"""
try:
val = self[key]
except:
if not default:
raise KeyError(key)
else:
val = default
return val
del self[key]
return val
def __contains__(self, key):
self.cursor.execute('SELECT count(*) FROM {docbin} WHERE binid = (SELECT id FROM {bin} WHERE hash = %s) AND docid = %s;'.format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name),
(key, self.id))
return bool(self.cursor.fetchone()[0])
if hasattr({}, 'has_key'):
def has_key(self, key):
"""D.has_key(k) -> True if D has a key k, else False."""
return self.__contains__(key)
def get(self, key, default=None):
"""D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
try:
val = self[key]
return val
except:
return default
def update(self, d):
"""D.update(E) -> None. Update D from E: for k in E: D[k] = E[k]."""
for k in d:
self[k] = d[k]
def setdefault(self, key, default=None):
"""D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D."""
try:
val = self[key]
except:
self[key] = val = default
return val
def clear(self):
"""D.clear() -> None. Remove all items from D."""
self.cursor.execute('DELETE FROM {docbin} WHERE docid = %s;'.format(docbin = lybcfg.docbin_table_name), (self.id,))
self.db.commit()
def keys(self):
"""D.keys() -> list of D's keys."""
k = []
self.cursor.execute('SELECT hash FROM {bin} WHERE id = (SELECT binid FROM {docbin} WHERE docid = %s);'.format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name),
(self.id,))
res = self.cursor.fetchall()
for i in res:
k.append(i[0])
return k
def items(self):
"""D.items() -> list of D's (key, value) pairs, as 2-tuples."""
i = []
self.cursor.execute('SELECT hash, type, body FROM {bin} WHERE id = (SELECT binid FROM {docbin} WHERE docid = %s);'.format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name),
(self.id,))
res = self.cursor.fetchall()
for k, t, v in res:
i.append((k, {'type': t, 'body': str(v)}))
return i
def values(self):
"""D.values() -> list of D's values."""
v = []
self.cursor.execute('SELECT body FROM {bin} WHERE id = (SELECT binid FROM {docbin} WHERE docid = %s);'.format(bin = lybcfg.bin_table_name,
docbin = lybcfg.docbin_table_name),
(self.id,))
res = self.cursor.fetchall()
for i in res:
v.append(str(i[0]))
return v
def __del__(self):
self.db.commit()
self.cursor.close()
self.cursor = None
self.db = None
def __repr__(self):
return '<DocObject %r>' % (self.id,)
class Cat:
def __init__(self, cid = 0):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
self.id = cid
def search(self, query):
if self.id == 0:
self.cursor.execute("""SELECT tab.id FROM (SELECT ts_rank_cd(fts_index, q) as rank, id
FROM %s, plainto_tsquery(%%s) q
WHERE q @@ fts_index ORDER BY rank DESC) AS tab;""" % lybcfg.doc_table_name, (query, ))
else:
self.cursor.execute("""SELECT tab.id FROM (SELECT ts_rank_cd(fts_index, q) AS rank, id
FROM %s, plainto_tsquery(%%s) q
WHERE q @@ fts_index AND catid = %%s ORDER BY rank DESC) AS tab;""" % lybcfg.doc_table_name, (query, self.id))
return [x[0] for x in self.cursor.fetchall()]
@property
def name(self):
if self.id == 0:
return 'Корневой'
self.cursor.execute('SELECT name FROM %s WHERE id = %%s;' % lybcfg.cat_table_name, (self.id,))
return self.cursor.fetchone()[0]
def setname(self, name):
if self.id == 0:
return False
self.cursor.execute('UPDATE %s SET name = %%s WHERE id = %%s;' % lybcfg.cat_table_name, (name, self.id))
self.db.commit()
return True
@property
def parent(self):
if self.id == 0: return Cat(0)
self.cursor.execute('SELECT parent FROM %s WHERE id = %%s;' % lybcfg.cat_table_name, (self.id,))
parid = self.cursor.fetchone()[0]
return Cat(parid)
@property
def path(self):
def np(p, cid):
p.append(cid)
if cid > 0:
p = np(p, Cat(cid).parent.id)
return p
p = []
if self.id == 0: return []
p = np(p, self.parent.id)
p.reverse()
return p
def setparentid(self, parid):
if self.id == 0:
return False
self.cursor.execute('UPDATE %s SET parent = %%s WHERE id = %%s;' % lybcfg.cat_table_name, (parid, self.id))
self.db.commit()
return True
@property
def categories(self):
v = []
self.cursor.execute('SELECT id FROM {cat} WHERE parent = %s ORDER BY name ASC;'.format(cat = lybcfg.cat_table_name), (self.id,))
res = self.cursor.fetchall()
for i in res:
v.append(Cat(i[0]))
return v
def delete(self):
self.cursor.execute('DELETE FROM {cat} WHERE id = %s;'.format(cat = lybcfg.cat_table_name), (self.id,))
self.db.commit()
@property
def documents(self):
v = []
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s ORDER BY name;' % lybcfg.doc_table_name, (self.id,))
res = self.cursor.fetchall()
for i in res:
v.append(Doc(i[0]))
return v
@property
def catcount(self):
self.cursor.execute('SELECT count(*) FROM %s WHERE parent = %%s;' % lybcfg.cat_table_name, (self.id,))
return self.cursor.fetchone()[0]
@property
def doccount(self):
self.cursor.execute('SELECT count(*) FROM %s WHERE catid = %%s;' % lybcfg.doc_table_name, (self.id,))
return self.cursor.fetchone()[0]
def docidbyname(self, name):
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s AND name = %%s' % lybcfg.doc_table_name, (self.id, name))
res = self.cursor.fetchone()
if res:
return res[0]
else:
return None
def __getitem__(self, did):
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s AND id = %%s;' % lybcfg.doc_table_name, (self.id, did))
docid = self.cursor.fetchone()
if docid:
return Doc(docid[0])
else:
raise KeyError(did)
def __setitem__(self, did, value):
muser = cherrypy.session.get(lybtools.SESSION_KEY, None)
self.cursor.execute('SELECT id FROM %s WHERE id = %%s AND catid = %%s;' % lybcfg.doc_table_name, (did, self.id))
docid = self.cursor.fetchone()
if docid:
self.cursor.execute('UPDATE %s SET name = %%s, body = %%s, muser = %%s, mtime = now() WHERE id = %%s;' % lybcfg.doc_table_name, (value['name'],
value['body'],
muser,
docid[0]))
else:
raise KeyError(did)
self.db.commit()
def insert(self, value):
muser = cherrypy.session.get(lybtools.SESSION_KEY, None)
self.cursor.execute('INSERT INTO %s (name, catid, muser, body) VALUES (%%s, %%s, %%s, %%s) RETURNING id;' % lybcfg.doc_table_name,
(value['name'], self.id, muser, value['body']))
did = self.cursor.fetchone()[0]
self.db.commit()
return did
def __delitem__(self, did):
muser = cherrypy.session.get(lybtools.SESSION_KEY, None)
self.cursor.execute('DELETE FROM %s WHERE catid = %%s AND id = %%s;' % lybcfg.doc_table_name, (self.id, did))
self.db.commit()
def pop(self, key, default = None):
"""Remove the specified key and return the corresponding value.
If key is not found, default is returned if given,
otherwise KeyError is raised.
"""
try:
val = self[key]
except:
if not default:
raise KeyError(key)
else:
val = default
return val
del self[key]
return val
def __contains__(self, did):
self.cursor.execute('SELECT count(*) FROM %s WHERE catid = %%s AND id = %%s;' % lybcfg.doc_table_name, (self.id, did))
return bool(self.cursor.fetchone()[0])
if hasattr({}, 'has_key'):
def has_key(self, key):
"""D.has_key(k) -> True if D has a key k, else False."""
return self.__contains__(key)
def get(self, key, default=None):
"""D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
try:
val = self[key]
return val
except:
return default
def update(self, d):
"""D.update(E) -> None. Update D from E: for k in E: D[k] = E[k]."""
for k in d:
self[k] = d[k]
def setdefault(self, key, default=None):
"""D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D."""
try:
val = self[key]
except:
self[key] = val = default
return val
def clear(self):
"""D.clear() -> None. Remove all items from D."""
self.cursor.execute('DELETE FROM %s WHERE catid = %%s;' % lybcfg.doc_table_name, (self.id,))
self.db.commit()
def keys(self):
"""D.keys() -> list of D's keys."""
k = []
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s;' % lybcfg.doc_table_name, (self.id,))
res = self.cursor.fetchall()
for i in res:
k.append(i[0])
return k
def items(self):
"""D.items() -> list of D's (key, value) pairs, as 2-tuples."""
i = []
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s;' % lybcfg.doc_table_name, (self.id,))
res = self.cursor.fetchall()
for did in res:
i.append((did, Doc(did)))
return tuple(i)
def values(self):
"""D.values() -> list of D's values."""
v = []
self.cursor.execute('SELECT id FROM %s WHERE catid = %%s;' % lybcfg.doc_table_name, (self.id,))
res = self.cursor.fetchall()
for i in res:
v.append(Doc(i))
return v
def newcat(self, name):
self.cursor.execute('SELECT count(*) FROM %s WHERE name = %%s AND parent = %%s;' % lybcfg.cat_table_name, (name, self.id))
exist = self.cursor.fetchone()[0]
if exist:
return False
else:
self.cursor.execute('INSERT INTO %s (name, parent) VALUES (%%s, %%s);' % lybcfg.cat_table_name, (name, self.id))
self.db.commit()
return True
def __del__(self):
self.db.commit()
self.cursor.close()
self.cursor = None
self.db = None
def __repr__(self):
return '<CatObject %r>' % (self.id,)
class Usr:
def __init__(self, name):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
self.name = name
def new(self, password, dname = None):
dname = True and dname or self.name
p = sha1(self.name.encode("utf-8"))
p.update(password.encode("utf-8"))
try:
self.cursor.execute('INSERT INTO %s (username, dname, password) VALUES (%%s, %%s, %%s);' % lybcfg.pwd_table_name,
(self.name, dname, p.hexdigest()))
except:
self.db.commit()
return False
self.db.commit()
return True
def delete(self):
self.cursor.execute('DELETE FROM %s WHERE username = %%s;' % lybcfg.pwd_table_name, (self.name,))
self.db.commit()
@property
def uid(self):
self.cursor.execute('SELECT id FROM %s WHERE username = %%s' % lybcfg.pwd_table_name, (self.name,))
uid = self.cursor.fetchone()
if uid:
return uid[0]
else:
raise KeyError(self.name)
@property
def groups(self):
v = []
self.cursor.execute("""SELECT name FROM {grp}
WHERE id = (SELECT gid FROM {mbrsh} WHERE uid = %s)
ORDER BY dname ASC;""".format(grp = lybcfg.grp_table_name, mbrsh = lybcfg.mbrsh_table_name), (self.uid,))
res = self.cursor.fetchall()
for i in res:
v.append(Grp(i[0]))
return v
@property
def fgroups(self):
v = []
self.cursor.execute("""SELECT name FROM {grp}
WHERE id NOT IN (SELECT gid FROM {mbrsh} WHERE uid = %s)
ORDER BY dname ASC;""".format(grp = lybcfg.grp_table_name, mbrsh = lybcfg.mbrsh_table_name), (self.uid,))
res = self.cursor.fetchall()
for i in res:
v.append(Grp(i[0]))
return v
def setpwd(self, pwd):
p = sha1(self.name.encode("utf-8"))
p.update(pwd.encode("utf-8"))
self.cursor.execute('UPDATE %s SET password = %%s WHERE id = %%s;' % lybcfg.pwd_table_name, (p.hexdigest(), self.uid))
self.db.commit()
def setname(self, name):
try:
self.cursor.execute('UPDATE %s SET username = %%s WHERE username = %%s;' % lybcfg.pwd_table_name,
(name, self.name))
except:
self.db.commit()
return False
self.db.commit()
self.name = name
return True
def setdname(self, dname):
self.cursor.execute('UPDATE %s SET dname = %%s WHERE username = %%s;' % lybcfg.pwd_table_name,
(dname, self.name))
self.db.commit()
return True
@property
def dname(self):
self.cursor.execute('SELECT dname FROM %s WHERE username = %%s' % lybcfg.pwd_table_name, (self.name,))
dn = self.cursor.fetchone()
if dn:
return dn[0]
else:
raise KeyError(self.name)
class Grp:
def __init__(self, name):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
self.name = name
def new(self, dname = None):
dname = True and dname or self.name
try:
self.cursor.execute('INSERT INTO %s (name, dname) VALUES (%%s, %%s);' % lybcfg.grp_table_name,
(self.name, dname))
except:
self.db.commit()
return False
self.db.commit()
return True
def delete(self):
self.cursor.execute('DELETE FROM %s WHERE name = %%s;' % lybcfg.grp_table_name, (self.name,))
self.db.commit()
def setname(self, name):
try:
self.cursor.execute('UPDATE %s SET name = %%s WHERE name = %%s;' % lybcfg.grp_table_name,
(name, self.name))
except:
self.db.commit()
return False
self.db.commit()
self.name = name
return True
def setdname(self, dname):
self.cursor.execute('UPDATE %s SET dname = %%s WHERE name = %%s;' % lybcfg.grp_table_name,
(dname, self.name))
self.db.commit()
return True
@property
def gid(self):
self.cursor.execute('SELECT id FROM %s WHERE name = %%s' % lybcfg.grp_table_name, (self.name,))
gid = self.cursor.fetchone()
if gid:
return gid[0]
else:
raise KeyError(self.name)
@property
def members(self):
v = []
self.cursor.execute("""SELECT username FROM {pwd}
WHERE id IN (SELECT uid FROM {mbrsh} WHERE gid = %s)
ORDER BY dname ASC;""".format(pwd = lybcfg.pwd_table_name, mbrsh = lybcfg.mbrsh_table_name), (self.gid,))
res = self.cursor.fetchall()
for i in res:
v.append(Usr(i[0]))
return v
@property
def notmembers(self):
v = []
self.cursor.execute("""SELECT username FROM {pwd}
WHERE id NOT IN (SELECT uid FROM {mbrsh} WHERE gid = %s)
ORDER BY dname ASC;""".format(pwd = lybcfg.pwd_table_name, mbrsh = lybcfg.mbrsh_table_name), (self.gid,))
res = self.cursor.fetchall()
for i in res:
v.append(Usr(i[0]))
return v
@property
def dname(self):
self.cursor.execute('SELECT dname FROM %s WHERE name = %%s' % lybcfg.grp_table_name, (self.name,))
dn = self.cursor.fetchone()
if dn:
return dn[0]
else:
raise KeyError(self.name)
def delmember(self, name):
self.cursor.execute("""DELETE FROM {mbrsh}
WHERE uid = (SELECT id FROM {pwd} WHERE username = %s)
AND gid = %s;""".format(pwd = lybcfg.pwd_table_name, mbrsh = lybcfg.mbrsh_table_name), (name, self.gid))
self.db.commit()
def addmember(self, name):
self.cursor.execute("""SELECT count(*) FROM {mbrsh}
WHERE uid = (SELECT id FROM {pwd} WHERE username = %s)
AND gid = %s;""".format(pwd = lybcfg.pwd_table_name, mbrsh = lybcfg.mbrsh_table_name), (name, self.gid))
count = self.cursor.fetchone()[0]
if count:
return False
else:
self.cursor.execute("""INSERT INTO {mbrsh}
(uid, gid)
VALUES ((SELECT id FROM {pwd} WHERE username = %s), %s);""".format(mbrsh = lybcfg.mbrsh_table_name, pwd = lybcfg.pwd_table_name),
(name, self.gid))
self.db.commit()
return True
class Users:
def __init__(self):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
@property
def list(self):
v = []
self.cursor.execute('SELECT username FROM %s ORDER BY dname;' % lybcfg.pwd_table_name)
res = self.cursor.fetchall()
for i in res:
v.append(Usr(i[0]))
return v
class Groups:
def __init__(self):
self.sess = cherrypy.session
self.db = self.sess.db
self.cursor = self.db.cursor()
@property
def list(self):
v = []
self.cursor.execute('SELECT name FROM %s ORDER BY dname;' % lybcfg.grp_table_name)
res = self.cursor.fetchall()
for i in res:
v.append(Grp(i[0]))
return v

244
lybmods/lybctl.py 100644
View File

@ -0,0 +1,244 @@
# -*- coding: utf-8 -*-
import cherrypy
from lybmods.lybformauth import member_of, name_is, require
from lybmods.lybclasses import Users, Groups, Usr, Grp
import lybmods.lybtools as lybtools
import lybmods.lybhtdata as lybhtdata
class Ctl:
_cp_config = {
'auth.require': []
}
def __init__(self, root):
self.root = root
@cherrypy.expose
def index(self):
raise cherrypy.HTTPRedirect("/")
@cherrypy.expose
def user(self, name = None, err = ''):
name = True and name or cherrypy.session.get(lybtools.SESSION_KEY, None)
html = ''
usr = Usr(name)
dname = usr.dname
grps = usr.groups
if len(grps):
html += '<tr><td><b>Пользователь "' + dname + '[' + name + ']" находится в следующих группах:</b></td></tr>'
for grp in grps:
if member_of('admins')():
html += '<tr><form action="/ctl/grpfromusr"><td></td><td><a href="/ctl/group?name=' + grp.name + '">' + grp.dname + '[' + grp.name + ']</a></td><input type="hidden" name="uname" value="' + name + '"><input type="hidden" name="gname" value="' + grp.name + '"><td><input type="submit" value="Удалить из этой группы"></td></form></tr>\n'
else:
html += '<tr><td>' + grp.dname + '[' + grp.name + ']</td></tr>\n'
else: html += '<tr><td><b>Пользователь "' + dname + '[' + name + ']" не находится ни в каких группах</b></td></tr>'
if member_of('admins')():
l = '<select name="gname">\n{lbody}</select>\n'
lo = '<option value={gname}>{dname}[{gname}]</option>\n'
lopts = ''
fg = usr.fgroups
if len(fg):
for g in usr.fgroups:
lopts += lo.format(gname = g.name, dname = g.dname)
sel = l.format(lbody = lopts)
html += '<tr>\n<form action="/ctl/grptousr">\n<td>Добавить пользователя в группу: <input type="hidden" name="uname" value="' + name + '">\n{sel}<input type="submit" value="Добавить">\n</td>\n</form>\n</tr>\n'.format(sel = sel)
if member_of('admins')() or name_is(name)():
html += '<tr><form action="/ctl/setpwd"><input type="hidden" name="name" value="' + name + '"><td>Новый пароль<input type="password" name="password"><input type="submit" value="Установить"></td></form></tr>'
return self.root.buildhtml('Панель управления[Пользователь "' + name + '"]' + err, lybhtdata.user_body.format(name = name, groups = html))
@cherrypy.expose
def setpwd(self, name = None, password = ''):
if password == '': return self.user(name = name, err = '!!!Пароль не может быть пустым!!!')
name = True and name or cherrypy.session.get(lybtools.SESSION_KEY, None)
if not member_of('admins')() and not name_is(name)(): raise cherrypy.HTTPRedirect("/denied")
usr = Usr(name)
usr.setpwd(password)
return self.user(name, '<Пароль установлен>')
@cherrypy.expose
@require(member_of('admins'))
def useredit(self, submit = None, name = None, err = ''):
if submit and name:
usr = Usr(name)
dname = usr.dname
if submit == 'Переименовать':
html = '<div align="center"><form action="/ctl/renameuser" method="post"><input type="hidden" name="name" value=' + name + '>\nНовое имя пользователя\n<input type="text" name="newname" value="' + name + '">\nНовое отображаемое имя<input type="text" name="newdname" value="' + dname + '"><input type="submit" value="Готово"></form></div><div align="center"><a href="/ctl/users"><i>&larr; Обратно к списку пользователей</i></a></div>'
title, document = ('Панель управления[Переименовать пользователя "' + name + '"]', html)
elif submit == 'Удалить':
html = '<div align="center"><form action="/ctl/deleteuser" method="post"><input type="hidden" name="name" value="' + name + '">Действительно удалить пользователея "' + name + '"?<br><input type="submit" value="Удалить"></form><div align="center"><a href="/ctl/users"><i>&larr; Обратно к списку пользователей</i></a></div>'
title, document = ('Панель управления[Удалить пользователя "' + name + '"]', html)
return self.root.buildhtml(title + err, document)
@cherrypy.expose
@require(member_of('admins'))
def renameuser(self, name = None, newname = '', newdname = ''):
if newname == '': return self.useredit('Переименовать', name, '!!!Имя пользователя не может быть пустым!!!')
if newdname == '': newdname = newname
name = True and name or cherrypy.session.get(lybtools.SESSION_KEY, None)
usr = Usr(name)
dname = usr.dname
if name == newname and dname == newdname: return self.useredit('Переименовать', name, '!!!Данные не изменены!!!')
if name != newname:
if not usr.setname(newname):return self.useredit('Переименовать', name, '!!!Ошибка. Возможно пользователь с таким именем уже существует!!!')
if dname != newdname:
usr.setdname(newdname)
return self.users()
@cherrypy.expose
@require(member_of('admins'))
def deleteuser(self, name = None):
if not name: return
if name_is(name)(): return self.useredit('Удалить', name, '!!!Нельзя удалить своего же пользователея!!!')
usr = Usr(name)
if 'admins' in usr.groups and len(Grp('admins').members) == 1:
return self.useredit('Удалить', name, '!!!Нельзя удалить единственного администратора!!!')
try:
usr.delete()
except:
pass
return self.users()
@cherrypy.expose
@require(member_of('admins'))
def users(self):
usrs = Users()
html = '<div align="center">\n<table>\n'
html += '<tr><td><b><u>Отображаемое имя|</u></b></td><td><b><u>|Имя пользователя</u></b></td>\n'
for user in usrs.list:
name = user.name
dname = user.dname
rec = '<td><a href="/ctl/user?name=' + name + '">' + dname + '</a></td><td><a href="/ctl/user?name=' + name + '">[' + name + ']</a></td>\n'
html += '<tr><form action="/ctl/useredit" method="post">\n{rec}<input type="hidden" name="name" value="'.format(rec = rec) + name + '">\n<td><input type="submit" name="submit" value="Переименовать"></td>\n<td><input type="submit" name="submit" value="Удалить"></td>\n</form></tr>\n'
html += '</table>\n</div>\n'
html += '<div align="right">\n<form action="/ctl/newuser" method="post">\n<input type="submit" value="Создать пользователя">\n</form>\n</div>\n'
return self.root.buildhtml('Панель управления[Пользователи]', html)
@cherrypy.expose
@require(member_of('admins'))
def groups(self):
grps = Groups()
html = '<div align="center">\n<table>\n'
html += '<tr><td><b><u>Отображаемое имя|</u></b></td><td><b><u>|Имя группы</u></b></td>\n'
for grp in grps.list:
name = grp.name
dname = grp.dname
rec = '<td><a href="/ctl/group?name=' + name + '">' + dname + '</a></td><td><a href="/ctl/group?name=' + name + '">[' + name + ']</a></td>\n'
if name == 'admins' or name == 'editors': html += '<tr>%s</tr>' % rec
else: html += '<tr><form action="/ctl/groupedit" method="post">\n{rec}<input type="hidden" name="name" value="'.format(rec = rec) + name + '">\n<td><input type="submit" name="submit" value="Переименовать"></td>\n<td><input type="submit" name="submit" value="Удалить"></td>\n</form></tr>\n'
html += '</table>\n</div>\n'
html += '<div align="right">\n<form action="/ctl/newgroup" method="post">\n<input type="submit" value="Создать группу">\n</form>\n</div>\n'
return self.root.buildhtml('Панель управления[Группы]', html)
@cherrypy.expose
@require(member_of('admins'))
def newuser(self, err = ''):
html = '<div align="left">\n<table>\n'
html += '<tr><form action="/ctl/createuser" method="post">\n<td>Имя пользователя<input type="input" name="name"></td>\n<td>Отображаемое имя<input type="input" name="dname"></td>\n<td>Пароль<input type="password" name="password"></td>\n<td><input type="submit" value="Создать"></td>\n</form></tr>\n'
html += '</table>\n</div>\n'
html += '<div align="right"><a href="/ctl/users">\n<i>&larr; Обратно в список пользователей</i>\n</a>\n</div>\n'
return self.root.buildhtml('Панель управления[Пользователи]' + err, html)
@cherrypy.expose
@require(member_of('admins'))
def newgroup(self, err = ''):
html = '<div align="left">\n<table>\n'
html += '<tr><form action="/ctl/creategroup" method="post">\n<td>Имя группы<input type="input" name="name"></td>\n<td>Отображаемое имя<input type="input" name="dname"></td>\n<td><input type="submit" value="Создать"></td>\n</form></tr>\n'
html += '</table>\n</div>\n'
html += '<div align="right"><a href="/ctl/groups">\n<i>&larr; Обратно в список групп</i>\n</a>\n</div>\n'
return self.root.buildhtml('Панель управления[Группы]' + err, html)
@cherrypy.expose
@require(member_of('admins'))
def createuser(self, name = '', dname = '', password = ''):
if name == '' or password == '': return self.newuser(err = '!!!Имя пользователя или пароль не могут быть пустыми!!!')
if dname == '': dname = name
usr = Usr(name)
if usr.new(password, dname): return self.user(name)
else: return self.newuser(err = '!!!Ошибка. Возможно пользователь с таким именем уже существует!!!')
@cherrypy.expose
@require(member_of('admins'))
def creategroup(self, name = '', dname = ''):
if name == '': return self.newuser(err = '!!!Имя группы не может быть пустыми!!!')
if dname == '': dname = name
grp = Grp(name)
if grp.new(dname): return self.group(name)
else: return self.newgroup(err = '!!!Ошибка. Возможно группа с таким именем уже существует!!!')
@cherrypy.expose
@require(member_of('admins'))
def group(self, name = None, err = ''):
if not name: return
html = ''
grp = Grp(name)
dname = grp.dname
members = grp.members
if len(members):
html += '<tr><td><b>В группе "' + dname + '[' + name + ']" находятся следующие пользователи:</b></td></tr>'
for member in members:
html += '<tr><form action="/ctl/usrfromgrp"><td></td><td><a href="/ctl/user?name=' + member.name + '">' + member.dname + '[' + member.name + ']</a></td><input type="hidden" name="uname" value="' + member.name + '"><input type="hidden" name="gname" value="' + name + '"><td><input type="submit" value="Удалить из группы"></td></form></tr>\n'
else: html += '<tr><td><b>В группе "' + dname + '[' + name + ']" нет ни одного пользователя</b></td></tr>'
l = '<select name="uname">\n{lbody}</select>\n'
lo = '<option value={uname}>{udname}[{uname}]</option>\n'
lopts = ''
nm = grp.notmembers
if len(nm):
for u in nm:
lopts += lo.format(uname = u.name, udname = u.dname)
sel = l.format(lbody = lopts)
html += '<tr>\n<form action="/ctl/usrtogrp">\n<td>Добавить в эту группу пользователя: <input type="hidden" name="gname" value="' + name + '">\n{sel}<input type="submit" value="Добавить">\n</td>\n</form>\n</tr>\n'.format(sel = sel)
return self.root.buildhtml('Панель управления[Группа "' + name + '"]' + err, lybhtdata.group_body.format(name = name, groups = html))
@cherrypy.expose
@require(member_of('admins'))
def groupedit(self, submit = None, name = None, err = ''):
if submit and name:
grp = Grp(name)
dname = grp.dname
if submit == 'Переименовать':
html = '<div align="center"><form action="/ctl/renamegroup" method="post"><input type="hidden" name="name" value=' + name + '>\nНовое имя группы\n<input type="text" name="newname" value="' + name + '">\nНовое отображаемое имя<input type="text" name="newdname" value="' + dname + '"><input type="submit" value="Готово"></form></div><div align="center"><a href="/ctl/groups"><i>&larr; Обратно к списку групп</i></a></div>'
title, document = ('Панель управления[Переименовать группу "' + name + '"]' + err, html)
elif submit == 'Удалить':
html = '<div align="center"><form action="/ctl/deletegroup" method="post"><input type="hidden" name="name" value="' + name + '">Действительно удалить группу "' + name + '"?<br><input type="submit" value="Удалить"></form><div align="center"><a href="/ctl/groups"><i>&larr; Обратно к списку групп</i></a></div>'
title, document = ('Панель управления[Удалить группу "' + name + '"]', html)
return self.root.buildhtml(title + err, document)
@cherrypy.expose
@require(member_of('admins'))
def renamegroup(self, name = None, newname = '', newdname = ''):
if newname == '': return self.groupedit('Переименовать', name, '!!!Имя группы не может быть пустым!!!')
if not name or name =='' or name == 'admins' or name == 'editors': return
if newdname == '': newdname = newname
grp = Grp(name)
dname = grp.dname
if name == newname and dname == newdname: return self.groupedit('Переименовать', name, '!!!Данные не изменены!!!')
if name != newname:
if not grp.setname(newname): return self.groupedit('Переименовать', name, '!!!Ошибка. Возможно группа с таким именем уже существует!!!')
if dname != newdname:
grp.setdname(newdname)
return self.group()
@cherrypy.expose
@require(member_of('admins'))
def deletegroup(self, name = None):
if not name or name =='' or name == 'admins' or name == 'editors': return
grp = Grp(name)
try:
grp.delete()
except:
pass
return self.groups()
@cherrypy.expose
@require(member_of('admins'))
def grpfromusr(self, uname = '', gname = ''):
if uname == '' or gname == '': return
grp = Grp(gname)
if gname == 'admins' and len(Grp('admins').members) == 1:
return self.user(uname, '!!!Нельзя удалить единственного пользователя из группы администраторов!!!')
grp.delmember(uname)
return self.user(uname)
@cherrypy.expose
@require(member_of('admins'))
def usrfromgrp(self, uname = '', gname = ''):
if uname == '' or gname == '': return
grp = Grp(gname)
if gname == 'admins' and len(Grp('admins').members) == 1:
return self.group(gname, '!!!Нельзя удалить единственного пользователя из группы администраторов!!!')
grp.delmember(uname)
return self.group(gname)
@cherrypy.expose
@require(member_of('admins'))
def grptousr(self, uname ='', gname = ''):
if uname == '' or gname == '': return
grp = Grp(gname)
if not grp.addmember(uname): return self.user(uname, '!!!Пользователь уже в группе "' + grp.dname + '[' + gname + ']"!!!')
return self.user(uname)
@cherrypy.expose
@require(member_of('admins'))
def usrtogrp(self, uname ='', gname = ''):
if uname == '' or gname == '': return
grp = Grp(gname)
if not grp.addmember(uname): return self.group(gname, '!!!Пользователь "' + Usr(uname).dname + '[' + uname + ']"уже в этой группе!!!')
return self.group(uname)

235
lybmods/lybedit.py 100644
View File

@ -0,0 +1,235 @@
# -*- coding: utf-8 -*-
from lybmods.lybformauth import member_of, any_of
from urllib.request import FancyURLopener
from lybmods.lybclasses import Cat
from random import random
from lybmods import lybshared
import lybmods.lybhtdata as lybhtdata
import cherrypy
import lybmods.lybtools as lybtools
class URLOpener(FancyURLopener):
version = 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0.6) Gecko/20100101 Firefox/10.0.6 Iceweasel/10.0.6'
class Edit:
'''Управление хранилищем'''
_cp_config = {
'auth.require': [any_of(member_of('admins'),member_of('editors'))]
}
def __init__(self, root):
self.root = root
@cherrypy.expose
def index(self):
cherrypy.HTTPRedirect("/")
@cherrypy.expose
def nejs(self):
return lybhtdata.ne_js()
@cherrypy.expose
def negif(self):
response = cherrypy.response
response.headers['Content-Type'] = 'image/gif'
return lybhtdata.ne_gif()
@cherrypy.expose
def upl(self, catid = 0):
catid = int(catid)
cat = Cat(catid)
catname = cat.name
return self.root.buildhtml('Архив [Загрузка документа в архив]', lybhtdata.form_upload.format(catid = catid, catname = catname) + '<a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + catname + '"</i>\n</a>\n')
@cherrypy.expose
def upload(self, catid = 0, filein=''):
if catid: catid = int(catid)
url = filein
try:
res = URLOpener().open(url)
except:
cat = Cat(catid)
catname = cat.name
return self.root.buildhtml('Архив [Загрузка документа в архив]', lybhtdata.form_upload.format(catid = catid, catname = catname) + '<div class="document" style="color: red; ">Ошибка загрузки документа</div><div><a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + catname + '"</i>\n</a>\n</div>')
return self.root.buildhtml('Архив [Обработка файла закончена]', self.fileproc(catid, res.url, lybtools.ctype(res.headers['Content-Type']), res.read()))
@cherrypy.expose
def tmpstore(self, lybsrcobj=None):
sess = cherrypy.session
cherrypy.response.headers["Content-Type"] = sess[lybsrcobj]['type']
res = sess[lybsrcobj]['body']
try:
return res
except:
pass
def fileproc(self, catid, url, ctype, htfile):
if ctype == 'text/html':
return self.html(catid, url, htfile)
if ctype == 'text/plain':
htxt = self.txt(htfile)
return self.html(catid, url, htxt)
def txt(self, text):
text = "".join(lybtools.edit_esc.get(c,c) for c in text)
return text.replace('\n', '<br />\n')
def html(self, catid, url, htfile, md = None, sub = False, ins = False):
return lybshared.html(self, catid, url, htfile, md, sub, ins)
def ne(self, catid, url = None, html = None, md = None, docname = "Новый документ"):
return lybshared.ne(self, catid, url, html, md, docname)
@cherrypy.expose
def newcat(self, catid = 0, catname = None):
catid = int(catid)
category = Cat(catid)
if not catname or catname == '':
return self.root.buildhtml('Архив[Ошибка создания раздела]', '<p>Имя раздела не должно быть пустым</p>\n<div align="center"><a href="/category?catid=' + str(category.id) + '">\n<i>&larr; Обратно в раздел "' + category.name + '"</i>\n</a>\n</div>')
catname = catname.strip()
if category.newcat(catname):
return self.root.category(catid = catid)
else:
return self.root.buildhtml('Архив[Ошибка создания раздела]', '<p>Ошибка создания раздела "' + catname + '"</p>\n<div align="center"><a href="/category?catid=' + str(category.id) + '">\n<i>&larr; Обратно в раздел "' + category.name + '"</i>\n</a>\n</div>')
@cherrypy.expose
def catrename(self, catid = None, catname = None):
if catid: catid = int(catid)
if not catname: return self.catedit(submit = 'Переименовать', catid = catid, err = '!!!Пустое имя раздела!!!')
catname = catname.strip()
cat = Cat(catid)
parent = cat.parent
for subc in parent.categories:
if subc.name == catname: return self.catedit(submit = 'Переименовать', catid = catid, err = '!!!В разделе "' + parent.name + '" есть подраздел с именем "' + catname + '"!!!')
if catname == cat.name:
return self.catedit(submit = 'Переименовать', catid = catid, err = '!!!Исходное имя равно новому!!!')
cat.setname(catname)
return self.root.category(catid = parent.id)
@cherrypy.expose
def catdelete(self, catid = None):
if catid: catid = int(catid)
cat = Cat(catid)
parent = cat.parent
cat.delete()
return self.root.category(catid = parent.id)
@cherrypy.expose
def catmove(self, catid = None, toid = None):
if not catid: return
if not toid: return self.catedit(submit = 'Перенести', catid = catid, err = '!!!Не выбран раздел!!!')
catid = int(catid)
toid = int(toid)
cat = Cat(catid)
tocat = Cat(toid)
if catid == toid: return self.catedit(submit = 'Перенести', catid = catid, err = '!!!Нельзя перенести себя в себя!!!')
if toid == cat.parent.id: return self.catedit(submit = 'Перенести', catid = catid, err = '!!!Раздел уже в этом месте!!!')
if cat.name in [x.name for x in tocat.categories]: return self.catedit(submit = 'Перенести', catid = catid, err = '!!!В разделе "' + cat.parent.name + '" уже есть подраздел с таким именем!!!')
cat.setparentid(toid)
return self.root.category(catid = cat.parent.id)
@cherrypy.expose
def catedit(self, submit = None, catid = None, err = ''):
if submit and catid:
catid = int(catid)
cat = Cat(catid)
catname = cat.name
parent = cat.parent
if submit == 'Переименовать':
html = '<div align="center"><form action="/edit/catrename" method="post"><input type="hidden" name="catid" value=' + str(catid) + '>Сменить имя раздела "' + catname + '" на <input type="text" name="catname" value="' + catname + '"><input type="submit" value="Готово"></form></div><div align="center"><a href="/category?catid=' + str(parent.id) + '"><i>&larr; Обратно в раздел "' + parent.name + '"</i></a></div>'
title, document = ('Архив[Переименовать раздел "' + catname + '"]' + err, html)
elif submit == 'Перенести':
l = '<select name="toid">\n{lbody}</select>\n'
lo = '<option value={id}>{name}</option>\n'
lopts = ''
def lgen(lopts, mod = '', cid = 0):
c = Cat(cid)
lopts += lo.format(id = cid, name = mod + c.name)
for subcat in c.categories:
if subcat.id == catid: continue
lopts = lgen(lopts, mod = mod + '...', cid = subcat.id)
return lopts
lopts = lgen(lopts)
sel = l.format(lbody = lopts)
html = '<div align="center"><form action="/edit/catmove" method="post"><input type="hidden" name="catid" value=' + str(catid) + '>Переместить раздел "' + catname + '" в\n{sel}<input type="submit" value="Готово"></form></div><div align="center"><a href="/category?catid='.format(sel = sel) + str(parent.id) + '"><i>&larr; Обратно в раздел "' + parent.name + '"</i></a></div>'
title, document = ('Архив[Переместить раздел "' + catname + '"]' + err, html)
elif submit == 'Удалить':
html = '<div align="center"><form action="/edit/catdelete" method="post"><input type="hidden" name="catid" value="' + str(catid) + '">Действительно удалить раздел "' + catname + '"?<br>Все хранящиеся в нём подразделы и документы будут перенесены в родительский раздел.<br><input type="submit" value="Удалить"></form><div align="center"><a href="/category?catid=' + str(parent.id) + '"><i>&larr; Обратно в раздел "' + parent.name + '"</i></a></div>'
title, document = ('Архив[Удалить раздел "' + catname + '"]', html)
return self.root.buildhtml(title, document)
@cherrypy.expose
def insert_doc(self, catid = 0, url = None, html = None, doc_name = '', md = None):
return lybshared.insert_doc(self, catid, url, html, doc_name, md)
@cherrypy.expose
def docedit(self, catid = None, doc = None, submit = None, err = ''):
if catid: catid = int(catid)
if doc: doc = int(doc)
if not doc or not submit: return
cat = Cat(catid)
doco = cat[doc]
catname = cat.name
docname = doco.name
if submit == 'Редактировать':
return self.root.buildhtml('Архив[Редактирование документа "' + docname + '"]', self.html(catid, '/', self.root.get_doc(doc=doc, catid=catid, plain=True), md = str(doc)))
if submit == 'Переименовать':
html = '<div align="center">\n<form action="/edit/docrename" method="post">\n<input type="hidden" name="catid" value=' + str(catid) + '>\n<input type="hidden" name="doc" value=' + str(doc) + '>\nСменить имя документа "' + docname + '" на <input type="text" name="doc_name" value="' + docname + '">\n<input type="submit" value="Готово">\n</form>\n</div>\n<div align="center">\n<a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + catname + '"</i>\n</a>\n</div>'
title, document = ('Архив[Переименовать документ "' + doco.name + '"]' + err, html)
elif submit == 'Удалить':
shtml = '<div align="center">\nУдалить документ "' + docname + '"?\n<form action="/edit/docdelete" method="post">\n<input type="hidden" name="catid" value=' + str(catid) + '>\n<input type="hidden" name="doc" value="' + str(doc) + '">\n<input type="submit" value="Удалить документ">\n</form>\n<br>\n<a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + catname + '"</i>\n</a>\n</div>'
title, document = ('Архив[Удалить Документ "' + doco.name + '"]' + err, shtml)
elif submit == 'Перенести':
if len(Cat(0).categories) == 0:
shtml = '<div align="center">Других разделов нет\n<br><a href="/category?catid=' + str(catid) + '"><i>&larr; Обратно в раздел "' + catname + '"</i></a></div>'
else:
l = '<select name="toid">\n{lbody}</select>\n'
lo = '<option value={id}>{name}</option>\n'
lopts = ''
def lgen(lopts, mod = '', cid = 0):
c = Cat(cid)
lopts += lo.format(id = cid, name = mod + c.name)
for subcat in c.categories:
lopts = lgen(lopts, mod = mod + '...', cid = subcat.id)
return lopts
lopts = lgen(lopts)
sel = l.format(lbody = lopts)
shtml = '<div align="center">Перенести документ "' + docname + '"<form action="/edit/docmove" method="post">\n<input type="hidden" name="catid" value=' + str(catid) + '><input type="hidden" name="doc" value="' + str(doc) + '">в категорию ' + sel + ' ?<br><br><input type="submit" value="Перенести"></form>\n<br><a href="/category?catid=' + str(catid) + '"><i>&larr; Обратно в раздел "' + catname + '"</i></a></div>'
title, document = ('Архив[Перенос документов]' + err, shtml)
return self.root.buildhtml(title, document)
@cherrypy.expose
def docdelete(self, catid = None, doc = None):
if not catid or not doc: return
catid = int(catid)
doc = int(doc)
cat = Cat(catid)
try:
del cat[doc]
except:
doco = cat[doc]
docname = doco.name
return self.root.buildhtml('Архив[Документ "' + docname + '" не удалён]', '<div align="center">Документ "' + docname + '" не удалён.<br><a href="/category?catid=' + str(catid) + '"><i>&larr; Обратно в раздел "' + cat.name + '"</i></a></div>')
return self.root.category(catid = catid)
@cherrypy.expose
def docrename(self, catid = None, doc = None, doc_name = None):
if catid: catid = int(catid)
if not doc_name or not doc: return self.docedit(submit = 'Переименовать', catid = catid, doc = doc, err = '!!!Пустое имя документа!!!')
doc = int(doc)
doc_name = doc_name.strip()
cat = Cat(catid)
doco = cat[doc]
if doc_name == doco.name:
return self.docedit(submit = 'Переименовать', catid = catid, doc = doc, err = '!!!Исходное имя равно новому!!!')
for docs in cat.documents:
if docs.name == doc_name: return self.docedit(submit = 'Переименовать', catid = catid, doc = doc, err = '!!!В разделе "' + cat.name + '" есть документ с именем "' + doc_name + '"!!!')
doco.setname(doc_name)
return self.root.category(catid = catid)
@cherrypy.expose
def newdoc(self, catid = 0):
url = 'http://new.doc/' + (str(random() * int(random() * 10)) + str(random() * int(random() * 10)) + str(random() * int(random() * 10))).replace('.', '')
return self.root.buildhtml('Архив[Новый документ]', self.fileproc(catid, url, 'text/html', '<body><br></body>'))
@cherrypy.expose
def docmove(self, catid = None, doc = None, toid = None):
if not catid or not doc: return
if not toid: return self.docedit(submit = 'Перенести', catid = catid, doc = doc, err = '!!!Не выбран раздел!!!')
catid = int(catid)
doc = int(doc)
toid = int(toid)
cat = Cat(catid)
tocat = Cat(toid)
doco = cat[doc]
catname = cat.name
tocatname = tocat.name
docname = doco.name
if catid == toid: return self.docedit(submit = 'Перенести', catid = catid, doc = doc, err = '!!!Документ "' + docname + '" уже в разделе "' + catname + '"!!!')
if toid == catid: return self.docedit(submit = 'Перенести', catid = catid, doc = doc, err = '!!!Раздел уже в этом месте!!!')
if doc in list(tocat.keys()): return self.docedit(submit = 'Перенести', catid = catid, doc = doc, err = '!!!В разделе "' + tocatname + '" уже есть документ с таким содержимым!!!')
if tocat.docidbyname(doco.name): return self.docedit(submit = 'Перенести', catid = catid, doc = doc, err = '!!!В разделе "' + tocatname + '" уже есть документ с именем"' + docname + '"!!!')
doco.setcatid(toid)
return self.root.category(catid = catid)

View File

@ -0,0 +1,138 @@
import cherrypy
import hashlib
import urllib.parse
import lybmods.lybtools as lybtools
import lybmods.lybpqconn as lybpqconn
from lybmods.lybclasses import Grp
def check_credentials(username, password):
"""Проверяет имя пользователи и пароль.
Возвращает None при успехе иначе строку с описанием ошибки"""
cur = lybpqconn.conn.cursor()
newhash = hashlib.sha1()
newhash.update(username.encode("utf-8"))
newhash.update(password.encode("utf-8"))
cur.execute(lybtools.qChkUserAndPwd, (username, newhash.hexdigest()))
res = cur.fetchone()[0]
if res > 0:
return None
else:
return "Incorrect username or password."
def check_auth(*args, **kwargs):
"""A tool that looks in config for 'auth.require'. If found and it
is not None, a login is required and the entry is evaluated as alist of
conditions that the user must fulfill"""
conditions = cherrypy.request.config.get('auth.require', None)
# format GET params
get_parmas = urllib.parse.quote(cherrypy.request.request_line.split()[1])
if conditions is not None:
username = cherrypy.session.get(lybtools.SESSION_KEY)
if username:
cherrypy.request.login = username
for condition in conditions:
# A condition is just a callable that returns true orfalse
if not condition():
# Send old page as from_page parameter
raise cherrypy.HTTPRedirect("/denied")
else:
# Send old page as from_page parameter
raise cherrypy.HTTPRedirect("/loginpage?from_page=%s" % get_parmas)
#cherrypy.lybtools.auth = cherrypy.Tool('before_handler', check_auth)
def require(*conditions):
"""A decorator that appends conditions to the auth.require config
variable."""
def decorate(f):
if not hasattr(f, '_cp_config'):
f._cp_config = dict()
if 'auth.require' not in f._cp_config:
f._cp_config['auth.require'] = []
f._cp_config['auth.require'].extend(conditions)
return f
return decorate
# Conditions are callables that return True
# if the user fulfills the conditions they define, False otherwise
#
# They can access the current username as cherrypy.request.login
#
# Define those at will however suits the application.
def member_of(groupname):
def check():
username = cherrypy.session.get(lybtools.SESSION_KEY, None)
try:
if username and username in [x.name for x in Grp(groupname).members]:
return True
else:
return False
except KeyError:
return False
return check
def name_is(reqd_username):
return lambda: reqd_username == cherrypy.session.get(lybtools.SESSION_KEY, None)
# These might be handy
def any_of(*conditions):
"""Returns True if any of the conditions match"""
def check():
for c in conditions:
if c():
return True
return False
return check
# By default all conditions are required, but this might still be
# needed if you want to use it inside of an any_of(...) condition
def all_of(*conditions):
"""Returns True if all of the conditions match"""
def check():
for c in conditions:
if not c():
return False
return True
return check
# Controller to provide login and logout actions
class AuthController(object):
def on_login(self, username):
"""Called on successful login"""
def on_logout(self, username):
"""Called on logout"""
@cherrypy.expose
def login(self, username=None, password=None, from_page="/"):
if username is None or password is None:
raise cherrypy.HTTPRedirect("/")
error_msg = check_credentials(username, password)
if error_msg:
raise cherrypy.HTTPRedirect("/loginpage?from_page=" + from_page)
else:
cherrypy.session.regenerate()
cherrypy.session[lybtools.SESSION_KEY] = cherrypy.request.login = username
self.on_login(username)
raise cherrypy.HTTPRedirect(from_page or "/")
@cherrypy.expose
def logout(self, from_page="/"):
sess = cherrypy.session
username = sess.get(lybtools.SESSION_KEY, None)
# for key in sess.keys():
# del sess[key]
# sess[lybtools.SESSION_KEY] = None
sess.regenerate()
sess.delete()
if username:
cherrypy.request.login = None
self.on_logout(username)
raise cherrypy.HTTPRedirect("/")

1686
lybmods/lybhtdata.py 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import psycopg2
import psycopg2.extensions
import lybmods.lybcfg as lybcfg
import lybmods.lybtools as lybtools
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
conn = psycopg2.connect(database=lybcfg.dbname, user=lybcfg.dbuser, password=lybcfg.dbpass, host=lybcfg.dbhost)
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
def create_schema():
cur = conn.cursor()
d = {
lybcfg.pwd_table_name: lybtools.AUTH_SCHEMA,
lybcfg.sess_table_name: lybtools.SESS_SCHEMA,
lybcfg.cat_table_name: lybtools.CAT_SCHEMA,
lybcfg.doc_table_name: lybtools.DOC_SCHEMA,
lybcfg.bin_table_name: lybtools.BIN_SCHEMA,
lybcfg.docbin_table_name: lybtools.DOCBIN_SCHEMA,
lybcfg.grpcat_access_table: lybtools.GRPCAT_ACCESS_SCHEMA,
lybcfg.grpdoc_access_table: lybtools.GRPDOC_ACCESS_SCHEMA,
lybcfg.pwdcat_access_table: lybtools.PWDCAT_ACCESS_SCHEMA,
lybcfg.pwddoc_access_table: lybtools.PWDDOC_ACCESS_SCHEMA
}
for k in d.keys():
try:
cur.execute('SELECT COUNT(*) FROM %s' % k)
except:
cur.execute(d[k])
conn.commit()

View File

@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-
import cherrypy
import logging
import threading
import psycopg2
import lybmods.lybcfg as lybcfg
import pickle as pickle
import lybmods.lybtools as lybtools
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
logger = logging.getLogger('Session')
class PgsqlSession(cherrypy.lib.sessions.Session):
sess_table_name = lybcfg.sess_table_name
sess_data_table_name = lybcfg.sess_data_table_name
connect_arguments = "dbname={0} user={1} password={2} host={3}".format(lybcfg.dbname, lybcfg.dbuser, lybcfg.dbpass, lybcfg.dbhost)
missing = False
"True if the session requested by the client did not exist."
_database = None
def __init__(self, sid=None, **kwargs):
logger.debug('Initializing PgsqlSession with %r' % kwargs)
for k, v in list(kwargs.items()):
setattr(PgsqlSession, k, v)
self.db = self.get_db()
self.cursor = self.db.cursor()
super(PgsqlSession, self).__init__(sid, **kwargs)
@classmethod
def get_db(cls):
##
## Use thread-local connections
local = threading.local()
if hasattr(local, 'db'):
return local.db
else:
logger.debug("Connecting to %r" % cls.connect_arguments)
db = psycopg2.connect(cls.connect_arguments)
local.db = db
return db
def load(self):
"""Copy stored session data into this session instance."""
exptime = self._load()
# data is either None or a tuple (session_data, expiration_time)
if exptime is None or exptime < self.now().utcnow():
if self.debug:
cherrypy.log('Expired session, flushing data', 'TOOLS.SESSIONS')
self.regenerate()
self.loaded = True
# Stick the clean_thread in the class, not the instance.
# The instances are created and destroyed per-request.
cls = self.__class__
if self.clean_freq and not cls.clean_thread:
# clean_up is in instancemethod and not a classmethod,
# so that tool config can be accessed inside the method.
t = cherrypy.process.plugins.Monitor(
cherrypy.engine, self.clean_up, self.clean_freq * 60,
name='Session cleanup')
t.subscribe()
cls.clean_thread = t
t.start()
def _load(self):
logger.debug('_load %r' % self)
# Select session data from table
self.cursor.execute('select expiration_time from %s '
'where id = %%s' % PgsqlSession.sess_table_name, (self.id,))
row = self.cursor.fetchone()
if row:
expiration_time = row[0].utcfromtimestamp(row[0].timestamp())
return expiration_time
else:
return None
def _save(self, expiration_time):
logger.debug('_save %r' % self)
self.cursor.execute('select count(*) from %s where id = %%s and expiration_time > now()' % PgsqlSession.sess_table_name, (self.id,))
(count,) = self.cursor.fetchone()
if count:
self.cursor.execute('update %s set expiration_time = %%s where id = %%s' % PgsqlSession.sess_table_name,
(expiration_time, self.id))
else:
self.cursor.execute('insert into %s (expiration_time, id) values (%%s, %%s)' % PgsqlSession.sess_table_name,
(expiration_time, self.id))
self.db.commit()
def acquire_lock(self):
logger.debug('acquire_lock %r' % self)
self.locked = True
self.cursor.execute('select id from %s where id = %%s for update' % PgsqlSession.sess_table_name,
(self.id,))
self.db.commit()
def release_lock(self):
logger.debug('release_lock %r' % self)
self.locked = False
self.db.commit()
def clean_up(self):
logger.debug('clean_up %r' % self)
self.cursor.execute('DELETE FROM %s WHERE expiration_time < now()' % PgsqlSession.sess_table_name)
self.db.commit()
def _delete(self):
logger.debug('_delete %r' % self)
self.cursor.execute('DELETE FROM %s WHERE id=%%s' % PgsqlSession.sess_table_name, (self.id,))
self.db.commit()
def _exists(self):
# Select session data from table
self.cursor.execute('select count(*) from %s '
'where id = %%s and expiration_time > now()' % PgsqlSession.sess_table_name, (self.id,))
(count,) = self.cursor.fetchone()
logger.debug('_exists %r (%r)' % (self, bool(count)))
return bool(count)
def __getitem__(self, key):
if not self.loaded: self.load()
self.cursor.execute('SELECT value FROM %s WHERE key=%%s AND sid=%%s;' % PgsqlSession.sess_data_table_name, (key, self.id))
try:
res = self.cursor.fetchall()[0][0]
val = pickle.loads(res)
return val
except:
raise KeyError(key)
def __setitem__(self, key, value):
if not self.loaded: self.load()
self.cursor.execute('SELECT count(*) FROM %s WHERE key = %%s AND sid = %%s;' % PgsqlSession.sess_data_table_name, (key, self.id))
count = self.cursor.fetchone()[0]
if count:
self.cursor.execute('UPDATE %s SET value = %%s WHERE key = %%s AND sid = %%s;' % PgsqlSession.sess_data_table_name,
(pickle.dumps(value), key, self.id))
else:
self.cursor.execute('INSERT INTO %s (value, key, sid) VALUES (%%s, %%s, %%s);' % PgsqlSession.sess_data_table_name,
(pickle.dumps(value), key, self.id))
self.db.commit()
def __delitem__(self, key):
if not self.loaded: self.load()
self.cursor.execute('DELETE FROM %s WHERE key = %%s AND sid = %%s;' % PgsqlSession.sess_data_table_name, (key, self.id))
self.db.commit()
def pop(self, key, default=None):
"""Remove the specified key and return the corresponding value.
If key is not found, default is returned if given,
otherwise KeyError is raised.
"""
if not self.loaded: self.load()
try:
val = self[key]
except:
if default is None:
raise KeyError(key)
else:
val = default
return val
del self[key]
return val
def __contains__(self, key):
if not self.loaded: self.load()
self.cursor.execute('SELECT count(*) FROM %s WHERE key = %%s AND sid = %%s;' % PgsqlSession.sess_data_table_name, (key, self.id))
return bool(self.cursor.fetchone()[0])
if hasattr({}, 'has_key'):
def has_key(self, key):
"""D.has_key(k) -> True if D has a key k, else False."""
if not self.loaded: self.load()
return self.__contains__(key)
def get(self, key, default=None):
"""D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
if not self.loaded: self.load()
try:
val = self[key]
return val
except:
return default
def update(self, d):
"""D.update(E) -> None. Update D from E: for k in E: D[k] = E[k]."""
if not self.loaded: self.load()
for k in d:
self[k] = d[k]
def setdefault(self, key, default=None):
"""D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D."""
if not self.loaded: self.load()
try:
val = self[key]
except:
self[key] = val = default
return val
def clear(self):
"""D.clear() -> None. Remove all items from D."""
if not self.loaded: self.load()
self.cursor.execute('DELETE FROM %s WHERE sid=%%s AND key != %%s;' % PgsqlSession.sess_data_table_name, (self.id, lybtools.SESSION_KEY))
self.db.commit()
def keys(self):
"""D.keys() -> list of D's keys."""
if not self.loaded: self.load()
k = []
self.cursor.execute('SELECT key FROM %s WHERE sid = %%s;' % PgsqlSession.sess_data_table_name, (self.id,))
res = self.cursor.fetchall()
if res != []:
for i in res:
k.append(i[0])
return k
def items(self):
"""D.items() -> list of D's (key, value) pairs, as 2-tuples."""
if not self.loaded: self.load()
i = []
self.cursor.execute('SELECT key, value FROM %s WHERE sid = %%s;' % PgsqlSession.sess_data_table_name, (self.id,))
res = self.cursor.fetchall()
if res != []:
for k, v in res:
i.append({k, pickle.loads(v)})
return i
def values(self):
"""D.values() -> list of D's values."""
if not self.loaded: self.load()
v = []
self.cursor.execute('SELECT value FROM %s WHERE sid = %%s;' % PgsqlSession.sess_data_table_name, (self.id,))
res = self.cursor.fetchall()
if res != []:
for i in res:
v.append(pickle.loads(i[0]))
return v
def __del__(self):
logger.debug('__del__ %r' % self)
self.db.commit()
self.db.close()
self.db = None
def __repr__(self):
return '<PgsqlSession %r>' % (self.id,)

151
lybmods/lybrary.py 100644
View File

@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
from time import tzname
from lybmods.lybformauth import AuthController, member_of, any_of
from lybmods.lybclasses import Doc, Cat
import cherrypy
from lybmods import lybtools
from lybmods import lybhtdata
from lybmods import lybedit
from lybmods import lybctl
class Root:
_cp_config = {
'tools.sessions.on': True,
'tools.sessions.storage_type' : 'pgsql',
'tools.sessions.name' : 'PGSQLSes',
'tools.sessions.timeout': 120,
'tools.auth.on': True
}
auth = AuthController()
mdict = lybtools.mdict
from_page = '/'
def __init__(self):
self.edit = lybedit.Edit(self)
self.ctl = lybctl.Ctl(self)
@cherrypy.expose
def index(self):
self.from_page = '/'
return self.buildhtml('Архив', lybhtdata.index_html)
@cherrypy.expose
def css(self):
return lybhtdata.style
@cherrypy.expose
def poisk(self, search = None, catid = 0):
if not search or search == '':
cherrypy.HTTPRedirect("/")
search.replace('<', '').replace('>', '').replace(';', '').replace(',', '')
html_cont = '<div align="left">\n<table>\n{html}\n</table></div>'
html = ''
cat = Cat(0)
res = cat.search(search)
html += '<tr><td><h1>Найдено ' + str(len(res)) + ' документов</h1></td></tr>'
for did in res:
doc = Doc(did)
catid = doc.cat.id
html += '<tr><td><a href="/get_doc?catid=' + str(catid) + '&doc=' + str(did) + '">' + doc.name + '</a></td>\n<td>(' + self.path(did, doc = True) + ')</td></tr>\n'
return self.buildhtml('Архив[Запрос: ' + search + ']', html_cont.format(html=html))
@cherrypy.expose
def loginpage(self, from_page='/'):
self.from_page = from_page.replace('<', '').replace('>', '').replace(';', '').replace(',', '')
username = cherrypy.session.get(lybtools.SESSION_KEY, None)
if username:
raise cherrypy.HTTPRedirect("/")
else:
return self.buildhtml('Архив [Получение доступа]', lybhtdata.login_body)
@cherrypy.expose
def denied(self):
return self.buildhtml('Архив [Доступ закрыт]', lybhtdata.access_denied_body)
def path(self, oid, doc = False, lcat = False):
if doc:
p = Doc(oid).path
else:
p = Cat(oid).path
html = ''
for i in p:
name = Cat(i).name
if len(name) > 15: name = name[:15] + '...'
if i > 0: q = '&raquo;'
else: q = ''
html += q + '<a href="/category?catid=' + str(i) + '" title="' + Cat(i).name + '"><small><i><b>[' + name + ']</b></i></small></a>'
if p == []: q = ''
else: q = '&raquo;'
if not doc and not lcat: html += q + '<small><i><b>' + Cat(oid).name + '</b></i></small>'
if lcat: html += q + '<a href="/category?catid=' + str(oid) + '"><small><i><b>[' + Cat(oid).name + ']</b></i></small></a>'
return html
@cherrypy.expose
def category(self, catid = 0):
catid = int(catid)
category = Cat(catid)
html = '<div align="left">' + self.path(catid) + '</div><div align="center">\n<table>\n'
if category.catcount: html += '<tr><td><h2>Подразделы:</h2></td>\n'
for cat in category.categories:
rec = '<td><b>[<a href="/category?catid=' + str(cat.id) + '" title="Подразделов: ' + str(cat.catcount) + '/Документов: ' + str(cat.doccount) + '">' + cat.name + '</a>]</b></td>\n'
if any_of(member_of('admins'), member_of('editors'))():
html += '<tr><form action="/edit/catedit" method="post">\n{rec}<input type="hidden" name="catid" value="'.format(rec = rec) + str(cat.id) + '">\n<td><input type="submit" name="submit" value="Переименовать"></td>\n<td><input type="submit" name="submit" value="Перенести"></td>\n<td><input type="submit" name="submit" value="Удалить"></td>\n</form></tr>\n'
else:
html += '<tr>{rec}</tr>'.format(rec = rec)
if category.catcount and category.doccount: html += '<tr><td>***</td><td>***</td><td>***</td><td>***</td></tr>\n'
if category.doccount: html += '<tr><td><h2>Документы:</h2></td>\n'
for doc in category.documents:
did = doc.id
rec = '<td><a href="/get_doc?catid=' + str(catid) + '&doc=' + str(did) + '">' + doc.name + '</a></td>\n'
if any_of(member_of('admins'), member_of('editors'))():
html += '<tr><form action="/edit/docedit" method="post">\n{rec}<input type="hidden" name="catid" value="'.format(rec = rec) + str(catid) + '">\n<input type="hidden" name="doc" value="' + str(did) + '">\n<td><input type="submit" name="submit" value="Переименовать"></td>\n<td><input type="submit" name="submit" value="Редактировать"></td>\n<td><input type="submit" name="submit" value="Перенести"></td>\n<td><input type="submit" name="submit" value="Удалить"></td>\n</form></tr>\n'
else:
html += '<tr>{rec}</tr>'.format(rec = rec)
html += '</table>\n</div>\n'
if any_of(member_of('admins'), member_of('editors'))():
html += '<div align="right">\n<form action="/edit/newdoc" method="post">\n<input type="hidden" name="catid" value="' + str(catid) + '">\n<input type="submit" value="Новый документ">\n</form>\n</div>\n'
html += '<div align="right">\n<form action="/edit/upl" method="post">\n<input type="hidden" name="catid" value="' + str(catid) + '">\n<input type="submit" value="Загрузить документ">\n</form>\n</div>\n'
html += '<div align="right">\n<form action="/edit/newcat" method="post">\nНовый раздел\n<input type="hidden" name="catid" value=' + str(catid) + '>\n<input type="text" name="catname">\n<input type="submit" value="Создать">\n</form>\n</div>\n'
if catid != 0:
parent = Cat(catid).parent
html += '<div align="center"><a href="/category?catid=' + str(parent.id) + '"><i>&larr; Обратно в раздел "' + parent.name + '"</i></a></div>\n'
title = 'Архив[Раздел: ' + category.name + ']'
return self.buildhtml(title, html)
@cherrypy.expose
def get_doc(self, catid = 0, doc = None, plain = False):
catid = int(catid)
cat = Cat(catid)
if not doc: return
doc = int(doc)
doco = cat[doc]
txt = doco.text
muser = doco.muser
mtime = doco.mtime.strftime('%d.%m.%Y %H:%M:%S') + ' /' + tzname[0] + '/'
if plain:
return txt
else:
doct = '<div align="left">' + self.path(doco.id, doc = True) + '</div><div align="right"><a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + cat.name + '"</i>\n</a>\n</div>\n' + txt
if any_of(member_of('admins'), member_of('editors'))():
doct += '<div align="right"><form action="/edit/docedit" method="post">\n<input type="hidden" name="catid" value="' + str(catid) + '">\n<input type="hidden" name="doc" value="' + str(doc) + '">\n<input type="submit" name="submit" value="Переименовать">\n<input type="submit" name="submit" value="Редактировать">\n<input type="submit" name="submit" value="Перенести">\n<input type="submit" name="submit" value="Удалить">\n</form></div>\n'
doct += '<div align="right">\nОтредактировано пользователем: <a href="/ctl/user?name=' + muser.name + '"><b>' + muser.dname + '[' + muser.name + ']</b></a>.</div>\n<div align="right">Дата редактирования: ' + mtime + '\n</div>\n'
doct += '<br><div align="right"><a href="/category?catid=' + str(catid) + '">\n<i>&larr; Обратно в раздел "' + cat.name + '"</i>\n</a>\n</div>\n'
return self.buildhtml('Архив[' + doco.name + ']', doct)
@cherrypy.expose
def getobj(self, lybsrcobj = None):
obj = lybtools.getbin(cherrypy.session, lybsrcobj)
cherrypy.response.headers["Content-Type"] = obj['type']
return obj['body']
@cherrypy.expose
def menu_img(self):
cherrypy.response.headers["Content-Type"] = 'image/png'
return lybhtdata.m_i()
def menupanel(self, mdict):
menu = lybtools.menu_gen(mdict)
menu += lybhtdata.form_minisearch
username = cherrypy.session.get(lybtools.SESSION_KEY, None)
if username:
menu += lybhtdata.form_user.format(username=username)
else:
menu += lybhtdata.form_auth.format(from_page=self.from_page)
return menu
def buildhtml(self, title, doc):
return lybhtdata.html.format(title=title, style='/css',
body=lybhtdata.body.format(menu = self.menupanel(self.mdict), document = doc))

View File

@ -0,0 +1,166 @@
'''Этот модуль предназначен для конвертации в C-код и последующей компиляции в исполняюмую библиотеку. Здесь находятся тяжёлые функции.'''
import cherrypy
from lxml import etree
from lybmods import lybtools
from urllib.request import FancyURLopener
from urllib.parse import urlencode
from urllib.parse import urljoin, parse_qs, urlsplit
from lybmods.lybclasses import Cat
from lybmods import lybhtdata
from base64 import decodestring
import re
import hashlib
class URLOpener(FancyURLopener):
version = 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0.6) Gecko/20100101 Firefox/10.0.6 Iceweasel/10.0.6'
def html(self, catid, url, htfile, md = None, sub = False, ins = False):
htfile = lybtools.htfile_tounicode(htfile)
body = etree.ElementTree(etree.HTML(htfile).xpath('//body')[0])
sessdata = cherrypy.session
strip_tags = ["script", "link"]
etree.strip_elements(body, *strip_tags, with_tail=False)
chg_tags = ["body", "a", "form", "input", "noscript"]
etree.strip_tags(body, *chg_tags)
etree.strip_tags(body, etree.Comment)
#safe_tags = ['img']
for elem in body.xpath('//*'):
if elem.tag == 'body': elem.tag = 'old-body'
attr = elem.attrib
if elem.tag in chg_tags:
etree.strip_attributes(elem, *attr)
if "class" in attr:
etree.strip_attributes(elem, "class")
if "id" in attr:
etree.strip_attributes(elem, "id")
if "onclick" in attr:
etree.strip_attributes(elem, "onclick")
if "style" in attr:
attr['style'] = re.sub('url\(.+\)', 'url()', attr['style'])
#if elem.tag not in safe_tags and (elem.text is None or elem.text.strip() == '') and elem.getchildren() == []:
# elem.getparent().remove(elem)
# continue
if "src" in attr:
m = re.search('data:(\S+);base64,(.+)', attr['src'])
if not m:
srcurl = urljoin(url, attr['src'])
srcobjquery = urlsplit(srcurl)[3]
srcqdict = parse_qs(srcobjquery)
if 'lybsrcobj' in list(srcqdict.keys()):
ohash = srcqdict['lybsrcobj'][0]
srcquerydata = {'lybsrcobj': ohash}
srcquery = urlencode(srcquerydata)
# if ins:
page = '/getobj?'
# else:
# page = '/edit/tmpstore?'
# if ohash not in sessdata:
# if md:
# cat = Cat(int(catid))
# doco = cat[int(md)]
# sessdata[ohash] = doco[ohash]
elem.set('src', page + srcquery)
continue
try:
srcu = URLOpener().open(srcurl)
except:
continue
if srcu.code >= 400:
continue
srcdata = srcu.read()
cont_type = srcu.headers['Content-Type']
srcftype = cont_type and lybtools.ctype(srcu.headers['Content-Type']) or 'none'
else:
srcdata = decodestring(m.group(2).encode('utf-8'))
srcftype = m.group(1)
srchashname = hashlib.sha1(srcdata).hexdigest()
if srcftype == 'text/html':
if elem.tag == 'img': continue
srcdata = self.html(catid, srcu.url, srcdata, sub = True)
if srchashname not in sessdata:
sessdata[srchashname] = {'body': srcdata, 'type': srcftype}
srcquerydata = {'lybsrcobj': srchashname}
srcquery = urlencode(srcquerydata)
if ins:
page = '/getobj?'
else:
page = '/edit/tmpstore?'
elem.set('src', page + srcquery)
etree.strip_tags(body, 'old-body')
ht_ml = etree.tounicode(body, method='html', pretty_print = True)
if not sub and not ins:
return self.ne(catid, url=url, html=ht_ml, md=md)
else:
return ht_ml
def insert_doc(self, catid = 0, url = None, html = None, doc_name = '', md = None):
catid = int(catid)
doc_name = doc_name.strip()
cat = Cat(catid)
catname = cat.name
if md:
md = int(md)
modify_doc = '<input type="hidden" name="md" value="' + str(md) + '">'
else:
modify_doc = ''
html = self.html(catid, '/', html, ins = True)
if doc_name == '':
return self.root.buildhtml('Архив [Укажи имя документа]', lybhtdata.nicedit_html1 + lybhtdata.nicedit_html2.format(docname=doc_name,
url=url,
modify_doc=modify_doc,
textarea=lybhtdata.nicedit_textarea.format(input_html=html), catid=catid,
catname = catname))
did = cat.docidbyname(doc_name)
if did and did != md:
return self.root.buildhtml('Архив [Документ с таким именем в разделе "' + cat.name + '" существует]', lybhtdata.nicedit_html1 + lybhtdata.nicedit_html2.format(docname=doc_name,
url=url,
modify_doc=modify_doc,
textarea=lybhtdata.nicedit_textarea.format(input_html=html), catid=catid,
catname = catname))
if md:
cat[md] = { 'name': doc_name, 'body': html}
did = md
else:
did = cat.insert({ 'name': doc_name, 'body': html})
xdata = etree.HTML(html)
if md:
new_bin_list = [ parse_qs(urlsplit(x)[3])['lybsrcobj'][0] for x in xdata.xpath('//@src') if 'lybsrcobj' in parse_qs(urlsplit(x)[3])]
old_bin_list = cat[did].bins
list_to_del = list(set(old_bin_list) - set(new_bin_list))
for bhash in list_to_del:
del cat[did][bhash]
for src in xdata.xpath('//@src'):
srcquery = urlsplit(src)[3]
try:
src_obj = parse_qs(srcquery)['lybsrcobj'][0]
except:
continue
try:
obj = cherrypy.session[src_obj]
except KeyError:
continue
cat[did][src_obj] = obj
cherrypy.session.clear()
args = {'doc':did, 'catid':catid}
return self.root.get_doc(**args)
def ne(self, catid, url = None, html = None, md = None, docname = "Новый документ"):
html = lybhtdata.nicedit_textarea.format(input_html=html)
xhtml = etree.HTML(html).xpath('//textarea[@id=\'nicedit-js-area\']')[0]
if md:
docname = Cat(int(catid))[int(md)].name
modify_doc = '<input type="hidden" name="md" value="' + md + '">'
else:
modify_doc = ''
return self.root.path(int(catid), lcat = True) + '<br><br>' + lybhtdata.nicedit_html1 + lybhtdata.nicedit_html2.format(url=url,
catid=catid,
catname = Cat(int(catid)).name,
docname = docname,
modify_doc=modify_doc,
textarea=etree.tounicode(xhtml,
method="html",
pretty_print=True)
)

440
lybmods/lybtools.py 100644
View File

@ -0,0 +1,440 @@
import lybmods.lybhtdata as lybhtdata
from base64 import b64encode, b64decode, encodestring, decodestring, urlsafe_b64encode, urlsafe_b64decode
import lybmods.lybcfg as lybcfg
from lxml import etree
from chardet import detect
SESSION_KEY = '_cp_username'
mdict = {
'Меню':[
[
'Хранилище',
'/category',
None
],
[
'Панель управления',
'#',
{
'Пользователи':'/ctl/users',
'Группы': '/ctl/groups'
}
]
]
}
edit_esc = {
"&": "&amp;amp;",
'"': "&amp;quot;",
"'": "&amp;apos;",
">": "&amp;gt;",
"<": "&amp;lt;",
}
def menu_gen(mdict):
m = ''
sm = ''
smi = ''
for menu in list(mdict.keys()):
if type(mdict[menu]) is list:
for submenu in mdict[menu]:
if type(submenu[2]) is dict:
for item in list(submenu[2].keys()):
smi = smi + lybhtdata.submenuitem.format(name = item, url = submenu[2][item])
sm = sm + lybhtdata.submenu.format(name = submenu[0], url = submenu[1],
submenuitem = lybhtdata.submenuitem_container.format(submenuitem = smi))
smi = ''
else:
sm = sm + lybhtdata.submenuitem.format(name = submenu[0], url = submenu[1],
submenuitem = '')
m = m + lybhtdata.menu.format(name = menu, submenu = sm)
sm = ''
else:
m = m + lybhtdata.menu.format(name = menu, submenu = '')
return lybhtdata.menu_container.format(menu = m)
def b64dec(self, data, ascii=False):
if ascii:
return urlsafe_b64decode(data)
else:
return b64decode(data)
def b64enc(data, ascii=False):
if ascii:
return urlsafe_b64encode(data)
else:
return b64encode(data)
def b64decstr(data):
return decodestring(data)
def b64encstr(data):
return encodestring(data)
def ctype(c_t):
t = c_t.split(';')
return t[0]
def htfile_tounicode(htfile):
if type(htfile) is str:
return htfile
ct = etree.HTML(htfile).xpath('//meta/@http-equiv')
enc = detect(htfile)['encoding']
if ct != []:
c_t = ct[0].getparent().attrib['content']
if 'charset' in c_t:
enc = c_t.split('charset')[1].strip().split('=')[1].strip().split(' ')[0]
return str(htfile, enc, 'ignore')
def getbin(sess, hhash):
cur = sess.db.cursor()
cur.execute('SELECT type, body FROM %s WHERE hash = %%s;' % lybcfg.bin_table_name, (hhash,))
res = cur.fetchone()
return {'type': res[0], 'body': res[1].tobytes()}
qChkUserAndPwd = """\
SELECT COUNT(*) FROM auth_passwd
WHERE username=%s AND password=%s;"""
qChkUserInGroup = """\
SELECT COUNT(*) FROM auth_membership AS m
WHERE m.uid=(
SELECT id FROM auth_passwd AS p
WHERE p.username=%s
)
AND m.gid=(
SELECT id FROM auth_group AS g
WHERE g.name=%s
);"""
SESS_SCHEMA = """\
CREATE TABLE {sess} (
id VARCHAR(40),
expiration_time TIMESTAMP,
CONSTRAINT {sess}_pkey PRIMARY KEY (id)
);
CREATE TABLE {sessdata} (
sid VARCHAR(40),
key VARCHAR,
value BYTEA
);
CREATE INDEX {sessdata}_idx on {sessdata} USING btree(sid);
CREATE OR REPLACE FUNCTION del_{sessdata}() RETURNS trigger AS
'
BEGIN
DELETE FROM {sessdata} where sid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{sess}_del
AFTER DELETE
ON {sess} FOR EACH ROW
EXECUTE PROCEDURE del_{sessdata}();
""".format(sess = lybcfg.sess_table_name, sessdata = lybcfg.sess_data_table_name)
AUTH_SCHEMA = """\
CREATE TABLE {grp} (
id serial NOT NULL,
name varchar(40) NOT NULL UNIQUE,
dname varchar(40) NOT NULL,
CONSTRAINT group_pkey PRIMARY KEY (id)
);
CREATE TABLE {pwd} (
id serial NOT NULL,
username varchar(40) NOT NULL UNIQUE,
dname varchar(40) NOT NULL,
password varchar(40) NOT NULL,
CONSTRAINT passwd_pkey PRIMARY KEY (id)
);
CREATE TABLE {mbrsh} (
id serial NOT NULL,
uid integer,
gid integer,
CONSTRAINT membership_pkey PRIMARY KEY (id)
);
CREATE OR REPLACE FUNCTION del_uid() RETURNS trigger AS
'
BEGIN
DELETE FROM {mbrsh} where uid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{pwd}_del
AFTER DELETE
ON {pwd} FOR EACH ROW
EXECUTE PROCEDURE del_uid();
CREATE OR REPLACE FUNCTION del_gid() RETURNS trigger AS
'
BEGIN
DELETE FROM {mbrsh} where gid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{grp}_del
AFTER DELETE
ON {grp} FOR EACH ROW
EXECUTE PROCEDURE del_uid();
INSERT INTO {pwd}
(username, dname, password)
VALUES ('admin', 'Администратор', 'dd94709528bb1c83d08f3088d4043f4742891f4f');
INSERT INTO {grp}
(name, dname)
VALUES ('admins', 'Администраторы');
INSERT INTO {grp}
(name, dname)
VALUES ('editors', 'Редакторы');
INSERT INTO {mbrsh}
(uid, gid)
VALUES ((SELECT id FROM {pwd} WHERE username = 'admin'), (SELECT id FROM {grp} WHERE name = 'admins'));
""".format(grp = lybcfg.grp_table_name, pwd = lybcfg.pwd_table_name, mbrsh = lybcfg.mbrsh_table_name)
CAT_SCHEMA = """\
CREATE TABLE {cat} (
id bigserial NOT NULL,
name varchar(512) NOT NULL,
parent bigint,
CONSTRAINT {cat}_pkey PRIMARY KEY (id)
);
CREATE OR REPLACE FUNCTION update_{cat}_in_{doc}() RETURNS trigger AS
'
BEGIN
UPDATE {doc} SET catid = old.parent WHERE catid = old.id;
UPDATE {cat} SET parent = old.parent WHERE parent = old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{cat}_del
AFTER DELETE
ON {cat} FOR EACH ROW
EXECUTE PROCEDURE update_{cat}_in_{doc}();
CREATE INDEX name_idx ON {cat} USING gin(to_tsvector('russian', name));
""".format(cat = lybcfg.cat_table_name, doc = lybcfg.doc_table_name)
DOC_SCHEMA = """\
CREATE TABLE {doc} (
id bigserial NOT NULL,
name varchar(512) NOT NULL,
catid bigint NOT NULL,
muser varchar(40) NOT NULL,
mtime timestamp without time zone DEFAULT now() NOT NULL,
body text,
fts_index tsvector,
CONSTRAINT {doc}_pkey PRIMARY KEY (id)
);
CREATE INDEX fts_idx on {doc} USING gin(fts_index);
CREATE OR REPLACE FUNCTION del_{docbin}() RETURNS trigger AS
'
BEGIN
DELETE FROM {docbin} WHERE docid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{doc}_del
AFTER DELETE
ON {doc} FOR EACH ROW
EXECUTE PROCEDURE del_{docbin}();
CREATE FUNCTION update_fts_index() RETURNS trigger AS $$
BEGIN
new.fts_index :=
setweight(to_tsvector('pg_catalog.russian', coalesce(new.name,'')), 'A') ||
setweight(to_tsvector('pg_catalog.russian', coalesce(new.body,'')), 'B');
RETURN new;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER fts_index_update
BEFORE INSERT OR UPDATE
ON {doc} FOR EACH ROW
EXECUTE PROCEDURE update_fts_index();
""".format(doc = lybcfg.doc_table_name, docbin = lybcfg.docbin_table_name)
#CATDOC_SCHEMA = """\
#CREATE TABLE {catdoc} (
# id serial NOT NULL,
# docid bigint NOT NULL,
# catid bigint NOT NULL,
# CONSTRAINT {catdoc}_pkey PRIMARY KEY (id)
# );
#CREATE OR REPLACE FUNCTION del_orphaned_{doc}() RETURNS trigger AS
#'
#BEGIN
# PERFORM * FROM {catdoc} WHERE docid = old.docid;
# IF NOT FOUND THEN
# DELETE FROM {doc} where id=old.docid;
# RETURN old;
# END IF;
# RETURN old;
#END
#'
#LANGUAGE plpgsql;
#CREATE TRIGGER on_{catdoc}_del
# AFTER DELETE
# ON {catdoc} FOR EACH ROW
# EXECUTE PROCEDURE del_orphaned_{doc}();
#""".format(catdoc = lybcfg.catdoc_table_name, doc = lybcfg.doc_table_name)
BIN_SCHEMA = """\
CREATE TABLE {bin} (
id bigserial NOT NULL,
hash varchar(40) NOT NULL,
type varchar(40) NOT NULL,
body bytea,
CONSTRAINT {bin}_pkey PRIMARY KEY (id, hash)
);
""".format(bin = lybcfg.bin_table_name)
DOCBIN_SCHEMA = """\
CREATE TABLE {docbin} (
id bigserial NOT NULL,
binid bigint NOT NULL,
docid bigint NOT NULL,
CONSTRAINT {docbin}_pkey PRIMARY KEY (id)
);
CREATE OR REPLACE FUNCTION del_orphaned_{bin}() RETURNS trigger AS
'
BEGIN
PERFORM * FROM {docbin} WHERE binid = old.binid;
IF NOT FOUND THEN
DELETE FROM {bin} where id=old.binid;
RETURN old;
END IF;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER on_{docbin}_del
AFTER DELETE
ON {docbin} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{bin}();
""".format(docbin = lybcfg.docbin_table_name, bin = lybcfg.bin_table_name)
GRPCAT_ACCESS_SCHEMA = """\
CREATE TABLE {grpcat_access} (
id bigserial NOT NULL,
gid bigint NOT NULL,
catid bigint NOT NULL,
attr varchar(2),
CONSTRAINT {grpcat_access}_pkey PRIMARY KEY (id)
);
CREATE INDEX {grpcat_access}_idx on {grpcat_access} USING btree(gid, catid);
CREATE OR REPLACE FUNCTION del_orphaned_{grpcat_access}_on_del_{cat}() RETURNS trigger AS
'
BEGIN
DELETE FROM {grpcat_access} WHERE catid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{grpcat_access}_on_del_{cat}
AFTER DELETE
ON {cat} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{grpcat_access}_on_del_{cat}();
CREATE OR REPLACE FUNCTION del_orphaned_{grpcat_access}_on_del_{grp}() RETURNS trigger AS
'
BEGIN
DELETE FROM {grpcat_access} WHERE gid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{grpcat_access}_on_del_{grp}
AFTER DELETE
ON {grp} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{grpcat_access}_on_del_{grp}();
""".format(grpcat_access = lybcfg.grpcat_access_table, cat = lybcfg.cat_table_name, grp = lybcfg.grp_table_name)
GRPDOC_ACCESS_SCHEMA = """\
CREATE TABLE {grpdoc_access} (
id bigserial NOT NULL,
gid bigint NOT NULL,
docid bigint NOT NULL,
attr varchar(2),
CONSTRAINT {grpdoc_access}_pkey PRIMARY KEY (id)
);
CREATE INDEX {grpdoc_access}_idx on {grpdoc_access} USING btree(gid, docid);
CREATE OR REPLACE FUNCTION del_orphaned_{grpdoc_access}_on_del_{doc}() RETURNS trigger AS
'
BEGIN
DELETE FROM {grpdoc_access} WHERE docid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{grpdoc_access}_on_del_{doc}
AFTER DELETE
ON {doc} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{grpdoc_access}_on_del_{doc}();
CREATE OR REPLACE FUNCTION del_orphaned_{grpdoc_access}_on_del_{grp}() RETURNS trigger AS
'
BEGIN
DELETE FROM {grpdoc_access} WHERE gid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{grpdoc_access}_on_del_{grp}
AFTER DELETE
ON {grp} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{grpdoc_access}_on_del_{grp}();
""".format(grpdoc_access = lybcfg.grpdoc_access_table, doc = lybcfg.doc_table_name, grp = lybcfg.grp_table_name)
PWDCAT_ACCESS_SCHEMA = """\
CREATE TABLE {pwdcat_access} (
id bigserial NOT NULL,
pid bigint NOT NULL,
catid bigint NOT NULL,
attr varchar(2),
CONSTRAINT {pwdcat_access}_pkey PRIMARY KEY (id)
);
CREATE INDEX {pwdcat_access}_idx on {pwdcat_access} USING btree(pid, catid);
CREATE OR REPLACE FUNCTION del_orphaned_{pwdcat_access}_on_del_{cat}() RETURNS trigger AS
'
BEGIN
DELETE FROM {pwdcat_access} WHERE catid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{pwdcat_access}_on_del_{cat}
AFTER DELETE
ON {cat} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{pwdcat_access}_on_del_{cat}();
CREATE OR REPLACE FUNCTION del_orphaned_{pwdcat_access}_on_del_{pwd}() RETURNS trigger AS
'
BEGIN
DELETE FROM {pwdcat_access} WHERE pid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{pwdcat_access}_on_del_{pwd}
AFTER DELETE
ON {pwd} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{pwdcat_access}_on_del_{pwd}();
""".format(pwdcat_access = lybcfg.pwdcat_access_table, cat = lybcfg.cat_table_name, pwd = lybcfg.pwd_table_name)
PWDDOC_ACCESS_SCHEMA = """\
CREATE TABLE {pwddoc_access} (
id bigserial NOT NULL,
pid bigint NOT NULL,
docid bigint NOT NULL,
attr varchar(2),
CONSTRAINT {pwddoc_access}_pkey PRIMARY KEY (id)
);
CREATE INDEX {pwddoc_access}_idx on {pwddoc_access} USING btree(pid, docid);
CREATE OR REPLACE FUNCTION del_orphaned_{pwddoc_access}_on_del_{doc}() RETURNS trigger AS
'
BEGIN
DELETE FROM {pwddoc_access} WHERE docid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{pwddoc_access}_on_del_{doc}
AFTER DELETE
ON {doc} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{pwddoc_access}_on_del_{doc}();
CREATE OR REPLACE FUNCTION del_orphaned_{pwddoc_access}_on_del_{pwd}() RETURNS trigger AS
'
BEGIN
DELETE FROM {pwddoc_access} WHERE pid=old.id;
RETURN old;
END
'
LANGUAGE plpgsql;
CREATE TRIGGER del_orphaned_{pwddoc_access}_on_del_{pwd}
AFTER DELETE
ON {pwd} FOR EACH ROW
EXECUTE PROCEDURE del_orphaned_{pwddoc_access}_on_del_{pwd}();
""".format(pwddoc_access = lybcfg.pwddoc_access_table, doc = lybcfg.doc_table_name, pwd = lybcfg.pwd_table_name)