diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/survey/views | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/survey/views')
| -rw-r--r-- | addons/survey/views/assets.xml | 69 | ||||
| -rw-r--r-- | addons/survey/views/gamification_badge_views.xml | 28 | ||||
| -rw-r--r-- | addons/survey/views/res_partner_views.xml | 40 | ||||
| -rw-r--r-- | addons/survey/views/survey_menus.xml | 22 | ||||
| -rw-r--r-- | addons/survey/views/survey_question_views.xml | 307 | ||||
| -rw-r--r-- | addons/survey/views/survey_report_templates.xml | 89 | ||||
| -rw-r--r-- | addons/survey/views/survey_reports.xml | 17 | ||||
| -rw-r--r-- | addons/survey/views/survey_survey_views.xml | 300 | ||||
| -rw-r--r-- | addons/survey/views/survey_templates.xml | 591 | ||||
| -rw-r--r-- | addons/survey/views/survey_templates_management.xml | 149 | ||||
| -rw-r--r-- | addons/survey/views/survey_templates_print.xml | 56 | ||||
| -rw-r--r-- | addons/survey/views/survey_templates_statistics.xml | 422 | ||||
| -rw-r--r-- | addons/survey/views/survey_templates_user_input_session.xml | 196 | ||||
| -rw-r--r-- | addons/survey/views/survey_user_views.xml | 232 |
14 files changed, 2518 insertions, 0 deletions
diff --git a/addons/survey/views/assets.xml b/addons/survey/views/assets.xml new file mode 100644 index 00000000..a05c8f93 --- /dev/null +++ b/addons/survey/views/assets.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <!-- survey assets --> + <template id="survey_assets" name="Survey Results assets"> + <script src="/web/static/lib/Chart/Chart.js"></script> + + <script type="text/javascript" src="/web/static/src/js/fields/field_utils.js"></script> + + <script type="text/javascript" src="/survey/static/src/js/survey_quick_access.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_timer.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_breadcrumb.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_form.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_print.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_result.js" /> + + <t t-call="web._assets_helpers"> + <t t-call="web._assets_frontend_helpers"/> + </t> + <link href="/survey/static/src/css/survey_print.css" rel="stylesheet" type="text/css"/> + <link href="/survey/static/src/css/survey_result.css" rel="stylesheet" type="text/css"></link> + <link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_form.scss"/> + </template> + + <!-- survey user input session assets --> + <template id="survey_user_input_session_assets" name="Survey User Input Session Assets"> + <script type="text/javascript" src="/survey/static/src/js/libs/chartjs-plugin-datalabels.min.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_session_colors.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_session_chart.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_session_text_answers.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_session_leaderboard.js" /> + <script type="text/javascript" src="/survey/static/src/js/survey_session_manage.js" /> + </template> + + <template id="survey_report_assets_pdf" inherit_id="web.report_assets_pdf"> + <xpath expr="link[last()]" position="after"> + <link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_reports.scss"/> + </xpath> + </template> + + <template id="assets_backend" name="survey assets" inherit_id="web.assets_backend"> + <xpath expr="." position="inside"> + <link rel="stylesheet" href="/survey/static/src/css/survey_result.css"/> + <script type="text/javascript" src="/survey/static/src/js/fields_section_one2many.js"/> + <script type="text/javascript" src="/survey/static/src/js/fields_form_page_description.js"/> + </xpath> + </template> + + <template id="assets_backend_inherit_survey" inherit_id="web.assets_backend" name="Survey backend assets"> + <xpath expr="link[last()]" position="after"> + <link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_views.scss"/> + </xpath> + </template> + + <template id="assets_tests" name="Survey Assets Tests" inherit_id="web.assets_tests"> + <xpath expr="." position="inside"> + <script type="text/javascript" src="/survey/static/tests/tours/certification_failure.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/certification_success.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_prefill.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_tour_session_tools.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_tour_session_create.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_tour_session_start.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_tour_session_manage.js"></script> + <script type="text/javascript" src="/survey/static/tests/tours/survey_session_manage_test.js"></script> + </xpath> + </template> +</data> +</odoo> diff --git a/addons/survey/views/gamification_badge_views.xml b/addons/survey/views/gamification_badge_views.xml new file mode 100644 index 00000000..fa404edf --- /dev/null +++ b/addons/survey/views/gamification_badge_views.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo><data> + <record id="gamification_badge_form_view_simplified" model="ir.ui.view"> + <field name="name">gamification.badge.form.view.simplified</field> + <field name="model">gamification.badge</field> + <field name="priority">100</field> + <field name="arch" type="xml"> + <form string="Badge"> + <sheet> + <div class="oe_button_box" name="button_box"/> + <field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'image_128'}"/> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/> + <h1> + <field name="name" placeholder="Badge Name"/> + </h1> + </div> + <group> + <field name="description" nolabel="1" placeholder="Badge Description"/> + </group> + <group string="Rewards for challenges"> + <field name="level"/> + </group> + </sheet> + </form> + </field> + </record> +</data></odoo> diff --git a/addons/survey/views/res_partner_views.xml b/addons/survey/views/res_partner_views.xml new file mode 100644 index 00000000..604e5853 --- /dev/null +++ b/addons/survey/views/res_partner_views.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo><data> + + <record id="res_partner_action_certifications" model="ir.actions.act_window"> + <field name="name">Certifications Succeeded</field> + <field name="res_model">survey.user_input</field> + <field name="view_mode">tree</field> + <field name="context">{'search_default_scoring_success': 1}</field> + </record> + + <record id="res_partner_view_form" model="ir.ui.view"> + <field name="name">res.partner.view.form.inherit.survey</field> + <field name="model">res.partner</field> + <field name="inherit_id" ref="base.view_partner_form"/> + <field name="groups_id" eval="[(4, ref('survey.group_survey_user'))]"/> + <field name="arch" type="xml"> + <xpath expr="//div[@name='button_box']" position="inside"> + <button class="oe_stat_button" type="object" + icon="fa-trophy" name="action_view_certifications" + attrs="{'invisible': ['|', ('certifications_count', '=', 0), ('is_company', '=', True)]}"> + <div class="o_field_widget o_stat_info"> + <span class="o_stat_value"><field name="certifications_count" /></span> + <span class="o_stat_text" attrs="{'invisible': [('certifications_count', '<', 2)]}">Certifications</span> + <span class="o_stat_text" attrs="{'invisible': [('certifications_count', '>', 1)]}">Certification</span> + </div> + </button> + <button class="oe_stat_button" type="object" + icon="fa-trophy" name="action_view_certifications" + attrs="{'invisible': ['|', ('certifications_company_count', '=', 0), ('is_company', '=', False)]}"> + <div class="o_field_widget o_stat_info"> + <span class="o_stat_value"><field name="certifications_company_count" /></span> + <span class="o_stat_text" attrs="{'invisible': [('certifications_company_count', '<', 2)]}">Certifications</span> + <span class="o_stat_text" attrs="{'invisible': [('certifications_company_count', '>', 1)]}">Certification</span> + </div> + </button> + </xpath> + </field> + </record> + +</data></odoo> diff --git a/addons/survey/views/survey_menus.xml b/addons/survey/views/survey_menus.xml new file mode 100644 index 00000000..2f87d818 --- /dev/null +++ b/addons/survey/views/survey_menus.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- Main menu --> + <menuitem name="Surveys" + id="menu_surveys" + sequence="70" + groups="group_survey_user" + web_icon="survey,static/description/icon.png"/> + + <!-- Parent menus --> + <menuitem name="Questions" + id="survey_menu_questions" + parent="menu_surveys" + groups="base.group_no_one" + sequence="80"/> + <menuitem name="Participations" + id="survey_menu_user_inputs" + parent="menu_surveys" + sequence="90"/> + </data> +</odoo> diff --git a/addons/survey/views/survey_question_views.xml b/addons/survey/views/survey_question_views.xml new file mode 100644 index 00000000..d9e7bedc --- /dev/null +++ b/addons/survey/views/survey_question_views.xml @@ -0,0 +1,307 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <!-- QUESTIONS --> + <record model="ir.ui.view" id="survey_question_form"> + <field name="name">Form view for survey question</field> + <field name="model">survey.question</field> + <field name="arch" type="xml"> + <form string="Survey Question" create="false"> + <field name="is_page" invisible="1"/> + <field name="page_id" invisible="1" required="False"/> + <field name="survey_id" invisible="1"/> + <field name="sequence" invisible="1"/> + <field name="scoring_type" invisible="1"/> + <sheet> + <div class="oe_title" style="width: 100%;"> + <label for="title" string="Section" attrs="{'invisible': [('is_page', '=', False)]}"/> + <label for="title" string="Question" attrs="{'invisible': [('is_page', '=', True)]}"/> + <separator /> + <field name="title" colspan="4"/> + <separator /> + <field name="questions_selection" invisible="1"/> + </div> + <group class="o_label_nowrap" attrs="{'invisible': ['|', ('is_page', '=', False), ('questions_selection', '=', 'all')]}"> + <field name="random_questions_count"/> + </group> + <group attrs="{'invisible': [('is_page', '=', True)]}"> + <group> + <field name="question_type" widget="radio" attrs="{'required': [('is_page', '=', False)]}" /> + </group> + <group> + <div class="col-lg-6 offset-lg-3 o_preview_questions"> + <!-- Multiple Lines Text Zone --> + <div attrs="{'invisible': [('question_type', '!=', 'text_box')]}"> + <i class="fa fa-align-justify fa-4x" role="img" aria-label="Multiple lines" title="Multiple Lines"/> + </div> + <!-- Single Line Text Zone --> + <div attrs="{'invisible': [('question_type', '!=', 'char_box')]}"> + <i class="fa fa-minus fa-4x" role="img" aria-label="Single Line" title="Single Line"/> + </div> + <!-- Numerical Value --> + <div attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}"> + <i class="fa fa-2x" role="img" aria-label="Numeric" title="Numeric">123..</i> + </div> + <!-- Date --> + <div attrs="{'invisible': [('question_type', '!=', 'date')]}"> + <p class="o_datetime">YYYY-MM-DD + <i class="fa fa-calendar fa-2x" role="img" aria-label="Calendar" title="Calendar"/> + </p> + </div> + <!-- Date and Time --> + <div attrs="{'invisible': [('question_type', '!=', 'datetime')]}"> + <p class="o_datetime">YYYY-MM-DD hh:mm:ss + <i class="fa fa-calendar fa-2x" role="img" aria-label="Calendar" title="Calendar"/> + </p> + </div> + <!-- Multiple choice: only one answer --> + <div attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}" role="img" aria-label="Multiple choice with one answer" title="Multiple choice with one answer"> + <div class="row"><i class="fa fa-circle-o fa-lg"/> answer</div> + <div class="row"><i class="fa fa-dot-circle-o fa-lg"/> answer</div> + <div class="row"><i class="fa fa-circle-o fa-lg"/> answer</div> + </div> + <!-- Multiple choice: multiple answers allowed --> + <div attrs="{'invisible': [('question_type', '!=', 'multiple_choice')]}" role="img" aria-label="Multiple choice with multiple answers" title="Multiple choice with multiple answers"> + <div class="row"><i class="fa fa-square-o fa-lg"/> answer</div> + <div class="row"><i class="fa fa-check-square-o fa-lg"/> answer</div> + <div class="row"><i class="fa fa-square-o fa-lg"/> answer</div> + </div> + <!-- Matrix --> + <div attrs="{'invisible': [('question_type', '!=', 'matrix')]}"> + <div class="row o_matrix_head"> + <div class="col-lg-3"></div> + <div class="col-lg-3">ans</div> + <div class="col-lg-3">ans</div> + <div class="col-lg-3">ans</div> + </div> + <div class="row o_matrix_row"> + <div class="col-lg-3">Row1</div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + <div class="col-lg-3"><i class="fa fa-dot-circle-o fa-lg" role="img" aria-label="Checked" title="Checked"/></div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + </div> + <div class="row o_matrix_row"> + <div class="col-lg-3">Row2</div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + <div class="col-lg-3"><i class="fa fa-dot-circle-o fa-lg" role="img" aria-label="Checked" title="Checked"/></div> + </div> + <div class="row o_matrix_row"> + <div class="col-lg-3">Row3</div> + <div class="col-lg-3"><i class="fa fa-dot-circle-o fa-lg" role="img" aria-label="Checked" title="Checked"/></div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + <div class="col-lg-3"><i class="fa fa-circle-o fa-lg" role="img" aria-label="Not checked" title="Not checked"/></div> + </div> + </div> + </div> + </group> + </group> + <notebook> + <page string="Answers" name="answers" attrs="{'invisible': [('is_page', '=', True)]}"> + <div class="row"> + <div class="col-6" + attrs="{'invisible': [('question_type', 'not in', ['char_box', 'numerical_box', 'date', 'datetime'])]}"> + <div attrs="{'invisible': [('question_type', '!=', 'char_box')]}"> + <field name="validation_email" nolabel="1"/> + <label for="validation_email"/><br /> + + <field name="save_as_email" nolabel="1" attrs="{'invisible': [('validation_email', '=', False)]}"/> + <label for="save_as_email" attrs="{'invisible': [('validation_email', '=', False)]}"/> + <br attrs="{'invisible': [('validation_email', '=', False)]}"/> + + <field name="save_as_nickname" nolabel="1"/> + <label for="save_as_nickname"/> + </div> + <div> + <field name="validation_required" nolabel="1"/> + <label for="validation_required"/> + <group attrs="{'invisible': [('validation_required', '=', False)]}"> + <field name="validation_length_min" attrs="{'invisible': [('question_type', '!=', 'char_box')]}"/> + <field name="validation_length_max" attrs="{'invisible': [('question_type', '!=', 'char_box')]}"/> + <field name="validation_min_float_value" attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}"/> + <field name="validation_max_float_value" attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}"/> + <field name="validation_min_date" attrs="{'invisible': [('question_type', '!=', 'date')]}"/> + <field name="validation_max_date" attrs="{'invisible': [('question_type', '!=', 'date')]}"/> + <field name="validation_min_datetime" widget="datetime" attrs="{'invisible': [('question_type', '!=', 'datetime')]}"/> + <field name="validation_max_datetime" widget="datetime" attrs="{'invisible': [('question_type', '!=', 'datetime')]}"/> + <field name="validation_error_msg"/> + </group> + </div> + </div> + + <div class="col-6" attrs="{'invisible': [ + '|', ('scoring_type', '=', 'no_scoring'), + ('question_type', 'not in', ['numerical_box', 'date', 'datetime'])] + }"> + <group> + <field name="answer_numerical_box" string="Correct Answer" attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}"/> + <field name="answer_date" string="Correct Answer" attrs="{'invisible': [('question_type', '!=', 'date')]}"/> + <field name="answer_datetime" string="Correct Answer" attrs="{'invisible': [('question_type', '!=', 'datetime')]}"/> + <field name="is_scored_question"/> + <field name="answer_score"/> + </group> + </div> + </div> + <group attrs="{'invisible': [('question_type', 'not in', ['simple_choice', 'multiple_choice', 'matrix'])]}"> + <field name="suggested_answer_ids" string="Answers" context="{'default_question_id': active_id}"> + <tree editable="bottom"> + <field name="sequence" widget="handle"/> + <field name="value" string="Choices"/> + <field name="value_image" options="{'accepted_file_extensions': 'image/*'}" + attrs="{'column_invisible': ['|', ('parent.allow_value_image', '=', False), ('parent.question_type', '=', 'matrix')]}"/> + <field name="is_correct" attrs="{'column_invisible': ['|', ('parent.scoring_type', '=', 'no_scoring'), ('parent.question_type', '=', 'matrix')]}"/> + <field name="answer_score" attrs="{'column_invisible': ['|', ('parent.scoring_type', '=', 'no_scoring'), ('parent.question_type', '=', 'matrix')]}"/> + </tree> + </field> + </group> + <group attrs="{'invisible': [('question_type', '!=', 'matrix')]}"> + <field name="matrix_row_ids" context="{'default_matrix_question_id': active_id}" attrs="{'invisible': [('question_type', '!=', 'matrix')]}"> + <tree editable="bottom"> + <field name="sequence" widget="handle"/> + <field name="value" string="Rows"/> + </tree> + </field> + </group> + </page> + <page string="Description" name="survey_description"> + <field name="description" widget="html"/> + </page> + <page string="Options" name="options" attrs="{'invisible': [('is_page', '=', True)]}"> + <group string="Constraints"> + <group> + <field name="constr_mandatory" string="Mandatory Answer"/> + <field name="constr_error_msg" attrs="{'invisible': [('constr_mandatory', '=', False)]}"/> + <field name="matrix_subtype" attrs="{'invisible':[('question_type','not in',['matrix'])],'required':[('question_type','=','matrix')]}"/> + </group> + </group> + <group> + <group string="Display" attrs="{'invisible':[('question_type','not in',['simple_choice', 'multiple_choice'])]}"> + <field name="column_nb" string="Number of columns"/> + <field name="allow_value_image"/> + </group> + <group string="Conditional Display" attrs="{'invisible': [('questions_selection', '=', 'random')]}"> + <field name="is_conditional"/> + <field name="triggering_question_id" options="{'no_open': True, 'no_create': True}" + attrs="{'invisible': [('is_conditional','=', False)], 'required': [('is_conditional','=', True)]}"/> + <field name="triggering_answer_id" options="{'no_open': True, 'no_create': True}" + attrs="{'invisible': ['|', ('is_conditional','=', False), ('triggering_question_id','=', False)], + 'required': [('is_conditional','=', True)]}"/> + </group> + </group> + <group string="Allow Comments" attrs="{'invisible':[('question_type','not in',['simple_choice','multiple_choice', 'matrix'])]}"> + <field name='comments_allowed' /> + <field name='comments_message' attrs="{'invisible': [('comments_allowed', '=', False)]}"/> + <field name='comment_count_as_answer' attrs="{'invisible': ['|', ('comments_allowed', '=', False), ('question_type', 'in', ['matrix'])]}" /> + </group> + <group string="Live Sessions"> + <label for="is_time_limited" string="Question Time Limit"/> + <div> + <field name="is_time_limited" nolabel="1"/> + <field name="time_limit" nolabel="1" class="oe_inline" + attrs="{'invisible': [('is_time_limited', '=', False)]}" /> + <span attrs="{'invisible': [('is_time_limited', '=', False)]}"> seconds</span> + </div> + </group> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + <record model="ir.ui.view" id="survey_question_tree"> + <field name="name">Tree view for survey question</field> + <field name="model">survey.question</field> + <field name="arch" type="xml"> + <tree string="Survey Question" create="false"> + <field name="sequence" widget="handle"/> + <field name="title"/> + <field name="survey_id"/> + <field name="question_type"/> + <field name="triggering_question_id" invisible="1"/> + <button icon="fa-code-fork" attrs="{'invisible': [('triggering_question_id', '=', False)]}" + title="This question depends on another question's answer." class="icon_rotates"/> + </tree> + </field> + </record> + <record model="ir.ui.view" id="survey_question_search"> + <field name="name">Search view for survey question</field> + <field name="model">survey.question</field> + <field name="arch" type="xml"> + <search string="Search Question"> + <field name="title"/> + <field name="survey_id" string="Survey"/> + <field name="question_type" string="Type"/> + <group expand="1" string="Group By"> + <filter name="group_by_type" string="Type" domain="[]" context="{'group_by':'question_type'}"/> + <filter name="group_by_survey" string="Survey" domain="[]" context="{'group_by':'survey_id'}"/> + </group> + </search> + </field> + </record> + + <record model="ir.actions.act_window" id="action_survey_question_form"> + <field name="name">Questions</field> + <field name="res_model">survey.question</field> + <field name="view_mode">tree,form</field> + <field name="search_view_id" ref="survey_question_search"/> + <field name="context">{'search_default_group_by_page': True}</field> + <field name="domain">[('is_page', '=', False)]</field> + <field name="help" type="html"> + <p class="o_view_nocontent_empty_folder"> + No questions found + </p> + </field> + </record> + + <!-- LABELS --> + <record id="survey_question_answer_view_tree" model="ir.ui.view"> + <field name="name">survey.question.answer.view.tree</field> + <field name="model">survey.question.answer</field> + <field name="arch" type="xml"> + <tree string="Survey Label" create="false"> + <field name="sequence" widget="handle"/> + <field name="question_id"/> + <field name="matrix_question_id"/> + <field name="value"/> + <field name="answer_score" groups="base.group_no_one"/> + </tree> + </field> + </record> + <record id="survey_question_answer_view_search" model="ir.ui.view"> + <field name="name">survey.question.answer.view.search</field> + <field name="model">survey.question.answer</field> + <field name="arch" type="xml"> + <search string="Search Label"> + <field name="question_id"/> + <group expand="1" string="Group By"> + <filter name="group_by_question" string="Question" domain="[]" context="{'group_by':'question_id'}"/> + </group> + </search> + </field> + </record> + + <record id="survey_question_answer_action" model="ir.actions.act_window"> + <field name="name">Suggested Values</field> + <field name="res_model">survey.question.answer</field> + <field name="view_mode">tree,form</field> + <field name="search_view_id" ref="survey_question_answer_view_search"/> + <field name="context">{'search_default_group_by_question': True}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_empty_folder"> + No survey labels found + </p> + </field> + </record> + + <menuitem name="Questions" + id="menu_survey_question_form1" + action="action_survey_question_form" + parent="survey_menu_questions" + sequence="2"/> + <menuitem name="Suggested Values" + id="menu_survey_label_form1" + action="survey_question_answer_action" + parent="survey_menu_questions" + sequence="3"/> +</data> +</odoo> diff --git a/addons/survey/views/survey_report_templates.xml b/addons/survey/views/survey_report_templates.xml new file mode 100644 index 00000000..3f3d3767 --- /dev/null +++ b/addons/survey/views/survey_report_templates.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <template id="certification_report_view_modern"> + <!-- Style classes to be applyed to '#o_survey_certification': [no class](purple), gold, blue --> + <div id="o_survey_certification" t-att-data-oe-model="user_input._name" t-att-data-oe-id="user_input.id" t-att-class="'article certification-wrapper modern %s' % layout_color"> + <div class="certification"> + <div t-if="user_input.test_entry" class="test-entry"/> + <div class="certification-top"> + <div class="certification-bg-dark"> + <div class="certification-company-wrapper" t-field="user_input.survey_id.create_uid.company_id.logo" t-options="{'widget': 'image'}" role="img"/> + </div> + <div class="certification-name" t-field="user_input.survey_id.display_name"/> + </div> + + <div class="certification-bottom"> + <div t-if="user_input.scoring_success"> + <h5 class="user-name-label">This Certificate is Presented to</h5> + <div class="user-name" t-esc="user_input.partner_id.name or user_input.email"/> + + <div class="certification-description"> + <span t-field="user_input.survey_id.create_uid.company_id.display_name"/> is proud to award this certificate to + <span t-esc="user_input.partner_id.name or user_input.email"/> for graduating <span t-field="user_input.survey_id.display_name"/>. + </div> + </div> + <div t-else=""> + <h5>Certification Failed</h5> + </div> + + <div class="certification-bottom-group"> + <div class="certification-bg-dark"/> + <div class="certification-date"> + <h5>Date</h5> + <h5 t-field="user_input.create_date" t-options='{"widget": "date"}'/> + </div> + <div class="certification-seal"> + <img src="/survey/static/src/img/certification_seal.svg" alt="Certification Seal" role="image"/> + </div> + </div> + </div> + </div> + </div> + </template> + + <template id="certification_report_view_classic"> + <!-- Style classes to be applyed to '#o_survey_certification': [no class](purple), gold, blue --> + <div id="o_survey_certification" t-att-data-oe-model="user_input._name" t-att-data-oe-id="user_input.id" t-att-class="'article certification-wrapper classic %s' % layout_color"> + <div class="certification"> + <div t-if="user_input.test_entry" class="test-entry"/> + <h5 class="certification-name-label">CERTIFICATE</h5> + <div class="certification-name" t-field="user_input.survey_id.display_name"/> + <hr/> + + <div t-if="user_input.scoring_success"> + <h5>This Certificate is Presented to</h5> + <div class="user-name" t-esc="user_input.partner_id.name or user_input.email"/> + <hr class="small"/> + + <div class="certification-description"> + <span t-field="user_input.survey_id.create_uid.company_id.display_name"/> is proud to award this certificate to + <span t-esc="user_input.partner_id.name or user_input.email"/> for graduating <span t-field="user_input.survey_id.display_name"/>. + </div> + </div> + <div t-else=""> + <h5>Certification Failed</h5> + </div> + + <div class="certification-date" t-field="user_input.create_date" t-options='{"widget": "date"}'/> + <img src="/survey/static/src/img/certification_seal.svg" alt="Certification Seal" role="img"/> + + <div class="certificate-signature"> + <span t-field="user_input.survey_id.create_uid.company_id.logo" t-options="{'widget': 'image'}" role="img"/> + </div> + </div> + </div> + </template> + + <template id="certification_report_view"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="user_input"> + <t t-set="layout_values" t-value="user_input.survey_id.certification_report_layout.split('_') if user_input.survey_id.certification_report_layout else ['modern', 'purple']"/> + <t t-set="layout_template" t-value="'survey.certification_report_view_%s' % (layout_values[0])"/> + <t t-set="layout_color" t-value="layout_values[1]"/> + <t t-call="{{layout_template}}"/> + </t> + </t> + </template> + </data> +</odoo> diff --git a/addons/survey/views/survey_reports.xml b/addons/survey/views/survey_reports.xml new file mode 100644 index 00000000..7293b0b1 --- /dev/null +++ b/addons/survey/views/survey_reports.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- QWeb Reports --> + <record id="certification_report" model="ir.actions.report"> + <field name="name">Certifications</field> + <field name="model">survey.user_input</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">survey.certification_report_view</field> + <field name="report_file">survey.certification_report_view</field> + <field name="print_report_name">'Certification - %s' % (object.survey_id.display_name)</field> + <field name="attachment">'certification.pdf'</field> + <field name="binding_model_id" ref="model_survey_user_input"/> + <field name="binding_type">report</field> + </record> + </data> +</odoo> diff --git a/addons/survey/views/survey_survey_views.xml b/addons/survey/views/survey_survey_views.xml new file mode 100644 index 00000000..0d206322 --- /dev/null +++ b/addons/survey/views/survey_survey_views.xml @@ -0,0 +1,300 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <record model="ir.ui.view" id="survey_form"> + <field name="name">Form view for survey</field> + <field name="model">survey.survey</field> + <field name="arch" type="xml"> + <form string="Survey" class="o_survey_form"> + <field name="id" invisible="1"/> + <field name="session_state" invisible="1"/> + <header> + <button name="action_open" string="Start Survey" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('id', '=', False)]}"/> + <button name="action_send_survey" string="Share" type="object" class="oe_highlight" states="open"/> + <button name="action_result_survey" string="See results" type="object" class="oe_highlight" + attrs="{'invisible': ['|', ('state', '=', 'draft'), ('answer_done_count', '<=', 0)]}"/> + <button name="action_start_session" string="Create Live Session" type="object" + attrs="{'invisible': ['|', ('session_state', '!=', False), '|', ('state', '!=', 'open'), ('certification', '=', True)]}" /> + <button name="action_open_session_manager" string="Open Session Manager" type="object" + attrs="{'invisible': ['|', ('session_state', '=', False), ('state', '=', 'draft')]}" /> + <button name="action_end_session" string="Close Live Session" type="object" + attrs="{'invisible': ['|', ('session_state', 'not in', ['ready', 'in_progress']), ('state', '=', 'draft')]}" /> + <button name="action_draft" string="Set to draft" type="object" states="closed"/> + <button name="action_test_survey" string="Test" type="object" attrs="{'invisible': ['|', ('state', '=', 'closed'), ('id', '=', False)]}"/> + <button name="action_print_survey" string="Print" type="object" attrs="{'invisible': [('id', '=', False)]}"/> + <button name="action_close" string="Close" type="object" states="open"/> + <field name="state" widget="statusbar"/> + </header> + <sheet> + <div class="oe_button_box" name="button_box"> + <button name="action_survey_user_input" + type="object" + class="oe_stat_button" + attrs="{'invisible': [('access_mode', '=', 'public')]}" + icon="fa-envelope-o"> + <field string="Registered" name="answer_count" widget="statinfo"/> + </button> + <button name="action_survey_user_input_certified" + type="object" + class="oe_stat_button" + attrs="{'invisible': [('certification', '=', False)]}" + icon="fa-trophy"> + <field string="Certified" name="success_count" widget="statinfo"/> + </button> + <button name="action_survey_user_input_completed" + type="object" + class="oe_stat_button" + icon="fa-pencil-square-o"> + <field string="Answers" name="answer_done_count" widget="statinfo"/> + </button> + </div> + <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/> + <field name="background_image" widget="image" class="oe_avatar"/> + <div class="oe_title" style="width: 100%;"> + <label for="title" class="oe_edit_only"/> + <h1><field name="title" placeholder="e.g. Satisfaction Survey"/></h1> + </div> + <group> + <group> + <field name="active" invisible="1"/> + <field name="has_conditional_questions" invisible="1"/> + </group> + </group> + <notebook> + <page string="Questions" name="questions"> + <field name="question_and_page_ids" nolabel="1" widget="question_page_one2many" mode="tree,kanban" context="{'default_survey_id': active_id, 'default_questions_selection': questions_selection}"> + <tree decoration-bf="is_page" editable="bottom"> + <field name="sequence" widget="handle"/> + <field name="title" widget="survey_description_page"/> + <field name="question_type" /> + <field name="is_page" invisible="1"/> + <field name="questions_selection" invisible="1"/> + <field name="survey_id" invisible="1"/> + <field name="triggering_question_id" invisible="1"/> + <field name="random_questions_count" attrs="{'column_invisible': [('parent.questions_selection', '=', 'all')], 'invisible': [('is_page', '=', False)]}" /> + <button icon="fa-code-fork" attrs="{'invisible': [('triggering_question_id', '=', False)]}" + title="This question depends on another question's answer." class="icon_rotates"/> + <control> + <create name="add_section_control" string="Add a section" context="{'default_is_page': True, 'default_questions_selection': 'all'}"/> + <create name="add_question_control" string="Add a question"/> + </control> + </tree> + </field> + </page> + <page string="Description" name="description"> + <field name="description" nolabel="1"></field> + </page> + <page string="Options" name="options"> + <group name="options"> + <group string="Questions" name="questions"> + <field name="questions_layout" widget="radio" + attrs="{'readonly': [('session_state', 'in', ['ready', 'in_progress'])]}"/> + <field name="progression_mode" widget="radio" /> + <label for="is_time_limited" string="Survey Time Limit"/> + <div> + <field name="is_time_limited" nolabel="1"/> + <field name="time_limit" widget="float_time" attrs="{'invisible': [('is_time_limited', '=', False)]}" nolabel="1" class="oe_inline" /> <span attrs="{'invisible': [('is_time_limited', '=', False)]}"> minutes</span> + </div> + <field name="questions_selection" widget="radio" /> + <field name="users_can_go_back" string="Back Button" attrs="{'invisible': [('questions_layout', '=', 'one_page')]}"/> + </group> + <group string="Candidates" name="candidates"> + <field name="access_mode"/> + <field name="users_login_required"/> + <label for="is_attempts_limited" string="Attempts Limit" + attrs="{'invisible': ['|', ('has_conditional_questions', '=', True), '&', ('access_mode', '=', 'public'), ('users_login_required', '=', False)]}"/> + <div attrs="{'invisible': ['|', ('has_conditional_questions', '=', True), '&', ('access_mode', '=', 'public'), ('users_login_required', '=', False)]}"> + <field name="is_attempts_limited" nolabel="1"/> + <field name="attempts_limit" attrs="{'invisible': [('is_attempts_limited', '=', False)]}" nolabel="1" class="oe_inline" /> <span attrs="{'invisible': [('is_attempts_limited', '=', False)]}"> attempts</span> + </div> + </group> + <group string="Scoring" name="scoring"> + <field name="scoring_type" widget="radio" /> + <field name="scoring_success_min" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}" /> + <field name="certification" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}" /> + <field name="certification_mail_template_id" attrs="{'invisible': [('certification', '=', False)]}" /> + <label for="certification_report_layout" string="Certification Template" attrs="{'invisible': [('certification', '=', False)]}"/> + <div attrs="{'invisible': [('certification', '=', False)]}" class="d-flex"> + <field name="certification_report_layout" class="oe_inline"/> + <button name="action_survey_preview_certification_template" + string="Preview" type="object" + icon="fa-external-link" target="_blank" class="btn-link pt-0"/> + </div> + <field name="certification_give_badge" attrs="{'invisible': ['|', ('certification', '=', False), ('users_login_required', '=', False)]}" /> + <field name="certification_badge_id" attrs="{'invisible': ['|', ('certification_give_badge', '=', False), ('certification_badge_id', '!=', False)]}" + domain="[('survey_id', '=', active_id), ('survey_id', '!=', False)]" + context="{'default_name': title, + 'default_description': 'Congratulations, you have succeeded this certification', + 'default_rule_auth': 'nobody', + 'default_level': None, + 'form_view_ref': 'survey.gamification_badge_form_view_simplified', + 'default_website_published': True}"/> + <field name="certification_badge_id_dummy" attrs="{'invisible': ['|', ('certification_give_badge', '=', False), ('certification_badge_id', '=', False)]}" + options="{'no_create': True}" + context="{'form_view_ref': 'survey.gamification_badge_form_view_simplified'}"/> + </group> + <group string="Live Session"> + <field name="session_speed_rating" /> + <div class="o_td_label"> + <label for="session_code" string="Session Code" class="oe_edit_only o_form_label" /> + <label for="session_link" string="Session Link" class="oe_read_only o_form_label" /> + </div> + <div class="d-flex"> + <field name="session_code" nolabel="1" class="mr-2 oe_edit_only" /> + <field name="session_link" nolabel="1" widget="CopyClipboardChar" class="oe_read_only" /> + </div> + </group> + </group> + </page> + </notebook> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids"/> + <field name="activity_ids"/> + <field name="message_ids"/> + </div> + </form> + </field> + </record> + <record model="ir.ui.view" id= "survey_tree"> + <field name="name">Tree view for survey</field> + <field name="model">survey.survey</field> + <field name="arch" type="xml"> + <tree string="Survey" sample="1"> + <field name="active" invisible="1"/> + <field name="certification" invisible="1"/> + <field name="title"/> + <field name="state" widget="badge" decoration-info="state == 'draft'" decoration-success="state == 'open'"/> + <field name="answer_count"/> + <field name="answer_done_count"/> + <field name="success_count"/> + <field name="success_ratio"/> + <field name="answer_score_avg"/> + <button name="certification" type="button" disabled="disabled" + icon="fa-trophy" title="Certification" aria-label="Certification" + attrs="{'invisible': [('certification', '=', False)]}"/> + <!-- Tweak as icons aren't directly supported in xml --> + </tree> + </field> + </record> + <record model="ir.ui.view" id="survey_kanban"> + <field name="name">Kanban view for survey</field> + <field name="model">survey.survey</field> + <field name="arch" type="xml"> + <kanban sample="1"> + <field name="state" /> + <field name="title" /> + <field name="answer_done_count" /> + <field name="certification" /> + <field name="scoring_type" /> + <field name="color" /> + <field name="access_mode"/> + <field name="activity_ids" /> + <field name="activity_state" /> + <field name="success_count"/> + <field name="success_ratio"/> + <templates> + <div t-name="kanban-box" + t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click o_kanban_card_survey + #{record.certification.raw_value ? 'o_kanban_card_survey_successed' : ''}"> + <div class="o_dropdown_kanban dropdown" t-if="widget.editable"> + + <a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" data-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu"> + <span class="fa fa-ellipsis-v"/> + </a> + <div class="dropdown-menu" role="menu"> + <a role="menuitem" type="edit" class="dropdown-item">Edit Survey</a> + <a t-if="record.state.raw_value != 'closed'" role="menuitem" type="object" class="dropdown-item" name="action_send_survey">Share</a> + <a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a> + <div role="separator" class="dropdown-divider"/> + <div role="separator" class="dropdown-item-text">Color</div> + <ul class="oe_kanban_colorpicker" data-field="color"/> + </div> + </div> + <div class="o_kanban_record_top"> + <h4 class="o_kanban_record_title p-0 mb4"><field name="title" /></h4> + </div> + <div class="row"> + <div class="col-10 p-0 pb-1"> + <div class="container o_kanban_card_content" t-if="record.answer_done_count.raw_value != 0 or record.state.raw_value != 'draft'"> + <div class="row mt-4 ml-5"> + <div class="col-4 p-0"> + <a name="action_result_survey" type="object" class="d-flex flex-column align-items-center"> + <span class="font-weight-bold"><field name="answer_done_count"/></span> + <span class="text-muted">Answers</span> + </a> + </div> + <div class="col-4 p-0 border-left" t-if="record.scoring_type.raw_value != 'no_scoring'" > + <a name="action_survey_user_input_certified" type="object" class="d-flex flex-column align-items-center"> + <span class="font-weight-bold"><field name="success_count"/></span> + <span class="text-muted" t-if="!record.certification.raw_value">Passed</span> + <span class="text-muted" t-else="">Certified</span> + </a> + </div> + <div class="col-4 p-0 border-left" t-if="record.scoring_type.raw_value != 'no_scoring'" > + <a name="action_survey_user_input_completed" type="object" class="d-flex flex-column align-items-center"> + <span class="font-weight-bold"> <t t-esc="Math.round(record.success_ratio.raw_value)"></t> %</span> + <span class="text-muted" >Success</span> + </a> + </div> + </div> + </div> + </div> + <div class="col-2 align-self-end"> + <div class="o_kanban_record_bottom"> + <div class="oe_kanban_bottom_left"/> + <div class="oe_kanban_bottom_right"> + <field name="activity_ids" widget="kanban_activity"/> + </div> + </div> + </div> + </div> + </div> + </templates> + </kanban> + </field> + </record> + <record id="survey_survey_view_search" model="ir.ui.view"> + <field name="name">survey.survey.search</field> + <field name="model">survey.survey</field> + <field name="arch" type="xml"> + <search string="Survey"> + <field string="Survey" name="title"/> + <filter string="Certification" name="certification" domain="[('certification', '=', True)]"/> + <separator/> + <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/> + <separator/> + <filter invisible="1" string="Late Activities" name="activities_overdue" + domain="[('my_activity_date_deadline', '<', context_today().strftime('%Y-%m-%d'))]" + help="Show all records which has next action date is before today"/> + <filter invisible="1" string="Today Activities" name="activities_today" + domain="[('my_activity_date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/> + <filter invisible="1" string="Upcoming Activities" name="activities_upcoming_all" + domain="[('my_activity_date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/> + <group expand="0" string="Group By"> + <filter string="State" name="groupby_state" context="{'group_by': 'state'}"/> + </group> + </search> + </field> + </record> + + <record model="ir.actions.act_window" id="action_survey_form"> + <field name="name">Surveys</field> + <field name="res_model">survey.survey</field> + <field name="view_mode">kanban,tree,form,activity</field> + <field name="context">{'search_default_groupby_state': 1}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_smiling_face"> + Add a new survey + </p><p> + You can create surveys for different purposes: customer opinion, services feedback, recruitment interviews, employee's periodical evaluations, marketing campaigns, etc. + </p><p> + Design easily your survey, send invitations and analyze answers. + </p> + </field> + </record> + + <menuitem name="Surveys" id="menu_survey_form" action="action_survey_form" parent="menu_surveys" sequence="1"/> + +</data> +</odoo> diff --git a/addons/survey/views/survey_templates.xml b/addons/survey/views/survey_templates.xml new file mode 100644 index 00000000..c1656efa --- /dev/null +++ b/addons/survey/views/survey_templates.xml @@ -0,0 +1,591 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <!-- Main survey layout --> + <template id="survey.layout" name="Survey Layout" inherit_id="web.frontend_layout" primary="True"> + <xpath expr="//div[@id='wrapwrap']" position="before"> + <!--TODO DBE Fix me : If one day, there is a survey_livechat bridge module, put this in that module--> + <t t-set="no_livechat" t-value="True"/> + </xpath> + <xpath expr="//div[@id='wrapwrap']" position="attributes"> + <attribute name="t-att-style" add="(('height: 100%; overflow: auto; background: url(' + '/survey/get_background_image/%s/%s' % (survey.access_token, answer.access_token) + ') no-repeat fixed center; box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7); background-size: cover;') if survey and survey.background_image and answer else 'height: 100%; overflow: auto;')"/> + </xpath> + <xpath expr="//head/t[@t-call-assets][last()]" position="after"> + <t t-call-assets="survey.survey_assets" lazy_load="True"/> + </xpath> + <xpath expr="//header" position="before"> + <t t-set="no_header" t-value="True"/> + <t t-set="no_footer" t-value="True"/> + </xpath> + <xpath expr="//header" position="after"> + <div id="wrap" class="oe_structure oe_empty"/> + </xpath> + <xpath expr="//footer" position="after"> + <div class="py-3 m-0 p-0 text-right"> + <div class="o_survey_progress_wrapper d-inline-block pr-5 text-left"> + <t t-call="survey.survey_progression" + t-if="survey and survey.questions_layout != 'one_page' and answer and answer.state == 'in_progress' and (not question or not question.is_page)"> + <t t-if="survey.questions_layout == 'page_per_section'"> + <t t-set="page_ids" t-value="survey.page_ids.ids"/> + <t t-set="page_number" t-value="page_ids.index(page.id) + (1 if survey.progression_mode == 'number' else 0)"/> + </t> + <t t-else=""> + <t t-set="page_ids" t-value="survey.question_ids.ids"/> + <t t-set="page_number" t-value="page_ids.index(question.id)"/> + </t> + </t> + </div> + <div class="o_survey_brand_message float-right rounded mr-3 border"> + <div class="px-2 py-2 d-inline-block" t-call="web.brand_promotion_message"> + <t t-set="_message"></t> + <t t-set="_utm_medium" t-valuef="survey"/> + </div> + <div class="o_survey_navigation_wrapper d-inline-block" t-call="survey.survey_navigation"> + </div> + </div> + </div> + </xpath> + </template> + + <!-- Main survey template --> + <template id="survey_page_fill" name="Survey: main page (take survey)"> + <t t-call="survey.layout"> + <t t-if="answer.test_entry" t-call="survey.survey_button_form_view" /> + <div class="wrap o_survey_wrap d-flex"> + <div class="container o_survey_form d-flex flex-column mb-5"> + + <t t-call="survey.survey_fill_header" /> + <t t-call="survey.survey_fill_form" /> + </div> + </div> + </t> + </template> + + <template id="survey_fill_header" name="Survey: main page header"> + <div class="o_survey_nav pt16 mb-2"> + <div class="container m-0 p-0"> + <div class="row"> + <div class="col-10"> + <h1 t-if="answer.state == 'new' or survey.questions_layout != 'page_per_question'" + t-esc="survey.title" class="o_survey_main_title pt-4"></h1> + </div> + <div class="o_survey_timer col-2 pt-4"> + <h1 class="o_survey_timer_container timer text-right"> + </h1> + </div> + </div> + </div> + <div t-att-class="'o_survey_breadcrumb_container mt8' + (' d-none ' if answer.state != 'in_progress' else '')" + t-if="not survey.has_conditional_questions and survey.questions_layout == 'page_per_section' and answer.state != 'done'" + t-att-data-can-go-back="survey.users_can_go_back" + t-att-data-pages="json.dumps(breadcrumb_pages)" /> + </div> + </template> + + <template id="survey_fill_form" name="Survey: main page content"> + <t t-set="survey_form_readonly" t-value="answer.state == 'done'"/> + <form role="form" method="post" t-att-name="survey.id" + class="d-flex flex-grow-1 align-items-center" + t-att-data-answer-token="answer.access_token" + t-att-data-survey-token="survey.access_token" + t-att-data-users-can-go-back="survey.users_can_go_back and not answer.is_session_answer" + t-att-data-session-in-progress="answer.is_session_answer" + t-att-data-is-start-screen="answer.state == 'new'" + t-att-data-readonly="survey_form_readonly" + t-att-data-has-answered="bool(has_answered)" + t-att-data-is-page-description="bool(question and question.is_page and not is_html_empty(question.description))" + t-att-data-questions-layout="survey.questions_layout" + t-att-data-triggered-questions-by-answer="json.dumps(triggered_questions_by_answer)" + t-att-data-selected-answers="json.dumps(selected_answers)"> + <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/> + <input type="hidden" name="token" t-att-value="answer.access_token" /> + <div class="o_survey_error alert alert-danger d-none" role="alert"> + <p>There was an error during the validation of the survey.</p> + </div> + + <div class="o_survey_form_content w-100"> + <t t-if="answer.state == 'new'" t-call="survey.survey_fill_form_start"/> + <t t-elif="answer.state == 'in_progress'" t-call="survey.survey_fill_form_in_progress" /> + <t t-else="" t-call="survey.survey_fill_form_done"/> + </div> + </form> + + <!-- Modal used to display error message, i.c.o. ajax error --> + <div role="dialog" class="modal fade" id="MasterTabErrorModal" > + <div class="modal-dialog"> + <div class="modal-content"> + <header class="modal-header"> + <h4 class="modal-title">A problem has occured</h4> + <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button> + </header> + <main class="modal-body"><p>To take this survey, please close all other tabs on <strong class="text-danger"></strong>.</p></main> + <footer class="modal-footer"><button type="button" class="btn btn-primary" data-dismiss="modal">Continue here</button></footer> + </div> + </div> + </div> + </template> + + <template id="survey_fill_form_start" name="Survey: start form content"> + <div class="wrap o_survey_start"> + <div class='mb32'> + <div t-field='survey.description' class="oe_no_empty pb-5"/> + <t t-if="answer.is_session_answer"> + <div class="font-weight-bold"> + The session will begin automatically when the host starts. + </div> + </t> + <t t-else=""> + <div t-if="survey.is_time_limited"> + <p> + <span>Time limit for this survey: </span> + <span class="font-weight-bold text-danger" t-field="survey.time_limit" t-options="{'widget': 'duration', 'unit': 'minute'}"></span> + </p> + </div> + <button type="submit" value="start" class="btn btn-primary btn-lg"> + <t t-if="survey.certification"> + Start Certification + </t> + <t t-else=""> + Start Survey + </t> + </button> + <span class="o_survey_enter font-weight-bold text-muted ml-2">or press Enter</span> + </t> + </div> + </div> + </template> + + <template id="survey_fill_form_in_progress" name="Survey: form with questions"> + <div class="o_survey_form_content_data d-none" + t-att-data-question-time-limit-reached="answer.question_time_limit_reached" + t-att-data-has-answered="bool(has_answered)" + t-att-data-is-page-description="bool(question and question.is_page and not is_html_empty(question.description))" + t-att-data-timer="timer_start" + t-att-data-time-limit-minutes="time_limit_minutes"/> + <t t-if="survey.questions_layout == 'one_page'"> + <t t-foreach='survey.question_and_page_ids' t-as='question'> + <h2 t-if="question.is_page" t-field='question.title' class="o_survey_title pb16" /> + <div t-if="question.is_page" t-field='question.description'/> + <t t-if="not question.is_page and question in answer.predefined_question_ids" t-call="survey.question_container"/> + </t> + + <div class="text-center mt16 mb256"> + <button type="submit" value="finish" class="btn btn-primary"> + Submit</button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline">or press Enter</span> + </div> + </t> + + <t t-if="survey.questions_layout == 'page_per_section'"> + <h2 t-field='page.title' class="o_survey_title pb16" /> + <div t-field='page.description' class="oe_no_empty"/> + + <input type="hidden" name="page_id" t-att-value="page.id" /> + <t t-foreach='page.question_ids' t-as='question'> + <t t-if="question in answer.predefined_question_ids" t-call="survey.question_container"/> + </t> + + <div class="row"> + <div class="col-12 text-center mt16"> + <button type="submit" t-att-value="'next' if not survey_last else 'finish'" class="btn btn-primary"> + <t t-if="not survey_last">Continue</t> + <t t-else="">Submit</t> + </button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline"> or press Enter</span> + </div> + </div> + </t> + + <!-- If we have a choice question and less than 6 options, we want minimized display. + Minimized display means we display the choices vertically (instead of optimized based on screen space). + An exception is made for options with images, where we always want to optimize screen space. + Numeric, date and datetime questions are also displayed "minimized", with a smaller screen width.--> + <t t-set="minimized_display" t-value="survey.questions_layout == 'page_per_question' and len(question.suggested_answer_ids) <= 5 and not any(suggestion.value_image for suggestion in question.suggested_answer_ids)" /> + <div t-if="survey.questions_layout == 'page_per_question'" + t-attf-class="o_survey_page_per_question o_survey_#{question.question_type} #{'o_survey_minimized_display' if minimized_display else ''}"> + <input type="hidden" name="question_id" t-att-value="question.id" /> + <!-- User has already answered for this session --> + <t t-if="answer.is_session_answer and (has_answered or answer.question_time_limit_reached)"> + <fieldset disabled="disabled"> + <t t-set="survey_form_readonly" t-value="True" /> + <div class="mt-5"> + <t t-call="survey.question_container" /> + </div> + </fieldset> + <div t-if="answer.question_time_limit_reached and not has_answered" class="font-weight-bold">Sorry, you have not been fast enough.</div> + <div t-else="" class="font-weight-bold">We have registered your answer! Please wait for the host to go to the next question.</div> + </t> + <t t-elif="answer.is_session_answer and question.is_page and not is_html_empty(question.description)"> + <div class="font-weight-bold mt-5">Pay attention to the host screen until the next question.</div> + </t> + <t t-else=""> + <div class="mt-5"> + <t t-call="survey.question_container"/> + </div> + + <div class="row"> + <div class="col-12 text-center mt16"> + <button type="submit" t-att-value="'next' if not survey_last else 'finish'" class="btn btn-primary"> + <t t-if="answer.is_session_answer or survey_last">Submit</t> + <t t-else="">Continue</t> + </button> + <span class="font-weight-bold text-muted ml-2 d-none d-md-inline">or press Enter</span> + </div> + </div> + </t> + </div> + </template> + + <!-- Finished (taken and finished) survey page --> + <template id="survey_fill_form_done" name="Survey: finished"> + <div class="wrap"> + <div class="o_survey_finished mt32 mb32"> + <h1>Thank you!</h1> + <div t-field="survey.description_done" class="oe_no_empty" /> + <div class="row"> + <div class="col"> + <t t-if="survey.scoring_type != 'no_scoring' and survey.scoring_success_min"> + <div>You scored <t t-esc="answer.scoring_percentage" />%</div> + <t t-if="answer.scoring_success"> + <div>Congratulations, you have passed the test!</div> + + <div t-if="survey.certification" class="mt16 mb16"> + <a role="button" + class="btn btn-primary btn-lg" + t-att-href="'/survey/%s/get_certification' % survey.id"> + <i class="fa fa-fw fa-trophy" role="img" aria-label="Download certification" title="Download certification"/> + Download certification + </a> + </div> + </t> + <t t-else=""> + <div>Unfortunately, you have failed the test.</div> + </t> + </t> + <t t-call="survey.survey_button_retake"/> + <div t-if="survey.scoring_type != 'scoring_without_answers'"> + If you wish, you can <a t-att-href="'/survey/print/%s?answer_token=%s&review=True' % (survey.access_token, answer.access_token)">review your answers</a> + </div> + </div> + <div class="col-6 text-center" t-if="survey.certification_give_badge and answer.scoring_success"> + <img t-att-src="'/web/image/gamification.badge/%s/image_128' % survey.certification_badge_id.id"/> + <div>You received the badge <span class="font-weight-bold" t-esc="survey.certification_badge_id.name"/>!</div> + </div> + </div> + </div> + <div class="container o_survey_result p-4" t-if="graph_data"> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active survey_graph" + t-att-data-scoring_percentage="answer.scoring_percentage" + t-att-id="'survey_graph_question_%d' % answer.id" + t-att-data-question_id="answer.id" + data-graph-type="doughnut" + t-att-data-graph-data="graph_data"> + <canvas id="doughnut_chart"></canvas> + <span class="o_overall_performance"></span> + </div> + </div> + </div> + </div> + </template> + + <!-- Question widgets --> + <template id="question_container" name="Survey: question container"> + <t t-set="display_question" + t-value="survey.questions_layout == 'page_per_question' + or (survey.questions_layout == 'one_page' and not question.is_conditional) + or (survey.questions_layout == 'page_per_section' and (not question.is_conditional or triggering_answer_by_question[question.id] in selected_answers))"/> + + <t t-set="answer_lines" t-value="answer.user_input_line_ids.filtered(lambda line: line.question_id == question)"/> + <!--Use Key selection if number of choices is < 26 to keep Z for other choice if any--> + <t t-set="letters" t-value="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> + <t t-set="useKeySelection" t-value="len(question.suggested_answer_ids) < len(letters) and survey.questions_layout == 'page_per_question'"/> + <!-- Extra 'right' margin is added on layouts that are not "page_per_question" to align with choices questions, since all choices have a mr-2 class (pixel perfect yay...) --> + <t t-set="extra_right_margin" t-value="survey.questions_layout != 'page_per_question' and question.question_type not in ['simple_choice', 'multiple_choice']"/> + <div t-att-class="'js_question-wrapper pb-4 %s %s' % ('d-none' if not display_question else '', 'mr-2' if extra_right_margin else '')" + t-att-id="question.id" t-att-data-required="question.constr_mandatory" + t-att-data-constr-error-msg="question.constr_error_msg" + t-att-data-validation-error-msg="question.validation_error_msg"> + <div class="mb-4"> + <h3 t-if="not hide_question_title"> + <span t-field='question.title' /> + <span t-if="question.constr_mandatory" class="text-danger">*</span> + </h3> + <div t-if="not is_html_empty(question.description)" t-field='question.description' class="text-muted oe_no_empty mt-1"/> + </div> + <t t-if="question.question_type == 'text_box'"><t t-call="survey.question_text_box"/></t> + <t t-if="question.question_type == 'char_box'"><t t-call="survey.question_char_box"/></t> + <t t-if="question.question_type == 'numerical_box'"><t t-call="survey.question_numerical_box"/></t> + <t t-if="question.question_type == 'date'"><t t-call="survey.question_date"/></t> + <t t-if="question.question_type == 'datetime'"><t t-call="survey.question_datetime"/></t> + <t t-if="question.question_type == 'simple_choice'"><t t-call="survey.question_simple_choice"/></t> + <t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.question_multiple_choice"/></t> + <t t-if="question.question_type == 'matrix'"><t t-call="survey.question_matrix"/></t> + <div class="o_survey_question_error overflow-hidden border-0 py-0 px-3 alert alert-danger" role="alert"></div> + </div> + </template> + + <template id="question_text_box" name="Question: free text box"> + <div class="o_survey_comment_container p-0"> + <textarea class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" rows="3" t-att-name="question.id" + t-att-data-question-type="question.question_type"><t t-if="answer_lines" t-esc="answer_lines[0].value_text_box or None"/></textarea> + </div> + </template> + + <template id="question_char_box" name="Question: text box"> + <div class="o_survey_comment_container p-0"> + <input t-att-type="'email' if question.validation_email else 'text'" + class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" t-att-name="question.id" + t-att-value="answer_lines[0].value_char_box if answer_lines else None" + t-att-data-question-type="question.question_type" + t-att-data-validation-length-min="question.validation_length_min if question.validation_required else False" + t-att-data-validation-length-max="question.validation_length_max if question.validation_required else False"/> + </div> + </template> + + <template id="question_numerical_box" name="Question: numerical box"> + <input type="number" step="any" class="form-control o_survey_question_numerical_box bg-transparent text-dark rounded-0 p-0" + t-att-name="question.id" t-att-value="answer_lines[0].value_numerical_box if answer_lines else None" + t-att-data-question-type="question.question_type" + t-att-data-validation-float-min="question.validation_min_float_value if question.validation_required else False" + t-att-data-validation-float-max="question.validation_max_float_value if question.validation_required else False"/> + </template> + + <template id="question_date" name="Question: date box"> + <div class="input-group o_survey_form_date" t-attf-id="datetimepicker_#{question.id}" data-target-input="nearest" + t-att-data-mindate="question.validation_min_date" + t-att-data-maxdate="question.validation_max_date"> + <input type="text" class="form-control datetimepicker-input o_survey_question_date bg-transparent text-dark rounded-0 p-0" + t-attf-data-target="#datetimepicker_#{question.id}" t-att-name="question.id" + t-att-value="format_date(answer_lines[0].value_date) if answer_lines else None" + t-att-data-question-type="question.question_type"/> + <div t-if="not survey_form_readonly" class="input-group-append position-absolute" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker"> + <div class="input-group-text text-primary border-0 bg-transparent p-0"><i class="fa fa-calendar"></i></div> + </div> + </div> + </template> + + <template id="question_datetime" name="Question: datetime box"> + <div class="input-group o_survey_form_date" t-attf-id="datetimepicker_#{question.id}" data-target-input="nearest" + t-att-data-mindate="question.validation_min_datetime" + t-att-data-maxdate="question.validation_max_datetime"> + <input type="text" class="form-control datetimepicker-input o_survey_question_datetime bg-transparent text-dark rounded-0 p-0" + t-attf-data-target="#datetimepicker_#{question.id}" t-att-name="question.id" + t-att-value="format_datetime(answer_lines[0].value_datetime) if answer_lines else None" + t-att-data-question-type="question.question_type"/> + <div t-if="not survey_form_readonly" class="input-group-append position-absolute" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker"> + <div class="input-group-text text-primary border-0 bg-transparent p-0"><i class="fa fa-calendar"></i></div> + </div> + </div> + </template> + + <template id="question_suggested_value_image" name="Image from the question suggested answer"> + <t t-if="question.allow_value_image and label.value_image"> + <!-- Directly use field or route if the user doesn't have access rights --> + <div t-if="not env.user.has_group('survey.group_survey_user')" + class="d-flex my-3 justify-content-center"> + <img t-att-src="'/survey/get_question_image/%s/%s/%s/%s' % (survey.access_token, answer.access_token, question.id, label.id)"/> + </div> + <div t-else="" t-field="label.value_image" + class="d-flex my-3 justify-content-center" + t-options="{'widget': 'image', 'alt-field': 'name', 'itemprop': 'image'}"/> + </t> + </template> + + <template id="question_simple_choice" name="Question: simple choice"> + <t t-set="answer_line" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id)"/> + <t t-set="comment_line" t-value="answer_lines.filtered(lambda line: line.value_char_box)"/> + <div class="row o_survey_form_choice" + t-att-data-name="question.id" + data-question-type="simple_choice_radio"> + <t t-set="item_idx" t-value="0"/> + <div t-attf-class="col-lg-12 d-flex flex-wrap"> + <t t-set="has_correct_answer" t-value="scoring_display_correction and any(label.is_correct for label in question.suggested_answer_ids)"/> + <t t-foreach='question.suggested_answer_ids' t-as='label'> + <t t-set="item_idx" t-value="label_index"/> + <t t-set="answer_selected" t-value="answer_line and answer_line.suggested_answer_id.id == label.id"/> + <t t-set="is_correct" t-value="label.answer_score > 0.0"/> + + <!--Used for print mode with corrections --> + <t t-set="answer_class" t-if="not has_correct_answer" t-value="''" /> + <t t-set="answer_class" t-elif="is_correct" t-value="'bg-success'" /> + <t t-set="answer_class" t-elif="not is_correct" t-value="'bg-danger'" /> + + <label t-att-for="str(question.id) + '_' + str(label.id)" + t-att-class="'o_survey_choice_btn mr-2 mb-2 py-1 px-3 rounded %s %s' % (answer_class, 'o_survey_selected' if answer_selected else '')"> + <t t-call="survey.survey_selection_key"> + <t t-set="selection_key_class" t-value="'position-relative o_survey_radio_btn float-left d-flex'"/> + </t> + <span class="ml-2" t-field='label.value'/> + <input t-att-id="str(question.id) + '_' + str(label.id)" type="radio" t-att-value='label.id' class="o_survey_form_choice_item invisible position-absolute" + t-att-name='question.id' + t-att-checked="answer_line and answer_line.suggested_answer_id.id == label.id and 'checked' or None" + t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/> + <i class="fa fa-check-circle float-right mt-1 position-relative"></i> + <i class="fa fa-circle-thin float-right mt-1 position-relative"></i> + <t t-call="survey.question_suggested_value_image"/> + </label> + </t> + </div> + <div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_comments col-lg-12" > + <div class="d-flex flex-wrap"> + <label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s' % ('o_survey_selected' if comment_line else '')"> + <t t-set="item_idx" t-value="item_idx + 1"/> + <t t-call="survey.survey_selection_key"> + <t t-set="selection_key_class" t-value="'position-relative o_survey_radio_btn float-left d-flex'"/> + </t> + <input type="radio" class="o_survey_form_choice_item o_survey_js_form_other_comment invisible position-absolute" value="-1" + t-att-name='question.id' + t-att-checked="comment_line and 'checked' or None" + t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/> + <span class="ml-2" t-field="question.comments_message" /> + <i class="fa fa-check-circle float-right mt-1 position-relative"></i> + <i class="fa fa-circle-thin float-right mt-1 position-relative"></i> + </label> + </div> + <div t-attf-class="o_survey_comment_container mt-3 py-0 px-1 #{'d-none' if not comment_line else ''}"> + <textarea type="text" class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" + t-att-disabled="None if comment_line else 'disabled'"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea> + </div> + </div> + <div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 mt-3 pl-3 pr-4"> + <textarea type="text" class="form-control o_survey_comment o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" + t-att-placeholder="question.comments_message if not survey_form_readonly else ''"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea> + </div> + </div> + </template> + + <template id="question_multiple_choice" name="Question: multiple choice"> + <t t-set="comment_line" t-value="answer_lines.filtered(lambda line: line.value_char_box)"/> + <div class="row o_survey_form_choice o_survey_question_multiple_choice" + t-att-data-name="question.id" + t-att-data-question-type="question.question_type"> + <t t-set="item_idx" t-value="0"/> + <div class="d-flex flex-wrap col-lg-12"> + <t t-set="has_correct_answer" t-value="scoring_display_correction and any(label.is_correct for label in question.suggested_answer_ids)"/> + <t t-foreach='question.suggested_answer_ids' t-as='label'> + <t t-set="item_idx" t-value="label_index"/> + <t t-set="answer_line" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == label)"/> + <t t-set="answer_selected" t-value="answer_line and answer_line.suggested_answer_id.id == label.id"/> + <t t-set="is_correct" t-value="label.answer_score > 0.0"/> + + <!--Used for print mode with corrections --> + <t t-set="answer_class" t-if="not has_correct_answer" t-value="''" /> + <t t-set="answer_class" t-elif="is_correct" t-value="'bg-success'" /> + <t t-set="answer_class" t-elif="not is_correct" t-value="'bg-danger'" /> + + <label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s %s' % (answer_class, 'o_survey_selected' if answer_line else '')"> + <t t-call="survey.survey_selection_key"> + <t t-set="selection_key_class" t-value="'position-relative float-left d-flex'"/> + </t> + <input type="checkbox" t-att-value='label.id' class="o_survey_form_choice_item invisible position-absolute" + t-att-name="question.id" + t-att-checked="'checked' if answer_line else None" + t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/> + <span class="ml-2" t-field='label.value'/> + <i class="fa fa-check-square float-right mt-1 position-relative"></i> + <i class="fa fa-square-o float-right mt-1 position-relative"></i> + <t t-call="survey.question_suggested_value_image"/> + </label> + </t> + </div> + <div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_ck_comments col-lg-12" > + <div class="d-flex flex-wrap"> + <label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s' % ('o_survey_selected' if comment_line else '')"> + <t t-set="item_idx" t-value="item_idx + 1"/> + <t t-call="survey.survey_selection_key"> + <t t-set="selection_key_class" t-value="'position-relative float-left d-flex'"/> + </t> + <input type="checkbox" class="o_survey_form_choice_item o_survey_js_form_other_comment invisible position-absolute" value="-1" + t-att-name="question.id" + t-att-checked="comment_line and 'checked' or None" + t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/> + <span class="ml-2" t-field="question.comments_message" /> + <i class="fa fa-check-square float-right mt-1 position-relative"></i> + <i class="fa fa-square-o float-right mt-1 position-relative"></i> + </label> + </div> + <div t-attf-class="o_survey_comment_container mt-3 py-0 px-1 #{'d-none' if not comment_line else ''}"> + <textarea type="text" class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" + t-att-disabled="None if comment_line else 'disabled'"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea> + </div> + </div> + <div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 mt-3 py-0 pl-3 pr-4"> + <textarea type="text" class="form-control o_survey_comment o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" + t-att-placeholder="question.comments_message if not survey_form_readonly else ''"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea> + </div> + </div> + </template> + + <template id="question_matrix" name="Question: matrix"> + <t t-set="comment_line" t-value="answer_lines.filtered(lambda line: line.value_char_box)"/> + <table class="table table-hover o_survey_question_matrix text-white text-center mb-0" + t-att-data-name="question.id" + t-att-data-question-type="question.question_type" + t-att-data-sub-questions="question.matrix_row_ids.ids"> + <thead> + <tr> + <th class="border-0"> </th> + <th class="border-0 font-weight-normal" t-foreach="question.suggested_answer_ids" t-as="col_label"><span t-field="col_label.value" /></th> + </tr> + </thead> + <tbody> + <t t-set="item_idx" t-value="0"/> + <!-- For matrix, we have an extra check because we have rows * columns total options --> + <t t-set="useKeySelection" t-value="useKeySelection and (len(question.suggested_answer_ids) * len(question.matrix_row_ids)) < len(letters)" /> + <tr class="bg-white text-white" t-foreach="question.matrix_row_ids" t-as="row_label" t-att-id="row_label.id"> + <th class="border-0 font-weight-normal text-left"><span t-field="row_label.value" /></th> + <t t-foreach="question.suggested_answer_ids" t-as="col_label"> + <t t-set="answer" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == col_label and line.matrix_row_id == row_label)"/> + <td t-att-class="'o_survey_matrix_btn text-primary border-0 position-relative %s' + % ('o_survey_selected' if answer else '')"> + <input t-att-type="'checkbox' if question.matrix_subtype == 'multiple' else 'radio'" + t-att-name="'%s_%s' % (question.id, row_label.id)" t-att-value='col_label.id' + t-att-checked="'checked' if answer else None" + t-att-data-row-id="row_label.id" + t-att-data-selection-key="letters[item_idx] if useKeySelection else ''" + class="o_survey_form_choice_item d-none"/> + <i t-att-class="'o_survey_matrix_empty_checkbox fa fa-%s position-relative' + % ('square-o' if question.matrix_subtype == 'multiple' else 'circle-thin')"></i> + <i t-att-class="'fa fa-%s position-relative' + % ('check-square' if question.matrix_subtype == 'multiple' else 'check-circle')"></i> + <t t-call="survey.survey_selection_key"> + <t t-set="selection_key_class" + t-value="'position-absolute float-right font-weight-bold %s' % ('o_survey_radio_btn' if question.matrix_subtype != 'multiple' else '')"/> + </t> + <t t-set="item_idx" t-value="item_idx + 1"/> + </td> + </t> + </tr> + </tbody> + </table> + <div t-if='question.comments_allowed'> + <textarea type="text" class="form-control o_survey_question_text_box o_survey_comment bg-transparent text-dark rounded-0 p-0 mt-3" + t-att-placeholder="question.comments_message if not survey_form_readonly else ''" + t-att-name="'%s_%s' % (question.id, 'comment')"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea> + </div> + </template> + + <template id="survey_selection_key"> + <div t-if="useKeySelection" t-att-class="'o_survey_choice_key bg-white rounded %s' % selection_key_class"> + <span class="o_survey_key text-center position-absolute bg-white rounded-left py-0 pl-2"><span class="text-primary text-center text-center w-100 position-relative">Key</span></span> + <span class="text-primary text-center w-100 position-relative" t-esc="letters[item_idx]"/> + </div> + </template> + + <template id="survey_progression" name="Survey: Progression"> + <t t-if="len(page_ids) > 1 and not survey.has_conditional_questions"> + <t t-set="percentage" t-value="round(100*(page_number/len(page_ids)))"/> + <t t-if="survey.progression_mode == 'percent'"> + <span class="o_survey_progress_percent" t-esc="percentage"/> % completed + </t> + <t t-else=""> + <span class="o_survey_progress_number" t-esc="page_number"/> of <span t-esc="len(page_ids)"/> + <span t-if="survey.questions_layout == 'page_per_question'">answered</span> + <span t-else="">pages</span> + </t> + <div class="o_survey_progress progress flex-grow-1"> + <div class="progress-bar bg-primary" t-att-style="'width: ' + str(percentage) + '%'"/> + </div> + </t> + </template> +</data> +</odoo> diff --git a/addons/survey/views/survey_templates_management.xml b/addons/survey/views/survey_templates_management.xml new file mode 100644 index 00000000..4612b6c0 --- /dev/null +++ b/addons/survey/views/survey_templates_management.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo><data> + + <!-- ============================================================ --> + <!-- Errors / Corner case management --> + <!-- ============================================================ --> + + <!-- Forbidden error messages--> + <template id="survey_403_page" name="Survey: custom 403 page"> + <t t-call="survey.layout"> + <div id="wrap"> + <div class="container"> + <h1 class="mt32">403: Forbidden</h1> + <p>The page you were looking for could not be authorized.</p> + <p>Maybe you were looking for + <a t-attf-href="/web#view_type=form&model=survey.survey&id=#{survey.id}&action=survey.action_survey_form">this page</a> ? + </p> + </div> + </div> + </t> + </template> + + <!-- Error: void survey --> + <template id="survey_void_content" name="Survey: void content"> + <t t-call="survey.layout"> + <t t-if="answer.test_entry" t-call="survey.survey_button_form_view" /> + <div class="wrap"> + <div class="container"> + <div class="jumbotron mt32"> + <h1><span t-field="survey.title"/> survey is void</h1> + <p>Please make sure you have at least one question in your survey. You also need at least one section if you chose the "Page per section" layout.<br /> + <a t-att-href="'/web#view_type=form&model=survey.survey&id=%s&action=survey.action_survey_form' % survey.id" + class="btn btn-secondary" + groups="survey.group_survey_manager">Edit in backend</a> + </p> + </div> + </div> + </div> + </t> + </template> + + <!-- Error: auth required --> + <template id="survey_auth_required" name="Survey: login required"> + <t t-call="survey.layout"> + <div class="wrap"> + <div class="container"> + <div class="jumbotron mt32"> + <h1>Login required</h1> + <p>This survey is open only to registered people. Please + <a t-att-href="redirect_url">log in</a>. + </p> + </div> + </div> + </div> + </t> + </template> + + <!-- Expired (closed) survey page --> + <template id="survey_closed_expired" name="Survey: expired"> + <t t-call="survey.layout"> + <div class="wrap"> + <div class="container"> + <div class="jumbotron mt32"> + <h1><span t-field="survey.title"/> survey expired</h1> + <p>This survey is now closed. Thank you for your interest !</p> + </div> + </div> + </div> + </t> + </template> + + <!-- ============================================================ --> + <!-- Tools / Utilities --> + <!-- ============================================================ --> + + <template id="survey_button_form_view" name="Survey: back to form view"> + <div groups="survey.group_survey_manager" t-ignore="true" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden mb-0"> + <div t-ignore="true" class="text-center"> + <a t-attf-href="/web#view_type=form&model=survey.survey&id=#{survey.id}&action=survey.action_survey_form"><i class="fa fa-fw fa-arrow-right"/><span t-if="answer and answer.test_entry">This is a test survey. </span>Edit Survey</a> + </div> + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> × </button> + </div> + </template> + + <template id="survey_button_retake" name="Survey: retake button"> + <div> + <t t-if="not answer.scoring_success and not answer.is_session_answer"> + <t t-if="survey.is_attempts_limited"> + <t t-set="attempts_left" t-value="survey._get_number_of_attempts_lefts(answer.partner_id, answer.email, answer.invite_token)" /> + <t t-if="attempts_left > 0"> + <p><span>Number of attemps left</span>: <span t-esc="attempts_left"></span></p> + <p><a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/retry/%s/%s' % (survey.access_token, answer.access_token)"> + Retry</a></p> + </t> + </t> + <t t-else=""> + <p><a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/retry/%s/%s' % (survey.access_token, answer.access_token)"> + Retry</a></p> + </t> + </t> + </div> + </template> + + <!-- Survey Home page - Session Code + Used in 'session mode' to give an easy access to the survey through the '/s' route. --> + <template id="survey_session_code" name="Survey: Access Code page"> + <t t-call="survey.layout"> + <div class="wrap o_survey_wrap pb16 d-flex"> + <div class="container o_survey_quick_access d-flex flex-column"> + <div class="d-flex flex-grow-1 align-items-center"> + <div class="col-lg-12"> + <div class="text-center mb32"> + <h3>Please enter the code</h3> + </div> + <div class="row"> + <div class="offset-md-4 col-md-4 text-center"> + <input id="session_code" type="text" placeholder="4812" + class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary text-center rounded-0 p-2 w-100"/> + </div> + <div class="col-3 text-center o_survey_error text-danger d-none pt-2" role="alert"> + <span>Code is incorrect. Please try again.</span> + </div> + </div> + <div class="text-center mt32 p-2"> + <button type="submit" class="btn btn-primary">Submit</button> + </div> + <div class="text-center p-2"> + <span class="text-muted">The code is found on the screen in front of you</span> + </div> + </div> + </div> + </div> + </div> + </t> + </template> + + <template id="survey_navigation" name="Survey: Navigation"> + <button t-if="can_go_back" + type="submit" class="btn p-0 shadow-none o_survey_navigation_submit" name="button_submit" value="previous" t-att-data-previous-page-id="previous_page_id"> + <i class="border-left fa fa-chevron-left p-2" /> + </button> + <button t-if="survey and survey.questions_layout in ['page_per_question', 'page_per_section'] and answer and answer.state != 'done' and not answer.is_session_answer" + type="submit" class="btn p-0 shadow-none o_survey_navigation_submit" t-att-value="'next' if not survey_last else 'finish'"> + <i class="border-left fa fa-chevron-right p-2" /> + </button> + </template> + +</data> +</odoo> diff --git a/addons/survey/views/survey_templates_print.xml b/addons/survey/views/survey_templates_print.xml new file mode 100644 index 00000000..a1f27ecb --- /dev/null +++ b/addons/survey/views/survey_templates_print.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <!-- Survey: printable page view (all pages) --> + <template id="survey_page_print" name="Survey: print page"> + <t t-call="survey.layout"> + <t t-set="survey_form_readonly" t-value="true"/> + <t t-if="answer.test_entry" t-call="survey.survey_button_form_view" /> + <div class="wrap"> + <div class="o_survey_print container"> + <div class='py-5 mt32'> + <h1><span t-field='survey.title'/></h1> + <t t-if="survey.description"><div t-field='survey.description' class="oe_no_empty"/></t> + <t t-if="review" t-call="survey.survey_button_retake"/> + </div> + <div role="form"> + <fieldset disabled="disabled"> + <t t-foreach='survey.question_and_page_ids' t-as='question'> + <t t-if="question.is_page and + (any(q in questions_to_display for q in question.question_ids) + or not is_html_empty(question.description))"> + <hr t-if="question != survey.page_ids[0]" /> + <div class="o_page_header"> + <h1 t-field='question.title' /> + <div t-if="question.description" t-field='question.description' class="oe_no_empty"/> + </div> + </t> + <t t-if="not question.is_page and not answer or (question in answer.predefined_question_ids & questions_to_display)" > + <t t-set="answer_lines" t-value="answer.user_input_line_ids.filtered(lambda line: line.question_id == question)"/> + <div class="js_question-wrapper" t-att-id="question.id"> + <h2> + <span t-field='question.title'/> + <span t-if="question.constr_mandatory" class="text-danger">*</span> + <span t-if="scoring_display_correction" class="badge badge-pill" t-att-data-score-question="question.id"></span> + </h2> + <t t-if="question.description"><div class="text-muted oe_no_empty" t-field='question.description'/></t> + <t t-if="question.question_type == 'text_box'"><t t-call="survey.question_text_box"/></t> + <t t-if="question.question_type == 'char_box'"><t t-call="survey.question_char_box"/></t> + <t t-if="question.question_type == 'numerical_box'"><t t-call="survey.question_numerical_box"/></t> + <t t-if="question.question_type == 'date'"><t t-call="survey.question_date"/></t> + <t t-if="question.question_type == 'datetime'"><t t-call="survey.question_datetime"/></t> + <t t-if="question.question_type == 'simple_choice'"><t t-call="survey.question_simple_choice"/></t> + <t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.question_multiple_choice"/></t> + <t t-if="question.question_type == 'matrix'"><t t-call="survey.question_matrix"/></t> + <div class="o_survey_question_error overflow-hidden border-0 py-0 px-3 alert alert-danger" role="alert"></div> + </div> + </t> + </t> + </fieldset> + </div> + </div> + </div> + </t> + </template> +</data> +</odoo> diff --git a/addons/survey/views/survey_templates_statistics.xml b/addons/survey/views/survey_templates_statistics.xml new file mode 100644 index 00000000..20de2dd7 --- /dev/null +++ b/addons/survey/views/survey_templates_statistics.xml @@ -0,0 +1,422 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <template id="survey_page_statistics" name="Survey: result statistics page"> + <t t-call="survey.layout"> + <t t-call="survey.survey_button_form_view" /> + <t t-set="page_record_limit" t-value="10"/><!-- Change this record_limit to change number of record per page--> + <div class="container o_survey_result"> + <t t-call="survey.survey_page_statistics_header" /> + <t t-call="survey.survey_page_statistics_inner" /> + </div> + </t> + </template> + + <template id="survey_page_statistics_header" name="Survey: result statistics header"> + <div class="py-5 mt32"> + <h1> + <span t-field="survey.title"/> + <span style="font-size:1.5em;" + t-attf-class="fa fa-bar-chart-o #{'fa-bar-chart-o' if survey.scoring_type == 'no_scoring' else 'fa-trophy' if survey.certification else 'fa-question-circle-o'} float-right " role="img" aria-label="Chart" title="Chart"/> + </h1> + <div t-field="survey.description" class="oe_no_empty"/> + <h2 t-if="not question_and_page_data"> + Sorry, no one answered this survey yet. + </h2> + </div> + </template> + + <template id="survey_page_statistics_inner" name="Survey: result statistics content"> + <div t-if="survey.session_show_leaderboard" class="o_survey_session_leaderboard mb-5 mt-1"> + <h2 class="mt16 text-uppercase text-muted">Leaderboard</h2> + <t t-call="survey.user_input_session_leaderboard" /> + </div> + <div t-if="question_and_page_data" class="card d-print-none"> + <div class="card-header"><span class="fa fa-filter"></span> Filters <span t-if="search_filters" class="float-right text-primary clear_survey_filter"><i class="fa fa-times"></i> Clear All Filters</span></div> + <div class="card-body"> + <span t-att-class="'badge only_left_radius filter-all %s' % 'badge-secondary' if search_finished else 'badge-primary o_active_filter'">All surveys</span> + <span t-att-class="'badge only_right_radius filter-finished %s' % 'badge-secondary' if not search_finished else 'badge-primary o_active_filter'">Finished surveys</span> + <span t-foreach="search_filters" t-as="filter_data"> + <span class="badge badge-primary only_left_radius"><i class="fa fa-filter" role="img" aria-label="Filter" title="Filter"></i></span><span class="badge badge-primary no_radius" t-esc="filter_data['question']"></span><span class="badge badge-success only_right_radius" t-esc="' > '.join(filter_data['answers'])"></span> + </span> + </div> + </div> + <div t-if="survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers']"> + <h2 class="mt16 text-uppercase text-muted">Results Overview</h2> + <div>Success rate: <mark class="font-weight-bold"><t t-esc="survey_data['global_success_rate']"></t>%</mark></div> + <div class="survey_graph" data-graph-type="pie" t-att-data-graph-data="survey_data['global_success_graph']"> + <!-- canvas element for drawing pie chart --> + <canvas/> + </div> + <hr/> + </div> + + <div t-foreach="question_and_page_data" t-as='question_data'> + <t t-set="question" t-value="question_data['question']"/> + <t t-if="question_data['is_page']"> + <h2 class="mt16 text-uppercase text-muted" t-field="question.title"/> + <div t-field="question.description" class="oe_no_empty" /> + <hr class="mt-2 pb-1 mb-1"/> + </t> + <div t-else="" class="ml-4 mt-4"> + <t t-call="survey.survey_page_statistics_question" /> + </div> + </div> + </template> + + <template id="survey_page_statistics_question" name="Question: result statistics"> + <t t-set="question_scoring" t-value="survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers']"/> + <t t-set="comment_lines" t-value="question_data['comment_line_ids']"/> + <t t-set="graph_data" t-value="question_data['graph_data']"/> + <t t-set="table_data" t-value="question_data['table_data']"/> + + <div> + <div class="d-flex align-items-start mb-2"> + <div class="mr-auto"> + <h5 t-field="question.title" class="mb-1"/> + <div t-if="question_scoring"> + <t t-if="question.question_type in ['simple_choice', 'multiple_choice']"> + <t t-if="len(question_data['right_answers']) >= 1"> + <span t-if="len(question_data['right_answers']) == 1">Right answer: </span> + <span t-if="len(question_data['right_answers']) > 1">Right answers: </span> + <strong> + <t t-foreach="question_data['right_answers']" t-as="answer"> + <span t-if="answer_index != 0">, </span><span t-esc="answer.value"/> + </t> + </strong> + </t> + </t> + <t t-elif="question.is_scored_question"> + <span>Right answer:</span> + <strong> + <t t-if="question.question_type == 'numerical_box'"> + <span t-esc="question.answer_numerical_box"/> + </t> + <t t-if="question.question_type == 'date'"> + <span t-field="question.answer_date"/> + </t> + <t t-if="question.question_type == 'datetime'"> + <span t-field="question.answer_datetime"/> + </t> + </strong><br/> + <span>Score:</span> + <strong> + <span t-esc="question.answer_score"/> + </strong> + </t> + </div> + </div> + <!-- Question info --> + <span class="badge badge-info" t-field='question.question_type'/> + <t t-if="question.question_type == 'matrix'"> + <span class="badge badge-info ml-2" t-field='question.matrix_subtype'/> + </t> + <!-- Scoring info --> + <t t-if="question_scoring"> + <t t-if="question.question_type in ['simple_choice', 'multiple_choice']"> + <span class="badge badge-success ml-3"><span t-esc="question_data['right_inputs_count']"></span> Correct</span> + <span class="badge badge-warning ml-1" t-if="question.question_type == 'multiple_choice'"> + <span t-esc="question_data['partial_inputs_count']"></span> Partial + </span> + </t> + <t t-if="question.is_scored_question"> + <span class="badge badge-success ml-3"><span t-esc="question_data['right_inputs_count']"></span> Correct</span> + </t> + </t> + <!-- Inputs info --> + <span class="badge badge-info ml-3"><span t-esc="len(question_data['answer_input_done_ids'])"></span> Answered</span> + <span class="badge badge-info ml-1"><span t-esc="len(question_data['answer_input_skipped_ids'])"></span> Skipped</span> + </div> + + <!-- Question Description --> + <div class="ml-3 text-muted" t-field="question.description"/> + <t t-if="question.question_type in ['text_box', 'char_box', 'datetime']"> + <t t-call="survey.question_result_text"/> + </t> + <t t-elif="question.question_type in ['numerical_box', 'date']"> + <t t-call="survey.question_result_number_or_date"></t> + </t> + <t t-elif="question.question_type in ['simple_choice', 'multiple_choice']"> + <t t-call="survey.question_result_choice"></t> + </t> + <t t-elif="question.question_type in ['matrix']"> + <t t-call="survey.question_result_matrix"></t> + </t> + </div> + </template> + + <template id="question_result_text" name="Question: text result (text_box, char_box, datetime)"> + <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id"> + <thead> + <tr> + <th>#</th> + <th>User Responses</th> + </tr> + </thead> + <tbody> + <t t-foreach="table_data" t-as="input_line"> + <tr t-if="not input_line.skipped" t-att-class="'d-none' if input_line_index >= page_record_limit else ''"> + <td> + <t t-if="no_print_url"><t t-esc="input_line_index + 1"></t></t> + <t t-else=""> + <a t-att-href="input_line.user_input_id.get_print_url()"> + <t t-esc="input_line_index + 1"></t> + </a> + </t> + </td> + <td> + <t t-if="question.question_type == 'text_box'"> + <span t-if="no_print_url" t-field="input_line.value_text_box" /> + <a t-else="" t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_text_box"></a> + </t> + <t t-if="question.question_type == 'char_box'"> + <span t-if="no_print_url" t-field="input_line.value_char_box" /> + <a t-else="" t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_char_box"></a> + </t> + <t t-if="question.question_type == 'datetime'"> + <span t-if="no_print_url" t-field="input_line.value_datetime" /> + <a t-else="" t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_datetime"></a> + </t> + </td> + </tr> + </t> + </tbody> + </table> + <t t-call="survey.question_table_pagination"/> + </template> + + <template id="question_result_number_or_date" name="Question: number or date result (numerical_box or date)"> + <t t-if="question.question_type == 'numerical_box'"> + <span class="float-right mt8"> + <span class="badge badge-secondary only_left_radius">Maximum </span> <span class="badge badge-success only_right_radius" t-esc="question_data['numerical_max']"></span> + <span class="badge badge-secondary only_left_radius">Minimum </span> <span class="badge badge-danger only_right_radius" t-esc="question_data['numerical_min']"></span> + <span class="badge badge-secondary only_left_radius">Average </span> <span class="badge badge-warning only_right_radius" t-esc="question_data['numerical_average']"></span> + </span> + </t> + + <ul class="nav nav-tabs d-print-none" role="tablist"> + <li class="nav-item"> + <a t-att-href="'#survey_stats_question_%d' % question.id" + t-att-aria-controls="'survey_stats_question_%d' % question.id" + class="nav-link active" data-toggle="tab" role="tab"> + <i class="fa fa-list-ol"></i> Most Common + </a> + </li> + <li class="nav-item"> + <a t-att-href="'#survey_data_question_%d' % question.id" + t-att-aria-controls="'survey_data_question_%d' % question.id" + class="nav-link" data-toggle="tab" role="tab"> + <i class="fa fa-list-alt"></i> All Data + </a> + </li> + </ul> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions" t-att-id="'survey_stats_question_%d' % question.id"> + <table class="table table-hover table-sm"> + <thead> + <tr> + <th>User Responses</th> + <th>Occurrence</th> + </tr> + </thead> + <tbody> + <tr t-foreach="question_data['common_lines']" t-as="common_line"> + <td> + <span t-if="question.question_type == 'numerical_box'" t-esc="common_line[0]"/> + <span t-if="question.question_type == 'date'" t-esc="common_line[0]" t-options='{"widget": "date"}'/> + </td> + <td><span t-esc="common_line[1]"></span></td> + </tr> + </tbody> + </table> + </div> + <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id"> + <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id"> + <thead> + <tr> + <th>#</th> + <th>User Responses</th> + </tr> + </thead> + <tbody> + <t t-foreach="table_data" t-as="input_line"> + <tr t-if="not input_line.skipped" t-att-class="'d-none' if input_line_index >= page_record_limit else ''"> + <td> + <t t-if="no_print_url"><t t-esc="input_line_index + 1"></t></t> + <t t-else=""> + <a t-att-href="input_line.user_input_id.get_print_url()"> + <t t-esc="input_line_index + 1"></t> + </a> + </t> + </td> + <td> + <a t-if="question.question_type == 'numerical_box'" t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_numerical_box"/> + <a t-if="question.question_type == 'date'" t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_date"/> + </td> + </tr> + </t> + </tbody> + </table> + <t t-call="survey.question_table_pagination"/> + </div> + </div> + </template> + + <template id="question_result_choice" name="Question: choice result (simple_choice, multiple_choice)"> + <ul class="nav nav-tabs d-print-none" role="tablist"> + <li class="nav-item"> + <a t-att-href="'#survey_graph_question_%d' % question.id" + t-att-aria-controls="'survey_graph_question_%d' % question.id" + class="nav-link active" data-toggle="tab" role="tab"> + <i class="fa fa-bar-chart-o"></i> + <span t-if="question.question_type == 'simple_choice'">Pie Graph</span> + <span t-else="">Graph</span> + </a> + </li> + <li class="nav-item"> + <a t-att-href="'#survey_data_question_%d' % question.id" + t-att-aria-controls="'survey_data_question_%d' % question.id" + class="nav-link" data-toggle="tab" role="tab"> + <i class="fa fa-list-alt"></i> Data + </a> + </li> + </ul> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active survey_graph" + t-att-id="'survey_graph_question_%d' % question.id" + t-att-data-question_id="question.id" + t-att-data-graph-type="'pie' if question.question_type == 'simple_choice' else 'bar'" + t-att-data-graph-data="graph_data"> + <!-- canvas element for drawing bar chart --> + <canvas/> + </div> + <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id"> + <table class="table table-hover table-sm"> + <thead> + <tr> + <th>Answer</th> + <th>User Choice</th> + <th t-if="question_scoring">Score</th> + </tr> + </thead> + <tbody> + <tr t-foreach="table_data" t-as="choice_data"> + <td> + <p t-esc="choice_data['value']"></p> + </td> + <td class="survey_answer"> + <span t-esc="round(choice_data['count'] * 100.0/ (len(question_data['answer_line_done_ids']) or 1), 2)"></span> % + <span t-esc="'%s Votes' % choice_data['count']" class="badge badge-primary"/> + <i t-if="choice_data['suggested_answer'].id" + class="fa fa-filter text-primary survey_filter" + t-att-data-question_id="question.id" + t-att-data-answer_id="choice_data['suggested_answer'].id" + role="img" aria-label="Filter question" title="Filter question"/> + </td> + <td t-if="question_scoring" t-esc="choice_data['suggested_answer'].answer_score"/> + </tr> + </tbody> + </table> + </div> + </div> + <div t-if="comment_lines" t-call="survey.question_result_comments" /> + </template> + + <template id="question_result_matrix" name="Question: matrix result (matrix)"> + <t t-set="graph_data" t-value="question_data['graph_data']"/> + <t t-set="table_data" t-value="question_data['table_data']"/> + <ul class="nav nav-tabs d-print-none" role="tablist"> + <li class="nav-item"> + <a t-att-href="'#survey_graph_question_%d' % question.id" + t-att-aria-controls="'survey_graph_question_%d' % question.id" + class="nav-link active" data-toggle="tab" role="tab"> + <i class="fa fa-bar-chart"></i> Graph + </a> + </li> + <li class="nav-item"> + <a t-att-href="'#survey_data_question_%d' % question.id" + t-att-aria-controls="'survey_data_question_%d' % question.id" + class="nav-link" data-toggle="tab" role="tab"> + <i class="fa fa-list-alt"></i> Data + </a> + </li> + </ul> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions survey_graph" + t-att-id="'survey_graph_question_%d' % question.id" + t-att-data-question_id= "question.id" + data-graph-type= "multi_bar" + t-att-data-graph-data="graph_data"> + <!-- canvas element for drawing Multibar chart --> + <canvas/> + </div> + <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id"> + <table class="table table-hover table-sm text-right"> + <thead> + <tr> + <th></th> + <th class="text-right" t-foreach="table_data[0]['columns']" t-as="column_data"> + <span t-esc="column_data['suggested_answer'].value"></span> + </th> + </tr> + </thead> + <tbody> + <tr t-foreach="table_data" t-as="choice_data"> + <td> + <span t-esc="choice_data['row'].value"></span> + </td> + <td class="survey_answer" t-foreach="choice_data['columns']" t-as="column_data"> + <span t-esc="round(column_data['count'] * 100.0/ (len(question_data['answer_line_done_ids']) or 1), 2)"></span> % + <span class="badge badge-primary" t-esc="column_data['count']"></span> + <i class="fa fa-filter text-primary survey_filter" + t-att-data-question_id="question.id" + t-att-data-row_id="choice_data['row'].id" + t-att-data-answer_id="column_data['suggested_answer'].id" role="img" aria-label="Survey filter" title="Survey filter"></i> + </td> + </tr> + </tbody> + </table> + </div> + <div t-if="comment_lines" t-call="survey.question_result_comments" /> + </div> + </template> + + <template id="question_result_comments" name="Question: comments"> + <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id"> + <thead> + <tr> + <th>#</th> + <th>Comment</th> + </tr> + </thead> + <tbody> + <tr t-foreach="comment_lines" t-as="input_line"> + <td> + <t t-if="no_print_url"><t t-esc="input_line_index + 1"></t></t> + <t t-else=""> + <a t-att-href="input_line.user_input_id.get_print_url()"> + <t t-esc="input_line_index + 1"></t> + </a> + </t> + </td> + <td> + <span t-field="input_line.value_char_box"></span><br/> + </td> + </tr> + </tbody> + </table> + </template> + + <template id="question_table_pagination" name="Survey: statistics table pagination"> + <ul t-att-id="'pagination_%d' % question.id" class="pagination" t-att-data-question_id="question.id" t-att-data-record_limit="page_record_limit"> + <t t-if="len(table_data) > page_record_limit"> + <t t-set="total" t-value="ceil(len(table_data) / page_record_limit) + 1"/> + <li t-foreach="range(1, total)" t-as="num" + t-att-class="'page-item o_survey_js_results_pagination %s' % ('active' if num == 1 else '')"> + <a href="#" class="page-link" t-esc="num"></a> + </li> + </t> + </ul> + </template> +</data> +</odoo> diff --git a/addons/survey/views/survey_templates_user_input_session.xml b/addons/survey/views/survey_templates_user_input_session.xml new file mode 100644 index 00000000..68156c0c --- /dev/null +++ b/addons/survey/views/survey_templates_user_input_session.xml @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <template id="user_input_session" name="Survey User Input Session" inherit_id="web.frontend_layout" primary="True"> + <xpath expr="//div[@id='wrapwrap']" position="before"> + <!--TODO DBE Fix me : If one day, there is a survey_livechat bridge module, put this in that module--> + <t t-set="no_livechat" t-value="True"/> + </xpath> + <xpath expr="//div[@id='wrapwrap']" position="attributes"> + <attribute name="t-att-style" add="('height: 100%; overflow: auto; background: url(' + '/web/image/survey.survey/%s/background_image' % survey.id + ') no-repeat fixed center; box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7); background-size: cover;') if survey and survey.background_image else 'height: 100%; overflow: auto;'"/> + </xpath> + <xpath expr="//head/t[@t-call-assets][last()]" position="after"> + <t t-call-assets="survey.survey_assets" lazy_load="True"/> + <t t-call-assets="survey.survey_user_input_session_assets" lazy_load="True"/> + </xpath> + <xpath expr="//header" position="before"> + <t t-set="no_header" t-value="True"/> + <t t-set="no_footer" t-value="True"/> + </xpath> + <xpath expr="//header" position="after"> + <div id="wrap" class="oe_structure oe_empty"/> + </xpath> + </template> + + <template id="user_input_session_open" name="Survey: Open Session"> + <t t-call="survey.user_input_session"> + <div class="wrap py-3 min-vh-100 align-items-center justify-content-center d-flex o_survey_session_open o_survey_session_manage" + t-att-data-survey-access-token="survey.access_token" + t-att-data-survey-id="survey.id" + t-att-data-is-start-screen="True"> + <div class="w-75 p-4"> + <div class="text-center"> + <h1 class="mb-4" t-field="survey.title" /> + <h2 class="mb-5 font-weight-normal"> + <span>Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span> + <i class="fa fa-copy font-weight-normal ml-3 o_survey_session_copy" /> + <input class="o_survey_session_copy_url d-none" type="text" t-att-value="survey.session_link" /> + </h2> + <h2 class="font-weight-normal"><span>Waiting for attendees...</span> + <span class="ml-1 font-weight-bold o_survey_session_attendees_count" t-esc="survey.session_answer_count" /> + </h2> + </div> + <a role="button" + class="font-weight-bold fa fa-chevron-right o_survey_session_navigation o_survey_session_navigation_next p-3" /> + </div> + </div> + </t> + </template> + + <template id="user_input_session_manage" name="Survey: Manage Session"> + <t t-call="survey.user_input_session"> + <t t-call="survey.user_input_session_manage_content" /> + </t> + </template> + + <template id="user_input_session_manage_content" name="Survey User Input Session Manage"> + <t t-set="question" t-value="survey.session_question_id" /> + <t t-set="is_scored_question" t-value="any(answer.answer_score for answer in question.suggested_answer_ids)" /> + <t t-set="show_bar_chart" t-value="question.question_type in ['simple_choice', 'multiple_choice']" /> + <t t-set="show_text_answers" t-value="question.question_type in ['char_box', 'date', 'datetime'] and not question.save_as_email and not question.save_as_nickname" /> + <div class="wrap min-vh-100 align-items-center justify-content-center d-flex flex-column o_survey_session_manage" + t-att-style="'display: none;' if is_rpc_call else ''" + t-att-data-is-rpc-call="is_rpc_call" + t-att-data-survey-id="survey.id" + t-att-data-attendees-count="survey.session_answer_count" + t-att-data-survey-access-token="survey.access_token" + t-att-data-timer="survey.session_question_start_time.isoformat()" + t-att-data-time-limit-minutes="question.time_limit / 60" + t-att-data-is-scored-question="is_scored_question" + t-att-data-session-show-leaderboard="survey.session_show_leaderboard" + t-att-data-question-statistics="question_statistics_graph" + t-att-data-question-type="question.question_type" + t-att-data-has-correct-answers="any(answer.is_correct for answer in question.suggested_answer_ids)" + t-att-data-answers-validity="answers_validity" + t-att-data-is-last-question="is_last_question" + t-att-data-current-screen="'question' if is_scored_question else 'userInputs'" + t-att-data-show-bar-chart="show_bar_chart" + t-att-data-show-text-answers="show_text_answers"> + <div class="o_survey_question_header flex-wrap px-3 w-100 d-flex justify-content-between align-items-center position-absolute"> + <h3> + <span>Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span> + <i class="fa fa-copy font-weight-normal ml-3 mr-1 o_survey_session_copy" /> + <input class="o_survey_session_copy_url d-none" type="text" t-att-value="survey.session_link" /> + </h3> + <h1 t-if="question.is_time_limited" class="o_survey_timer_container"> + <span class="o_survey_timer d-inline-block"/> + </h1> + <div class="text-right d-flex flex-column justify-content-center"> + <div t-if="show_bar_chart or show_text_answers"> + <div class="progress" title="Attendees are answering the question..."> + <div class="progress-bar o_survey_session_progress_small font-weight-bold" + role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"> + <span class="px-2">0 / <t t-esc="survey.session_answer_count" /></span> + </div> + </div> + </div> + </div> + </div> + <div class="container px-4 pb-3 pt96 d-flex flex-column o_survey_session_manage_container"> + <a role="button" + class="font-weight-bold fa fa-chevron-right o_survey_session_navigation o_survey_session_navigation_next p-3" /> + <a role="button" + class="font-weight-bold fa fa-chevron-left o_survey_session_navigation o_survey_session_navigation_previous p-3 d-none" /> + <div class="o_survey_session_results flex-column flex-grow-1"> + <div class="row"> + <div class="col-lg-12"><h1 t-esc="question.title"></h1></div> + </div> + <div t-attf-class="d-flex flex-column flex-grow-1 #{'justify-content-center' if not show_text_answers else ''} #{'align-items-center' if show_bar_chart else ''}"> + <!-- Has to stay in 'style' attribute for Chartjs --> + <div t-if="show_bar_chart" class="p-2 o_survey_session_chart" + style="position: relative; width: 75vw; height: 70vh;"> + <!-- canvas element for drawing bar chart --> + <canvas /> + </div> + <div t-elif="show_text_answers" class="p-2 pt-4 o_survey_session_text_answers_container"> + </div> + <div t-elif="question.is_page and not is_html_empty(question.description)" class="mb-6 o_survey_manage_fontsize_14" t-field="question.description" /> + <div t-else="" class="mb-6"> + <h2 class="font-weight-normal mb-3"> + <span>Waiting for attendees...</span> + <span> + <span class="o_survey_session_answer_count">0</span> + / + <span t-esc="survey.session_answer_count" /> + </span> + </h2> + <div class="progress"> + <div class="progress-bar font-weight-bold" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> + </div> + <fieldset disabled="disabled" class="mt-5" t-if="question.question_type == 'matrix'" t-call="survey.question_container"> + <t t-set="hide_question_title" t-value="true" /> + <t t-set="answer" t-value="env['survey.user_input']" /> + <t t-set="survey_form_readonly" t-value="True"/> + </fieldset> + </div> + </div> + </div> + <div class="o_survey_session_leaderboard w-100 flex-column flex-grow-1" style="display: none;"> + <div class="d-flex"> + <h1 class="o_survey_session_leaderboard_title flex-grow-1"> + <span t-if="is_last_question">Final Leaderboard</span> + <span t-else="">Leaderboard</span> + </h1> + <div t-att-class="'o_survey_leaderboard_buttons font-weight-bold %s' % 'd-none' if not is_last_question else ''"> + <a href="#" role="button" class="o_survey_session_close btn btn-primary mr-4"><i class="fa fa-close"/> Close</a> + <a href="#" role="button" class="o_survey_session_close btn btn-primary" t-att-data-show-results="True"><i class="fa fa-bar-chart"/> Results</a> + </div> + </div> + <div class="justify-content-center d-flex flex-column flex-grow-1 mt-5 mb-5 pb-5 o_survey_session_leaderboard_container"/> + </div> + </div> + </div> + </template> + + <template id="user_input_session_leaderboard" name="Survey User Input Leaderboard"> + <div t-if="leaderboard" class="position-relative mb-5" t-attf-style="height: calc(3.8rem * #{len(leaderboard)});"> + <t t-set="max_score" t-value="max(score.get('scoring_total', 1) for score in leaderboard) or 1" /> + <t t-set="max_updated_score" t-value="max(score.get('updated_score', 1) for score in leaderboard)" /> + <t t-foreach="leaderboard" t-as="score"> + <div class="o_survey_session_leaderboard_item ml-2 d-flex position-absolute" + t-attf-style="top: calc(#{score_index} * 3.8rem);" + t-att-data-current-position="str(score_index)" + t-att-data-new-position="str(score.get('leaderboard_position', score_index))" + t-att-data-question-score="str(round(score.get('question_score', 0)))" + t-att-data-current-score="str(round(score.get('scoring_total', 0)))" + t-att-data-updated-score="str(round(score.get('updated_score', 0)))" + t-att-data-max-question-score="round(score.get('max_question_score', 1))" + t-att-data-max-updated-score="round(max_updated_score)"> + <div class="d-inline-block font-weight-bold align-top"> + <div class="d-inline-block mr-2 o_survey_session_leaderboard_score" t-esc="'%.0f p' % score['scoring_total']" /> + </div> + <!-- We keep "18rem" of space to display the points / nickname. + Then, the length of the bar is a percentage of the attendee's score compared to the max_score. --> + <t t-set="width_ratio" t-value="round(round(score['scoring_total']) / round(max_score), 3)"/> + <t t-set="width_ratio_question" t-value="str(round(round(score.get('question_score', 0)) / round(score.get('max_question_score', 1)), 3))"/> + <div class="o_survey_session_leaderboard_bar ml-2 align-top d-inline-block text-right font-weight-bold" + t-att-style="'width: calc(calc(%s - 18rem) * %s)' % ('100%', width_ratio)" + t-att-data-width-ratio="width_ratio"> + </div> + <div class="o_survey_session_leaderboard_bar_question mr-2 align-top d-inline-block text-right font-weight-bold position-relative" + style="width: 0px;" + t-att-data-width-ratio="width_ratio_question" + t-att-data-max-question-score="str(score.get('max_question_score', 1))" + t-att-data-question-score="str(score.get('question_score', 0))"> + <div class="o_survey_session_leaderboard_bar_question_score position-absolute"></div> + </div> + <div class="o_survey_session_leaderboard_name d-inline-block"> + <span t-if="score.get('nickname')" t-esc="score['nickname']" /> + <span t-else="">Anonymous</span> + </div> + </div> + </t> + </div> + </template> +</data> +</odoo> diff --git a/addons/survey/views/survey_user_views.xml b/addons/survey/views/survey_user_views.xml new file mode 100644 index 00000000..bda3624d --- /dev/null +++ b/addons/survey/views/survey_user_views.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<data> + <!-- USER INPUTS --> + <record id="survey_user_input_view_search" model="ir.ui.view"> + <field name="name">survey.user_input.view.search</field> + <field name="model">survey.user_input</field> + <field name="arch" type="xml"> + <search string="Search Survey"> + <field name="survey_id"/> + <field name="email"/> + <field name="partner_id"/> + <filter name="completed" string="Completed" domain="[('state', '=', 'done')]"/> + <filter string="In Progress" name="in_progress" domain="[('state', '=', 'in_progress')]"/> + <filter string="New" name="new" domain="[('state', '=', 'new')]"/> + <separator/> + <filter string="Quizz passed" name="scoring_success" domain="[('scoring_success','=', True)]"/> + <separator/> + <filter string="Test Entries" name="test" domain="[('test_entry','=', True)]"/> + <filter string="Except Test Entries" name="not_test" domain="[('test_entry','=', False)]" invisible="1"/> + <group expand="0" string="Group By"> + <filter name="group_by_survey" string="Survey" domain="[]" context="{'group_by': 'survey_id'}"/> + <filter string="Email" name="group_by_email" domain="[]" context="{'group_by': 'email'}"/> + <filter string="Partner" name="group_by_partner" domain="[]" context="{'group_by': 'partner_id'}"/> + </group> + </search> + </field> + </record> + + <record id="survey_user_input_view_form" model="ir.ui.view"> + <field name="name">survey.user_input.view.form</field> + <field name="model">survey.user_input</field> + <field name="arch" type="xml"> + <form string="Survey User inputs" create="false"> + <header> + <button name="action_resend" string="Resend Invitation" type="object" class="oe_highlight" + attrs="{'invisible': ['|', ('state', '=', 'done'), '&', ('partner_id', '=', False), ('email', '=', False)]}"/> + <button name="action_print_answers" states="done" string="Print" type="object" class="oe_highlight"/> + <field name="state" widget="statusbar"/> + </header> + <sheet> + <div class="oe_button_box" name="button_box"/> + <group col="2"> + <group> + <field name="survey_id"/> + <field name="create_date"/> + <field name="is_attempts_limited" invisible="1"/> + <label for="attempts_number" string="Attempt n°" attrs="{'invisible': ['|', ('is_attempts_limited', '=', False), '|', ('test_entry', '=', True), ('state', '!=', 'done')]}"/> + <div attrs="{'invisible': ['|', ('is_attempts_limited', '=', False), '|', ('test_entry', '=', True), ('state', '!=', 'done')]}"> + <field name="attempts_number" nolabel="1"/> + / + <field name="attempts_limit" nolabel="1" /> + </div> + <field name="access_token" groups="base.group_no_one"/> + </group> + <group> + <field name="deadline"/> + <field name="partner_id"/> + <field name="email" widget="email"/> + <field name="test_entry" groups="base.group_no_one"/> + <field name="scoring_type" invisible="1"/> + <field name="scoring_percentage" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}"/> + <field name="scoring_success" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}"/> + </group> + </group> + <field name="user_input_line_ids" mode="tree" attrs="{'readonly': False}"> + <tree> + <field name="question_sequence" invisible="1"/> + <field name="question_id"/> + <field name="page_id"/> + <field name="answer_type"/> + <field name="skipped"/> + <field name="create_date"/> + <field name="answer_is_correct"/> + <field name="answer_score"/> + </tree> + </field> + </sheet> + </form> + </field> + </record> + + <record id="survey_user_input_view_tree" model="ir.ui.view"> + <field name="name">survey.user_input.view.tree</field> + <field name="model">survey.user_input</field> + <field name="arch" type="xml"> + <tree string="Survey User inputs" decoration-muted="test_entry == True" create="false"> + <field name="survey_id"/> + <field name="create_date"/> + <field name="deadline"/> + <field name="partner_id"/> + <field name="email"/> + <field name="attempts_number"/> + <field name="state"/> + <field name="test_entry" invisible="True"/> + <field name="scoring_success"/> + <field name="scoring_percentage"/> + </tree> + </field> + </record> + + <record id="survey_user_input_viuew_kanban" model="ir.ui.view"> + <field name="name">survey.user_input.view.kanban</field> + <field name="model">survey.user_input</field> + <field name="arch" type="xml"> + <kanban create="false"> + <field name="survey_id"/> + <field name="create_date"/> + <field name="partner_id"/> + <field name="email"/> + <field name="state"/> + <templates> + <t t-name="kanban-box"> + <div t-attf-class="oe_kanban_global_click"> + <div class="o_kanban_record_top"> + <div class="o_kanban_record_headings"> + <strong class="o_kanban_record_title"><t t-esc="record.survey_id.value"/></strong> + </div> + </div> + <div class="o_kanban_record_bottom"> + <div class="oe_kanban_bottom_left"> + <field name="create_date"/> + </div> + <div class="oe_kanban_bottom_right mr4"> + <field name="state" widget="label_selection" options="{'classes': {'new': 'default', 'done': 'success', 'in_progress':'warning'}}"/> + </div> + </div> + </div> + </t> + </templates> + </kanban> + </field> + </record> + + <record model="ir.actions.act_window" id="action_survey_user_input"> + <field name="name">Participations</field> + <field name="res_model">survey.user_input</field> + <field name="view_mode">tree,kanban,form</field> + <field name="view_id" ref="survey_user_input_view_tree"></field> + <field name="search_view_id" ref="survey_user_input_view_search"/> + <field name="context">{'search_default_group_by_survey': True}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_empty_folder"> + Nobody has replied to your surveys yet + </p> + </field> + </record> + + <!-- USER INPUT LINES + .. note:: these views are useful mainly for technical users/administrators --> + <record id="survey_user_input_line_view_form" model="ir.ui.view"> + <field name="name">survey.user_input.line.view.form</field> + <field name="model">survey.user_input.line</field> + <field name="arch" type="xml"> + <form string="User input line details" create="false"> + <sheet> + <group col="4"> + <field name="question_id"/> + <field name="create_date"/> + <field name="answer_type"/> + <field name="skipped" /> + <field name="answer_score" groups="base.group_no_one"/> + </group> + <group> + <field name="value_char_box" colspan='2' attrs="{'invisible': [('answer_type','!=','char_box')]}"/> + <field name="value_numerical_box" colspan='2' attrs="{'invisible': [('answer_type','!=','numerical_box')]}"/> + <field name="value_date" colspan='2' attrs="{'invisible': [('answer_type','!=','date')]}"/> + <field name="value_datetime" colspan='2' attrs="{'invisible': [('answer_type','!=','datetime')]}"/> + <field name="value_text_box" colspan='2' attrs="{'invisible': [('answer_type','!=','text_box')]}"/> + <field name="matrix_row_id" colspan='2' /> + <field name="suggested_answer_id" colspan='2' attrs="{'invisible': [('answer_type','!=','suggestion')]}"/> + </group> + </sheet> + </form> + </field> + </record> + <record id="survey_response_line_view_tree" model="ir.ui.view"> + <field name="name">survey.user_input.line.view.tree</field> + <field name="model">survey.user_input.line</field> + <field name="arch" type="xml"> + <tree string="Survey Answer Line" create="false"> + <field name="survey_id"/> + <field name="user_input_id"/> + <field name="question_id"/> + <field name="create_date"/> + <field name="answer_type"/> + <field name="skipped"/> + <field name="answer_score" groups="base.group_no_one"/> + </tree> + </field> + </record> + <record id="survey_user_input_line_view_search" model="ir.ui.view"> + <field name="name">survey.user_input.line.view.search</field> + <field name="model">survey.user_input.line</field> + <field name="arch" type="xml"> + <search string="Search User input lines"> + <field name="user_input_id"/> + <field name="survey_id"/> + <group expand="1" string="Group By"> + <filter name="group_by_survey" string="Survey" domain="[]" context="{'group_by':'survey_id'}"/> + <filter name="group_by_user_input" string="User Input" domain="[]" context="{'group_by':'user_input_id'}"/> + </group> + </search> + </field> + </record> + + <record id="survey_user_input_line_action" model="ir.actions.act_window"> + <field name="name">Detailed Answers</field> + <field name="res_model">survey.user_input.line</field> + <field name="view_mode">tree,form</field> + <field name="search_view_id" ref="survey_user_input_line_view_search"/> + <field name="context">{'search_default_group_by_survey': True, 'search_default_group_by_user_input': True}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_empty_folder"> + No user input lines found + </p> + </field> + </record> + + <menuitem name="Participations" + id="menu_survey_type_form1" + action="action_survey_user_input" + parent="survey_menu_user_inputs" + sequence="1"/> + <menuitem name="Detailed Answers" + id="menu_survey_response_line_form" + action="survey_user_input_line_action" + parent="survey_menu_user_inputs" + sequence="4" + groups="base.group_no_one"/> +</data> +</odoo> |
