[[TOC]] = Sahana Eden Framework = We have built an S3 framework as a higher level of abstraction on top of Web2Py.[[BR]] This should be used by modules where possible, but if more power is needed then drop down a level of two (to T2, to base Web2Py or to raw Python). == REST Controller == We extend Web2Py's CRUD with a [wiki:RESTController REST Controller] & [wiki:S3XRC S3XRC].[[BR]] This provides details on how to configure your Model & Controllers. Views may not be required, other than index.html gluon/tools.py is used for: * [wiki:DeveloperGuidelinesAuthenticationAccess AAA] * simplified [wiki:DeveloperGuidelinesCreateReadUpdateDelete CRUD] T2 is used for: * search We extend the Auth/Crud/T2 classes in {{{modules/sahana.py}}}.[[BR]] Patches to the framework should be done here.[[BR]] If they're generic enough then they can be suggested to Massimo for moving upstream to Web2Py. === Custom Functions Plugged into REST === In many cases it may be much easier to implement non-CRUD resource functions as REST-plugins instead of separate controllers. * see search_simple for an example The advantage of doing it as REST plugin is that you have a pre-parsed XRequest instead of the original request, as well as you'd preserve the actions and setting from your REST interface for this resource. XRequest is already checked and parsed for the REST syntax, and is resolved for the format extension (XRequest.representation) and record ID's, contains all handles to the necessary tables as well as the complete primary record (if any). It contains the interface to export or import the resource in XML and JSON (.import_xml() and .export_xml() resp. _json()), and of course you'd have the nice backlink helpers to quickly produce URL's: {{{ .here() (=URL of the current resource), .same() (=URL of the current resource, record ID replaced as "[id]"), .there() (=URL of the current resource, no function, no record ID), and .other() (=URL of the current resource, other function) }}} and finally, XRequest remembers the record ID until the next request to the same resource in the same session, which can be helpful when producing links (especially _next's) not knowing the record ID. == Mandatory == Each Controller should start like this: {{{ module = "module" # Options Menu (available in all Functions' Views) response.menu_options = [ [T("Home"), False, URL(r=request, f="index")], [ [T("Resources"), False, URL(r=request, f="resource")], [T("List"), False, URL(r=request, f="person")], [T("Add"), False, URL(r=request, f="resource", args="create")], [T("Search People"), False, URL(r=request, f="resource", args="search")] ]] ] }}} === Conflict Detection === Sahana is a multi-user system so there is a potential for multiple users to be editing the same record at once.[[BR]] We use Web2Py's CRUD to handle this for us. Add the {{{timestamp}}} reusable field to each table which needs protecting (in {{{models/module.py}}}): === Synchronization === All tables which are user-editable need to be protected for conflict resolution & synchronization using the predefined reusable fields {{{timestamp}}} & {{{uuidstamp}}}. * [wiki:DeveloperGuidelinesDatabaseSynchronization Database Synchronization] == Optional == Other reusable fields are available to add Foreign Key links to key tables, such as 'location_id' to add a GIS location for Mapping, 'person_id' to add a Person & 'organisation_id' to add a link to an Organisation. List output are made more functional by this .represent 'widget': {{{ def shn_list_item(table, resource, action, display="table.name", extra=None): if extra: items = DIV(TR(TD(A(eval(display), _href=t2.action(resource, [action, table.id]))), TD(eval(extra)))) else: items = DIV(A(eval(display), _href=t2.action(resource, [action, table.id]))) return DIV(*items) }}} This is called in {{{models/01_crud.py}}} in the REST Controller: {{{ db.pr_person.represent = lambda table:shn_list_item(table, resource="person", action="display", display="table.full_name") }}} Form labels can be set in a translatable manner using: {{{db.table.field.label = T("label")}}} Form field can be made to use a TEXTAREA by marking the field as being type 'text': {{{Field("field", "text"),}}} Form field can be made to use a SELECT dropdown by setting the field as a lookup to another table...linked to the 'id' field to allow [wiki:DeveloperGuidelinesDatabaseSynchronization Database Synchronization], but displaying a more user-friendly field (such as 'name'): {{{ Field("field", db.othertable), db.table.field.requires = IS_NULL_OR(IS_ONE_OF(db, "othertable.id", "othertable.name")) }}} NOTE: The IS_IN_DB validator is not deletion status sensitive, i.e. even deleted entries in the key table can be referenced. To exclude entries that are flagged as 'deleted', use the [wiki:IS_ONE_OF IS_ONE_OF validator]. Form field being required can be marked using: {{{db.table.field.comment = SPAN("*", _class="req")}}} Help for a form field can be set using: {{{ DIV(DIV(_class="tooltip", _title=T("Help Title|This is what this field is for."))) }}} Different Flash styles can be set via: {{{ session.error = T("Unsupported format!") redirect(URL(r=request, f=resource)) }}} or (in a Multiple Table form.accepts): {{{ response.error = T("Form invalid!") }}} Supported styles are: * .warning * .error * .information * .confirmation (Default response.flash is styled like this) === Global variables === Use session for per-user persistent variables: {{{session.s3.myvariable}}} Use response for one-off variables which are visible in views without explicit passing: {{{response.s3.myvariable}}} === Settings === System-wide settings have their default values set in {{{models/000_config.py}}}. A few settings are still in the s3_settings table. Upon 1st run of the system, these settings are loaded into the database from where they can subsequently be edited to configure the running instance. If these settings are required by all Views then can patch the session to see them in {{{models/00_db.py}}}'s {{{shn_sessions()}}}.[[BR]] e.g. session.s3.debug is made available to the default {{{layout.html}}} this way. If you wish to clean out all settings to import afresh, then close down Web2Py & delete all files from {{{/databases}}}) Modules can set up their own configuration settings tables in a similar way (e.g. GIS does this) === jQuery === jQuery scripts are located in multiple places to provide for ease of maintenance & for performance gains: * If there is no need for server-side processing our scripts should be added to {{{/static/scripts/S3/S3.js}}} * This gets automatically compiled into {{{S3.min.js}}} via: [wiki:DeveloperGuidelinesReleaseProcess#CompressJSCSSfiles] * If there is a need for server-side pre-processing, they should be added to {{{/views/sahana_scripts_debug.html}}} * Also needs manually copying to {{{/views/sahana_scripts_min.html}}} * Scripts from base Web2Py or T2 are stored in the appropriate areas of {{{/static/scripts}}} or {{{/views/sahana_scripts_*.html}}} Specific widgets we use: * dataTables plugin to enhance list output with clickable columns, pagination & search box * autocomplete plugin for not having dropdowns get too huge (e.g. {{{/views/msg/sms_outbox_create.html}}}) * [wiki:DeveloperGuidelinesPopup Popup for Form reference data] * [wiki:DeveloperGuidelinesSubtypes Subtypes in Select dropdowns] * [wiki:DeveloperGuidelinesDeletableList Deletable List] (old) ---- DeveloperGuidelines