def __init__(self, simple=None, advanced=None, any=False, **args): """ Constructor @param simple: the widgets for the simple search form as list @param advanced: the widgets for the advanced search form as list @param any: match "any of" (True) or "all of" (False) the options in advanced search """ S3CRUD.__init__(self) args = Storage(args) if simple is None: if "field" in args: if "name" in args: name = args.name elif "_name" in args: name = args._name else: name = "search_simple" simple = S3SearchSimpleWidget(field=args.field, name=name, label=args.label, comment=args.comment) names = [] self.__simple = [] if not isinstance(simple, (list, tuple)): simple = [simple] for widget in simple: if widget is not None: name = widget.attr._name if name in names: raise SyntaxError("Duplicate widget: %s") % name elif not name: raise SyntaxError("Widget with no name") else: self.__simple.append((name, widget)) names.append(name) names = [] self.__advanced = [] if not isinstance(advanced, (list, tuple)): advanced = [advanced] for widget in advanced: if widget is not None: name = widget.attr._name if name in names: raise SyntaxError("Duplicate widget: %s") % name elif not name: raise SyntaxError("Widget with no name") else: self.__advanced.append((name, widget)) names.append(name) self.__any = any if self.__simple or self.__advanced: self.__interactive = True else: self.__interactive = False
def __init__(self, simple=None, advanced=None, any=False): """ Constructor @param simple: the widgets for the simple search form as list @param advanced: the widgets for the advanced search form as list @param any: match "any of" (True) or "all of" (False) the options in advanced search """ S3CRUD.__init__(self) names = [] self.__simple = [] if not isinstance(simple, (list, tuple)): simple = [simple] for widget in simple: if widget is not None: name = widget.attr._name if name in names: raise SyntaxError("Duplicate widget: %s") % name elif not name: raise SyntaxError("Widget with no name") else: self.__simple.append((name, widget)) names.append(name) names = [] self.__advanced = [] if not isinstance(advanced, (list, tuple)): advanced = [advanced] for widget in advanced: if widget is not None: name = widget.attr._name if name in names: raise SyntaxError("Duplicate widget: %s") % name elif not name: raise SyntaxError("Widget with no name") else: self.__advanced.append((name, widget)) names.append(name) self.__any = any if self.__simple or self.__advanced: self.__interactive = True else: self.__interactive = False
def __init__(self, label=None, comment=None, fields=None): """ Constructor """ S3CRUD.__init__(self) self.__label = label self.__comment = comment self.__fields = fields if self.__fields: self.interactive_search = True else: self.interactive_search = False
def defaultActionButtons(resource, custom_actions=None, r=None): """ Configure default action buttons @param resource: the resource @param r: the request, if specified, all action buttons will be linked to the controller/function of this request rather than to prefix/name of the resource @param custom_actions: custom actions as list of dicts like {"label":label, "url":url, "_class":class}, will be appended to the default actions """ from s3crud import S3CRUD auth = current.auth actions = current.response.s3.actions table = resource.table actions = None has_permission = auth.s3_has_permission ownership_required = auth.permission.ownership_required labels = current.manager.LABEL args = ["[id]"] # Choose controller/function to link to if r is not None: c = r.controller f = r.function else: c = resource.prefix f = resource.name # "Open" button if has_permission("update", table) and \ not ownership_required("update", table): update_url = URL(c=c, f=f, args=args + ["update"]) S3CRUD.action_button(labels.UPDATE, update_url) else: read_url = URL(c=c, f=f, args=args) S3CRUD.action_button(labels.READ, read_url) # Delete action # @todo: does not apply selective action (renders DELETE for # all items even if the user is only permitted to delete # some of them) => should implement "restrict", see # S3CRUD.action_buttons deletable = current.s3db.get_config(resource.tablename, "deletable", True) if deletable and \ has_permission("delete", table) and \ not ownership_required("delete", table): delete_url = URL(c=c, f=f, args=args + ["delete"]) S3CRUD.action_button(labels.DELETE, delete_url) # Append custom actions if custom_actions: actions = actions + custom_actions if actions else custom_actions
def defaultActionButtons(resource, custom_actions=None, r=None): """ Configure default action buttons @param resource: the resource @param r: the request, if specified, all action buttons will be linked to the controller/function of this request rather than to prefix/name of the resource @param custom_actions: custom actions as list of dicts like {"label":label, "url":url, "_class":class}, will be appended to the default actions """ from s3crud import S3CRUD auth = current.auth actions = current.response.s3.actions table = resource.table actions = None has_permission = auth.s3_has_permission ownership_required = auth.permission.ownership_required labels = current.manager.LABEL args = ["[id]"] # Choose controller/function to link to if r is not None: c = r.controller f = r.function else: c = resource.prefix f = resource.name # "Open" button if has_permission("update", table) and not ownership_required("update", table): update_url = URL(c=c, f=f, args=args + ["update"]) S3CRUD.action_button(labels.UPDATE, update_url) else: read_url = URL(c=c, f=f, args=args) S3CRUD.action_button(labels.READ, read_url) # Delete action # @todo: does not apply selective action (renders DELETE for # all items even if the user is only permitted to delete # some of them) => should implement "restrict", see # S3CRUD.action_buttons deletable = current.s3db.get_config(resource.tablename, "deletable", True) if deletable and has_permission("delete", table) and not ownership_required("delete", table): delete_url = URL(c=c, f=f, args=args + ["delete"]) S3CRUD.action_button(labels.DELETE, delete_url) # Append custom actions if custom_actions: actions = actions + custom_actions if actions else custom_actions
def _datalist(self, r, widget, **attr): """ Generate a dataList @param r: the S3Request instance @param widget: the widget as a tuple: (label, tablename, icon, filter) @param attr: controller attributes for the request """ T = current.T s3db = current.s3db id = r.id context = widget.get("context", None) if context: context = self._resolve_context(context, id) s3db.context = context tablename = widget.get("tablename", None) resource = s3db.resource(tablename, context=True) table = resource.table # Config Options: # 1st choice: Widget # 2nd choice: get_config # 3rd choice: Default config = resource.get_config list_fields = widget.get("list_fields", config("list_fields", None)) list_layout = widget.get("list_layout", config("list_layout", None)) orderby = widget.get("orderby", config("list_orderby", ~resource.table.created_on)) filter = widget.get("filter", None) if filter: resource.add_filter(filter) # Use the widget-index to create a unique ID listid = "profile-list-%s-%s" % (tablename, widget["index"]) # Page size pagesize = widget.get("pagesize", 4) representation = r.representation if representation == "dl": # Ajax-update get_vars = r.get_vars record_id = get_vars.get("record", None) if record_id is not None: # Ajax-update of a single record resource.add_filter(S3FieldSelector("id") == record_id) start, limit = 0, 1 else: # Ajax-update of full page start = get_vars.get("start", None) limit = get_vars.get("limit", None) if limit is not None: try: start = int(start) limit = int(limit) except ValueError: start, limit = 0, pagesize else: start = None else: # Page-load start, limit = 0, pagesize # Ajax-delete items? if representation == "dl" and r.http in ("DELETE", "POST"): if "delete" in r.get_vars: return self._dl_ajax_delete(r, resource) else: r.error(405, r.ERROR.BAD_METHOD) # dataList datalist, numrows, ids = resource.datalist( fields=list_fields, start=start, limit=limit, listid=listid, orderby=orderby, layout=list_layout ) # Render the list ajaxurl = r.url(vars={"update": widget["index"]}, representation="dl") data = datalist.html( ajaxurl=ajaxurl, pagesize=pagesize, empty=P( I(_class="icon-folder-open-alt"), BR(), S3CRUD.crud_string(tablename, "msg_no_match"), _class="empty_card-holder", ), ) if representation == "dl": # This is an Ajax-request, so we don't need the wrapper current.response.view = "plain.html" return data # Interactive only below here label = widget.get("label", "") if label: label = T(label) icon = widget.get("icon", "") if icon: icon = TAG[""](I(_class=icon), " ") # Permission to create new items? create = "" insert = widget.get("insert", True) if insert and current.auth.s3_has_permission("create", table): # if r.tablename = "org_organisation": # @ToDo: Special check for creating resources on Organisation profile if filter: vars = filter.serialize_url(filter) else: vars = Storage() vars.refresh = listid if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] default = widget.get("default", None) if default: k, v = default.split("=", 1) vars[k] = v title_create = widget.get("title_create", None) if title_create: title_create = T(title_create) else: title_create = S3CRUD.crud_string(tablename, "title_create") c, f = tablename.split("_", 1) c = widget.get("create_controller", c) f = widget.get("create_function", f) add_url = URL(c=c, f=f, args=["create.popup"], vars=vars) if callable(insert): create = insert(r, title_create, add_url) else: create = A( I(_class="icon icon-plus-sign small-add"), _href=add_url, _class="s3_modal", _title=title_create ) multiple = widget.get("multiple", True) if not multiple and hasattr(create, "update"): # If this is a multiple=False widget and we already # have a record, we hide the create-button if numrows: create.update(_style="display:none;") else: create.update(_style="display:block;") # Script to hide/unhide the create-button on Ajax # list updates createid = create["_id"] if not createid: createid = "%s-add-button" % listid create.update(_id=createid) script = """ $('#%(listid)s').on('listUpdate', function() { $('#%(createid)s').css({display: $(this).datalist('getTotalItems') ? 'none' : 'block'}); });""" % dict( listid=listid, createid=createid ) current.response.s3.jquery_ready.append(script) if pagesize and numrows > pagesize: # Button to display the rest of the records in a Modal more = numrows - pagesize vars = {} if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] if filter: filters = filter.serialize_url(resource) for f in filters: vars[f] = filters[f] c, f = tablename.split("_", 1) url = URL(c=c, f=f, args=["datalist.popup"], vars=vars) more = DIV( A( BUTTON("%s (%s)" % (T("see more"), more), _class="btn btn-mini", _type="button"), _class="s3_modal", _href=url, _title=label, ), _class="more_profile", ) else: more = "" # Render the widget output = DIV( create, H4(icon, label, _class="profile-sub-header"), DIV(data, more, _class="card-holder"), _class="span6" ) return output
def _datalist(self, r, widget, **attr): """ Generate a data list @param r: the S3Request instance @param widget: the widget definition as dict @param attr: controller attributes for the request """ T = current.T s3db = current.s3db context = widget.get("context", None) tablename = widget.get("tablename", None) resource, context = self._resolve_context(r, tablename, context) # Config Options: # 1st choice: Widget # 2nd choice: get_config # 3rd choice: Default config = resource.get_config list_fields = widget.get("list_fields", config("list_fields", None)) list_layout = widget.get("list_layout", config("list_layout", None)) orderby = widget.get("orderby", config("list_orderby", ~resource.table.created_on)) filter = widget.get("filter", None) if filter: resource.add_filter(filter) # Use the widget-index to create a unique ID list_id = "profile-list-%s-%s" % (tablename, widget["index"]) # Page size pagesize = widget.get("pagesize", 4) representation = r.representation if representation == "dl": # Ajax-update get_vars = r.get_vars record_id = get_vars.get("record", None) if record_id is not None: # Ajax-update of a single record resource.add_filter(S3FieldSelector("id") == record_id) start, limit = 0, 1 else: # Ajax-update of full page start = get_vars.get("start", None) limit = get_vars.get("limit", None) if limit is not None: try: start = int(start) limit = int(limit) except ValueError: start, limit = 0, pagesize else: start = None else: # Page-load start, limit = 0, pagesize # Ajax-delete items? if representation == "dl" and r.http in ("DELETE", "POST"): if "delete" in r.get_vars: return self._dl_ajax_delete(r, resource) else: r.error(405, current.ERROR.BAD_METHOD) # dataList datalist, numrows, ids = resource.datalist(fields=list_fields, start=start, limit=limit, list_id=list_id, orderby=orderby, layout=list_layout) # Render the list ajaxurl = r.url(vars={"update": widget["index"]}, representation="dl") data = datalist.html(ajaxurl=ajaxurl, pagesize=pagesize, empty = P(I(_class="icon-folder-open-alt"), BR(), S3CRUD.crud_string(tablename, "msg_no_match"), _class="empty_card-holder" ), ) if representation == "dl": # This is an Ajax-request, so we don't need the wrapper current.response.view = "plain.html" return data # Interactive only below here label = widget.get("label", "") if label: label = T(label) icon = widget.get("icon", "") if icon: icon = TAG[""](I(_class=icon), " ") s3 = current.response.s3 if pagesize and numrows > pagesize: # Button to display the rest of the records in a Modal more = numrows - pagesize vars = {} if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] if filter: filters = filter.serialize_url(resource) for f in filters: vars[f] = filters[f] c, f = tablename.split("_", 1) url = URL(c=c, f=f, args=["datalist.popup"], vars=vars) more = DIV(A(BUTTON("%s (%s)" % (T("see more"), more), _class="btn btn-mini", _type="button", ), _class="s3_modal", _href=url, _title=label, ), _class="more_profile") else: more = "" # Link for create-popup create_popup = self._create_popup(r, widget, list_id, resource, context, numrows) colspan = widget.get("colspan", 1) if colspan == 1: _class = "span6" elif colspan == 2: _class = "span12" else: # Unsupported raise # Render the widget output = DIV(create_popup, H4(icon, label, _class="profile-sub-header"), DIV(data, more, _class="card-holder"), _class=_class) return output
def _create_popup(self, r, widget, list_id, resource, context, numrows): """ Render an action link for a create-popup (used in data lists and data tables). @param r: the S3Request instance @param widget: the widget definition as dict @param list_id: the list ID @param resource: the target resource @param context: the context filter @param numrows: the total number of rows in the list/table """ create = "" insert = widget.get("insert", True) table = resource.table if insert and current.auth.s3_has_permission("create", table): s3 = current.response.s3 tablename = resource.tablename #if tablename = "org_organisation": # @ToDo: Special check for creating resources on Organisation profile # URL-serialize the widget filter widget_filter = widget.get("filter") if widget_filter: vars = widget_filter.serialize_url(resource) else: vars = Storage() # URL-serialize the context filter if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] # URL-serialize the widget default default = widget.get("default") if default: k, v = default.split("=", 1) vars[k] = v # URL-serialize the list ID (refresh-target of the popup) vars.refresh = list_id # CRUD string title_create = widget.get("title_create", None) if title_create: title_create = current.T(title_create) else: title_create = S3CRUD.crud_string(tablename, "title_create") # Popup URL # Default to primary REST controller for the resource being added c, f = tablename.split("_", 1) c = widget.get("create_controller", c) f = widget.get("create_function", f) component = widget.get("create_component", None) if component: args = [r.id, component, "create.popup"] else: args = ["create.popup"] add_url = URL(c=c, f=f, args=args, vars=vars) if callable(insert): # Custom widget create = insert(r, list_id, title_create, add_url) elif s3.crud.formstyle == "bootstrap": # Bootstrap-style action icon create = A(I(_class="icon icon-plus-sign small-add"), _href=add_url, _class="s3_modal", _title=title_create, ) else: # Standard action button create = A(title_create, _href=add_url, _class="action-btn profile-add-btn s3_modal", ) if widget.get("type") == "datalist": # If this is a multiple=False widget and we already # have a record, we hide the create-button multiple = widget.get("multiple", True) if not multiple and hasattr(create, "update"): if numrows: create.update(_style="display:none;") else: create.update(_style="display:block;") # Script to hide/unhide the create-button on Ajax # list updates createid = create["_id"] if not createid: createid = "%s-add-button" % list_id create.update(_id=createid) script = \ '''$('#%(list_id)s').on('listUpdate',function(){ $('#%(createid)s').css({display:$(this).datalist('getTotalItems')?'none':'block'}) })''' % dict(list_id=list_id, createid=createid) s3.jquery_ready.append(script) return create
def _create_popup(r, widget, list_id, resource, context, numrows): """ Render an action link for a create-popup (used in data lists and data tables). @param r: the S3Request instance @param widget: the widget definition as dict @param list_id: the list ID @param resource: the target resource @param context: the context filter @param numrows: the total number of rows in the list/table """ create = "" insert = widget.get("insert", True) table = resource.table tablename = resource.tablename # Default to primary REST controller for the resource being added c, f = tablename.split("_", 1) c = widget.get("create_controller", c) f = widget.get("create_function", f) if insert and \ current.auth.s3_has_permission("create", table, c=c, f=f): #if tablename = "org_organisation": # @ToDo: Special check for creating resources on Organisation profile # URL-serialize the widget filter widget_filter = widget.get("filter") if widget_filter: url_vars = widget_filter.serialize_url(resource) else: url_vars = Storage() # URL-serialize the context filter if context: filters = context.serialize_url(resource) for selector in filters: url_vars[selector] = filters[selector] # URL-serialize the widget default default = widget.get("default") if default: k, v = default.split("=", 1) url_vars[k] = v # URL-serialize the list ID (refresh-target of the popup) url_vars.refresh = list_id # Indicate that popup comes from profile (and which) url_vars.profile = r.tablename # CRUD string label_create = widget.get("label_create", None) if label_create: label_create = current.T(label_create) else: label_create = S3CRUD.crud_string(tablename, "label_create") # Popup URL component = widget.get("create_component", None) if component: args = [r.id, component, "create.popup"] else: args = ["create.popup"] add_url = URL(c=c, f=f, args=args, vars=url_vars) if callable(insert): # Custom widget create = insert(r, list_id, label_create, add_url) elif current.deployment_settings.ui.formstyle == "bootstrap": # Bootstrap-style action icon create = A(ICON("plus-sign", _class="small-add"), _href=add_url, _class="s3_modal", _title=label_create, ) else: # Standard action button create = A(label_create, _href=add_url, _class="action-btn profile-add-btn s3_modal", ) if widget.get("type") == "datalist": # If this is a multiple=False widget and we already # have a record, we hide the create-button multiple = widget.get("multiple", True) if not multiple and hasattr(create, "update"): if numrows: create.update(_style="display:none;") else: create.update(_style="display:block;") # Script to hide/unhide the create-button on Ajax # list updates createid = create["_id"] if not createid: createid = "%s-add-button" % list_id create.update(_id=createid) script = \ '''$('#%(list_id)s').on('listUpdate',function(){ $('#%(createid)s').css({display:$(this).datalist('getTotalItems')?'none':'block'}) })''' % dict(list_id=list_id, createid=createid) current.response.s3.jquery_ready.append(script) return create
def _datalist(self, r, widget, **attr): """ Generate a data list @param r: the S3Request instance @param widget: the widget definition as dict @param attr: controller attributes for the request """ T = current.T context = widget.get("context", None) tablename = widget.get("tablename", None) resource, context = self._resolve_context(r, tablename, context) # Config Options: # 1st choice: Widget # 2nd choice: get_config # 3rd choice: Default config = resource.get_config list_fields = widget.get("list_fields", config("list_fields", None)) list_layout = widget.get("list_layout", config("list_layout", None)) orderby = widget.get("orderby", config("list_orderby", config("orderby", ~resource.table.created_on))) filter = widget.get("filter", None) if filter: resource.add_filter(filter) # Use the widget-index to create a unique ID list_id = "profile-list-%s-%s" % (tablename, widget["index"]) # Page size pagesize = widget.get("pagesize", 4) representation = r.representation if representation == "dl": # Ajax-update get_vars = r.get_vars record_id = get_vars.get("record", None) if record_id is not None: # Ajax-update of a single record resource.add_filter(FS("id") == record_id) start, limit = 0, 1 else: # Ajax-update of full page start = get_vars.get("start", None) limit = get_vars.get("limit", None) if limit is not None: try: start = int(start) limit = int(limit) except ValueError: start, limit = 0, pagesize else: start = None else: # Page-load start, limit = 0, pagesize # Ajax-delete items? if representation == "dl" and r.http in ("DELETE", "POST"): if "delete" in r.get_vars: return self._dl_ajax_delete(r, resource) else: r.error(405, current.ERROR.BAD_METHOD) # dataList datalist, numrows, ids = resource.datalist(fields=list_fields, start=start, limit=limit, list_id=list_id, orderby=orderby, layout=list_layout) # Render the list ajaxurl = r.url(vars={"update": widget["index"]}, representation="dl") data = datalist.html(ajaxurl=ajaxurl, pagesize=pagesize, empty = P(ICON("folder-open-alt"), BR(), S3CRUD.crud_string(tablename, "msg_no_match"), _class="empty_card-holder" ), ) if representation == "dl": # This is an Ajax-request, so we don't need the wrapper current.response.view = "plain.html" return data # Interactive only below here label = widget.get("label", "") if label: label = T(label) icon = widget.get("icon", "") if icon: icon = ICON(icon) if pagesize and numrows > pagesize: # Button to display the rest of the records in a Modal more = numrows - pagesize get_vars_new = {} if context: filters = context.serialize_url(resource) for f in filters: get_vars_new[f] = filters[f] if filter: filters = filter.serialize_url(resource) for f in filters: get_vars_new[f] = filters[f] c, f = tablename.split("_", 1) f = widget.get("function", f) url = URL(c=c, f=f, args=["datalist.popup"], vars=get_vars_new) more = DIV(A(BUTTON("%s (%s)" % (T("see more"), more), _class="btn btn-mini", _type="button", ), _class="s3_modal", _href=url, _title=label, ), _class="more_profile") else: more = "" # Link for create-popup create_popup = self._create_popup(r, widget, list_id, resource, context, numrows) _class = self._lookup_class(r, widget) # Render the widget output = DIV(create_popup, H4(icon, label, _class="profile-sub-header"), DIV(data, more, _class="card-holder"), _class=_class) return output
def _datatable(self, r, widget, **attr): """ Generate a data table. @param r: the S3Request instance @param widget: the widget definition as dict @param attr: controller attributes for the request @todo: fix export formats """ # Parse context context = widget.get("context") tablename = widget.get("tablename") resource, context = self._resolve_context(r, tablename, context) # List fields list_fields = widget.get("list_fields") if not list_fields: # @ToDo: Set the parent so that the fkey gets removed from the list_fields #resource.parent = s3db.resource("") list_fields = resource.list_fields() # Widget filter option widget_filter = widget.get("filter") if widget_filter: resource.add_filter(widget_filter) # Use the widget-index to create a unique ID list_id = "profile-list-%s-%s" % (tablename, widget["index"]) # Default ORDERBY # - first field actually in this table def default_orderby(): for f in list_fields: selector = f[1] if isinstance(f, tuple) else f if selector == "id": continue rfield = resource.resolve_selector(selector) if rfield.field: return rfield.field return None # Pagination representation = r.representation get_vars = self.request.get_vars if representation == "aadata": start = get_vars.get("displayStart", None) limit = get_vars.get("pageLength", 0) else: start = get_vars.get("start", None) limit = get_vars.get("limit", 0) if limit: if limit.lower() == "none": limit = None else: try: start = int(start) limit = int(limit) except (ValueError, TypeError): start = None limit = 0 # use default else: # Use defaults start = None dtargs = attr.get("dtargs", {}) if r.interactive: s3 = current.response.s3 # How many records per page? if s3.dataTable_pageLength: display_length = s3.dataTable_pageLength else: display_length = widget.get("pagesize", 10) dtargs["dt_lengthMenu"] = [[10, 25, 50, -1], [10, 25, 50, str(current.T("All"))]] # ORDERBY fallbacks: widget->resource->default orderby = widget.get("orderby") if not orderby: orderby = resource.get_config("orderby") if not orderby: orderby = default_orderby() # Server-side pagination? if not s3.no_sspag: dt_pagination = "true" if not limit and display_length is not None: limit = 2 * display_length else: limit = None else: dt_pagination = "false" # Get the data table dt, totalrows, ids = resource.datatable(fields=list_fields, start=start, limit=limit, orderby=orderby) displayrows = totalrows if dt.empty: empty_str = self.crud_string(tablename, "msg_list_empty") else: empty_str = self.crud_string(tablename, "msg_no_match") empty = DIV(empty_str, _class="empty") dtargs["dt_pagination"] = dt_pagination dtargs["dt_pageLength"] = display_length # @todo: fix base URL (make configurable?) to fix export options s3.no_formats = True dtargs["dt_base_url"] = r.url(method="", vars={}) dtargs["dt_ajax_url"] = r.url(vars={"update": widget["index"]}, representation="aadata") actions = widget.get("actions") if callable(actions): actions = actions(r, list_id) if actions: dtargs["dt_row_actions"] = actions datatable = dt.html(totalrows, displayrows, id=list_id, **dtargs) if dt.data: empty.update(_style="display:none") else: datatable.update(_style="display:none") contents = DIV(datatable, empty, _class="dt-contents") # Link for create-popup create_popup = self._create_popup(r, widget, list_id, resource, context, totalrows) # Card holder label and icon label = widget.get("label", "") # Activate if-required #if label and isinstance(label, basestring): if label: label = current.T(label) else: label = S3CRUD.crud_string(tablename, "title_list") icon = widget.get("icon", "") if icon: icon = ICON(icon) _class = self._lookup_class(r, widget) # Render the widget output = DIV(create_popup, H4(icon, label, _class="profile-sub-header"), DIV(contents, _class="card-holder"), _class=_class) return output elif representation == "aadata": # Parse datatable filter/sort query searchq, orderby, left = resource.datatable_filter( list_fields, get_vars) # ORDERBY fallbacks - datatable->widget->resource->default if not orderby: orderby = widget.get("orderby") if not orderby: orderby = resource.get_config("orderby") if not orderby: orderby = default_orderby() # DataTable filtering if searchq is not None: totalrows = resource.count() resource.add_filter(searchq) else: totalrows = None # Get the data table if totalrows != 0: dt, displayrows, ids = resource.datatable(fields=list_fields, start=start, limit=limit, left=left, orderby=orderby, getids=False) else: dt, displayrows = None, 0 if totalrows is None: totalrows = displayrows # Echo draw = int(get_vars.draw or 0) # Representation if dt is not None: data = dt.json(totalrows, displayrows, list_id, draw, **dtargs) else: data = '{"recordsTotal":%s,' \ '"recordsFiltered":0,' \ '"dataTable_id":"%s",' \ '"draw":%s,' \ '"data":[]}' % (totalrows, list_id, draw) return data else: # Really raise an exception here? r.error(415, current.ERROR.BAD_FORMAT)
def _datatable(self, r, widget, **attr): """ Generate a data table. @param r: the S3Request instance @param widget: the widget definition as dict @param attr: controller attributes for the request @todo: fix export formats """ # Parse context context = widget.get("context") tablename = widget.get("tablename") resource, context = self._resolve_context(r, tablename, context) # List fields list_fields = widget.get("list_fields") if not list_fields: # @ToDo: Set the parent so that the fkey gets removed from the list_fields #resource.parent = s3db.resource("") list_fields = resource.list_fields() # Widget filter option widget_filter = widget.get("filter") if widget_filter: resource.add_filter(widget_filter) # Use the widget-index to create a unique ID list_id = "profile-list-%s-%s" % (tablename, widget["index"]) # Default ORDERBY # - first field actually in this table def default_orderby(): for f in list_fields: selector = f[1] if isinstance(f, tuple) else f if selector == "id": continue rfield = resource.resolve_selector(selector) if rfield.field: return rfield.field return None # Pagination representation = r.representation get_vars = self.request.get_vars if representation == "aadata": start = get_vars.get("displayStart", None) limit = get_vars.get("pageLength", 0) else: start = get_vars.get("start", None) limit = get_vars.get("limit", 0) if limit: if limit.lower() == "none": limit = None else: try: start = int(start) limit = int(limit) except (ValueError, TypeError): start = None limit = 0 # use default else: # Use defaults start = None dtargs = attr.get("dtargs", {}) if r.interactive: s3 = current.response.s3 # How many records per page? if s3.dataTable_pageLength: display_length = s3.dataTable_pageLength else: display_length = widget.get("pagesize", 10) dtargs["dt_lengthMenu"] = [[10, 25, 50, -1], [10, 25, 50, str(current.T("All"))] ] # ORDERBY fallbacks: widget->resource->default orderby = widget.get("orderby") if not orderby: orderby = resource.get_config("orderby") if not orderby: orderby = default_orderby() # Server-side pagination? if not s3.no_sspag: dt_pagination = "true" if not limit and display_length is not None: limit = 2 * display_length else: limit = None else: dt_pagination = "false" # Get the data table dt, totalrows, ids = resource.datatable(fields=list_fields, start=start, limit=limit, orderby=orderby) displayrows = totalrows if dt.empty: empty_str = self.crud_string(tablename, "msg_list_empty") else: empty_str = self.crud_string(tablename, "msg_no_match") empty = DIV(empty_str, _class="empty") dtargs["dt_pagination"] = dt_pagination dtargs["dt_pageLength"] = display_length # @todo: fix base URL (make configurable?) to fix export options s3.no_formats = True dtargs["dt_base_url"] = r.url(method="", vars={}) dtargs["dt_ajax_url"] = r.url(vars={"update": widget["index"]}, representation="aadata") actions = widget.get("actions") if callable(actions): actions = actions(r, list_id) if actions: dtargs["dt_row_actions"] = actions datatable = dt.html(totalrows, displayrows, id=list_id, **dtargs) if dt.data: empty.update(_style="display:none") else: datatable.update(_style="display:none") contents = DIV(datatable, empty, _class="dt-contents") # Link for create-popup create_popup = self._create_popup(r, widget, list_id, resource, context, totalrows) # Card holder label and icon label = widget.get("label", "") # Activate if-required #if label and isinstance(label, basestring): if label: label = current.T(label) else: label = S3CRUD.crud_string(tablename, "title_list") icon = widget.get("icon", "") if icon: icon = ICON(icon) _class = self._lookup_class(r, widget) # Render the widget output = DIV(create_popup, H4(icon, label, _class="profile-sub-header"), DIV(contents, _class="card-holder"), _class=_class) return output elif representation == "aadata": # Parse datatable filter/sort query searchq, orderby, left = resource.datatable_filter(list_fields, get_vars) # ORDERBY fallbacks - datatable->widget->resource->default if not orderby: orderby = widget.get("orderby") if not orderby: orderby = resource.get_config("orderby") if not orderby: orderby = default_orderby() # DataTable filtering if searchq is not None: totalrows = resource.count() resource.add_filter(searchq) else: totalrows = None # Get the data table if totalrows != 0: dt, displayrows, ids = resource.datatable(fields=list_fields, start=start, limit=limit, left=left, orderby=orderby, getids=False) else: dt, displayrows = None, 0 if totalrows is None: totalrows = displayrows # Echo draw = int(get_vars.draw or 0) # Representation if dt is not None: data = dt.json(totalrows, displayrows, list_id, draw, **dtargs) else: data = '{"recordsTotal":%s,' \ '"recordsFiltered":0,' \ '"dataTable_id":"%s",' \ '"draw":%s,' \ '"data":[]}' % (totalrows, list_id, draw) return data else: # Really raise an exception here? r.error(415, current.ERROR.BAD_FORMAT)
def _create_popup(r, widget, list_id, resource, context, numrows): """ Render an action link for a create-popup (used in data lists and data tables). @param r: the S3Request instance @param widget: the widget definition as dict @param list_id: the list ID @param resource: the target resource @param context: the context filter @param numrows: the total number of rows in the list/table """ create = "" insert = widget.get("insert", True) if not insert: return create table = resource.table tablename = resource.tablename # Default to primary REST controller for the resource being added c, f = tablename.split("_", 1) create_controller = widget.get("create_controller") if create_controller: c = create_controller create_function = widget.get("create_function") if create_function: f = create_function permit = current.auth.s3_has_permission create_ok = permit("create", table, c=c, f=f) if create_ok: if not create_controller or not create_function: # Assume not component context create_ok = permit("update", r.table, record_id=r.id, c=c, f=f) if create_ok: #if tablename = "org_organisation": # @ToDo: Special check for creating resources on Organisation profile # URL-serialize the widget filter widget_filter = widget.get("filter") if widget_filter: url_vars = widget_filter.serialize_url(resource) else: url_vars = Storage() # URL-serialize the context filter if context: filters = context.serialize_url(resource) for selector in filters: url_vars[selector] = filters[selector] # URL-serialize the widget default default = widget.get("default") if default: k, v = default.split("=", 1) url_vars[k] = v # URL-serialize the list ID (refresh-target of the popup) url_vars.refresh = list_id # Indicate that popup comes from profile (and which) url_vars.profile = r.tablename # CRUD string label_create = widget.get("label_create", None) # Activate if-required #if label_create and isinstance(label_create, basestring): if label_create: label_create = current.T(label_create) else: label_create = S3CRUD.crud_string(tablename, "label_create") # Popup URL component = widget.get("create_component", None) if component: args = [r.id, component, "create.popup"] else: args = ["create.popup"] add_url = URL(c=c, f=f, args=args, vars=url_vars) if callable(insert): # Custom widget create = insert(r, list_id, label_create, add_url) elif current.deployment_settings.ui.formstyle == "bootstrap": # Bootstrap-style action icon create = A(ICON("plus-sign", _class="small-add"), _href=add_url, _class="s3_modal", _title=label_create, ) else: # Standard action button create = A(label_create, _href=add_url, _class="action-btn profile-add-btn s3_modal", ) if widget.get("type") == "datalist": # If this is a multiple=False widget and we already # have a record, we hide the create-button multiple = widget.get("multiple", True) if not multiple and hasattr(create, "update"): if numrows: create.update(_style="display:none") else: create.update(_style="display:block") # Script to hide/unhide the create-button on Ajax # list updates createid = create["_id"] if not createid: createid = "%s-add-button" % list_id create.update(_id=createid) script = \ '''$('#%(list_id)s').on('listUpdate',function(){ $('#%(createid)s').css({display:$(this).datalist('getTotalItems')?'none':'block'}) })''' % dict(list_id=list_id, createid=createid) current.response.s3.jquery_ready.append(script) return create
def _datalist(self, r, widget, **attr): """ Generate a dataList @param r: the S3Request instance @param widget: the widget as a tuple: (label, tablename, icon, filter) @param attr: controller attributes for the request """ T = current.T s3db = current.s3db id = r.id context = widget.get("context", None) if context: context = self._resolve_context(context, id) s3db.context = context tablename = widget.get("tablename", None) resource = s3db.resource(tablename, context=True) table = resource.table # Config Options: # 1st choice: Widget # 2nd choice: get_config # 3rd choice: Default config = resource.get_config list_fields = widget.get("list_fields", config("list_fields", None)) list_layout = widget.get("list_layout", config("list_layout", None)) orderby = widget.get( "orderby", config("list_orderby", ~resource.table.created_on)) filter = widget.get("filter", None) if filter: resource.add_filter(filter) # Use the widget-index to create a unique ID listid = "profile-list-%s-%s" % (tablename, widget["index"]) # Page size pagesize = 4 representation = r.representation if representation == "dl": # Ajax-update get_vars = r.get_vars record_id = get_vars.get("record", None) if record_id is not None: # Ajax-update of a single record resource.add_filter(S3FieldSelector("id") == record_id) start, limit = 0, 1 else: # Ajax-update of full page start = get_vars.get("start", None) limit = get_vars.get("limit", None) if limit is not None: try: start = int(start) limit = int(limit) except ValueError: start, limit = 0, pagesize else: start = None else: # Page-load start, limit = 0, pagesize # Ajax-delete items? if representation == "dl" and r.http in ("DELETE", "POST"): if "delete" in r.get_vars: return self._dl_ajax_delete(r, resource) else: r.error(405, r.ERROR.BAD_METHOD) # dataList datalist, numrows, ids = resource.datalist(fields=list_fields, start=start, limit=limit, listid=listid, orderby=orderby, layout=list_layout) # Render the list ajaxurl = r.url(vars={"update": widget["index"]}, representation="dl") data = datalist.html(ajaxurl=ajaxurl, pagesize=pagesize) if numrows == 0: msg = P(I(_class="icon-folder-open-alt"), BR(), S3CRUD.crud_string(tablename, "msg_no_match"), _class="empty_card-holder") data.insert(1, msg) if representation == "dl": # This is an Ajax-request, so we don't need the wrapper current.response.view = "plain.html" return data # Interactive only below here label = widget.get("label", "") if label: label = T(label) icon = widget.get("icon", "") if icon: icon = TAG[""](I(_class=icon), " ") # Permission to create new items? insert = widget.get("insert", True) if insert and current.auth.s3_has_permission("create", table): #if r.tablename = "org_organisation": # @ToDo: Special check for creating resources on Organisation profile if filter: vars = filter.serialize_url(filter) else: vars = Storage() vars.refresh = listid if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] default = widget.get("default", None) if default: k, v = default.split("=", 1) vars[k] = v title_create = widget.get("title_create", None) if title_create: title_create = T(title_create) else: title_create = S3CRUD.crud_string(tablename, "title_create") c, f = tablename.split("_", 1) c = widget.get("create_controller", c) f = widget.get("create_function", f) create = A( I(_class="icon icon-plus-sign small-add"), _href=URL(c=c, f=f, args=["create.popup"], vars=vars), _class="s3_modal", _title=title_create, ) else: create = "" if numrows > pagesize: # Button to display the rest of the records in a Modal more = numrows - pagesize vars = {} if context: filters = context.serialize_url(resource) for f in filters: vars[f] = filters[f] if filter: filters = filter.serialize_url(resource) for f in filters: vars[f] = filters[f] c, f = tablename.split("_", 1) url = URL(c=c, f=f, args=["datalist.popup"], vars=vars) more = DIV(A( BUTTON( "%s (%s)" % (T("see more"), more), _class="btn btn-mini", _type="button", ), _class="s3_modal", _href=url, _title=label, ), _class="more_profile") else: more = "" # Render the widget output = DIV(create, H4(icon, label, _class="profile-sub-header"), DIV(data, more, _class="card-holder"), _class="span6") return output
def _datalist(self, r, widget, **attr): """ Generate a dataList @param r: the S3Request instance @param widget: the widget as a tuple: (label, tablename, icon, filter) @param attr: controller attributes for the request """ context = widget.get("context", None) if context: context = "(%s)" % context current.s3db.context = S3FieldSelector(context) == r.id tablename = widget.get("tablename", None) resource = current.s3db.resource(tablename, context=True) table = resource.table # Config Options: # 1st choice: Widget # 2nd choice: get_config # 3rd choice: Default config = resource.get_config list_fields = widget.get("list_fields", config("list_fields", None)) list_layout = widget.get("list_layout", config("list_layout", None)) orderby = widget.get("orderby", config("list_orderby", ~resource.table.created_on)) filter = widget.get("filter", None) if filter: resource.add_filter(filter) # Use the widget-index to create a unique ID listid = "profile-list-%s-%s" % (tablename, widget["index"]) c, f = tablename.split("_", 1) # Permission to create new items? # @ToDo: Special check for creating resources on Organisation profile if current.auth.s3_has_permission("create", table): if filter: vars = filter.serialize_url(filter) else: vars = Storage() vars.refresh = listid if context: vars[context] = r.id create = A(I(_class="icon icon-plus-sign small-add"), _href=URL(c=c, f=f, args=["create.popup"], vars=vars), _class="s3_modal", ) else: create = "" # Page size pagesize = 4 representation = r.representation if representation == "dl": # Ajax-update get_vars = r.get_vars record_id = get_vars.get("record", None) if record_id is not None: # Ajax-update of a single record resource.add_filter(S3FieldSelector("id") == record_id) start, limit = 0, 1 else: # Ajax-update of full page start = get_vars.get("start", None) limit = get_vars.get("limit", None) if limit is not None: try: start = int(start) limit = int(limit) except ValueError: start, limit = 0, 4 else: start = None else: # Page-load start, limit = 0, 4 # Ajax-delete items? if representation == "dl" and r.http in ("DELETE", "POST"): if "delete" in r.get_vars: return self._dl_ajax_delete(r, resource) else: r.error(405, r.ERROR.BAD_METHOD) # dataList datalist, numrows, ids = resource.datalist(fields=list_fields, start=start, limit=limit, listid=listid, orderby=orderby, layout=list_layout) # Render the list ajaxurl = r.url(vars={"update": widget["index"]}, representation="dl") data = datalist.html(ajaxurl=ajaxurl, pagesize=pagesize) if numrows == 0: msg = P(I(_class="icon-folder-open-alt"), BR(), S3CRUD.crud_string(resource.tablename, "msg_no_match"), _class="empty_card-holder") data.insert(1, msg) if representation == "dl": # This is an Ajax-request, so we don't need the wrapper current.response.view = "plain.html" return data label = widget.get("label", "") if label: label = current.T(label) icon = widget.get("icon", "") if icon: icon = TAG[""](I(_class=icon), " ") # Render the widget output = DIV(create, H4(icon, label, _class="profile-sub-header"), DIV(data, _class="card-holder"), _class="span6") return output