1
# -*- coding: utf-8 -*-
9
Extend session to support:
10
Multiple flash classes
18
response.error = session.error
19
response.confirmation = session.confirmation
20
response.warning = session.warning
22
session.confirmation = []
24
# Keep all our configuration options in a single pair of global variables
25
# Use session for persistent variables
27
session.s3 = Storage()
28
# Use response for one-off variables which are visible in views without explicit passing
29
response.s3 = Storage()
30
response.s3.formats = Storage()
34
user_id = auth.user.id
35
_memberships = db.auth_membership
36
memberships = db(_memberships.user_id == user_id).select(_memberships.group_id) # Cache this & invalidate when memberships are changed?
37
for membership in memberships:
38
roles.append(membership.group_id)
40
# User not authenticated therefore has no roles other than '0'
42
session.s3.roles = roles
44
controller_settings_table = "%s_setting" % request.controller
45
controller_settings = controller_settings_table in db.tables and \
46
db(db[controller_settings_table].id > 0).select(limitby=(0, 1)).first()
48
settings = db(db.s3_setting.id > 0).select(db.s3_setting.debug, db.s3_setting.security_policy, db.s3_setting.self_registration, db.s3_setting.audit_read, db.s3_setting.audit_write, limitby=(0, 1)).first()
49
# Are we running in debug mode?
50
session.s3.debug = "debug" in request.vars or settings and settings.debug
51
session.s3.self_registration = (settings and settings.self_registration) or 1
52
session.s3.security_policy = (settings and settings.security_policy) or 1
54
# We Audit if either the Global or Module asks us to
55
# (ignore gracefully if module author hasn't implemented this)
56
session.s3.audit_read = (settings and settings.audit_read) \
57
or (controller_settings and controller_settings.audit_read)
58
session.s3.audit_write = (settings and settings.audit_write) \
59
or (controller_settings and controller_settings.audit_write)
62
s3_settings = shn_sessions()
65
# List of supported languages
71
auth.settings.table_user.language.requires = IS_IN_SET(shn_languages, zero=None)
74
# List of Nations - added by nursix
76
shn_list_of_nations = {
82
6:T("Antigua and Barbuda"),
98
22:T("Bosnia and Herzegovina"),
103
27:T("Burkina Faso"),
109
33:T("Central African Republic"),
115
39:T("Congo, Democratic Republic of the (Congo-Kinshasa)"),
116
40:T("Congo, Republic of the (Congo-Brazzaville)"),
118
42:T("Côte d'Ivoire"),
122
46:T("Czech Republic"),
126
50:T("Dominican Republic"),
131
55:T("Equatorial Guinea"),
147
71:T("Guinea-Bissau"),
166
90:T("Korea, North"),
167
91:T("Korea, South"),
176
100:T("Liechtenstein"),
186
110:T("Marshall Islands"),
201
125:T("Netherlands"),
202
126:T("New Zealand"),
211
135:T("Papua New Guinea"),
214
138:T("Philippines"),
221
145:T("Saint Kitts and Nevis"),
222
146:T("Saint Lucia"),
223
147:T("Saint Vincent and the Grenadines"),
226
150:T("São Tomé and PrÃncipe"),
227
151:T("Saudi Arabia"),
231
155:T("Sierra Leone"),
235
159:T("Solomon Islands"),
237
161:T("South Africa"),
244
168:T("Switzerland"),
251
175:T("Trinidad and Tobago"),
254
178:T("Turkmenistan"),
258
182:T("United Arab Emirates"),
259
183:T("United Kingdom"),
260
184:T("United States"),
264
188:T("Vatican City"),
272
196:T("Nagorno-Karabakh"),
273
197:T("Northern Cyprus"),
275
199:T("South Ossetia"),
277
201:T("Transnistria"),
281
# User Time Zone Operations:
283
from datetime import timedelta
286
def shn_user_utc_offset():
288
returns the UTC offset of the current user or None, if not logged in
291
if auth.is_logged_in():
292
return db(db.auth_user.id == session.auth.user.id).select(db.auth_user.utc_offset, limitby=(0, 1)).first().utc_offset
295
offset = db().select(db.s3_setting.utc_offset, limitby=(0, 1)).first().utc_offset
301
def shn_as_local_time(value):
303
represents a given UTC datetime.datetime object as string:
305
- for the local time of the user, if logged in
306
- as it is in UTC, if not logged in, marked by trailing +0000
309
format="%Y-%m-%d %H:%M:%S"
311
offset = IS_UTC_OFFSET.get_offset_value(shn_user_utc_offset())
314
dt = value + timedelta(seconds=offset)
315
return dt.strftime(str(format))
318
return dt.strftime(str(format))+" +0000"
320
# Make URLs clickable
321
shn_url_represent = lambda url: (url and [A(url, _href=url, _target="blank")] or [""])[0]
323
# Phone number requires
324
shn_phone_requires = IS_NULL_OR(IS_MATCH('\+?\s*[\s\-\.\(\)\d]+(?:(?: x| ext)\s?\d{1,5})?$'))
327
"""Convenience function for non web2py modules"""
332
user = db.auth_user[user_id]
333
return user.first_name if user else "None"
336
def shn_last_update(table, record_id):
338
if table and record_id:
339
record = table[record_id]
341
mod_on_str = T(" on ")
342
mod_by_str = T(" by ")
345
if "modified_on" in table.fields:
346
modified_on = "%s%s" % (mod_on_str, shn_as_local_time(record.modified_on))
349
if "modified_by" in table.fields:
350
user = auth.settings.table_user[record.modified_by]
352
person = db(db.pr_person.uuid == user.person_uuid).select(limitby=(0, 1)).first()
354
modified_by = "%s%s" % (mod_by_str, vita.fullname(person))
356
if len(modified_on) or len(modified_by):
357
last_update = "%s%s%s" % (T("Record last updated"), modified_on, modified_by)
361
def shn_compose_message(data, template):
362
" Compose an SMS Message from an XSLT "
363
from lxml import etree
365
root = etree.Element("message")
366
for k in data.keys():
367
entry = etree.SubElement(root, k)
368
entry.text = s3xrc.xml.xml_encode(str(data[k]))
371
tree = etree.ElementTree(root)
374
template = os.path.join(request.folder, "static", template)
375
if os.path.exists(template):
376
message = s3xrc.xml.transform(tree, template)
381
return s3xrc.xml.tostring(tree, pretty_print=True)
384
def shn_crud_strings(table_name,
385
table_name_plural = None):
387
@author: Michael Howden (michael@aidiq.com)
390
Creates the strings for the title of/in the various CRUD Forms.
393
table_name - string - The User's name for the resource in the table - eg. "Person"
394
table_name_plural - string - The User's name for the plural of the resource in the table - eg. "People"
397
class "gluon.storage.Storage" (Web2Py)
400
s3.crud_strings[<table_name>] = shn_crud_strings(<table_name>, <table_name_plural>)
403
if not table_name_plural:
404
table_name_plural = table_name + "s"
406
ADD = T("Add " + table_name)
407
LIST = T("List "+ table_name_plural)
409
table_strings = Storage(
410
title = T(table_name),
411
title_plural = T(table_name_plural),
413
title_display = T(table_name + " Details"),
415
title_update = T("Edit "+ table_name) ,
416
title_search = T("Search " + table_name_plural) ,
417
subtitle_create = T("Add New " + table_name) ,
418
subtitle_list = T(table_name_plural),
419
label_list_button = LIST,
420
label_create_button = ADD,
421
msg_record_created = T(table_name +" added"),
422
msg_record_modified = T(table_name + " updated"),
423
msg_record_deleted = T( table_name + " deleted"),
424
msg_list_empty = T("No " + table_name_plural + " currently registered"))
429
def shn_get_crud_strings(tablename):
431
""" Get the CRUD strings for a table """
433
return s3.crud_strings.get(tablename, s3.crud_strings)
436
def shn_import_table(table_name,
437
import_if_not_empty = False):
439
@author: Michael Howden (michael@aidiq.com)
442
If a table is empty, it will import values into that table from:
443
/private/import/tables/<table>.csv.
446
table_name - string - The name of the table
447
import_if_not_empty - bool
450
table = db[table_name]
451
if not db(table.id).count() or import_if_not_empty:
452
import_file = os.path.join(request.folder,
453
"private", "import", "tables",
455
table.import_from_csv_file(open(import_file,"r"))
458
def shn_represent_file(file_name,
462
@author: Michael Howden (michael@aidiq.com)
465
Represents a file (stored in a table) as the filename with a link to that file
468
url_file = crud.settings.download_url + "/" + file_name
470
if db[table][field].uploadfolder:
471
path = db[table][field].uploadfolder
473
path = os.path.join(db[table][field]._db._folder, "..", "uploads")
474
pathfilename = os.path.join(path, file_name)
477
#f = open(pathfilename,"r")
478
#filename = f.filename
479
regex_content = re.compile("([\w\-]+\.){3}(?P<name>\w+)\.\w+$")
480
regex_cleanup_fn = re.compile('[\'"\s;]+')
482
m = regex_content.match(file_name)
483
filename = base64.b16decode(m.group("name"), True)
484
filename = regex_cleanup_fn.sub("_", filename)
488
return A(filename, _href = url_file)
491
def shn_rheader_tabs(jr, tabs=[]):
493
""" Constructs a DIV of component links for a S3RESTRequest """
496
for (title, component) in tabs:
497
_class = "rheader_tab_other"
499
if jr.component and jr.component.name == component or \
500
jr.custom_action and jr.method == component:
501
_class = "rheader_tab_here"
502
args = [jr.id, component]
503
_href = URL(r=request, f=jr.name, args=args)
506
_class = "rheader_tab_here"
508
_next = URL(r=request, f=jr.name, args=[jr.id])
509
_href = URL(r=request, f=jr.name, args=args, vars = {"_next": _next})
510
tab = SPAN(A(title, _href=_href), _class=_class)
511
rheader_tabs.append(tab)
514
rheader_tabs = DIV(rheader_tabs, _id="rheader_tabs")
520
def shn_action_buttons(jr, deletable=True):
521
""" Provide the usual Action Buttons for Column views. Designed to be called from a postp """
524
if auth.is_logged_in():
525
# Provide the ability to delete records in bulk
527
response.s3.actions = [
528
dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args=["[id]", "update"]))),
529
dict(label=str(DELETE), _class="action-btn", url=str(URL(r=request, args=["[id]", "delete"])))
532
response.s3.actions = [
533
dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args=["[id]", "update"])))
536
response.s3.actions = [
537
dict(label=str(READ), _class="action-btn", url=str(URL(r=request, args=["[id]"])))
b'\\ No newline at end of file'