This app provides support for services that follow the SaaS model. Traditionally known as multi-site or multi-tenant web applications where a single installation of a CMS provides accounts for multiple isolated tenants.
If the service needs to keep track of additional information you should provide an extra form and serializer. For example, WordPress requires you to provide an *email_address* for account creation, and the assigned blog ID is required for effectively update account state or delete it. In this case we provide two forms:
`WordPressForm` provides the email field, and `WordPressChangeForm` adds the `blog_id` on top of it. `blog_id` will be represented as a *readonly* field on the form (`widget=widgets.SpanWidget`), so no modification will be allowed.
`SaaSPasswordForm` provides a password field for the common case when a password needs to be provided in order to create a new account. You can subclass `SaaSPasswordForm` or use it directly on the `Service.form` field.
In case we need to save extra information of the service (email and blog_id in our current example) we should provide a serializer that will save this bits of information in JSON format inot the database data field.
Notice that two optional forms can be provided `form` and `change_form`. When non of them is provided, SaaS will provide a default one for you. When only `form` is provided, it will be used for both, *add view* and *change view*. If both are provided, `form` will be used for the *add view* and `change_form` for the change view. This last option allows us to display the `blog_id` back to the user, only when we have it (after creation).
A backend class is required to interface with the web application and perform `save()` and `delete()` operations on it. The more reliable way of interfacing with the application is by means of a CLI (e.g. [Moodle](backends/moodle.py), but not all CMS come with this tool. The second preferable way is using some sort of API, possibly HTTP-based (e.g. [gitLab](backends/gitlab.py). This is less reliable because additional moving parts are used underneath the interface; a busy web server can timeout our requests. The least preferred way is interfacing with an HTTP-HTML interface designed for human consumption, really painful to implement but sometimes is the only way (e.g. [WordPress](backends/wordpressmu.py)).
Some applications do not support multi-tenancy by default, but we can hack the configuration file of such apps and generate *table prefix* or *database name* based on some property of the URL. Example of this services are [moodle](backends/moodle.py) and [phplist](backends/phplist.py) respectively.
## Settings
Enabled services should be added into the `SAAS_ENABLED_SERVICES` settings tuple, providing its full module path, e.g. `'orchestra.contrib.saas.services.moodle.MoodleService'`. Parameters that should allow easy configuration on each deployment should be defined as settings. e.g. `SAAS_WORDPRESS_DOMAIN`. Take a look at the [`settings` module](settings.py).