S3Profile: Profile Pages
Table of Contents
A 'profile' page is designed as a way of representing a single record, along with various related resources (typically Components).
It consists of a Header, along with a number of different Widgets:
- Map
- displays component resources, can be made fullscreen, layers update as new records are added
- DataList ('cards')
- can add new records and the list will refresh accordingly
- DataTable
- can add new records and the list will refresh accordingly
- Form
- Report
- Comments (to come...)
The profile page is accessed by calling the 'profile' method of the record:
e.g.:
If there are no Widgets defined, then the profile method falls back to the simpler 'read' view for the resource (which can still include Components on Tabs if these are defined in the rheader)
Configuration
The profile pages are configured by configuring a few profile_* variables and storing these against the tablename with the usual s3db.configure(tablename, key=value)
, e.g.:
s3db.configure(tablename, profile_title = title, profile_header = DIV(H2(title), _class="profile_header", ), profile_cols = 4 profile_widgets = [alerts_widget, resources_widget, tasks_widget, map_widget, ], profile_layers = [layer], profile_update = True, )
profile_title
is a simple string to be used as the response.title of the page (which will show in the browser title)- if not defined, this falls-back to
r.record.name
orT("Profile Page")
- if not defined, this falls-back to
profile_header
is HTML prepared using Web2Py's HTML helpers (gluon/html.py
). It can be simple (as it is here) or can be as complex as you wish.- if not defined, this falls-back to
H2(title, _class="profile_header")
- if not defined, this falls-back to
profile_cols
is an integer showing how many columns the widgets are spread across- if not defined, this falls-back to 2
profile_widgets
are the Widgets (see below)profile_layers
are additional layers to display on the map, defined like:layer = dict(name = record_name, id = "profile-header-%s-%s" % (tablename, record_id), active = True, tablename = tablename, url = "/%s/event/incident.geojson?incident.id=%s" % \ (r.application, record_id), marker = marker, )
profile_update
embeds an update form for the master record below the profile header, possible values are False (no update form), "hidden" or True (render update form, initially hidden), or "visible" (render update form, initially visible)
Widget configuration
Here is an example configuration for a map widget:
map_widget = dict(label = "Map", type = "map", context = "incident", icon = "icon-map", height = 600, width = 200, colspan = 1, )
label
is used as a display title of the widgettype
identifies this as a Map widgetcontext
is used as a filter for the map layers. See S3/Contexticon
is used as an icon for the widgetheight
andwidth
are used to size the map viewportcolspan
says how many columns are taken up by the widget
Here is an example configuration for a datalist widget:
alerts_widget = dict(label = "Alerts", label_create = "Create Alert", type = "datalist", tablename = "cms_post", context = "incident", # Only show Active Alerts filter = S3FieldSelector("expired") == False, icon = "icon-alert", colspan = 1, #list_layout = s3db.cms_post_list_layout, )
Most of the options are the same as for the map widget, extra ones include:
label_create
is used as a title for the popup with the create formtablename
is used to show which resource is used for this datalistfilter
is used as a filter for the resource (usually an additional filter on top of the Context, although it can replace the context if-desired)list_layout
can define an alternate card renderer (falls-back to the defaults3db.get_config(tablename, "list_layout")
)
NB You should ensure that your create forms establish the context link automatically. This is usually done by checking for a context filter, and then setting table.field.default, e.g.:
incident_id = get_vars.get("~.(incident)", None) if incident_id: field = table.incident_id field.default = incident_id field.readable = field.writable = False
If the link is via a link table then you need to add the link onaccept, e.g.:
incident_id = r.get_vars.get("~.(incident)", None) if incident_id: # Coming from Profile page # Add link onaccept def create_onaccept(form): current.s3db.event_post.insert(incident_id=incident_id, post_id=form.vars.id) s3db.configure("cms_post", create_onaccept = create_onaccept, )
Code
The core functionality is in modules/s3/s3profile.py
although this is a very small file as most of the work is in the surrounding framework of S3REST and in the Widgets.
The hook to link the 'profile' method to this class is in s3_rest_controller()
in models/00_utils.py
:
set_handler("profile", s3base.S3Profile)
The default view template is in views/_profile.html
but this can be over-ridden for a sepcific template as modules/templates/mytemplate/views/_profile.html
Widget code
DataList
S3Profile._datalist()
(modules/s3/s3profile.py
) instantiates an S3DataList()
(modules/s3/s3data.py
) using resource.datalist()
and datalist.html()
.
Client-side JavaScript is in static/scripts/S3/s3.dataTables.js
.
DataTable
S3Profile._datatable()
(modules/s3/s3profile.py
) instantiates an S3DataTable()
(modules/s3/s3data.py
) using resource.datatable()
and datatable.html()
.
Client-side JavaScript is in static/scripts/S3/s3.dataLists.js
.
Map
S3Profile._map()
(modules/s3/s3profile.py
) instantiates an S3Map()
(modules/s3/s3gis.py
) with a layer for each widget which is a datalist, datatable or report (unless that widget is configured show_on_map=False
).
Client-side JavaScript is in static/scripts/S3/s3.gis.js
.
Form
S3Profile._form()
(modules/s3/s3profile.py
) instantiates an S3SQLForm()
(modules/s3/s3forms.py
).
If the form has Inline components, then the client-side JavaScript for this is in static/scripts/S3/s3.inline_component.js
.
Report
S3Profile._report()
(modules/s3/s3profile.py
) instantiates an S3Report()
(modules/s3/s3data.py
) using report.widget()
.
Client-side JavaScript is in static/scripts/S3/s3.jquery.ui.pivottable.js
.
Future Extensions
- Allow component resources to be filtered
- Comments widget
- Use widget() function of target method handler instead of internal widgets (like report does already)
See Also
- Summary Pages - the standard multi-record view