def set_priority_js(): """ Output json for priority field """ wptable = s3db.cap_warning_priority rows = db(wptable).select( wptable.name, wptable.urgency, wptable.severity, wptable.certainty, wptable.color_code, orderby=wptable.name, ) from gluon.serializers import json as jsons from s3 import s3_str p_settings = [(s3_str(T(r.name)), r.urgency, r.severity, r.certainty, r.color_code)\ for r in rows] priority_conf = '''S3.cap_priorities=%s''' % jsons(p_settings) js_global = s3.js_global if not priority_conf in js_global: js_global.append(priority_conf) return
def html(self, start=None, limit=None, pagesize=None, ajaxurl=None): """ Render list data as HTML (nested DIVs) @param start: index of the first item (in this page) @param limit: (actual) number of items (in this page) @param pagesize: maximum number of items per page @param ajaxurl: the URL to Ajax-update the datalist """ T = current.T resource = self.resource list_fields = self.list_fields rfields = resource.resolve_selectors(list_fields)[0] listid = self.listid render = self.layout records = self.records if records is not None: items = [ DIV(T("Total Records: %(numrows)s") % {"numrows": self.total}, _class="dl-header", _id="%s-header" % listid) ] _start = self.start for i in xrange(len(records)): _class = (i + _start) % 2 and "even" or "odd" item = render(listid, resource, rfields, records[i], _class=_class) # Class "dl-item" is required for pagination: if hasattr(item, "add_class"): item.add_class("dl-item") items.append(item) else: # template raise NotImplementedError dl = DIV(items, _class="dl", _id=listid) dl_data = { "startindex": start, "maxitems": limit, "totalitems": self.total, "pagesize": pagesize, "ajaxurl": ajaxurl } from gluon.serializers import json as jsons dl_data = jsons(dl_data) dl.append( DIV(FORM( INPUT(_type="hidden", _class="dl-pagination", _value=dl_data)), A(T("more..."), _href=ajaxurl, _class="dl-pagination"), _class="dl-navigation")) return dl
def priority_get(): try: event_type_id = request.args[0] except: result = current.xml.json_message(False, 400, "No Event Type provided!") else: try: event_type_id = int(event_type_id) except: result = current.xml.json_message(False, 400, "Invalid Event Type!") else: # Get Event Name for Event ID etable = s3db.event_event_type item = db(etable.id == event_type_id).select(etable.name, limitby=(0, 1) ).first() try: event_type_name = item.name except: result = current.xml.json_message(False, 400, "Event Type Not Found!") else: wptable = s3db.cap_warning_priority query = (wptable.event_type == event_type_name) rows = db(query).select(wptable.id, wptable.name, orderby = wptable.id) from gluon.serializers import json as jsons if rows: row_dict = [{"id": r.id, "name": T(r.name)} for r in rows] + \ [{"id": "", "name": T("Undefined")}] result = jsons(row_dict) else: rows = db(wptable.event_type == "others").select(wptable.id, wptable.name, orderby = wptable.id) row_dict = [{"id": r.id, "name": T(r.name)} for r in rows] + \ [{"id": "", "name": T("Undefined")}] result = jsons(row_dict) finally: response.headers["Content-Type"] = "application/json" return result
def html(self, start=None, limit=None, pagesize=None, ajaxurl=None): """ Render list data as HTML (nested DIVs) """ T = current.T resource = self.resource list_fields = self.list_fields rfields = resource.resolve_selectors(list_fields)[0] listid = self.listid render = self.layout records = self.records if records is not None: items = [ DIV(T("Total Records: %(numrows)s") % {"numrows": self.total}, _class="dl-header", _id="%s-header" % listid) ] _start = self.start for i in xrange(len(records)): _class = (i + _start) % 2 and "even" or "odd" item = render(listid, resource, rfields, records[i], _class=_class) # Class "dl-item" is required for pagination: if hasattr(item, "add_class"): item.add_class("dl-item") items.append(item) else: # template raise NotImplementedError dl = DIV(items, _class = "dl", _id = listid) dl_data = {"startindex": start, "maxitems": limit, "totalitems": self.total, "pagesize": pagesize, "ajaxurl": ajaxurl } from gluon.serializers import json as jsons dl_data = jsons(dl_data) dl.append(DIV( FORM( INPUT(_type="hidden", _class="dl-pagination", _value=dl_data)), A(T("more..."), _href=ajaxurl, _class="dl-pagination"), _class="dl-navigation")) return dl
def aadata(self, totalrows, displayrows, id, draw, flist, stringify=True, action_col=None, **attr): """ Method to render the data into a json object @param totalrows: The total rows in the unfiltered query. @param displayrows: The total rows in the filtered query. @param id: The id of the table for which this ajax call will respond to. @param draw: An unaltered copy of draw sent from the client used by dataTables as a draw count. @param flist: The list of fields @param attr: dictionary of attributes which can be passed in dt_action_col: The column where the action buttons will be placed dt_bulk_actions: list of labels for the bulk actions. dt_bulk_col: The column in which the checkboxes will appear, by default it will be the column immediately before the first data item dt_group_totals: The number of record in each group. This will be displayed in parenthesis after the group title. """ data = self.data if not flist: flist = self.colnames start = self.start end = self.end if action_col is None: action_col = attr.get("dt_action_col", 0) structure = {} aadata = [] for i in xrange(start, end): row = data[i] details = [] for field in flist: if field == "BULK": details.append( "<INPUT id='select%s' type='checkbox' class='bulkcheckbox'>" % row[flist[action_col]] ) else: details.append(s3_unicode(row[field])) aadata.append(details) structure["dataTable_id"] = id structure["dataTable_filter"] = self.filterString structure["dataTable_groupTotals"] = attr.get("dt_group_totals", []) structure["dataTable_sort"] = self.orderby structure["data"] = aadata structure["recordsTotal"] = totalrows structure["recordsFiltered"] = displayrows structure["draw"] = draw if stringify: from gluon.serializers import json as jsons return jsons(structure) else: return structure
def set_priority_js(): """ Output json for priority field """ wptable = s3db.cap_warning_priority rows = db(wptable).select( wptable.name, wptable.urgency, wptable.severity, wptable.certainty, wptable.color_code, orderby=wptable.name ) from gluon.serializers import json as jsons p_settings = [(T(r.name), r.urgency, r.severity, r.certainty, r.color_code) for r in rows] priority_conf = """S3.cap_priorities=%s""" % jsons(p_settings) js_global = s3.js_global if not priority_conf in js_global: js_global.append(priority_conf) return
def configure(self, dashboard, context): """ Controller for the widget configuration dialog @param dashboard: the calling S3Dashboard instance @param context: the S3DashboardContext @return: output dict for the view """ response = current.response s3 = response.s3 # Get the form fields from the widget class prototype = self.widget formfields = prototype.configure(self) # The current configuration as formdata formdata = dict(self.config) formdata["id"] = 0 # Form buttons T = current.T submit_btn = INPUT( _class="tiny primary button submit-btn", _name="submit", _type="submit", _value=T("Submit"), ) buttons = [submit_btn] # Construct the form settings = s3.crud formstyle = settings.formstyle form = SQLFORM.factory(*formfields, record=formdata, showid=False, formstyle=formstyle, table_name="config", upload=s3.download_url, separator="", submit_button=settings.submit_button, buttons=buttons) # Process the form formname = "config/%s" % self.agent_id if form.accepts( context.post_vars, current.session, onvalidation=prototype.validate_config, formname=formname, keepvalues=False, hideerror=False, ): # Get an updated config dict from the widget widget_config = self.config widget_id = widget_config.get("widget_id") new_config = prototype.accept_config(widget_config, form) # Pass new config to client via s3.popup_data popup_data = {"c": new_config} # Save the new config and add the new version key to the popup_data if dashboard: version = dashboard.config.save(context, {widget_id: new_config}) if version: popup_data["v"] = version # Using JSON serializer rather than raw json.dumps to catch T()'s from gluon.serializers import json as jsons s3.popup_data = jsons(popup_data) # Send a confirmation so the popup gets closed # (layout.html diverts to layout_popup.html with # "popup" request format + response.confirmation) response.confirmation = T("Configuration updated") # Set view (@todo: implement specific view?) response.view = "popup.html" return {# Context needed for layout.html to determine # the representation format: "r": context, "form": form, }
def html( self, start=None, limit=None, pagesize=None, rowsize=None, ajaxurl=None, empty=None, popup_url=None, popup_title=None, ): """ Render list data as HTML (nested DIVs) @param start: index of the first item (in this page) @param limit: total number of available items @param pagesize: maximum number of items per page @param rowsize: number of items per row @param ajaxurl: the URL to Ajax-update the datalist @param popup_url: the URL for the modal used for the 'more' button (=> we deactivate InfiniteScroll) @param popup_title: the title for the modal """ T = current.T resource = self.resource list_fields = self.list_fields rfields = resource.resolve_selectors(list_fields)[0] list_id = self.list_id render = self.layout render_row = self.row_layout if not rowsize: rowsize = 1 pkey = str(resource._id) records = self.records if records is not None: # Call prep if present if hasattr(render, "prep"): render.prep(resource, records) if current.response.s3.dl_no_header: items = [] else: items = [DIV(T("Total Records: %(numrows)s") % \ {"numrows": self.total}, _class="dl-header", _id="%s-header" % list_id, ) ] if empty is None: empty = resource.crud.crud_string(resource.tablename, "msg_no_match") empty = DIV(empty, _class="dl-empty") if self.total > 0: empty.update(_style="display:none") items.append(empty) row_idx = int(self.start / rowsize) + 1 for group in self.groups(records, rowsize): row = [] col_idx = 0 for record in group: if pkey in record: item_id = "%s-%s" % (list_id, record[pkey]) else: # template item_id = "%s-[id]" % list_id item = render(list_id, item_id, resource, rfields, record) if hasattr(item, "add_class"): _class = "dl-item dl-%s-cols dl-col-%s" % (rowsize, col_idx) item.add_class(_class) row.append(item) col_idx += 1 _class = "dl-row %s" % ((row_idx % 2) and "even" or "odd") if render_row: row = render_row(list_id, resource, rowsize, row) if hasattr(row, "add_class"): row.add_class(_class) else: row = DIV(row, _class=_class) items.append(row) row_idx += 1 else: # template raise NotImplementedError dl = DIV( items, _class="dl", _id=list_id, ) dl_data = { "startindex": start, "maxitems": limit, "totalitems": self.total, "pagesize": pagesize, "rowsize": rowsize, "ajaxurl": ajaxurl, } if popup_url: input_class = "dl-pagination" a_class = "s3_modal dl-more" #dl_data["popup_url"] = popup_url #dl_data["popup_title"] = popup_title else: input_class = "dl-pagination dl-scroll" a_class = "dl-more" from gluon.serializers import json as jsons dl_data = jsons(dl_data) dl.append( DIV( INPUT( _type="hidden", _class=input_class, _value=dl_data, ), A( T("more..."), _href=popup_url or ajaxurl, _class=a_class, _title=popup_title, ), _class="dl-navigation", )) return dl
def aadata(self, totalrows, displayrows, id, draw, flist, stringify=True, action_col=None, **attr): """ Method to render the data into a json object @param totalrows: The total rows in the unfiltered query. @param displayrows: The total rows in the filtered query. @param id: The id of the table for which this ajax call will respond to. @param draw: An unaltered copy of draw sent from the client used by dataTables as a draw count. @param flist: The list of fields @param attr: dictionary of attributes which can be passed in dt_action_col: The column where the action buttons will be placed dt_bulk_actions: list of labels for the bulk actions. dt_bulk_col: The column in which the checkboxes will appear, by default it will be the column immediately before the first data item dt_group_totals: The number of record in each group. This will be displayed in parenthesis after the group title. """ data = self.data if not flist: flist = self.colnames start = self.start end = self.end if action_col is None: action_col = attr.get("dt_action_col", 0) structure = {} aadata = [] for i in xrange(start, end): row = data[i] details = [] for field in flist: if field == "BULK": details.append("<INPUT id='select%s' type='checkbox' class='bulkcheckbox'>" % \ row[flist[action_col]]) else: details.append(s3_unicode(row[field])) aadata.append(details) structure["dataTable_id"] = id structure["dataTable_filter"] = self.filterString structure["dataTable_groupTotals"] = attr.get("dt_group_totals", []) structure["dataTable_sort"] = self.orderby structure["data"] = aadata structure["recordsTotal"] = totalrows structure["recordsFiltered"] = displayrows structure["draw"] = draw if stringify: from gluon.serializers import json as jsons return jsons(structure) else: return structure
def htmlConfig(html, id, orderby, rfields=None, cache=None, **attr): """ Method to wrap the html for a dataTable in a form, add the export formats and the config details required by dataTables @param html: The html table @param id: The id of the table @param orderby: the sort details see http://datatables.net/reference/option/order @param rfields: The list of resource fields @param attr: dictionary of attributes which can be passed in dt_lengthMenu: The menu options for the number of records to be shown dt_pageLength : The default number of records that will be shown dt_dom : The Datatable DOM initialisation variable, describing the order in which elements are displayed. See http://datatables.net/ref for more details. dt_pagination : Is pagination enabled, dafault 'true' dt_pagingType : How the pagination buttons are displayed dt_searching: Enable or disable filtering of data. dt_ajax_url: The URL to be used for the Ajax call dt_action_col: The column where the action buttons will be placed dt_bulk_actions: list of labels for the bulk actions. dt_bulk_col: The column in which the checkboxes will appear, by default it will be the column immediately before the first data item dt_group: The column(s) that is(are) used to group the data dt_group_totals: The number of record in each group. This will be displayed in parenthesis after the group title. dt_group_titles: The titles to be used for each group. These are a list of lists with the inner list consisting of two values, the repr from the db and the label to display. This can be more than the actual number of groups (giving an empty group). dt_group_space: Insert a space between the group heading and the next group dt_bulk_selected: A list of selected items dt_actions: dictionary of actions dt_styles: dictionary of styles to be applied to a list of ids for example: {"warning" : [1,3,6,7,9], "alert" : [2,10,13]} dt_text_maximum_len: The maximum length of text before it is condensed dt_text_condense_len: The length displayed text is condensed down to dt_shrink_groups: If set then the rows within a group will be hidden two types are supported, 'individulal' and 'accordion' dt_group_types: The type of indicator for groups that can be 'shrunk' Permitted valies are: 'icon' (the default) 'text' and 'none' dt_base_url: base URL to construct export format URLs, resource default URL without any URL method or query part @global current.response.s3.actions used to get the RowActions """ from gluon.serializers import json as jsons s3 = current.response.s3 settings = current.deployment_settings dataTableID = s3.dataTableID if not dataTableID or not isinstance(dataTableID, list): dataTableID = s3.dataTableID = [id] elif id not in dataTableID: dataTableID.append(id) # The configuration parameter from the server to the client will be # sent in a json object stored in an hidden input field. This object # will then be parsed by s3.dataTable.js and the values used. config = Storage() config.id = id _aget = attr.get config.dom = _aget("dt_dom", settings.get_ui_datatables_dom()) config.lengthMenu = _aget( "dt_lengthMenu", [[25, 50, -1], [25, 50, str(current.T("All"))]]) config.pageLength = _aget("dt_pageLength", s3.ROWSPERPAGE) config.pagination = _aget("dt_pagination", "true") config.pagingType = _aget("dt_pagingType", settings.get_ui_datatables_pagingType()) config.searching = _aget("dt_searching", "true") ajaxUrl = _aget("dt_ajax_url", None) if not ajaxUrl: request = current.request url = URL( c=request.controller, f=request.function, args=request.args, vars=request.get_vars, ) ajaxUrl = s3_set_extension(url, "aadata") config.ajaxUrl = ajaxUrl config.rowStyles = _aget("dt_styles", []) rowActions = _aget("dt_row_actions", s3.actions) if rowActions: config.rowActions = rowActions else: config.rowActions = [] bulkActions = _aget("dt_bulk_actions", None) if bulkActions and not isinstance(bulkActions, list): bulkActions = [bulkActions] config.bulkActions = bulkActions config.bulkCol = bulkCol = _aget("dt_bulk_col", 0) action_col = _aget("dt_action_col", 0) if bulkActions and bulkCol <= action_col: action_col += 1 config.actionCol = action_col group_list = _aget("dt_group", []) if not isinstance(group_list, list): group_list = [group_list] dt_group = [] for group in group_list: if bulkActions and bulkCol <= group: group += 1 if action_col >= group: group -= 1 dt_group.append([group, "asc"]) config.group = dt_group config.groupTotals = _aget("dt_group_totals", []) config.groupTitles = _aget("dt_group_titles", []) config.groupSpacing = _aget("dt_group_space", "false") for order in orderby: if bulkActions: if bulkCol <= order[0]: order[0] += 1 if action_col > 0 and action_col >= order[0]: order[0] -= 1 config.order = orderby config.textMaxLength = _aget("dt_text_maximum_len", 80) config.textShrinkLength = _aget("dt_text_condense_len", 75) config.shrinkGroupedRows = _aget("dt_shrink_groups", "false") config.groupIcon = _aget("dt_group_types", []) # Wrap the table in a form and add some data in hidden fields form = FORM(_class="dt-wrapper") if not s3.no_formats: # @todo: move export-format update into drawCallback() # @todo: poor UX with onclick-JS, better to render real # links which can be bookmarked, and then update them # in drawCallback() permalink = _aget("dt_permalink", None) base_url = _aget("dt_base_url", None) export_formats = S3DataTable.export_formats(rfields, permalink=permalink, base_url=base_url) # Nb These can be moved around in initComplete() form.append(export_formats) form.append(html) # Add the configuration details for this dataTable form.append( INPUT(_type="hidden", _id="%s_configurations" % id, _name="config", _value=jsons(config))) # If we have a cache set up then pass it in if cache: form.append( INPUT(_type="hidden", _id="%s_dataTable_cache" % id, _name="cache", _value=jsons(cache))) # If we have bulk actions then add the hidden fields if bulkActions: form.append( INPUT(_type="hidden", _id="%s_dataTable_bulkMode" % id, _name="mode", _value="Inclusive")) bulk_selected = _aget("dt_bulk_selected", "") if isinstance(bulk_selected, list): bulk_selected = ",".join(bulk_selected) form.append( INPUT(_type="hidden", _id="%s_dataTable_bulkSelection" % id, _name="selected", _value="[%s]" % bulk_selected)) form.append( INPUT(_type="hidden", _id="%s_dataTable_filterURL" % id, _class="dataTable_filterURL", _name="filterURL", _value="%s" % config.ajaxUrl)) # Set callback? initComplete = settings.get_ui_datatables_initComplete() if initComplete: # Processed in views/dataTables.html s3.dataTable_initComplete = initComplete return form
def json(self, resource, start=None, limit=None, fields=None, orderby=None, represent=False, tooltip=None): """ Export a resource as JSON @param resource: the resource to export from @param start: index of the first record to export @param limit: maximum number of records to export @param fields: list of field selectors for fields to include in the export (None for all fields) @param orderby: ORDERBY expression @param represent: whether values should be represented @param tooltip: additional tooltip field, either a field selector or an expression "f(k,v)" where f is a function name that can be looked up from s3db, and k,v are field selectors for the row, f will be called with a list of tuples (k,v) for each row and is expected to return a dict {k:tooltip} => used by filterOptionsS3 to extract onhover-tooltips for Ajax-update of options """ if fields is None: # json_fields falls back to list_fields if not-defined. # If that's not defined either, it falls back to all readable fields in the table fields = resource.list_fields("json_fields", id_column=True) if orderby is None: orderby = resource.get_config("orderby", None) tooltip_function = None if tooltip: if type(tooltip) is list: tooltip = tooltip[-1] import re match = re.match("(\w+)\((\w+),(\w+)\)", tooltip) if match: function_name, kname, vname = match.groups() # Try to resolve the function name tooltip_function = current.s3db.get(function_name) if tooltip_function: if kname not in fields: fields.append(kname) if vname not in fields: fields.append(vname) else: if tooltip not in fields: fields.append(tooltip) # Get the data _rows = resource.select(fields, start=start, limit=limit, orderby=orderby, represent=represent).rows # Simplify to plain fieldnames for fields in this table tn = "%s." % resource.tablename rows = [] rappend = rows.append for _row in _rows: row = {} for f in _row: v = _row[f] if tn in f: f = f.split(tn, 1)[1] row[f] = v rappend(row) if tooltip: if tooltip_function: # Resolve key and value names against the resource try: krfield = resource.resolve_selector(kname) vrfield = resource.resolve_selector(vname) except (AttributeError, SyntaxError): import sys current.log.error(sys.exc_info()[1]) else: # Extract key and value fields from each row and # build options dict for function call options = [] items = {} for row in rows: try: k = krfield.extract(row) except KeyError: break try: v = vrfield.extract(row) except KeyError: break items[k] = row options.append((k, v)) # Call tooltip rendering function try: tooltips = tooltip_function(options) except: import sys current.log.error(sys.exc_info()[1]) else: # Add tooltips as "_tooltip" to the corresponding rows if isinstance(tooltips, dict): from s3utils import s3_unicode for k, v in tooltips.items(): if k in items: items[k]["_tooltip"] = s3_unicode(v) else: # Resolve the tooltip field name against the resource try: tooltip_rfield = resource.resolve_selector(tooltip) except (AttributeError, SyntaxError): import sys current.log.error(sys.exc_info()[1]) else: # Extract the tooltip field from each row # and add it as _tooltip from s3utils import s3_unicode for row in rows: try: value = tooltip_rfield.extract(row) except KeyError: break if value: row["_tooltip"] = s3_unicode(value) # Return as JSON response = current.response if response: response.headers["Content-Type"] = "application/json" from gluon.serializers import json as jsons return jsons(rows)
def tour_builder(output): """ Helper function to attach a guided tour (if required) to the output """ auth = current.auth db = current.db s3db = current.s3db request = current.request s3 = current.response.s3 T = current.T req_vars = request.vars tour_id = req_vars.tour # Now see if the details are on the database for this user tour = None user_id = None if auth.is_logged_in(): user_id = auth.s3_logged_in_person() # Find out if the user has done this tour before utable = s3db.tour_user uquery = (utable.person_id == user_id) & \ (utable.tour_config_id == tour_id) tour = db(uquery).select(utable.id, utable.completed, utable.place, utable.resume, limitby=(0, 1)).first() # If the tour has just been started (from the menu) then # it may be necessary to redirect to a different controller # @todo: does place need to be changed to controller and function? if not req_vars.tour_running: if (tour and not tour.completed and tour.place != request.controller): redirect("%s?tour=%s" %(tour.resume, tour_id)) # get the details from the database dtable = s3db.tour_details dquery = (dtable.tour_config_id == tour_id) &\ (dtable.controller == request.controller) &\ (dtable.function == request.function) details = db(dquery).select(dtable.args, dtable.tip_title, dtable.tip_details, dtable.button, dtable.tip_location, dtable.html_id, dtable.html_class, dtable.datatable_id, dtable.datatable_row, dtable.redirect, orderby=(dtable.posn) ) # tour_filename = os.path.join(request.folder, # "private", # "tour", # tour_name) # tour_file = open (tour_filename, "rb") # # now open the details of the guided_tour into a dictionary # import csv # tour_details = csv.DictReader(tour_file, skipinitialspace=True) # load the list of tour items in the html joyride_OL = OL(_id="joyrideID_1") pre_step_data = [] post_step_data = [] post_ride_data = [] last_row = None last_used = None req_args = request.args cnt = -1 for row in details: if row.args: args = row.args.split(",") else: args = [] # if the page has a nested login form then "login" will be added to # the req_args list so it needs to be added to the args list as well if "login" in req_args: if "login" not in args: args.append("login") # The following will capture the actual id used for the req_arg # Example org/organisation/10, where 10 is the id from the database posn = 0 for arg in args: if arg == "dt_id": args[posn] = req_args[posn] posn += 1 # Now check that the tour url matches the current url if (args == req_args): cnt += 1 # number of records used in this part of the tour if row.datatable_id: dt_id = row.datatable_id # cols = [] # if "DataTable_columns" in row: # cols = row["DataTable_columns"].split(",") row_num = 0 if row.datatable_row: row_num = row.datatable_row # Now set this up for the pre-processor hook in joyride pre_step_data.append([cnt, dt_id, row_num]) if row.redirect: redirect_row = row.redirect.split(",") if len(redirect_row) >= 3: url = URL(c=redirect_row[0], f=redirect_row[1], args=redirect_row[2:], vars={"tour_running":True, "tour":tour_id} ) if "dt_id" in redirect_row[2]: post_step_data.append([cnt, url, dt_id, row_num]) elif len(redirect_row) == 2: url = URL(c=redirect_row[0], f=redirect_row[1], vars={"tour_running":True, "tour":tour_id} ) post_step_data.append([cnt, url]) else: url = URL(c=redirect_row[0],vars={"tour_running":True, "tour":tour_id}) post_step_data.append([cnt, url]) extra = {} if row.html_id: extra["_data-id"] = row.html_id elif row.html_class: extra["_data-class"] = row.html_class if row.button: extra["_data-button"] = row.button else: extra["_data-button"] = "Next" if row.tip_location: extra["_data-options"] = "tipLocation:%s" % row.tip_location.lower() else: extra["_data-options"] = "tipLocation:right" joyride_OL.append(LI(H2(T(row.tip_title)), P(T(row.tip_details)), **extra ) ) last_used = row last_row = row # The following redirect will be triggered if the user has moved away # from the tour, such as by clicking on a tab. However if a tab # is part of the tour we are unable to determine if they have moved # away or just visiting as part of the tour and so it will continue. if len(joyride_OL) == 0: del request.vars.tour redirect(URL(args=req_args, vars=request.vars)) if (user_id != None) and (last_row == last_used): # set up an AJAX call to record that the tour has been completed post_ride_data = [cnt, tour_id] joyride_div = DIV(joyride_OL, _class="hidden") # Add the javascript configuration data from gluon.serializers import json as jsons if pre_step_data: joyride_div.append(INPUT(_type="hidden", _id="prestep_data", _name="prestep_data", _value=jsons(pre_step_data)) ) if post_step_data: joyride_div.append(INPUT(_type="hidden", _id="poststep_data", _name="poststep_data", _value=jsons(post_step_data)) ) if post_ride_data: joyride_div.append(INPUT(_type="hidden", _id="postride_data", _name="postride_data", _value=jsons(post_ride_data)) ) # Now add the details to the tour_user table if user_id != None: if tour == None: # this user has never done this tour before so create a new record utable.insert(person_id = user_id, tour_config_id = tour_id, place = request.controller, resume = request.url) else: # the user has done some of this tour so update the record db(uquery).update(place = request.controller, resume = request.url, completed = False) output["joyride_div"] = joyride_div if s3.debug: appname = request.application s3.scripts.append("/%s/static/scripts/jquery.joyride.js" % appname) s3.scripts.append("/%s/static/scripts/S3/s3.guidedtour.js" % appname) s3.stylesheets.append("plugins/joyride.min.css") else: s3.scripts.append("/%s/static/scripts/S3/s3.guidedtour.min.js" % request.application) s3.stylesheets.append("plugins/joyride.css") return output
def configure(self, dashboard, context): """ Controller for the widget configuration dialog @param dashboard: the calling S3Dashboard instance @param context: the S3DashboardContext @return: output dict for the view """ response = current.response s3 = response.s3 # Get the form fields from the widget class prototype = self.widget formfields = prototype.configure(self) # The current configuration as formdata formdata = dict(self.config) formdata["id"] = 0 # Form buttons T = current.T submit_btn = INPUT(_class = "tiny primary button submit-btn", _name = "submit", _type = "submit", _value = T("Submit"), ) buttons = [submit_btn] # Construct the form settings = s3.crud formstyle = settings.formstyle form = SQLFORM.factory(*formfields, record = formdata, showid = False, formstyle = formstyle, table_name = "config", upload = s3.download_url, separator = "", submit_button = settings.submit_button, buttons = buttons) # Process the form formname = "config/%s" % self.agent_id if form.accepts(context.post_vars, current.session, onvalidation = prototype.validate_config, formname = formname, keepvalues = False, hideerror = False, ): # Get an updated config dict from the widget widget_config = self.config widget_id = widget_config.get("widget_id") new_config = prototype.accept_config(widget_config, form) # Pass new config to client via s3.popup_data popup_data = {"c": new_config} # Save the new config and add the new version key to the popup_data if dashboard: version = dashboard.config.save(context, {widget_id: new_config}) if version: popup_data["v"] = version # Using JSON serializer rather than raw json.dumps to catch T()'s from gluon.serializers import json as jsons s3.popup_data = jsons(popup_data) # Send a confirmation so the popup gets closed # (layout.html diverts to layout_popup.html with # "popup" request format + response.confirmation) response.confirmation = T("Configuration updated") # Set view (@todo: implement specific view?) response.view = "popup.html" return {# Context needed for layout.html to determine # the representation format: "r": context, "form": form, }
def html(self, start=None, limit=None, pagesize=None, rowsize=None, ajaxurl=None, empty=None, popup_url=None, popup_title=None, ): """ Render list data as HTML (nested DIVs) @param start: index of the first item (in this page) @param limit: total number of available items @param pagesize: maximum number of items per page @param rowsize: number of items per row @param ajaxurl: the URL to Ajax-update the datalist @param empty: message to display if the list is empty @param popup_url: the URL for the modal used for the 'more' button (=> we deactivate InfiniteScroll) @param popup_title: the title for the modal """ T = current.T resource = self.resource list_fields = self.list_fields rfields = resource.resolve_selectors(list_fields)[0] list_id = self.list_id render = self.layout render_row = self.row_layout if not rowsize: rowsize = 1 pkey = str(resource._id) records = self.records if records is not None: # Call prep if present if hasattr(render, "prep"): render.prep(resource, records) if current.response.s3.dl_no_header: items = [] else: items = [DIV(T("Total Records: %(numrows)s") % \ {"numrows": self.total}, _class="dl-header", _id="%s-header" % list_id, ) ] if empty is None: empty = resource.crud.crud_string(resource.tablename, "msg_no_match") empty = DIV(empty, _class="dl-empty") if self.total > 0: empty.update(_style="display:none") items.append(empty) row_idx = int(self.start / rowsize) + 1 for group in self.groups(records, rowsize): row = [] col_idx = 0 for record in group: if pkey in record: item_id = "%s-%s" % (list_id, record[pkey]) else: # template item_id = "%s-[id]" % list_id item = render(list_id, item_id, resource, rfields, record) if hasattr(item, "add_class"): _class = "dl-item dl-%s-cols dl-col-%s" % (rowsize, col_idx) item.add_class(_class) row.append(item) col_idx += 1 _class = "dl-row %s" % ((row_idx % 2) and "even" or "odd") if render_row: row = render_row(list_id, resource, rowsize, row) if hasattr(row, "add_class"): row.add_class(_class) else: row = DIV(row, _class=_class) items.append(row) row_idx += 1 else: # template raise NotImplementedError dl = DIV(items, _class="dl", _id=list_id, ) dl_data = {"startindex": start, "maxitems": limit, "totalitems": self.total, "pagesize": pagesize, "rowsize": rowsize, "ajaxurl": ajaxurl, } if popup_url: input_class = "dl-pagination" a_class = "s3_modal dl-more" #dl_data["popup_url"] = popup_url #dl_data["popup_title"] = popup_title else: input_class = "dl-pagination dl-scroll" a_class = "dl-more" from gluon.serializers import json as jsons dl_data = jsons(dl_data) dl.append(DIV(INPUT(_type="hidden", _class=input_class, _value=dl_data, ), A(T("more..."), _href = popup_url or ajaxurl, _class = a_class, _title = popup_title, ), _class="dl-navigation", )) return dl
def htmlConfig(html, id, orderby, rfields = None, cache = None, **attr ): """ Method to wrap the html for a dataTable in a form, add the export formats and the config details required by dataTables @param html: The html table @param id: The id of the table @param orderby: the sort details see http://datatables.net/reference/option/order @param rfields: The list of resource fields @param attr: dictionary of attributes which can be passed in dt_lengthMenu: The menu options for the number of records to be shown dt_pageLength : The default number of records that will be shown dt_dom : The Datatable DOM initialisation variable, describing the order in which elements are displayed. See http://datatables.net/ref for more details. dt_pagination : Is pagination enabled, dafault 'true' dt_pagingType : How the pagination buttons are displayed dt_searching: Enable or disable filtering of data. dt_ajax_url: The URL to be used for the Ajax call dt_action_col: The column where the action buttons will be placed dt_bulk_actions: list of labels for the bulk actions. dt_bulk_col: The column in which the checkboxes will appear, by default it will be the column immediately before the first data item dt_group: The column(s) that is(are) used to group the data dt_group_totals: The number of record in each group. This will be displayed in parenthesis after the group title. dt_group_titles: The titles to be used for each group. These are a list of lists with the inner list consisting of two values, the repr from the db and the label to display. This can be more than the actual number of groups (giving an empty group). dt_group_space: Insert a space between the group heading and the next group dt_bulk_selected: A list of selected items dt_actions: dictionary of actions dt_styles: dictionary of styles to be applied to a list of ids for example: {"warning" : [1,3,6,7,9], "alert" : [2,10,13]} dt_text_maximum_len: The maximum length of text before it is condensed dt_text_condense_len: The length displayed text is condensed down to dt_shrink_groups: If set then the rows within a group will be hidden two types are supported, 'individual' and 'accordion' dt_group_types: The type of indicator for groups that can be 'shrunk' Permitted valies are: 'icon' (the default) 'text' and 'none' dt_base_url: base URL to construct export format URLs, resource default URL without any URL method or query part @global current.response.s3.actions used to get the RowActions """ from gluon.serializers import json as jsons s3 = current.response.s3 settings = current.deployment_settings dataTableID = s3.dataTableID if not dataTableID or not isinstance(dataTableID, list): dataTableID = s3.dataTableID = [id] elif id not in dataTableID: dataTableID.append(id) # The configuration parameter from the server to the client will be # sent in a json object stored in an hidden input field. This object # will then be parsed by s3.dataTable.js and the values used. config = Storage() config.id = id _aget = attr.get config.dom = _aget("dt_dom", settings.get_ui_datatables_dom()) config.lengthMenu = _aget("dt_lengthMenu", [[25, 50, -1], [25, 50, s3_str(current.T("All"))] ] ) config.pageLength = _aget("dt_pageLength", s3.ROWSPERPAGE) config.pagination = _aget("dt_pagination", "true") config.pagingType = _aget("dt_pagingType", settings.get_ui_datatables_pagingType()) config.searching = _aget("dt_searching", "true") ajaxUrl = _aget("dt_ajax_url", None) if not ajaxUrl: request = current.request url = URL(c=request.controller, f=request.function, args=request.args, vars=request.get_vars, ) ajaxUrl = s3_set_extension(url, "aadata") config.ajaxUrl = ajaxUrl config.rowStyles = _aget("dt_styles", []) rowActions = _aget("dt_row_actions", s3.actions) if rowActions: config.rowActions = rowActions else: config.rowActions = [] bulkActions = _aget("dt_bulk_actions", None) if bulkActions and not isinstance(bulkActions, list): bulkActions = [bulkActions] config.bulkActions = bulkActions config.bulkCol = bulkCol = _aget("dt_bulk_col", 0) action_col = _aget("dt_action_col", 0) if bulkActions and bulkCol <= action_col: action_col += 1 config.actionCol = action_col group_list = _aget("dt_group", []) if not isinstance(group_list, list): group_list = [group_list] dt_group = [] for group in group_list: if bulkActions and bulkCol <= group: group += 1 if action_col >= group: group -= 1 dt_group.append([group, "asc"]) config.group = dt_group config.groupTotals = _aget("dt_group_totals", []) config.groupTitles = _aget("dt_group_titles", []) config.groupSpacing = _aget("dt_group_space", "false") for order in orderby: if bulkActions: if bulkCol <= order[0]: order[0] += 1 if action_col > 0 and action_col >= order[0]: order[0] -= 1 config.order = orderby config.textMaxLength = _aget("dt_text_maximum_len", 80) config.textShrinkLength = _aget("dt_text_condense_len", 75) config.shrinkGroupedRows = _aget("dt_shrink_groups", "false") config.groupIcon = _aget("dt_group_types", []) # Wrap the table in a form and add some data in hidden fields form = FORM(_class="dt-wrapper") if not s3.no_formats: # @todo: move export-format update into drawCallback() # @todo: poor UX with onclick-JS, better to render real # links which can be bookmarked, and then update them # in drawCallback() permalink = _aget("dt_permalink", None) base_url = _aget("dt_base_url", None) export_formats = S3DataTable.export_formats(rfields, permalink=permalink, base_url=base_url) # Nb These can be moved around in initComplete() form.append(export_formats) form.append(html) # Add the configuration details for this dataTable form.append(INPUT(_type="hidden", _id="%s_configurations" % id, _name="config", _value=jsons(config))) # If we have a cache set up then pass it in if cache: form.append(INPUT(_type="hidden", _id="%s_dataTable_cache" %id, _name="cache", _value=jsons(cache))) # If we have bulk actions then add the hidden fields if bulkActions: form.append(INPUT(_type="hidden", _id="%s_dataTable_bulkMode" % id, _name="mode", _value="Inclusive")) bulk_selected = _aget("dt_bulk_selected", "") if isinstance(bulk_selected, list): bulk_selected = ",".join(bulk_selected) form.append(INPUT(_type="hidden", _id="%s_dataTable_bulkSelection" % id, _name="selected", _value="[%s]" % bulk_selected)) form.append(INPUT(_type="hidden", _id="%s_dataTable_filterURL" % id, _class="dataTable_filterURL", _name="filterURL", _value="%s" % config.ajaxUrl)) # Set callback? initComplete = settings.get_ui_datatables_initComplete() if initComplete: # Processed in views/dataTables.html s3.dataTable_initComplete = initComplete return form