== !SahanaPy Framework == We have built an S3 framework as a higher level of abstraction on top of T2 (now Auth/Crud).[[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). T2 is used for: * simplified [wiki:DeveloperGuidelinesCreateReadUpdateDelete CRUD] * Conflict Detection Auth is used for: * [wiki:DeveloperGuidelinesAuthenticationAccess AAA] '''NB Auth & Crud are being moved from T2 into Web2Py's {{{gluon/tools.py}}} with an improved API, so we should follow this (once in Web2Py API will always be backwards-compatible unlike T2)''': * http://groups.google.com/group/web2py/browse_thread/thread/3a8aafb435cc1c6a 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 either Web2Py or T2. We extend the CRUD with a [wiki:BluePrintRESTImplementation RESTlike controller].[[BR]] This provides details on how to configure your Model & Controllers. Views may not be required, other than index.html === Mandatory === Each Controller should start like this (to populate the side navigation Menus): {{{ module='module' # Current Module (for sidebar title) module_name=db(db.module.name==module).select()[0].name_nice # List Modules (from which to build Menu of Modules) modules=db(db.module.enabled=='Yes').select(db.module.ALL,orderby=db.module.menu_priority) # List Options (from which to build Menu for this Module) options=db(db['%s_menu_option' % module].enabled=='Yes').select(db['%s_menu_option' % module].ALL,orderby=db['%s_menu_option' % module].priority) }}} Each function needs to return these values to the view: return dict(module_name=module_name,modules=modules,options=options) All tables which are user-editable need to be protected for conflict resolution & synchronization using the predefined fields {{{timestamp}}} & {{{uuidstamp}}}.[[BR]] These are defined in {{{models/__db.py}}}: {{{ # Define 'now' # 'modified_on' fields used by T2 to do edit conflict-detection & by DBSync to check which is more recent now=datetime.datetime.today() # Reusable timestamp fields timestamp=SQLTable(None,'timestamp', db.Field('created_on','datetime', writable=False, default=request.now), db.Field('modified_on','datetime', writable=False, default=request.now,update=request.now)) # We need UUIDs as part of database synchronization import uuid uuidstamp=SQLTable(None,'uuidstamp', db.Field('uuid',length=64, writable=False, default=uuid.uuid4())) }}} === Optional === 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/__db.py}}} in the RESTlike Controller: {{{ db.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': {{{db.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'): {{{ db.Field('field',db.othertable), db.table.field.requires=IS_NULL_OR(IS_IN_DB(db,'othertable.id','othertable.name')) }}} Form field being required can be marked using: {{{db.table.field.comment=SPAN("*",_class="req")}}} Help for a form field can be set using: {{{A(SPAN("[Help]"),_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 (Standard T2 Flash messages are usually of this sort so we class them in the same way) === Settings === System-wide settings have their default values set in {{{models/_db.py}}}'s {{{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/_db.py}}}'s {{{shn_sessions()}}}.[[BR]] e.g. session.s3.debug & session.s3.self_registration are 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: * [wiki:DeveloperGuidelinesDeletableList Deletable List] * [wiki:DeveloperGuidelinesPopup Popup for Form reference data] * [wiki:DeveloperGuidelinesSubtypes Subtypes in Select dropdowns] * [wiki:DeveloperGuidelinesSubmenus Sub-menus] === 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 T2 to handle this for us. Add this field to each table which needs protecting (in {{{models/module.py}}}): {{{ timestamp }}} This field is also used in [wiki:DeveloperGuidelinesDatabaseSynchronization Database Synchronization] ---- DeveloperGuidelines