|
DevFormLocalization
Localization of Bungeni Forms
Phase-Implementation Localization of Bungeni FormsRequirementsGeneral AssumptionsWe accept the following assumptions to set the scope and limits of what to allow as form localizations in Bungeni: [A0] All statements here are Field-oriented i.e. everything stated is within the context of a specific Field (within a UI Descriptor). [A1] Every localizable mode has a default localization i.e. for each localizable mode there is a bungeni defined default set of roles for whom the field is to be shown. OTOH, every non-localizable mode may not be associated to a set of roles for visibility (the related security declarations in domain.zcml must prevail).
[A2] Some fields are required for proper functioning of Bungeni (e.g. by the data schema, or are assumed to be there by a workflow definition) and thus may not be disabled/hidden i.e. may not be localized--at least in certain key views, such as a form in add and edit modes, to ensure that the schema/business constraints are always respected. As a consequence, a mode may be declared not localizable in Bungeni and any attempt to localize it for a specific deployment must result in an immediate error. [A3] The display order of fields in all forms is always localizable; furthermore the relative order of displayed fields will always be the same for all modes (and for all roles). [A4] If a mode is declared to be localizable, then it is localizable for all roles. [A5] Visibility of a field in any mode, localizable or not, is always subject to the privileges that the user has on the domain object's attribute that the field corresponds to. (The general case is that a field is bound to a model object's attribute, but this is not necessarily always the case; what should the behaviour be in such cases?). Extension of Bungeni UI Descriptor APITo support localization, some changes to how forms are processed internally within Bungeni are needed. These consist mostly in adding the current user's role as an additional modifier of what fields are shown or hidden per form mode. We retain the Field-oriented descriptor mechanism that is currently in place, and evolve it to cover the additional needs, that thus consist of: [B1] To be able to declare which modes a field is allowed to be localized in. [B2] For each localizable mode, to be able to declare which roles the field is to be displayed for. Some changes to bungeni/alchemist code are required to process these additional declarations. Localization by a Parliament's Site AdminTo support localization of a deployment's form views the following is required: [C1] To be able to state, per localizable UI descriptor, the display order (and inclusion) of all fields (in their entirety and independently of all modes/roles for which they may be enabled).
[C2] To be able to adjust, per localizable mode, the roles for which the field is to be displayed for.
Other considerations[X1] To facilitate the site admin's task of localizing a bungeni deployment, the Bungeni system should automatically generate a default configuration file, reflecting precisely all the display directives for all form fields (per mode per role). [X2] The source format selected for the configuration file should support easy commenting out of entire declarations or parts thereof. [X3] How a localization is picked by the system should be convenient--such that switching from one localization to another involves no more than simply modifying a file-system path parameter in the application's deploy.ini file and restarting the application. DesignDescriptor APIbungeni.alchemist.model.ModelDescriptorModelDescriptor.localizable:bool = FalseAs not every descriptor corresponds to a actual set of UI form views, content-type descriptors must explicitly be declared as localizable. Example is the base descriptors that are used for programmatic convenience to define other real descriptors e.g. ParliamentaryItemDescriptor does not directly correspond to a set of views. To address this, we add a localizable:bool = False class attribute to ModelDescriptor, like so: class ModelDescriptor(object):
localizable = FalseNote that localization of individual Fields is completely independent of localizability of a descriptor i.e. a field may be declared localizable even when the containing descriptor is declared not localizable. bungeni.alchemist.model.FieldThe meaning of the current modes class attribute stays unchanged. Field._rolesWe first need to state precisely what roles may be targetted by localization--by adding the following attribute on the Field class: # The list of roles exposed to localization
_roles = [
#"bungeni.Admin", # parliament, has all privileges
"bungeni.Clerk", "bungeni.Speaker",
#"bungeni.Owner", # instance
"bungeni.MP", # parliament
"bungeni.Minister", # ministry
#"bungeni.Translator", # parliament
#"bungeni.Authenticated", # all authenticated users, all above roles
"bungeni.Anonymous" # unauthenticated user, anonymous
]This set of (uncommented) roles is thus an explicit subset of all bungeni.* roles defined within the system (that typically are defined in models/roles.zcml). This list of roles is also what is to be used as the fallback default value whenever a list of roles is expected and not specified. Field.localizable:sequence = []We then need to conveniently state which modes the field is localizable in, and what is the default list of roles it is to be shown to -- we add a localizable parameter on the Field class, with the allowed value being: [ either(show(modes, roles), hide(modes, roles)) ] i.e. a list of either show(modes, roles) or hide(modes, roles) declarations, where the modes/roles keyword parameters are each a list of parameters, with the following constraints:
hide(roles="bungeni.Anonymous")
show(roles="""bungeni.Clerk bungeni.Speaker bungeni.MP bungeni.Minister""")
Example: class ParliamentaryItemDescriptor(ModelDescriptor):
localizable = False
fields = [
...,
Field(name="owner_id",
modes="view edit add listing",
localizable=[
hide("view listing", "bungeni.Anonymous"),
],
property=schema.Choice(title=_(u"Moved by"),
description=_(u"Select the user who moved the document"),
source=vocabulary.MemberOfParliamentDelegationSource("owner_id"),
),
...
),
...
]
class QuestionDescriptor(ParliamentaryItemDescriptor):
localizable = True
...
fields = [
...,
Field(name="ministry_submit_date",
modes="view edit",
localizable=[ show("view") ],
...
),
]The above code declares the following:
Note the following is an equivalent way to express the moved_by field localizable init parameter as the one in the example above: Field(name="owner_id",
modes="view edit add listing",
localizable=[
hide(modes="view", roles="bungeni.Anonymous"),
hide(modes="listing", roles="bungeni.Anonymous"),
],
...
),but the following would be an error (because listing mode appears more than once in the fields's localizable directives): Field(name="owner_id",
modes="view edit add listing",
localizable=[
show(modes="listing", roles="bungeni.MP bungeni.Clerk"),
hide(modes="view listing", roles="bungeni.Anonymous"),
],
...
),In the example below, omitting both the modes and the roles parameters (or equivalently specifiying them as None) in the call to show() will (a) enable the field for the field's list of displayable modes i.e. for view and edit in this case, and (b) enable the field for all roles: Field(name="ministry_submit_date",
modes="view edit",
localizable=[ show() ],
...
),Localization APIIf a descriptor is to be included in a localization, then all the fields are to be listed (as stated in requirements)--making the configuration of the order of appearance immediate, straighforward, and maintainable even by someone not especially familiar with bungeni. To continue with the example above, let's look at what a localized Question descriptor would look like (here we assume an XML syntax, but this could just as easily be JSON or other data format). The declaration structure follows the same lines as descriptor's Field.localizable. Let's assume that the only localizable (and localized) Fields in this descriptor are the moved_by in view and listing modes and ministry_submit_date in view mode: <ui roles="bungeni.Clerk bungeni.Speaker bungeni.MP bungeni.Minister bungeni.Anonymous">
...
<descriptor name="Question">
<field name="parliament_id" displayable="view edit" localizable="view" />
<field name="short_name" displayable="view edit add listing" localizable="view listing" />
<field name="full_name" displayable="view edit add" localizable="view edit add" />
<field name="registry_number" displayable="view edit" localizable="view" />
<field name="owner_id" displayable="view edit add listing" localizable="view listing">
<show modes="view" roles="bungeni.Clerk bungeni.Speaker" />
<hide modes="listing" roles="bungeni.Anonymous" />
</field>
<field name="language" displayable="view edit add" localizable="view" />
<field name="body_text" displayable="view edit add" localizable="view" />
<field name="submission_date" displayable="view listing" localizable="view listing" />
<field name="status" displayable="view edit listing" />
<field name="status_date" displayable="view listing" localizable="view listing" />
<field name="note" displayable="edit add" localizable="" />
<field name="receive_notification" displayable="view edit add" />
<field name="question_number" displayable="view listing" />
<field name="ministry_id" displayable="view edit add listing" />
<field name="admissible_date" displayable="view listing" localizable="view listing" />
<field name="ministry_submit_date" displayable="view edit" localizable="view">
<show modes="view" />
</field>
<field name="question_type" displayable="view edit add" />
<field name="response_type" displayable="view edit add" />
<field name="response_text" displayable="edit" />
</descriptor>
...
</ui>The above localization code is:
Other remarks about the above localization code sample:
As suggested in the requirements, a localization file with all localizable descriptors and their default settings should be automatically generated by bungeni. Should it ever become necessary, it is forseeable, using this syntax, that the <show> and <hide> directives here could gain further modifier attributes, such as one to state additional conditions e.g. an object's workflow states. | |