Disclaimer

In the Q2 platform, a Disclaimer is an interim page the end user must view and accept before being routed to their accounts or landing page. Disclaimers are usually configured as their own extension, a special Form that is displayed immediately after login and must be accepted before access to the wider application is granted. Another key feature of Disclaimers is that they can be configured to only display if a user has not already seen and accepted them previously.

In the Q2 database, we support Disclaimers with a host of tables, including Q2_Disclaimer, Q2_DisclaimerBundle, Q2_DisclaimerBundleToGroup, Q2_DisclaimerElement, Q2_DisclaimerFormData, and Q2_DisclaimerFormAcceptance. Each of these are accessed during a Disclaimer workflow, but to make things easier we created the Disclaimer DbObject.

Note

For a more thorough explanation of DbObjects, refer to Database Objects

Here is an example Disclaimer extension which will prompt the user to read and accept its contents before the landing page is accessed. To follow along, start by generating a new extension: q2 create_extension -t tecton_server Foo

from q2_sdk.core.http_handlers.tecton_server_handler import Q2TectonServerRequestHandler
from q2_sdk.hq.models.hq_commands import HqCommands
from q2_sdk.hq.db.disclaimer import Disclaimer
from .install.db_plan import DbPlan


class FooHandler(Q2TectonServerRequestHandler):

    DB_PLAN = DbPlan()

    FRIENDLY_NAME = 'Foo'  # this will be used for end user facing references to this extension like Central and Menu items.
    DEFAULT_MENU_ICON = 'landing-page'  # this will be the default icon used if extension placed at top level (not used if a child element)

    CONFIG_FILE_NAME = 'Foo'  # configuration/Foo.py file must exist if REQUIRED_CONFIGURATIONS exist

    TECTON_URL = 'https://cdn1.onlineaccess1.com/cdn/base/tecton/v1.38.0/q2-tecton-sdk.js'

    @property
    def router(self):
        router = super().router
        router.update(
            {
                'default': self.default,
                'submit': self.submit,
            }
        )

        return router

    async def default(self):
        template = self.get_template('index.html.jinja2', {})

        html = self.get_tecton_form(
            "Foo",
            custom_template=template,
            routing_key="submit"
        )
        return html


    async def submit(self):
        # Do not mark the disclaimer as accepted if the user did not check the box
        if self.form_fields.get('accept') != 'on':
            return ''

        self.logger.info('Accepting disclaimer for user %s' % self.online_user.user_id)
        disclaimer_obj = Disclaimer(self.logger, hq_credentials=self.hq_credentials)
        await disclaimer_obj.accept_for_user_id(
            self.online_user.user_id,
            ['Logon']  # This matches the name in Q2_Disclaimer (set by the DbPlan)
        )
        self.set_hq_commands(
            HqCommands(
                HqCommands.AccountReloadType.NO_RELOAD,
                [],
                force_success_response=True
            )
        )
        return ''

This must be paired with the following file in ./templates/index.html.jinja2


<q2-section label='Foo'>
    This is a legally binding disclaimer
    <div class="q2-row">
        <q2-checkbox name='accept' class="q2-col xs-6" type="toggle" alignment="left">I accept</q2-checkbox>
    </div>
</q2-section>
<script src="{{ this.base_assets_url }}/index.js"></script>

../../_images/disclaimer.png

As you can see, Disclaimers require a fair amount of interaction with HQ and the DB. First, let’s start with the DB_PLAN reference– the database must be configured so that your Disclaimer displays at the appropriate time in the user’s workflow. This database “plan” is defined in ./install/db_plan.py:

from q2_sdk.core.install_steps import db_plan


class DbPlan(db_plan.DbPlan):

    def __init__(self):
        super().__init__()
        self.ui_text_prefix = 'Foo'  # Will be used for all self.ui_text_elements
        self.disclaimers = [
            db_plan.Disclaimer(
                'Foo',  # Display Name
                'Foo',  # Corresponding Form Shortname
                disclaimer_type='Logon'  # Logon has special meaning. Otherwise leave blank to use this disclaimer's ShortName
            )
        ]

That db_plan.Disclaimer instance will make the required database updates for us when we run q2 install for this extension. You can execute only the database plan with q2 run_db_plan, or reverse the database updates with q2 run_db_plan --reverse.

Now that the database is updated, let’s examine the extension code. Just like any other form, the user will first be routed to the default() method. We’ve made a simple form containing a short message and a checkbox to signify acceptance.

The real power of Disclaimers is in the submit() method. First, we check to see whether the user has checked the I accept box. If not, we return immediately, which will log the user out of the application entirely.

If the user has accepted, we need to mark the form as accepted in the database and prompt HQ to refresh disclaimer acceptance to allow the user to continue using the application. HQ has a method called DisclaimerFormMarkAsAccepted, which takes in a user_id and a *disclaimer_form_id* as input. This is NOT the same value as the form_id referenced in self.form_info.form_id, so we must query it from the database. Luckily, the Caliper SDK has a built-in DbObject helper for this purpose, so we can instantiate a Disclaimer instance and call get_disclaimer_form_data().

After the form is marked as accepted in the database, we can communicate with HQ and refresh disclaimer acceptance by calling self.set_hq_commands with an appropriately shaped instance of the HqCommands object.

Note

Click here for more details on the set_hq_commands method.

Configuration Options

Other ways to configure Disclaimers include:

  • Configuring the Disclaimer to serve as a reminder, allowing the user to continue on to the main page even without Disclaimer acceptance

    • HqCommands contains a disclaimer_id_to_skip property which also refers to the value stored in disclaimer_form_id above. Passing this value to disclaimer_id_to_skip in the HqCommands object will mark the Disclaimer as ‘ignored’ for the rest of the user’s session, and allow access to the application if the Disclaimer is not accepted.