PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/applications/admin/controllers/appadmin.py

https://github.com/gokceneraslan/web2py
Python | 633 lines | 575 code | 41 blank | 17 comment | 51 complexity | fefc53cd72a8b0a71bae0c49da18f414 MD5 | raw file
Possible License(s): BSD-2-Clause, MIT, BSD-3-Clause
  1. # -*- coding: utf-8 -*-
  2. # ##########################################################
  3. # ## make sure administrator is on localhost
  4. # ###########################################################
  5. import os
  6. import socket
  7. import datetime
  8. import copy
  9. import gluon.contenttype
  10. import gluon.fileutils
  11. try:
  12. import pygraphviz as pgv
  13. except ImportError:
  14. pgv = None
  15. # ## critical --- make a copy of the environment
  16. global_env = copy.copy(globals())
  17. global_env['datetime'] = datetime
  18. http_host = request.env.http_host.split(':')[0]
  19. remote_addr = request.env.remote_addr
  20. try:
  21. hosts = (http_host, socket.gethostname(),
  22. socket.gethostbyname(http_host),
  23. '::1', '127.0.0.1', '::ffff:127.0.0.1')
  24. except:
  25. hosts = (http_host, )
  26. if request.env.http_x_forwarded_for or request.is_https:
  27. session.secure()
  28. elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \
  29. (request.function != 'manage'):
  30. raise HTTP(200, T('appadmin is disabled because insecure channel'))
  31. if request.function == 'manage':
  32. if not 'auth' in globals() or not request.args:
  33. redirect(URL(request.controller, 'index'))
  34. manager_action = auth.settings.manager_actions.get(request.args(0), None)
  35. if manager_action is None and request.args(0) == 'auth':
  36. manager_action = dict(role=auth.settings.auth_manager_role,
  37. heading=T('Manage Access Control'),
  38. tables=[auth.table_user(),
  39. auth.table_group(),
  40. auth.table_permission()])
  41. manager_role = manager_action.get('role', None) if manager_action else None
  42. auth.requires_membership(manager_role)(lambda: None)()
  43. menu = False
  44. elif (request.application == 'admin' and not session.authorized) or \
  45. (request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
  46. redirect(URL('admin', 'default', 'index',
  47. vars=dict(send=URL(args=request.args, vars=request.vars))))
  48. else:
  49. response.subtitle = 'Database Administration (appadmin)'
  50. menu = True
  51. ignore_rw = True
  52. response.view = 'appadmin.html'
  53. if menu:
  54. response.menu = [[T('design'), False, URL('admin', 'default', 'design',
  55. args=[request.application])], [T('db'), False,
  56. URL('index')], [T('state'), False,
  57. URL('state')], [T('cache'), False,
  58. URL('ccache')]]
  59. # ##########################################################
  60. # ## auxiliary functions
  61. # ###########################################################
  62. if False and request.tickets_db:
  63. from gluon.restricted import TicketStorage
  64. ts = TicketStorage()
  65. ts._get_table(request.tickets_db, ts.tablename, request.application)
  66. def get_databases(request):
  67. dbs = {}
  68. for (key, value) in global_env.items():
  69. cond = False
  70. try:
  71. cond = isinstance(value, GQLDB)
  72. except:
  73. cond = isinstance(value, SQLDB)
  74. if cond:
  75. dbs[key] = value
  76. return dbs
  77. databases = get_databases(None)
  78. def eval_in_global_env(text):
  79. exec ('_ret=%s' % text, {}, global_env)
  80. return global_env['_ret']
  81. def get_database(request):
  82. if request.args and request.args[0] in databases:
  83. return eval_in_global_env(request.args[0])
  84. else:
  85. session.flash = T('invalid request')
  86. redirect(URL('index'))
  87. def get_table(request):
  88. db = get_database(request)
  89. if len(request.args) > 1 and request.args[1] in db.tables:
  90. return (db, request.args[1])
  91. else:
  92. session.flash = T('invalid request')
  93. redirect(URL('index'))
  94. def get_query(request):
  95. try:
  96. return eval_in_global_env(request.vars.query)
  97. except Exception:
  98. return None
  99. def query_by_table_type(tablename, db, request=request):
  100. keyed = hasattr(db[tablename], '_primarykey')
  101. if keyed:
  102. firstkey = db[tablename][db[tablename]._primarykey[0]]
  103. cond = '>0'
  104. if firstkey.type in ['string', 'text']:
  105. cond = '!=""'
  106. qry = '%s.%s.%s%s' % (
  107. request.args[0], request.args[1], firstkey.name, cond)
  108. else:
  109. qry = '%s.%s.id>0' % tuple(request.args[:2])
  110. return qry
  111. # ##########################################################
  112. # ## list all databases and tables
  113. # ###########################################################
  114. def index():
  115. return dict(databases=databases)
  116. # ##########################################################
  117. # ## insert a new record
  118. # ###########################################################
  119. def insert():
  120. (db, table) = get_table(request)
  121. form = SQLFORM(db[table], ignore_rw=ignore_rw)
  122. if form.accepts(request.vars, session):
  123. response.flash = T('new record inserted')
  124. return dict(form=form, table=db[table])
  125. # ##########################################################
  126. # ## list all records in table and insert new record
  127. # ###########################################################
  128. def download():
  129. import os
  130. db = get_database(request)
  131. return response.download(request, db)
  132. def csv():
  133. import gluon.contenttype
  134. response.headers['Content-Type'] = \
  135. gluon.contenttype.contenttype('.csv')
  136. db = get_database(request)
  137. query = get_query(request)
  138. if not query:
  139. return None
  140. response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\
  141. % tuple(request.vars.query.split('.')[:2])
  142. return str(db(query, ignore_common_filters=True).select())
  143. def import_csv(table, file):
  144. table.import_from_csv_file(file)
  145. def select():
  146. import re
  147. db = get_database(request)
  148. dbname = request.args[0]
  149. regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
  150. if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'):
  151. regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)')
  152. if request.vars.query:
  153. match = regex.match(request.vars.query)
  154. if match:
  155. request.vars.query = '%s.%s.%s==%s' % (request.args[0],
  156. match.group('table'), match.group('field'),
  157. match.group('value'))
  158. else:
  159. request.vars.query = session.last_query
  160. query = get_query(request)
  161. if request.vars.start:
  162. start = int(request.vars.start)
  163. else:
  164. start = 0
  165. nrows = 0
  166. stop = start + 100
  167. table = None
  168. rows = []
  169. orderby = request.vars.orderby
  170. if orderby:
  171. orderby = dbname + '.' + orderby
  172. if orderby == session.last_orderby:
  173. if orderby[0] == '~':
  174. orderby = orderby[1:]
  175. else:
  176. orderby = '~' + orderby
  177. session.last_orderby = orderby
  178. session.last_query = request.vars.query
  179. form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
  180. _name='query', _value=request.vars.query or '',
  181. requires=IS_NOT_EMPTY(
  182. error_message=T("Cannot be empty")))), TR(T('Update:'),
  183. INPUT(_name='update_check', _type='checkbox',
  184. value=False), INPUT(_style='width:400px',
  185. _name='update_fields', _value=request.vars.update_fields
  186. or '')), TR(T('Delete:'), INPUT(_name='delete_check',
  187. _class='delete', _type='checkbox', value=False), ''),
  188. TR('', '', INPUT(_type='submit', _value=T('submit')))),
  189. _action=URL(r=request, args=request.args))
  190. tb = None
  191. if form.accepts(request.vars, formname=None):
  192. regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+')
  193. match = regex.match(form.vars.query.strip())
  194. if match:
  195. table = match.group('table')
  196. try:
  197. nrows = db(query).count()
  198. if form.vars.update_check and form.vars.update_fields:
  199. db(query).update(**eval_in_global_env('dict(%s)'
  200. % form.vars.update_fields))
  201. response.flash = T('%s %%{row} updated', nrows)
  202. elif form.vars.delete_check:
  203. db(query).delete()
  204. response.flash = T('%s %%{row} deleted', nrows)
  205. nrows = db(query).count()
  206. if orderby:
  207. rows = db(query, ignore_common_filters=True).select(limitby=(
  208. start, stop), orderby=eval_in_global_env(orderby))
  209. else:
  210. rows = db(query, ignore_common_filters=True).select(
  211. limitby=(start, stop))
  212. except Exception, e:
  213. import traceback
  214. tb = traceback.format_exc()
  215. (rows, nrows) = ([], 0)
  216. response.flash = DIV(T('Invalid Query'), PRE(str(e)))
  217. # begin handle upload csv
  218. csv_table = table or request.vars.table
  219. if csv_table:
  220. formcsv = FORM(str(T('or import from csv file')) + " ",
  221. INPUT(_type='file', _name='csvfile'),
  222. INPUT(_type='hidden', _value=csv_table, _name='table'),
  223. INPUT(_type='submit', _value=T('import')))
  224. else:
  225. formcsv = None
  226. if formcsv and formcsv.process().accepted:
  227. try:
  228. import_csv(db[request.vars.table],
  229. request.vars.csvfile.file)
  230. response.flash = T('data uploaded')
  231. except Exception, e:
  232. response.flash = DIV(T('unable to parse csv file'), PRE(str(e)))
  233. # end handle upload csv
  234. return dict(
  235. form=form,
  236. table=table,
  237. start=start,
  238. stop=stop,
  239. nrows=nrows,
  240. rows=rows,
  241. query=request.vars.query,
  242. formcsv=formcsv,
  243. tb=tb,
  244. )
  245. # ##########################################################
  246. # ## edit delete one record
  247. # ###########################################################
  248. def update():
  249. (db, table) = get_table(request)
  250. keyed = hasattr(db[table], '_primarykey')
  251. record = None
  252. db[table]._common_filter = None
  253. if keyed:
  254. key = [f for f in request.vars if f in db[table]._primarykey]
  255. if key:
  256. record = db(db[table][key[0]] == request.vars[key[
  257. 0]]).select().first()
  258. else:
  259. record = db(db[table].id == request.args(
  260. 2)).select().first()
  261. if not record:
  262. qry = query_by_table_type(table, db)
  263. session.flash = T('record does not exist')
  264. redirect(URL('select', args=request.args[:1],
  265. vars=dict(query=qry)))
  266. if keyed:
  267. for k in db[table]._primarykey:
  268. db[table][k].writable = False
  269. form = SQLFORM(
  270. db[table], record, deletable=True, delete_label=T('Check to delete'),
  271. ignore_rw=ignore_rw and not keyed,
  272. linkto=URL('select',
  273. args=request.args[:1]), upload=URL(r=request,
  274. f='download', args=request.args[:1]))
  275. if form.accepts(request.vars, session):
  276. session.flash = T('done!')
  277. qry = query_by_table_type(table, db)
  278. redirect(URL('select', args=request.args[:1],
  279. vars=dict(query=qry)))
  280. return dict(form=form, table=db[table])
  281. # ##########################################################
  282. # ## get global variables
  283. # ###########################################################
  284. def state():
  285. return dict()
  286. def ccache():
  287. cache.ram.initialize()
  288. cache.disk.initialize()
  289. form = FORM(
  290. P(TAG.BUTTON(
  291. T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
  292. P(TAG.BUTTON(
  293. T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
  294. P(TAG.BUTTON(
  295. T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
  296. )
  297. if form.accepts(request.vars, session):
  298. clear_ram = False
  299. clear_disk = False
  300. session.flash = ""
  301. if request.vars.yes:
  302. clear_ram = clear_disk = True
  303. if request.vars.ram:
  304. clear_ram = True
  305. if request.vars.disk:
  306. clear_disk = True
  307. if clear_ram:
  308. cache.ram.clear()
  309. session.flash += T("Ram Cleared")
  310. if clear_disk:
  311. cache.disk.clear()
  312. session.flash += T("Disk Cleared")
  313. redirect(URL(r=request))
  314. try:
  315. from guppy import hpy
  316. hp = hpy()
  317. except ImportError:
  318. hp = False
  319. import shelve
  320. import os
  321. import copy
  322. import time
  323. import math
  324. from gluon import portalocker
  325. ram = {
  326. 'entries': 0,
  327. 'bytes': 0,
  328. 'objects': 0,
  329. 'hits': 0,
  330. 'misses': 0,
  331. 'ratio': 0,
  332. 'oldest': time.time(),
  333. 'keys': []
  334. }
  335. disk = copy.copy(ram)
  336. total = copy.copy(ram)
  337. disk['keys'] = []
  338. total['keys'] = []
  339. def GetInHMS(seconds):
  340. hours = math.floor(seconds / 3600)
  341. seconds -= hours * 3600
  342. minutes = math.floor(seconds / 60)
  343. seconds -= minutes * 60
  344. seconds = math.floor(seconds)
  345. return (hours, minutes, seconds)
  346. for key, value in cache.ram.storage.iteritems():
  347. if isinstance(value, dict):
  348. ram['hits'] = value['hit_total'] - value['misses']
  349. ram['misses'] = value['misses']
  350. try:
  351. ram['ratio'] = ram['hits'] * 100 / value['hit_total']
  352. except (KeyError, ZeroDivisionError):
  353. ram['ratio'] = 0
  354. else:
  355. if hp:
  356. ram['bytes'] += hp.iso(value[1]).size
  357. ram['objects'] += hp.iso(value[1]).count
  358. ram['entries'] += 1
  359. if value[0] < ram['oldest']:
  360. ram['oldest'] = value[0]
  361. ram['keys'].append((key, GetInHMS(time.time() - value[0])))
  362. folder = os.path.join(request.folder,'cache')
  363. if not os.path.exists(folder):
  364. os.mkdir(folder)
  365. locker = open(os.path.join(folder, 'cache.lock'), 'a')
  366. portalocker.lock(locker, portalocker.LOCK_EX)
  367. disk_storage = shelve.open(
  368. os.path.join(folder, 'cache.shelve'))
  369. try:
  370. for key, value in disk_storage.items():
  371. if isinstance(value, dict):
  372. disk['hits'] = value['hit_total'] - value['misses']
  373. disk['misses'] = value['misses']
  374. try:
  375. disk['ratio'] = disk['hits'] * 100 / value['hit_total']
  376. except (KeyError, ZeroDivisionError):
  377. disk['ratio'] = 0
  378. else:
  379. if hp:
  380. disk['bytes'] += hp.iso(value[1]).size
  381. disk['objects'] += hp.iso(value[1]).count
  382. disk['entries'] += 1
  383. if value[0] < disk['oldest']:
  384. disk['oldest'] = value[0]
  385. disk['keys'].append((key, GetInHMS(time.time() - value[0])))
  386. finally:
  387. portalocker.unlock(locker)
  388. locker.close()
  389. disk_storage.close()
  390. total['entries'] = ram['entries'] + disk['entries']
  391. total['bytes'] = ram['bytes'] + disk['bytes']
  392. total['objects'] = ram['objects'] + disk['objects']
  393. total['hits'] = ram['hits'] + disk['hits']
  394. total['misses'] = ram['misses'] + disk['misses']
  395. total['keys'] = ram['keys'] + disk['keys']
  396. try:
  397. total['ratio'] = total['hits'] * 100 / (total['hits'] +
  398. total['misses'])
  399. except (KeyError, ZeroDivisionError):
  400. total['ratio'] = 0
  401. if disk['oldest'] < ram['oldest']:
  402. total['oldest'] = disk['oldest']
  403. else:
  404. total['oldest'] = ram['oldest']
  405. ram['oldest'] = GetInHMS(time.time() - ram['oldest'])
  406. disk['oldest'] = GetInHMS(time.time() - disk['oldest'])
  407. total['oldest'] = GetInHMS(time.time() - total['oldest'])
  408. def key_table(keys):
  409. return TABLE(
  410. TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))),
  411. *[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys],
  412. **dict(_class='cache-keys',
  413. _style="border-collapse: separate; border-spacing: .5em;"))
  414. ram['keys'] = key_table(ram['keys'])
  415. disk['keys'] = key_table(disk['keys'])
  416. total['keys'] = key_table(total['keys'])
  417. return dict(form=form, total=total,
  418. ram=ram, disk=disk, object_stats=hp != False)
  419. def table_template(table):
  420. from gluon.html import TR, TD, TABLE, TAG
  421. def FONT(*args, **kwargs):
  422. return TAG.font(*args, **kwargs)
  423. def types(field):
  424. f_type = field.type
  425. if not isinstance(f_type,str):
  426. return ' '
  427. elif f_type == 'string':
  428. return field.length
  429. elif f_type == 'id':
  430. return B('pk')
  431. elif f_type.startswith('reference') or \
  432. f_type.startswith('list:reference'):
  433. return B('fk')
  434. else:
  435. return ' '
  436. # This is horribe HTML but the only one graphiz understands
  437. rows = []
  438. cellpadding = 4
  439. color = "#000000"
  440. bgcolor = "#FFFFFF"
  441. face = "Helvetica"
  442. face_bold = "Helvetica Bold"
  443. border = 0
  444. rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
  445. _colspan=3, _cellpadding=cellpadding,
  446. _align="center", _bgcolor=color)))
  447. for row in db[table]:
  448. rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold),
  449. _align="left", _cellpadding=cellpadding,
  450. _border=border),
  451. TD(FONT(row.type, _color=color, _face=face),
  452. _align="left", _cellpadding=cellpadding,
  453. _border=border),
  454. TD(FONT(types(row), _color=color, _face=face),
  455. _align="center", _cellpadding=cellpadding,
  456. _border=border)))
  457. return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
  458. _cellborder=0, _cellspacing=0)
  459. ).xml()
  460. def bg_graph_model():
  461. graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
  462. subgraphs = dict()
  463. for tablename in db.tables:
  464. if hasattr(db[tablename],'_meta_graphmodel'):
  465. meta_graphmodel = db[tablename]._meta_graphmodel
  466. else:
  467. meta_graphmodel = dict(group='Undefined', color='#ECECEC')
  468. group = meta_graphmodel['group'].replace(' ', '')
  469. if not subgraphs.has_key(group):
  470. subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
  471. subgraphs[group]['tables'].append(tablename)
  472. else:
  473. subgraphs[group]['tables'].append(tablename)
  474. graph.add_node(tablename, name=tablename, shape='plaintext',
  475. label=table_template(tablename))
  476. for n, key in enumerate(subgraphs.iterkeys()):
  477. graph.subgraph(nbunch=subgraphs[key]['tables'],
  478. name='cluster%d' % n,
  479. style='filled',
  480. color=subgraphs[key]['meta']['color'],
  481. label=subgraphs[key]['meta']['group'])
  482. for tablename in db.tables:
  483. for field in db[tablename]:
  484. f_type = field.type
  485. if isinstance(f_type,str) and (
  486. f_type.startswith('reference') or
  487. f_type.startswith('list:reference')):
  488. referenced_table = f_type.split()[1].split('.')[0]
  489. n1 = graph.get_node(tablename)
  490. n2 = graph.get_node(referenced_table)
  491. graph.add_edge(n1, n2, color="#4C4C4C", label='')
  492. graph.layout()
  493. #return graph.draw(format='png', prog='dot')
  494. if not request.args:
  495. return graph.draw(format='png', prog='dot')
  496. else:
  497. response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
  498. if request.args(0) == 'dot':
  499. return graph.string()
  500. else:
  501. return graph.draw(format=request.args(0), prog='dot')
  502. def graph_model():
  503. return dict(databases=databases, pgv=pgv)
  504. def manage():
  505. tables = manager_action['tables']
  506. if isinstance(tables[0], str):
  507. db = manager_action.get('db', auth.db)
  508. db = globals()[db] if isinstance(db, str) else db
  509. tables = [db[table] for table in tables]
  510. if request.args(0) == 'auth':
  511. auth.table_user()._plural = T('Users')
  512. auth.table_group()._plural = T('Roles')
  513. auth.table_membership()._plural = T('Memberships')
  514. auth.table_permission()._plural = T('Permissions')
  515. if request.extension != 'load':
  516. return dict(heading=manager_action.get('heading',
  517. T('Manage %(action)s') % dict(action=request.args(0).replace('_', ' ').title())),
  518. tablenames=[table._tablename for table in tables],
  519. labels=[table._plural.title() for table in tables])
  520. table = tables[request.args(1, cast=int)]
  521. formname = '%s_grid' % table._tablename
  522. linked_tables = orderby = None
  523. if request.args(0) == 'auth':
  524. auth.table_group()._id.readable = \
  525. auth.table_membership()._id.readable = \
  526. auth.table_permission()._id.readable = False
  527. auth.table_membership().user_id.label = T('User')
  528. auth.table_membership().group_id.label = T('Role')
  529. auth.table_permission().group_id.label = T('Role')
  530. auth.table_permission().name.label = T('Permission')
  531. if table == auth.table_user():
  532. linked_tables=[auth.settings.table_membership_name]
  533. elif table == auth.table_group():
  534. orderby = 'role' if not request.args(3) or '.group_id' not in request.args(3) else None
  535. elif table == auth.table_permission():
  536. orderby = 'group_id'
  537. kwargs = dict(user_signature=True, maxtextlength=1000,
  538. orderby=orderby, linked_tables=linked_tables)
  539. smartgrid_args = manager_action.get('smartgrid_args', {})
  540. kwargs.update(**smartgrid_args.get('DEFAULT', {}))
  541. kwargs.update(**smartgrid_args.get(table._tablename, {}))
  542. grid = SQLFORM.smartgrid(table, args=request.args[:2], formname=formname, **kwargs)
  543. return grid