← Back to the web2py-plugin list
Solid Grid
A yet another grid using SOLIDFORM and SOLIDTABLE. (EXPERIMENTAL)
Demo
Usage
-
controllers/plugin_solidgrid.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.from plugin_solidgrid import SolidGrid
### define tables ##############################################################
table_product = db.define_table('plugin_solidgrid_product',
Field('name', notnull=True),
Field('status', requires=IS_IN_SET(['new', 'old'])),
Field('description', 'text', represent=lambda v: v or '---'),
Field('publish_date', 'date', represent=lambda v: v or '---'),
Field('price', 'integer', represent=lambda v: '$%s' % v if v else '---'),
Field('created_on', 'datetime', default=request.now, readable=False, writable=False),
)
### populate records ###########################################################
import datetime
if (db(table_product.created_on < request.now - datetime.timedelta(minutes=60)).count() or
not db(table_product.id > 0).count()):
table_product.truncate()
for i in range(5):
table_product.insert(name='p%s' % i, status='old')
for i in range(5, 12):
table_product.insert(name='p%s' % i, status='new')
session.flash = 'the database has been refreshed'
redirect(URL('index'))
### fake authentication ########################################################
from gluon.storage import Storage
session.auth = Storage(hmac_key='test', user=Storage(email='user@test.com'))
### demo functions #############################################################
def index():
SQLFORM.grid = SolidGrid(renderstyle=True) # override the original grid function
from plugin_tablecheckbox import TableCheckbox
tablecheckbox = TableCheckbox()
# Insert an extra element to select an action
tablecheckbox.components.insert(0,
SELECT('new', 'old', _name='action', _style='width:70px;',
requires=IS_IN_SET(['new', 'old'])))
if tablecheckbox.accepts(request.vars):
db(table_product.id.belongs(tablecheckbox.vars.tablecheckbox)).update(status=tablecheckbox.vars.action)
session.flash = 'Updated'
redirect(URL('index'))
extracolumns = [tablecheckbox.column()]
grid = SQLFORM.grid(table_product,
#fields=[table_product.ALL],
columns=[extracolumns[0], table_product.id,
(table_product.name, table_product.status),
table_product.description,
(table_product.publish_date, table_product.price)],
extracolumns=extracolumns,
scope=table_product.status,
)
return dict(buttons=grid.gridbuttons,
search=grid.search_form if hasattr(grid, 'search_form') else '',
status=DIV(tablecheckbox) if hasattr(grid, 'records') else '',
table=grid)
Source code
-
modules/plugin_solidgrid.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
697.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
729.
730.
731.
732.
733.
734.
735.
736.
737.
738.
739.
740.
741.
742.
743.
744.
745.
746.
747.
748.
749.
750.
751.
752.
753.
754.
755.
756.
757.
758.
759.from gluon import *
from gluon.storage import Storage
# For referencing static and views from other application
import os
APP = os.path.basename(os.path.dirname(os.path.dirname(__file__)))
def _gridbutton(buttonclass, buttontext, buttonurl, **attr):
if '_class' not in attr:
attr['_class'] = 'btn w2p_trap'
if '_style' not in attr:
attr['_style'] = 'float:left;margin-right:20px;'
return A(SPAN(_class='ui-icon %s' % buttonclass),
SPAN(buttontext, _class='ui-button-text'),
_href=buttonurl, **attr)
def _recordbutton(buttonclass, buttontext, buttonurl, showbuttontext=True, **attr):
if showbuttontext:
inner = SPAN(buttontext, _class='ui-button-text')
else:
inner = SPAN(XML(' '), _style='padding:6px;')
if '_class' not in attr:
attr['_class'] = 'ui-btn w2p_trap'
return A(SPAN(_class='ui-icon ' + buttonclass),
inner,
_title=buttontext, _href=buttonurl, **attr)
class SolidGrid(object):
def __init__(self, renderstyle=False):
settings = self.settings = Storage()
settings.gridbutton = _gridbutton
settings.recordbutton = _recordbutton
if renderstyle:
_url = URL(APP, 'static', 'plugin_solidgrid/solidgrid.css')
if _url not in current.response.files:
current.response.files.append(_url)
def _build_query_by_form(self, db, form, search_vars, queries={}, field_sep='___',
exclude_var_pattern='^.+_page$', formname='form'):
def _convert(field, val):
if field.requires:
if type(field.requires) in (list, tuple):
for r in field.requires:
# if isinstance(r, IS_EMPTY_OR) and val:
# val, err = r.other(val)
# else:
val, err = r(val)
else:
val, err = field.requires(val)
return val
request = current.request
if form.accepts(request.vars, keepvalues=True, formname=formname):
new_vars = search_vars.copy()
if 'page' in new_vars:
del new_vars['page']
for key, value in form.vars.items():
if key != 'id':
new_key = '%s_%s' % (formname, key)
if value is not None:
new_vars[new_key] = value
elif new_key in new_vars:
del new_vars[new_key]
import re
p = re.compile(exclude_var_pattern)
for key in new_vars.keys():
if p.match(key):
del new_vars[key]
redirect(URL(args=request.args, vars=new_vars))
new_queries = []
for input_el in form.elements('input[type=text]'):
if '_name' in input_el.attributes:
key = input_el.attributes['_name']
request_key = '%s_%s' % (formname, key)
_key = key.split(field_sep)
if request_key in search_vars:
val = search_vars[request_key]
if val:
if len(_key) >= 2:
tablename, fieldname = _key[:2]
field = db[tablename][fieldname]
_val = _convert(field, val)
if len(_key) == 3:
if _key[2] == 'from':
new_queries.append(field >= _val)
elif _key[2] == 'to':
new_queries.append(field <= _val)
else:
raise RuntimeError
else:
if field.unique:
new_queries.append(field == _val)
else:
new_queries.append(db[tablename][fieldname].like('%' + str(_val) + '%'))
else:
new_queries.append(queries[key](val))
input_el.attributes['_value'] = val
input_el.attributes['value'] = val
# if len(_key) >= 2:
# tablename, fieldname = _key[:2]
# requires = db[tablename][fieldname].requires
for input_el in form.elements('input[type=checkbox]'):
if '_name' in input_el.attributes:
key = input_el.attributes['_name']
request_key = '%s_%s' % (formname, key)
if request_key in search_vars:
val = True if str(search_vars[request_key]) == 'True' else False
else:
val = False
if val:
_key = key.split(field_sep)
if len(_key) == 2:
tablename, fieldname = _key
field = db[tablename][fieldname]
new_queries.append(field == _convert(field, val))
else:
new_queries.append(queries[key](val))
input_el.attributes['_checked'] = 'checked' if val else None
input_els = form.elements('input[type=radio]')
if input_els:
from collections import defaultdict
radios = defaultdict(list)
for input_el in input_els:
radios[input_el.attributes['_name']].append(input_el)
for key, els in radios.items():
request_key = '%s_%s' % (formname, key)
val = search_vars.get(request_key, '')
_key = key.split(field_sep)
if len(_key) == 2:
tablename, fieldname = _key
if val:
field = db[tablename][fieldname]
new_queries.append(field == _convert(field, val))
else:
if val:
new_queries.append(queries[key](val))
for el in els:
if el.attributes['_value'] == val:
el.attributes['_checked'] = 'checked'
else:
el.attributes['_checked'] = None
for select_el in form.elements('select'):
key = select_el.attributes.get('_name')
if not key:
continue
request_key = '%s_%s' % (formname, key)
if request_key in search_vars:
val = search_vars[request_key]
_key = key.split(field_sep)
if len(_key) == 2:
tablename, fieldname = _key
field = db[tablename][fieldname]
new_queries.append(field == _convert(field, val))
else:
new_queries.append(queries[key](val))
for option in select_el.elements('option'):
if option.attributes['_value'] == val:
option.attributes['_selected'] = 'selected'
else:
option.attributes['_selected'] = None
if new_queries:
return reduce(lambda a, b: a & b, new_queries)
else:
return None
def get_default_not_empty_marker(self):
from plugin_notemptymarker import default_not_empty_marker
return default_not_empty_marker
def mark_not_empty(self, table, marker=None):
from plugin_notemptymarker import mark_not_empty
return mark_not_empty(table, marker or self.get_default_not_empty_marker())
def unmark_not_empty(self, table, marker=None):
from plugin_notemptymarker import unmark_not_empty
return unmark_not_empty(table)
def url_factory(self, args=[], user_signature=True, hmac_key=None):
def _url(**b):
b['args'] = args + b.get('args', [])
b['user_signature'] = user_signature
b['hmac_key'] = hmac_key
return URL(**b)
return _url
def inline(self, form, table, fields, label, sep=' ', wrapper=None):
els = []
for field in fields:
els.append(form.elements('#%s_%s__label' % (table, field))[0].parent.parent)
children = []
if form.formstyle == 'table3cols' or callable(form.formstyle):
for el in els:
children.append(el.elements('td'))
elif form.formstyle == 'divs':
for el in els:
children.append(el.elements('div')[1:])
else:
raise RuntimeError('formstyle not supported')
child0 = children[0]
child0[0].components = [label]
components = []
for child in children:
components += child[1].components
components.append(sep)
child0[1].components = components[:-1]
if wrapper:
child0[1].components = [wrapper(*child0[1].components)]
for el in els[1:]:
el.attributes['_style'] = 'display:none;'
for child in children[1:]:
child[1].components = ''
def __call__(self,
query,
fields=None,
field_id=None,
left=None,
headers={},
columns=None,
orderby=None, # EXTENDED for permutation
searchable=True, # EXTENDED ex) [table.id, table.name, ...]
sortable=True, # EXTENDED ex) [table.id, table.name, ...]
paginate=(10, 25, 50, 100), # EXTENDED
deletable=True, # EXTENDED ex) lambda record_id: deleted
editable=True, # EXTENDED ex) [['id', 'name'], 'profile', ...] or lambda record: edit_form
details=True, # EXTENDED ex) [['id', 'name'], 'profile', ...] or lambda record: view_form
selectable=None, # TODO
create=True, # EXTENDED ex) [['id', 'name'], 'profile', ...] or lambda: create_form
csv=True,
links=None,
links_in_grid=True,
upload='<default>',
args=[],
user_signature=True,
maxtextlengths={}, # NOT WORK
maxtextlength=20,
onvalidation=None,
oncreate=None,
onupdate=None,
ondelete=None,
sorter_icons=('[^]', '[v]'), # NOT WORK
ui='ui', # ONLY WORK FOR "ui"
showbuttontext=True,
_class="web2py_grid",
formname='web2py_grid',
search_widget='default', # NOT WORK
extracolumns=None, # CUSTOM (same as in SQLTABLE)
search_queries={}, # CUSTOM
showid=True, # CUSTOM
onpermute=None, # CUSTOM
virtualtable=None, # CUSTOM
virtualrecord=None, # CUSTOM
virtualset=None, # CUSTOM
hmac_key=None, # CUSTOM
scope=None, # CUSTOM
scope_default=None, # CUSTOM
groupby=None, # CUSTOM
search_vars=None, # CUSTOM
):
from gluon.dal import SQLALL
from plugin_solidform import SOLIDFORM
from plugin_solidtable import SOLIDTABLE, OrderbySelector
from plugin_paginator import Paginator, PaginateSelector, PaginateInfo
gridbutton = self.settings.gridbutton
recordbutton = self.settings.recordbutton
request, response, session, T = current.request, current.response, current.session, current.T
search_vars = search_vars or request.get_vars
def __oncreate(form):
session.flash = T('Created')
def __onupdate(form):
session.flash = T('Updated')
def __ondelete(table, tablename):
session.flash = T('Deleted')
def __onpermute(table, tablename, ret):
session.flash = T('Permuted')
def redirect_patch():
onupdate
oncreate = oncreate or __oncreate
onupdate = onupdate or __onupdate
ondelete = ondelete or __ondelete
onpermute = onpermute or __onpermute
if ui == 'ui':
ui = dict(widget='',
header='',
content='',
default='',
cornerall='',
cornertop='',
cornerbottom='',
button='',
buttontext='',
buttonadd='ui-icon-plusthick',
buttonback='ui-icon-arrowreturnthick-1-w',
buttonexport='ui-icon ui-icon-transferthick-e-w',
buttondelete='ui-icon-close',
buttonedit='ui-icon-pencil',
buttontable='',
buttonview='ui-icon-zoomin',
)
elif not isinstance(ui, dict):
raise RuntimeError('SQLFORM.grid ui argument must be a dictionary')
wenabled = (not user_signature or (session.auth and session.auth.user))
deletable = wenabled and deletable
# if search_widget=='default':
# search_widget = SQLFORM.search_menu
url = self.url_factory(args, user_signature, hmac_key)
db = query._db
dbset = db(query)
tables = [db[tablename] for tablename in db._adapter.tables(dbset.query)]
if not fields:
fields = reduce(lambda a, b: a + b,
[[field for field in table] for table in tables])
new_fields = []
for item in fields:
if isinstance(item, SQLALL):
new_fields += item.table
else:
new_fields.append(item)
fields = new_fields
main_table = tables[0]
if not field_id:
field_id = main_table._id
table = field_id.table
tablename = table._tablename
referrer = session.get('_web2py_grid_referrer_' + formname, url())
def __from_process_redirect_patch(func):
def wrapper(form):
func(form)
redirect(referrer)
return wrapper
oncreate = __from_process_redirect_patch(oncreate)
onupdate = __from_process_redirect_patch(onupdate)
def check_authorization():
if user_signature or hmac_key:
if not URL.verify(request, user_signature=user_signature, hmac_key=hmac_key):
session.flash = T('not authorized')
redirect(referrer)
if upload == '<default>':
upload = lambda filename: url(args=['download', filename])
if len(request.args) > 1 and request.args[-2] == 'download':
check_authorization()
stream = response.download(request, db)
raise HTTP(200, stream, **response.headers)
gridbuttons = [gridbutton('%(buttonback)s' % ui, T('Back'), referrer)]
def _add_link_gridbuttons(record):
if record and links:
for link in links:
if isinstance(link, dict):
gridbuttons.append(link['body'](record))
elif link(record):
gridbuttons.append(link(record))
if create and len(request.args) > 1 and request.args[-2] == 'new':
check_authorization()
table = db[request.args[-1]]
if orderby:
inverted = (orderby.op == orderby.db._adapter.INVERT)
field = orderby.first if inverted else orderby
last = dbset.select(field_id, field, limitby=(0, 1), orderby=orderby.first if inverted else ~orderby).first()
last_value = (last[field] or 0) if last else 0
table[field.name].default = (-1 if inverted else 1) + last_value
self.mark_not_empty(virtualtable or table)
if callable(create):
create_form = create()
else:
create_form = SOLIDFORM(virtualtable or table,
fields=create if type(create) in (list, tuple) else None,
showid=showid,
_class='web2py_form',
submit_button=T('Create'),
).process( # next=referrer, for web2py-bug
onvalidation=onvalidation,
onsuccess=oncreate,
formname=formname)
self.unmark_not_empty(table)
res = DIV(create_form, _class=_class)
res.create_form = create_form
res.gridbuttons = gridbuttons
return res
elif details and len(request.args) > 2 and request.args[-3] == 'view':
check_authorization()
table = db[request.args[-2]]
record = table(request.args[-1]) or redirect(URL('error'))
if callable(details):
view_form = details(record)
else:
view_form = SOLIDFORM(virtualtable or table, virtualrecord or record,
fields=details if type(details) in (list, tuple) else
create if type(create) in (list, tuple) else None,
upload=upload,
readonly=True,
showid=showid,
_class='web2py_form')
res = DIV(view_form, _class=_class)
res.record = record # CUSTOM
res.view_form = view_form # CUSTOM
if editable:
gridbuttons.append(
gridbutton('%(buttonedit)s' % ui, T('Edit'),
url(args=['edit', tablename, record.id]))
)
_add_link_gridbuttons(record)
res.gridbuttons = gridbuttons
return res
elif editable and len(request.args) > 2 and request.args[-3] == 'edit':
check_authorization()
table = db[request.args[-2]]
record = table(request.args[-1]) or redirect(URL('error'))
self.mark_not_empty(virtualtable or table)
if callable(editable):
edit_form = editable(record)
else:
edit_form = SOLIDFORM(virtualtable or table, virtualrecord or record,
fields=editable if type(editable) in (list, tuple) else
create if type(create) in (list, tuple) else None,
upload=upload,
deletable=deletable is True,
showid=showid,
delete_label=T('Check to delete:'),
submit_button=T('Update'),
_class='web2py_form').process(
formname=formname,
onvalidation=onvalidation,
onsuccess=onupdate,
# #next=referrer, for web2py-bug
)
self.unmark_not_empty(table)
res = DIV(edit_form, _class=_class)
res.record = record # CUSTOM
res.edit_form = edit_form
if details:
gridbuttons.append(
gridbutton('%(buttonview)s' % ui, T('View'),
url(args=['view', tablename, record.id]))
)
_add_link_gridbuttons(record)
res.gridbuttons = gridbuttons
return res
elif deletable and len(request.args) > 2 and request.args[-3] == 'delete':
check_authorization()
table = db[request.args[-2]]
if callable(deletable):
deletable(request.args[-1])
else:
if ondelete:
ondelete(table, request.args[-1])
db(table.id == request.args[-1]).delete()
redirect(url())
elif request.vars.records and not isinstance(request.vars.records, list):
request.vars.records = [request.vars.records]
elif not request.vars.records:
request.vars.records = []
error = None
search_form = None
table_el_id = formname + '_maintable'
columns = columns or [str(f) for f in fields
if f.table == main_table and f.readable and (showid or f.type != 'id')]
if searchable:
field_sep = '___'
if searchable is True:
_exclude_types = ('upload', 'text') if showid else ('id', 'upload', 'text')
searchable = [f for f in fields
if f.table == main_table and
f.type not in _exclude_types and f.readable]
_search_fields = []
_from_tos = []
for f in searchable:
_requires = []
if f.requires and type(f.requires) not in (list, tuple):
if isinstance(f.requires, IS_EMPTY_OR):
_requires = [f.requires.other]
else:
_requires = [f.requires]
_requires = [r for r in _requires if not isinstance(r, IS_NOT_IN_DB)]
if _requires:
if len(_requires) == 1:
_requires = _requires[0]
_requires = IS_EMPTY_OR(_requires)
else:
_requires = None
_type = 'string' if f.type == 'text' else 'integer' if f.type == 'id' else f.type
if (f.type in ('double', 'decimal', 'date', 'datetime') or
(f.type == 'integer' and _requires and
isinstance(_requires.other, (IS_INT_IN_RANGE)))):
_from_to = [Field(str(f).replace('.', field_sep) + field_sep + 'from',
type=_type, requires=_requires,
label=f.label, widget=f.widget),
Field(str(f).replace('.', field_sep) + field_sep + 'to',
type=_type, requires=_requires,
label=f.label, widget=f.widget)]
_from_tos.append(_from_to)
_search_fields += _from_to
elif hasattr(f, 'table'):
_search_fields.append(Field(str(f).replace('.', field_sep),
type=_type, requires=_requires, label=f.label, widget=f.widget))
else:
_search_fields.append(f)
search_form = SQLFORM.factory(
formstyle='divs', submit_button=T('Search'),
_class='search_form',
*_search_fields)
for _from_to in _from_tos:
self.inline(search_form, 'no_table',
[f.name for f in _from_to], LABEL(_from_to[0].label), SPAN(' - '))
subquery = self._build_query_by_form(db, search_form,
search_vars,
queries=search_queries,
field_sep=field_sep,
formname='search_%s' % formname)
else:
subquery = None
if subquery:
dbset = dbset(subquery)
if scope:
from plugin_tablescope import TableScope
scope_el = TableScope(dbset, scope, default=scope_default)
dbset = scope_el.scoped_dataset
if sortable is True:
sortable = [~f if f.type in ('id', 'date', 'datetime') else f
for f in fields
if f.table == main_table and f.type not in ('text', 'upload')]
if not sortable:
sortable = []
if orderby:
sortable.insert(0, orderby)
orderby_selector = OrderbySelector(sortable)
if csv and len(request.args) > 1 and request.args[-2] == 'csv':
if not dbset:
raise HTTP(400)
check_authorization()
current.response.headers['Content-Type'] = 'text/csv; charset=Shift_JIS'
current.response.headers['Content-Disposition'] = 'attachment;filename=rows.csv;'
raise HTTP(200, str(dbset.select(limitby=(0, 1000))).encode('shift_jis', 'ignore'),
**current.response.headers)
session['_web2py_grid_referrer_' + formname] = URL(
r=request, args=request.args, vars=request.vars,
user_signature=user_signature, hmac_key=hmac_key)
current_orderby = orderby_selector.orderby()
permutable = (orderby and (not subquery) and sortable and current_orderby is sortable[0])
extracolumns = extracolumns or []
_links_in_row_buttons = []
def _get_link_extracolumn(link):
return {'label': link['header'], 'class': ui.get('default', ''),
'content': lambda row, rc: link['body'](row)}
if links and links_in_grid:
for link in links:
if isinstance(link, dict):
extracolumns.append(_get_link_extracolumn(link))
else:
_links_in_row_buttons.append(link)
if permutable:
if len(request.args) > 2 and request.args[-3] in ('up', 'down'):
check_authorization()
table = db[request.args[-2]]
record = table(request.args[-1]) or redirect(URL('error'))
inverted = (orderby.op == orderby.db._adapter.INVERT)
field = orderby.first if inverted else orderby
current_value = record[field]
if current_value is None:
first = dbset.select(field_id, limitby=(0, 1), orderby=orderby).first()
current_value = (1 if inverted else -1) + (first.id if first else 0)
if (request.args[-3] == ('down' if inverted else 'up')):
target = dbset(field < current_value
).select(field_id, field, limitby=(0, 1), orderby=orderby if inverted else ~orderby).first()
elif (request.args[-3] == ('up' if inverted else 'down')):
target = dbset(field > current_value
).select(field_id, field, limitby=(0, 1), orderby=orderby.first if inverted else orderby).first()
else:
raise NotImplementedError
if not target:
last = dbset.select(field_id, limitby=(0, 1), orderby=orderby.first if orderby.first else ~orderby).first()
target_value = (-1 if inverted else 1) + (last.id if last else 0)
else:
target_value = target[field]
db(table.id == record[field_id]).update(**{field.name: target_value})
if target:
db(table.id == target[field_id]).update(**{field.name: current_value})
if onpermute:
onpermute(table, request.args[-2], (record, target))
redirect(url())
first = dbset.select(field_id, limitby=(0, 1), orderby=orderby).first()
first_id = first.id if first else 0
last = dbset.select(field_id, limitby=(0, 1), orderby=orderby.first if orderby.first else ~orderby).first()
last_id = last.id if last else 0
extracolumns.append(
{'label': DIV(T('Move'), _style='text-align:center;'), 'width': '150px' if showbuttontext else '65px',
'content': lambda row, rc:
DIV(recordbutton('ui-icon-triangle-1-n', T('Up'),
url(args=['up', tablename, row[field_id]]), showbuttontext)
if row[field_id] != first_id else '',
recordbutton('ui-icon-triangle-1-s', T('Down'),
url(args=['down', tablename, row[field_id]]), showbuttontext)
if row[field_id] != last_id else '',
_style='text-align:center;')}
)
if details or editable or deletable or _links_in_row_buttons:
extracolumns.append(
{'label': '', # 'width':'%spx' % (_size + 12),
'content': lambda row, rc:
DIV(_style='white-space:nowrap;',
*([link(row) or '' for link in _links_in_row_buttons] +
[recordbutton('%(buttonview)s' % ui, T('View'),
url(args=['view', tablename, row[field_id]]), showbuttontext)
if details else '',
recordbutton('%(buttonedit)s' % ui, T('Edit'),
url(args=['edit', tablename, row[field_id]]), showbuttontext)
if editable else '',
recordbutton('%(buttondelete)s' % ui, T('Delete'),
url(args=['delete', tablename, row[field_id]]), showbuttontext,
_onclick="""
if(confirm("%s")){return true;} else {jQuery(this).unbind('click').fadeOut();return false;}""" %
T('Sure you want to delete them?'),)
if deletable else '',
])
)}
)
if paginate:
paginate_selector = PaginateSelector(paginate if type(paginate) in (list, tuple) else [paginate])
current_paginate = paginate_selector.paginate
paginator = Paginator(paginate=current_paginate)
# TODO for groupby
paginator.records = virtualset(dbset.query).count() if virtualset else dbset.count()
paginate_info = PaginateInfo(paginator.page, paginator.paginate, paginator.records)
limitby = paginator.limitby()
else:
limitby = None
current_paginate = None
# TODO
# if paginator.records == 0:
# error = 'Not Found'
if virtualset:
records = virtualset(dbset.query).select(left=left, limitby=limitby,
orderby=current_orderby, groupby=groupby, *fields)
records.db = virtualtable._db
else:
records = dbset.select(left=left, limitby=limitby,
orderby=current_orderby, groupby=groupby, *fields)
table = SOLIDTABLE(records,
columns=columns,
headers=headers,
orderby=orderby_selector,
truncate=maxtextlength, # TODO replace
extracolumns=extracolumns,
upload=upload)
table.attributes['_class'] = 'solidtable'
table.attributes['_id'] = table_el_id
inner = []
if scope:
inner.append(scope_el)
if current_paginate:
inner.append(DIV(paginate_info, _class='pagination_information'))
inner.append(table)
if current_paginate and paginator.records > current_paginate:
inner.append(DIV(paginate_selector, paginator, _class='index_footer'))
res = DIV(_class=_class, *inner)
res.records = records
res.search_form = search_form
res.error = error
res.gridbuttons = []
if create:
res.gridbuttons.append(
gridbutton('%(buttonadd)s' % ui, T('Add'), url(args=['new', tablename]))
)
if csv:
res.gridbuttons.append(
gridbutton('%(buttonexport)s' % ui, T('Export'), url(args=['csv', tablename], vars=request.vars))
)
return res
