= S3Profile: Profile Pages = [[TOC]] A 'profile' page is designed as a way of representing a single record, along with various related resources (typically [wiki:S3/S3Model/ComponentResources 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: * http://host.domain/eden/module/resource/record_id/profile e.g.: * http://host.domain/eden/pr/person/1/profile 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 [wiki:DeveloperGuidelines/Tutorial/RESTCustomisation#PutitonaTab 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.: {{{#!python 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}}} or {{{T("Profile Page")}}} * {{{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")}}} * {{{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: {{{#!python 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: {{{#!python 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 widget * {{{type}}} identifies this as a Map widget * {{{context}}} is used as a filter for the map layers. See [wiki:S3/Context] * {{{icon}}} is used as an icon for the widget * {{{height}}} and {{{width}}} are used to size the map viewport * {{{colspan}}} says how many columns are taken up by the widget Here is an example configuration for a datalist widget: {{{#!python 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 form * {{{tablename}}} is used to show which resource is used for this datalist * {{{filter}}} 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 default {{{s3db.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.: {{{#!python 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.: {{{#!python 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 [wiki:S3/S3REST 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}}}: {{{#!python 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()}}}.[[BR]] 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()}}}.[[BR]] 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}}}).[[BR]] 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}}}).[[BR]] 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()}}}.[[BR]] 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 == * [wiki:S3/S3Summary Summary Pages] - the standard multi-record view