Example #1
0
 def get_mod_dict(mod):
     return {
         'unique_id': mod.unique_id,
         'name': trans(mod.name, langs),
         'root_module_id': mod.root_module_id,
         'forms': [{'unique_id': f.unique_id, 'name': trans(f.name, langs)} for f in mod.get_forms()]
     }
Example #2
0
 def xform_resources(self):
     first = []
     last = []
     for form_stuff in self.app.get_forms(bare=False):
         form = form_stuff["form"]
         if form_stuff['type'] == 'module_form':
             path = './modules-{module.id}/forms-{form.id}.xml'.format(**form_stuff)
             this_list = first
         else:
             path = './user_registration.xml'
             this_list = last
         resource = XFormResource(
             id=self.id_strings.xform_resource(form),
             version=form.get_version(),
             local=path,
             remote=path,
         )
         if form_stuff['type'] == 'module_form' and self.app.build_version >= '2.9':
             resource.descriptor = u"Form: (Module {module_name}) - {form_name}".format(
                 module_name=trans(form_stuff["module"]["name"], langs=[self.app.default_language]),
                 form_name=trans(form["name"], langs=[self.app.default_language])
             )
         elif path == './user_registration.xml':
             resource.descriptor=u"User Registration Form"
         this_list.append(resource)
     for x in first:
         yield x
     for x in last:
         yield x
Example #3
0
    def get_section_elements(self):
        from corehq.apps.app_manager.models import ShadowForm
        for form_stuff in self.app.get_forms(bare=False):
            form = form_stuff["form"]
            if isinstance(form, ShadowForm):
                continue
            path = './modules-{module.id}/forms-{form.id}.xml'.format(**form_stuff)
            if self.build_profile_id:
                remote_path = '{path}?profile={profile}'.format(path=path, profile=self.build_profile_id)
            else:
                remote_path = path

            if form.is_release_notes_form:
                if form.enable_release_notes:
                    element_class = ReleaseInfoXFormResource
                else:
                    continue
            else:
                element_class = XFormResource
            resource = element_class(
                id=id_strings.xform_resource(form),
                version=form.get_version(),
                local=path,
                remote=remote_path,
            )
            if self.app.build_version and self.app.build_version >= LooseVersion('2.9'):
                default_lang = self.app.default_language if not self.build_profile_id \
                    else self.app.build_profiles[self.build_profile_id].langs[0]
                resource.descriptor = "Form: (Module {module_name}) - {form_name}".format(
                    module_name=trans(form_stuff["module"]["name"], langs=[default_lang]),
                    form_name=trans(form["name"], langs=[default_lang])
                )
            yield resource
Example #4
0
 def get_section_elements(self):
     for form_stuff in self.app.get_forms(bare=False):
         form = form_stuff["form"]
         path = './modules-{module.id}/forms-{form.id}.xml'.format(**form_stuff)
         resource = XFormResource(
             id=id_strings.xform_resource(form),
             version=form.get_version(),
             local=path,
             remote=path,
         )
         if self.app.build_version >= '2.9':
             resource.descriptor = u"Form: (Module {module_name}) - {form_name}".format(
                 module_name=trans(form_stuff["module"]["name"], langs=[self.app.default_language]),
                 form_name=trans(form["name"], langs=[self.app.default_language])
             )
         yield resource
Example #5
0
    def all_media(self, lang=None):
        kwargs = self.get_media_ref_kwargs()
        media = []

        media.extend(self.menu_media(self, lang=lang))

        # Registration from case list
        if self.case_list_form.form_id:
            media.extend(self.menu_media(self.case_list_form, lang=lang))

        # Case list menu item
        if hasattr(self, 'case_list') and self.case_list.show:
            media.extend(self.menu_media(self.case_list, lang=lang))

        for name, details, display in self.get_details():
            # Case list lookup - not language-specific
            if display and details.display == 'short' and details.lookup_enabled and details.lookup_image:
                media.append(ApplicationMediaReference(details.lookup_image, media_class=CommCareImage,
                                                       is_menu_media=True, **kwargs))

            # Print template - not language-specific
            if display and details.display == 'long' and details.print_template:
                media.append(ApplicationMediaReference(details.print_template['path'],
                                                       media_class=CommCareMultimedia, **kwargs))

            # Icon-formatted columns
            for column in details.get_columns():
                if column.format == 'enum-image':
                    for map_item in column.enum:
                        icon = trans(map_item.value, [lang] + self.get_app().langs, include_lang=False)
                        if icon:
                            media.append(ApplicationMediaReference(icon, media_class=CommCareImage,
                                                                   is_menu_media=True, **kwargs))

        return media
Example #6
0
 def module_to_form_options(self):
     return {
         module.unique_id: [
             {"text": trans(form.name, app.langs), "value": form.unique_id} for form in module.get_forms()
         ]
         for app in get_apps_in_domain(self.domain)
         for module in app.modules
     }
Example #7
0
 def app_to_module_options(self):
     return {
         app._id: [{
             'text': trans(module.name, app.langs),
             'value': module.unique_id,
         } for module in app.modules]
         for app in get_apps_in_domain(self.domain)
     }
Example #8
0
def _case_list_form_options(app, module, case_type_):
    options = OrderedDict()
    forms = [
        form
        for mod in app.get_modules() if module.unique_id != mod.unique_id
        for form in mod.get_forms() if form.is_registration_form(case_type_)
    ]
    options['disabled'] = gettext_lazy("Don't Show")
    options.update({f.unique_id: trans(f.name, app.langs) for f in forms})

    return options
Example #9
0
 def get_section_elements(self):
     for form_stuff in self.app.get_forms(bare=False):
         form = form_stuff["form"]
         path = "./modules-{module.id}/forms-{form.id}.xml".format(**form_stuff)
         if self.build_profile_id:
             remote_path = "{path}?profile={profile}".format(path=path, profile=self.build_profile_id)
         else:
             remote_path = path
         resource = XFormResource(
             id=id_strings.xform_resource(form), version=form.get_version(), local=path, remote=remote_path
         )
         if self.app.build_version >= "2.9":
             default_lang = (
                 self.app.default_language
                 if not self.build_profile_id
                 else self.app.build_profiles[self.build_profile_id].langs[0]
             )
             resource.descriptor = u"Form: (Module {module_name}) - {form_name}".format(
                 module_name=trans(form_stuff["module"]["name"], langs=[default_lang]),
                 form_name=trans(form["name"], langs=[default_lang]),
             )
         yield resource
Example #10
0
def schedule_detail_variables(module, detail, detail_column_infos):
    has_schedule_columns = any(ci.column.field_type == FIELD_TYPE_SCHEDULE for ci in detail_column_infos)
    has_schedule = getattr(module, 'has_schedule', False)
    if (has_schedule and module.all_forms_require_a_case() and has_schedule_columns):
        yield DetailVariable(name=SCHEDULE_DATE_CASE_OPENED, function="date_opened")  # date case is opened
        forms_due = []
        last_visit_dates = []
        for phase in module.get_schedule_phases():
            if not phase.anchor:
                raise ScheduleError(_("Schedule Phase in module '{module_name}' is missing an anchor")
                                    .format(module_name=module.default_name()))

            for form in phase.get_forms():
                """
                Adds the following variables for each form:
                <anchor_{form_id} function="{anchor}"/>
                <last_visit_number_{form_id} function="{last_visit_number}"/>
                <last_visit_date_{form_id} function="{last_visit_date}"/>
                <next_{form_id} function={phase_set}/>
                """
                if not form.schedule_form_id:
                    raise ScheduleError(
                        _("'{form_name}' in '{module_name}' is missing an abbreviation")
                        .format(form_name=trans(form["name"], langs=[module.get_app().default_language]),
                                module_name=module.default_name()))
                form_xpath = ScheduleFormXPath(form, phase, module)
                name = u"next_{}".format(form.schedule_form_id)
                forms_due.append(u"${}".format(name))

                # Add an anchor and last_visit variables so we can reference it in the calculation
                yield DetailVariable(name=form_xpath.anchor_detail_variable_name, function=phase.anchor)
                yield DetailVariable(name=form_xpath.last_visit_detail_variable_name,
                                     function=SCHEDULE_LAST_VISIT.format(form.schedule_form_id))
                yield DetailVariable(name=form_xpath.last_visit_date_detail_variable_name,
                                     function=SCHEDULE_LAST_VISIT_DATE.format(form.schedule_form_id))
                last_visit_dates.append(form_xpath.last_visit_date_detail_variable_name)
                if phase.id == 1:
                    # If this is the first phase, `current_schedule_phase` and
                    # last_visit_num might not be set yet
                    yield DetailVariable(name=name, function=form_xpath.first_visit_phase_set)
                else:
                    yield DetailVariable(name=name, function=form_xpath.xpath_phase_set)

        yield DetailVariable(name=SCHEDULE_GLOBAL_NEXT_VISIT_DATE,
                             function=u'date(min({}))'.format(','.join(forms_due)))
        yield DetailVariable(name=SCHEDULE_NEXT_DUE,
                             function=ScheduleFormXPath.next_visit_date(last_visit_dates))
        yield DetailVariable(name='is_late', function=u'${} < today()'.format(SCHEDULE_NEXT_DUE))

        if len(forms_due) != len(set(forms_due)):
            raise ScheduleError(_("Your app has multiple forms with the same schedule abbreviation"))
Example #11
0
def _case_list_form_options(app, module, case_type_, lang=None):
    options = OrderedDict()
    forms = [
        form
        for mod in app.get_modules() if module.unique_id != mod.unique_id
        for form in mod.get_forms() if form.is_registration_form(case_type_)
    ]
    langs = None if lang is None else [lang]
    options.update({f.unique_id: trans(f.name, langs) for f in forms})

    return {
        'options': options,
        'form': module.case_list_form,
    }
Example #12
0
    def get_section_elements(self):
        from corehq.apps.app_manager.models import ShadowForm
        for form_stuff in self.app.get_forms(bare=False):
            form = form_stuff["form"]
            if isinstance(form, ShadowForm):
                continue
            path = './modules-{module.id}/forms-{form.id}.xml'.format(
                **form_stuff)
            if self.build_profile_id:
                remote_path = '{path}?profile={profile}'.format(
                    path=path, profile=self.build_profile_id)
            else:
                remote_path = path

            if form.is_release_notes_form:
                if form.enable_release_notes:
                    element_class = ReleaseInfoXFormResource
                else:
                    continue
            else:
                element_class = XFormResource
            resource = element_class(
                id=id_strings.xform_resource(form),
                version=form.get_version(),
                local=path,
                remote=remote_path,
            )
            if self.app.build_version and self.app.build_version >= LooseVersion(
                    '2.9'):
                default_lang = self.app.default_language if not self.build_profile_id \
                    else self.app.build_profiles[self.build_profile_id].langs[0]
                resource.descriptor = "Form: (Module {module_name}) - {form_name}".format(
                    module_name=trans(form_stuff["module"]["name"],
                                      langs=[default_lang]),
                    form_name=trans(form["name"], langs=[default_lang]))
            yield resource
Example #13
0
def _get_vellum_core_context(request, domain, app, module, form):
    """
    Returns the core context that will be passed into vellum when it is
    initialized.
    """
    core = {
        'dataSourcesEndpoint':
        reverse('get_form_data_schema',
                kwargs={
                    'domain': domain,
                    'form_unique_id': form.get_unique_id()
                }),
        'form':
        form.source,
        'formId':
        form.get_unique_id(),
        'formName':
        trans(form.name, app.langs),
        'saveType':
        'patch',
        'saveUrl':
        reverse('edit_form_attr',
                args=[domain, app.id,
                      form.get_unique_id(), 'xform']),
        'patchUrl':
        reverse('patch_xform', args=[domain, app.id,
                                     form.get_unique_id()]),
        'allowedDataNodeReferences': [
            "meta/deviceID",
            "meta/instanceID",
            "meta/username",
            "meta/userID",
            "meta/timeStart",
            "meta/timeEnd",
            "meta/location",
        ] + _get_core_context_scheduler_data_nodes(module, form),
        'activityUrl':
        reverse('ping'),
        'sessionid':
        request.COOKIES.get('sessionid'),
        'externalLinks': {
            'changeSubscription':
            reverse("domain_subscription_view", kwargs={'domain': domain}),
        },
        'invalidCaseProperties': ['name'],
    }
    core.update(_get_core_context_help_text_context(form))
    return core
Example #14
0
    def all_media(self, lang=None):
        kwargs = self.get_media_ref_kwargs()
        media = []

        media.extend(self.menu_media(self, lang=lang))

        # Registration from case list
        if self.case_list_form.form_id:
            media.extend(self.menu_media(self.case_list_form, lang=lang))

        # Case list menu item
        if hasattr(self, 'case_list') and self.case_list.show:
            media.extend(self.menu_media(self.case_list, lang=lang))

        for name, details, display in self.get_details():
            # Case list lookup - not language-specific
            if display and details.display == 'short' and details.lookup_enabled and details.lookup_image:
                media.append(
                    ApplicationMediaReference(details.lookup_image,
                                              media_class=CommCareImage,
                                              is_menu_media=True,
                                              **kwargs))

            # Print template - not language-specific
            if display and details.display == 'long' and details.print_template:
                media.append(
                    ApplicationMediaReference(details.print_template['path'],
                                              media_class=CommCareMultimedia,
                                              **kwargs))

            # Icon-formatted columns
            for column in details.get_columns():
                if column.format == 'enum-image':
                    for map_item in column.enum:
                        icon = trans(map_item.value,
                                     [lang] + self.get_app().langs,
                                     include_lang=False)
                        if icon:
                            media.append(
                                ApplicationMediaReference(
                                    icon,
                                    media_class=CommCareImage,
                                    is_menu_media=True,
                                    **kwargs))

        return media
Example #15
0
def get_form_view_context_and_template(request,
                                       domain,
                                       form,
                                       langs,
                                       messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append(u"Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append(u"Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request, "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block.")

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append(u"Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append(u"Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
            pass
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request,
                             _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append(u"Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append(u"Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachemnts) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled(
                request.user.username)
            xform_questions = [
                q for q in xform_questions
                if q["tag"] != "upload" or is_previewer
            ]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                messages.error(request, u"Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, unicode(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(unicode(e))
                messages.error(request, u"Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USER_CASE)
    valid_index_names = DEFAULT_CASE_INDEX_IDENTIFIERS.values()
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])  # strip trailing slash

    form_has_schedule = isinstance(
        form, AdvancedForm) and form.get_module().has_schedule
    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': get_all_case_properties(app),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
    }
    context = {
        'nav_form':
        form,
        'xform_languages':
        languages,
        "xform_questions":
        xform_questions,
        'form_errors':
        form_errors,
        'xform_validation_errored':
        xform_validation_errored,
        'xform_validation_missing':
        xform_validation_missing,
        'allow_cloudcare':
        isinstance(form, Form),
        'allow_form_copy':
        isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering':
        not isinstance(form, CareplanForm) and not form_has_schedule,
        'allow_form_workflow':
        not isinstance(form, CareplanForm),
        'uses_form_workflow':
        form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase':
        allow_usercase,
        'is_usercase_in_use':
        is_usercase_in_use(request.domain),
        'is_module_filter_enabled':
        app.enable_module_filtering,
        'is_case_list_form':
        form.is_case_list_form,
        'edit_name_url':
        reverse('edit_form_attr',
                args=[app.domain, app.id, form.unique_id, 'name']),
        'case_xpath_pattern_matches':
        CASE_XPATH_PATTERN_MATCHES,
        'case_xpath_substring_matches':
        CASE_XPATH_SUBSTRING_MATCHES,
        'user_case_xpath_pattern_matches':
        USER_CASE_XPATH_PATTERN_MATCHES,
        'user_case_xpath_substring_matches':
        USER_CASE_XPATH_SUBSTRING_MATCHES,
        'custom_instances': [{
            'instanceId': instance.instance_id,
            'instancePath': instance.instance_path
        } for instance in form.custom_instances],
        'can_preview_form':
        request.couch_user.has_permission(domain, 'edit_data')
    }

    if tours.NEW_APP.is_enabled(
            request.user) and not toggles.APP_MANAGER_V2.enabled(
                request.user.username):
        request.guided_tour = tours.NEW_APP.get_tour_data()

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(
            domain):
        module = form.get_module()

        def qualified_form_name(form, auto_link):
            module_name = trans(form.get_module().name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return u"{}{} -> {}".format(star, module_name, form_name)

        modules = filter(lambda m: m.case_type == module.case_type,
                         all_modules)
        if getattr(module, 'root_module_id',
                   None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(
            itertools.chain.from_iterable(
                list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, CareplanForm):
        case_config_options.update({
            'save_url':
            reverse("edit_careplan_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
            'case_preload': [{
                'key': key,
                'path': path
            } for key, path in form.case_preload.items()],
            'customCaseUpdates': [{
                'key': key,
                'path': path
            } for key, path in form.custom_case_updates.items()],
            'fixedQuestions':
            form.get_fixed_questions(),
            'mode':
            form.mode,
        })
    elif isinstance(form, AdvancedForm):

        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{
                    'value': program.get_id,
                    'label': program.name
                } for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'save_url':
            reverse("edit_advanced_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
            'commtrack_enabled':
            app.commtrack_enabled,
            'commtrack_programs':
            all_programs + commtrack_programs(),
            'module_id':
            module.unique_id,
            'propertyDescriptions':
            get_case_property_description_dict(domain),
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })

        if module.has_schedule:
            visit_scheduler_options = get_schedule_context(form)
            visit_scheduler_options.update({
                'questions':
                xform_questions,
                'save_url':
                reverse("edit_visit_schedule",
                        args=[app.domain, app.id, module.id, form.id]),
                'schedule':
                form.schedule,
                'phase':
                visit_scheduler_options['schedule_phase'],
            })
            context.update(
                {'visit_scheduler_options': visit_scheduler_options})
    else:
        case_config_options.update({
            'actions':
            form.actions,
            'allowUsercase':
            allow_usercase,
            'valid_index_names':
            valid_index_names,
            'usercasePropertiesMap':
            get_usercase_properties(app),
            'propertyDescriptions':
            get_case_property_description_dict(domain),
            'save_url':
            reverse("edit_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
        })
        context.update({
            'show_custom_ref':
            toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
    context.update({'case_config_options': case_config_options})
    template = get_app_manager_template(
        request.user,
        "app_manager/v1/form_view.html",
        "app_manager/v2/form_view.html",
    )
    return template, context
Example #16
0
 def get_mod_dict(mod):
     return {
         'unique_id': mod.unique_id,
         'name': trans(mod.name, langs),
         'forms': [{'unique_id': f.unique_id, 'name': trans(f.name, langs)} for f in mod.get_forms()]
     }
Example #17
0
 def _form_name(form):
     module_name = _module_name(form.get_module())
     form_name = trans(form.name, langs)
     return "{} > {}".format(module_name, form_name)
Example #18
0
def get_form_view_context_and_template(request, domain, form, langs, current_lang, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append("Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append("Unexpected error in form: %s" % e)

    has_case_error = False
    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append("Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append("Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request, _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append("Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append("Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachments) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled_for_request(request)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                has_case_error = True
                messages.error(request, "Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, str(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(str(e))
                messages.error(request, "Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })
    module = form.get_module()

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USERCASE)
    valid_index_names = list(DEFAULT_CASE_INDEX_IDENTIFIERS.values())
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])     # strip trailing slash

    form_has_schedule = isinstance(form, AdvancedForm) and module.has_schedule

    try:
        case_properties_map = get_all_case_properties(app)
        usercase_properties_map = get_usercase_properties(app)
    except CaseError as e:
        case_properties_map = {}
        usercase_properties_map = {}
        if not has_case_error:
            messages.error(request, "Error in Case Management: %s" % e)

    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': case_properties_map,
        'propertyDescriptions': get_case_property_description_dict(domain),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
        'usercasePropertiesMap': usercase_properties_map,
    }
    context = {
        'nav_form': form,
        'xform_languages': languages,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'xform_validation_missing': xform_validation_missing,
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': not form_has_schedule,
        'uses_form_workflow': form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase': allow_usercase,
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': app.enable_module_filtering,
        'is_training_module': module.is_training_module,
        'is_allowed_to_be_release_notes_form': form.is_allowed_to_be_release_notes_form,
        'root_requires_same_case': module.root_requires_same_case(),
        'is_case_list_form': form.is_case_list_form,
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'form_filter_patterns': {
            'case_substring': CASE_XPATH_SUBSTRING_MATCHES,
            'usercase_substring': USERCASE_XPATH_SUBSTRING_MATCHES,
        },
        'custom_instances': [
            {'instanceId': instance.instance_id, 'instancePath': instance.instance_path}
            for instance in form.custom_instances
        ],
        'custom_assertions': [
            {'test': assertion.test, 'text': assertion.text.get(current_lang)}
            for assertion in form.custom_assertions
        ],
        'form_icon': None,
        'session_endpoints_enabled': toggles.SESSION_ENDPOINTS.enabled(domain),
        'module_is_multi_select': module.is_multi_select(),
        'module_loads_registry_case': module_loads_registry_case(module),
    }

    if toggles.CUSTOM_ICON_BADGES.enabled(domain):
        context['form_icon'] = form.custom_icon if form.custom_icon else CustomIcon()

    if toggles.COPY_FORM_TO_APP.enabled_for_request(request):
        context['apps_modules'] = get_apps_modules(domain, app.id, module.unique_id)

    if toggles.FORM_LINK_WORKFLOW.enabled(domain):
        context.update(_get_form_link_context(module, langs))

    if isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'commtrack_enabled': app.commtrack_enabled,
            'commtrack_programs': all_programs + commtrack_programs(),
            'module_id': module.unique_id,
            'save_url': reverse("edit_advanced_form_actions", args=[app.domain, app.id, form.unique_id]),
            'arbitrary_datums': form.arbitrary_datums,
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })
        if getattr(module, 'has_schedule', False):
            schedule_options = get_schedule_context(form)
            schedule_options.update({
                'phase': schedule_options['schedule_phase'],
                'questions': xform_questions,
                'save_url': reverse("edit_visit_schedule", args=[app.domain, app.id, form.unique_id]),
                'schedule': form.schedule,
            })
            context.update({
                'schedule_options': schedule_options,
            })
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
        case_config_options.update({
            'actions': form.actions,
            'allowUsercase': allow_usercase,
            'save_url': reverse("edit_form_actions", args=[app.domain, app.id, form.unique_id]),
            'valid_index_names': valid_index_names,
        })

    context.update({'case_config_options': case_config_options})
    return "app_manager/form_view.html", context
Example #19
0
def _edit_form_attr(request, domain, app_id, form_unique_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(form_unique_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(six.text_type(e))
        else:
            messages.error(request, _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if 'sha1' in request.POST and (should_edit("xform") or "xform" in request.FILES):
        conflict = _get_xform_conflict_response(form, request.POST['sha1'])
        if conflict is not None:
            return conflict

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        if not form.form_type == "shadow_form":
            xform = form.wrapped_xform()
            if xform.exists():
                xform.set_name(name)
                save_xform(app, form, xform.render())
        resp['update'] = {'.variable-form_name': trans(form.name, [lang], use_delim=False)}

    if should_edit('comment'):
        form.comment = request.POST['comment']

    if should_edit("name_enum"):
        name_enum = json.loads(request.POST.get("name_enum"))
        form.name_enum = [MappingItem(i) for i in name_enum]

    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = six.text_type(xform, encoding="utf-8")
                except Exception:
                    raise Exception("Error uploading form: Please make sure your form is encoded in UTF-8")
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile(r'>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
                    prettyXml = text_re.sub(r'>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                if isinstance(xform, six.text_type):
                    xform = xform.encode('utf-8')
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception as e:
            notify_exception(request, six.text_type(e))
            if ajax:
                return HttpResponseBadRequest(six.text_type(e))
            else:
                messages.error(request, six.text_type(e))
    if should_edit("references") or should_edit("case_references"):
        form.case_references = _get_case_references(request.POST)
    if should_edit("show_count"):
        show_count = request.POST['show_count']
        form.show_count = True if show_count == "True" else False
    if should_edit("put_in_root"):
        put_in_root = request.POST['put_in_root']
        form.put_in_root = True if put_in_root == "True" else False
    if should_edit('form_filter'):
        form.form_filter = request.POST['form_filter']
    if should_edit('post_form_workflow'):
        form.post_form_workflow = request.POST['post_form_workflow']
    if should_edit('auto_gps_capture'):
        form.auto_gps_capture = request.POST['auto_gps_capture'] == 'true'
    if should_edit('is_release_notes_form'):
        form.is_release_notes_form = request.POST['is_release_notes_form'] == 'true'
    if should_edit('enable_release_notes'):
        form.enable_release_notes = request.POST['enable_release_notes'] == 'true'
        if not form.is_release_notes_form and form.enable_release_notes:
            return json_response(
                {'message': _("You can't enable a form as release notes without allowing it as "
                    "a release notes form <TODO messaging>")},
                status_code=400
            )
    if should_edit('no_vellum'):
        form.no_vellum = request.POST['no_vellum'] == 'true'
    if (should_edit("form_links_xpath_expressions") and
            should_edit("form_links_form_ids") and
            toggles.FORM_LINK_WORKFLOW.enabled(domain)):
        form_links = zip(
            request.POST.getlist('form_links_xpath_expressions'),
            request.POST.getlist('form_links_form_ids'),
            [
                json.loads(datum_json) if datum_json else []
                for datum_json in request.POST.getlist('datums_json')
            ],
        )
        form.form_links = [FormLink(
            xpath=link[0],
            form_id=link[1],
            datums=[
                FormDatum(name=datum['name'], xpath=datum['xpath'])
                for datum in link[2]
            ]
        ) for link in form_links]

    if should_edit('post_form_workflow_fallback'):
        form.post_form_workflow_fallback = request.POST.get('post_form_workflow_fallback')

    if should_edit('custom_instances'):
        instances = json.loads(request.POST.get('custom_instances'))
        try:  # validate that custom instances can be added into the XML
            for instance in instances:
                etree.fromstring(
                    "<instance id='{}' src='{}' />".format(
                        instance.get('instanceId'),
                        instance.get('instancePath')
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom instances: {}").format(error.message)},
                status_code=400
            )

        form.custom_instances = [
            CustomInstance(
                instance_id=instance.get("instanceId"),
                instance_path=instance.get("instancePath"),
            ) for instance in instances
        ]

    if should_edit('custom_assertions'):
        assertions = json.loads(request.POST.get('custom_assertions'))
        try:  # validate that custom assertions can be added into the XML
            for assertion in assertions:
                etree.fromstring(
                    '<assertion test="{test}"><text><locale id="abc.def"/>{text}</text></assertion>'.format(
                        **assertion
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom assertions: {}").format(error.message)},
                status_code=400
            )

        existing_assertions = {assertion.test: assertion for assertion in form.custom_assertions}
        new_assertions = []
        for assertion in assertions:
            try:
                new_assertion = existing_assertions[assertion.get('test')]
                new_assertion.text[lang] = assertion.get('text')
            except KeyError:
                new_assertion = CustomAssertion(
                    test=assertion.get('test'),
                    text={lang: assertion.get('text')}
                )
            new_assertions.append(new_assertion)

        form.custom_assertions = new_assertions

    if should_edit("shadow_parent"):
        form.shadow_parent_form_id = request.POST['shadow_parent']

    if should_edit("custom_icon_form"):
        error_message = handle_custom_icon_edits(request, form, lang)
        if error_message:
            return json_response(
                {'message': error_message},
                status_code=400
            )
    handle_media_edits(request, form, should_edit, resp, lang)

    app.save(resp)
    notify_form_changed(domain, request.couch_user, app_id, form_unique_id)
    if ajax:
        return HttpResponse(json.dumps(resp))
    else:
        return back_to_main(request, domain, app_id=app_id, form_unique_id=form_unique_id)
Example #20
0
 def qualified_form_name(form, auto_link):
     module_name = trans(module.name, langs)
     form_name = trans(form.name, langs)
     star = '* ' if auto_link else '  '
     return "{}{} -> {}".format(star, module_name, form_name)
Example #21
0
def _edit_form_attr(request, domain, app_id, form_unique_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(form_unique_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(str(e))
        else:
            messages.error(request, _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if 'sha1' in request.POST and (should_edit("xform") or "xform" in request.FILES):
        conflict = _get_xform_conflict_response(form, request.POST['sha1'])
        if conflict is not None:
            return conflict

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        if not form.form_type == "shadow_form":
            xform = form.wrapped_xform()
            if xform.exists():
                xform.set_name(name)
                save_xform(app, form, xform.render())
        resp['update'] = {'.variable-form_name': trans(form.name, [lang], use_delim=False)}

    if should_edit('comment'):
        form.comment = request.POST['comment']

    if should_edit("name_enum"):
        name_enum = json.loads(request.POST.get("name_enum"))
        form.name_enum = [MappingItem(i) for i in name_enum]

    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = str(xform, encoding="utf-8")
                except Exception:
                    raise Exception("Error uploading form: Please make sure your form is encoded in UTF-8")
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile(r'>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
                    prettyXml = text_re.sub(r'>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                if isinstance(xform, str):
                    xform = xform.encode('utf-8')
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception as e:
            notify_exception(request, str(e))
            if ajax:
                return HttpResponseBadRequest(str(e))
            else:
                messages.error(request, str(e))
    if should_edit("references") or should_edit("case_references"):
        form.case_references = _get_case_references(request.POST)
    if should_edit("show_count"):
        show_count = request.POST['show_count']
        form.show_count = True if show_count == "True" else False
    if should_edit("put_in_root"):
        put_in_root = request.POST['put_in_root']
        form.put_in_root = True if put_in_root == "True" else False
    if should_edit('form_filter'):
        form.form_filter = request.POST['form_filter']
    if should_edit('post_form_workflow'):
        form.post_form_workflow = request.POST['post_form_workflow']
    if should_edit('auto_gps_capture'):
        form.auto_gps_capture = request.POST['auto_gps_capture'] == 'true'
    if should_edit('is_release_notes_form'):
        form.is_release_notes_form = request.POST['is_release_notes_form'] == 'true'
    if should_edit('enable_release_notes'):
        form.enable_release_notes = request.POST['enable_release_notes'] == 'true'
        if not form.is_release_notes_form and form.enable_release_notes:
            return json_response(
                {'message': _("You can't enable a form as release notes without allowing it as "
                    "a release notes form <TODO messaging>")},
                status_code=400
            )
    if should_edit('no_vellum'):
        form.no_vellum = request.POST['no_vellum'] == 'true'
    if (should_edit("form_links_xpath_expressions") and
            should_edit("form_links_form_ids") and
            toggles.FORM_LINK_WORKFLOW.enabled(domain)):
        form_links = zip(
            request.POST.getlist('form_links_xpath_expressions'),
            request.POST.getlist('form_links_form_ids'),
            [
                json.loads(datum_json) if datum_json else []
                for datum_json in request.POST.getlist('datums_json')
            ],
        )
        form.form_links = [FormLink(
            xpath=link[0],
            form_id=link[1],
            datums=[
                FormDatum(name=datum['name'], xpath=datum['xpath'])
                for datum in link[2]
            ]
        ) for link in form_links]

    if should_edit('post_form_workflow_fallback'):
        form.post_form_workflow_fallback = request.POST.get('post_form_workflow_fallback')

    if should_edit('custom_instances'):
        instances = json.loads(request.POST.get('custom_instances'))
        try:  # validate that custom instances can be added into the XML
            for instance in instances:
                etree.fromstring(
                    "<instance id='{}' src='{}' />".format(
                        instance.get('instanceId'),
                        instance.get('instancePath')
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom instances: {}").format(error)},
                status_code=400
            )

        form.custom_instances = [
            CustomInstance(
                instance_id=instance.get("instanceId"),
                instance_path=instance.get("instancePath"),
            ) for instance in instances
        ]

    if should_edit('custom_assertions'):
        assertions = json.loads(request.POST.get('custom_assertions'))
        try:  # validate that custom assertions can be added into the XML
            for assertion in assertions:
                etree.fromstring(
                    '<assertion test="{test}"><text><locale id="abc.def"/>{text}</text></assertion>'.format(
                        **assertion
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom assertions: {}").format(error)},
                status_code=400
            )

        existing_assertions = {assertion.test: assertion for assertion in form.custom_assertions}
        new_assertions = []
        for assertion in assertions:
            try:
                new_assertion = existing_assertions[assertion.get('test')]
                new_assertion.text[lang] = assertion.get('text')
            except KeyError:
                new_assertion = CustomAssertion(
                    test=assertion.get('test'),
                    text={lang: assertion.get('text')}
                )
            new_assertions.append(new_assertion)

        form.custom_assertions = new_assertions

    if should_edit("shadow_parent"):
        form.shadow_parent_form_id = request.POST['shadow_parent']

    if should_edit("custom_icon_form"):
        error_message = handle_custom_icon_edits(request, form, lang)
        if error_message:
            return json_response(
                {'message': error_message},
                status_code=400
            )
    handle_media_edits(request, form, should_edit, resp, lang)

    app.save(resp)
    notify_form_changed(domain, request.couch_user, app_id, form_unique_id)
    if ajax:
        return HttpResponse(json.dumps(resp))
    else:
        return back_to_main(request, domain, app_id=app_id, form_unique_id=form_unique_id)
Example #22
0
def get_form_view_context_and_template(request, domain, form, langs, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append(u"Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append(u"Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            form.validate_form()
            xform_questions = xform.get_questions(langs, include_triggers=True, form=form)
        except etree.XMLSyntaxError as e:
            form_errors.append(u"Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append(u"Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
            pass
        except XFormException as e:
            form_errors.append(u"Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append(u"Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachemnts) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled(request.user.username)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        try:
            form_action_errors = form.validate_for_build()
            if not form_action_errors:
                form.add_stuff_to_xform(xform)
        except CaseError as e:
            messages.error(request, u"Error in Case Management: %s" % e)
        except XFormException as e:
            messages.error(request, unicode(e))
        except Exception as e:
            if settings.DEBUG:
                raise
            logging.exception(unicode(e))
            messages.error(request, u"Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    form_has_schedule = isinstance(form, AdvancedForm) and form.get_module().has_schedule
    module_filter_preview = feature_previews.MODULE_FILTER.enabled(request.domain)
    context = {
        'nav_form': form,
        'xform_languages': languages,
        "xform_questions": xform_questions,
        'case_reserved_words_json': load_case_reserved_words(),
        'module_case_types': module_case_types,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'allow_cloudcare': isinstance(form, Form),
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': (module_filter_preview or
            (not isinstance(form, CareplanForm) and not form_has_schedule)),
        'allow_form_workflow': not isinstance(form, CareplanForm),
        'allow_usercase': domain_has_privilege(request.domain, privileges.USER_CASE),
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': (feature_previews.MODULE_FILTER.enabled(request.domain) and
                                     app.enable_module_filtering),
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'case_xpath_pattern_matches': CASE_XPATH_PATTERN_MATCHES,
        'case_xpath_substring_matches': CASE_XPATH_SUBSTRING_MATCHES,
        'user_case_xpath_pattern_matches': USER_CASE_XPATH_PATTERN_MATCHES,
        'user_case_xpath_substring_matches': USER_CASE_XPATH_SUBSTRING_MATCHES,
    }

    if tours.NEW_APP.is_enabled(request.user):
        request.guided_tour = tours.NEW_APP.get_tour_data()

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(domain):
        module = form.get_module()

        def qualified_form_name(form, auto_link):
            module_name = trans(form.get_module().name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return u"{}{} -> {}".format(star, module_name, form_name)

        modules = filter(lambda m: m.case_type == module.case_type, all_modules)
        if getattr(module, 'root_module_id', None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(itertools.chain.from_iterable(list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, CareplanForm):
        context.update({
            'mode': form.mode,
            'fixed_questions': form.get_fixed_questions(),
            'custom_case_properties': [
                {'key': key, 'path': path} for key, path in form.custom_case_updates.items()
            ],
            'case_preload': [
                {'key': key, 'path': path} for key, path in form.case_preload.items()
            ],
        })
        return "app_manager/form_view_careplan.html", context
    elif isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled(request.user.username),
            'commtrack_programs': all_programs + commtrack_programs(),
        })
        context.update(get_schedule_context(form))
        return "app_manager/form_view_advanced.html", context
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled(request.user.username),
        })
        return "app_manager/form_view.html", context
Example #23
0
def _edit_form_attr(request, domain, app_id, unique_form_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(unique_form_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(unicode(e))
        else:
            messages.error(request, _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        xform = form.wrapped_xform()
        if xform.exists():
            xform.set_name(name)
            save_xform(app, form, xform.render())
        resp['update'] = {'.variable-form_name': trans(form.name, [lang], use_delim=False)}
    if should_edit('comment'):
        form.comment = request.POST['comment']
    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = unicode(xform, encoding="utf-8")
                except Exception:
                    raise Exception("Error uploading form: Please make sure your form is encoded in UTF-8")
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
                    prettyXml = text_re.sub('>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception, e:
            if ajax:
                return HttpResponseBadRequest(unicode(e))
            else:
                messages.error(request, unicode(e))
Example #24
0
def edit_module_attr(request, domain, app_id, module_unique_id, attr):
    """
    Called to edit any (supported) module attribute, given by attr
    """
    attributes = {
        "all": None,
        "auto_select_case": None,
        "case_list": ('case_list-show', 'case_list-label'),
        "case_list-menu_item_media_audio": None,
        "case_list-menu_item_media_image": None,
        'case_list-menu_item_use_default_image_for_all': None,
        'case_list-menu_item_use_default_audio_for_all': None,
        "case_list_form_id": None,
        "case_list_form_label": None,
        "case_list_form_media_audio": None,
        "case_list_form_media_image": None,
        'case_list_form_use_default_image_for_all': None,
        'case_list_form_use_default_audio_for_all': None,
        "case_list_post_form_workflow": None,
        "case_type": None,
        'comment': None,
        "display_separately": None,
        "has_schedule": None,
        "media_audio": None,
        "media_image": None,
        "module_filter": None,
        "name": None,
        "name_enum": None,
        "parent_module": None,
        "put_in_root": None,
        "root_module_id": None,
        "source_module_id": None,
        "task_list": ('task_list-show', 'task_list-label'),
        "excl_form_ids": None,
        "display_style": None,
        "custom_icon_form": None,
        "custom_icon_text_body": None,
        "custom_icon_xpath": None,
        "use_default_image_for_all": None,
        "use_default_audio_for_all": None,
    }

    if attr not in attributes:
        return HttpResponseBadRequest()

    def should_edit(attribute):
        if attribute == attr:
            return True
        if 'all' == attr:
            if attributes[attribute]:
                for param in attributes[attribute]:
                    if not request.POST.get(param):
                        return False
                return True
            else:
                return request.POST.get(attribute) is not None

    app = get_app(domain, app_id)

    try:
        module = app.get_module_by_unique_id(module_unique_id)
    except ModuleNotFoundException:
        # temporary fallback
        module = app.get_module(module_unique_id)

    lang = request.COOKIES.get('lang', app.langs[0])
    resp = {'update': {}, 'corrections': {}}
    if should_edit("custom_icon_form"):
        error_message = handle_custom_icon_edits(request, module, lang)
        if error_message:
            return json_response(
                {'message': error_message},
                status_code=400
            )

    if should_edit("name_enum"):
        name_enum = json.loads(request.POST.get("name_enum"))
        module.name_enum = [MappingItem(i) for i in name_enum]

    if should_edit("case_type"):
        case_type = request.POST.get("case_type", None)
        if case_type == USERCASE_TYPE and not isinstance(module, AdvancedModule):
            return HttpResponseBadRequest('"{}" is a reserved case type'.format(USERCASE_TYPE))
        elif case_type and not is_valid_case_type(case_type, module):
            return HttpResponseBadRequest("case type is improperly formatted")
        else:
            old_case_type = module["case_type"]
            module["case_type"] = case_type

            # rename other reference to the old case type
            all_advanced_modules = []
            modules_with_old_case_type_exist = False
            for mod in app.modules:
                if isinstance(mod, AdvancedModule):
                    all_advanced_modules.append(mod)

                modules_with_old_case_type_exist |= mod.case_type == old_case_type
            for mod in all_advanced_modules:
                for form in mod.forms:
                    for action in form.actions.get_load_update_actions():
                        if action.case_type == old_case_type and action.details_module == module_unique_id:
                            action.case_type = case_type

                    if mod.unique_id == module_unique_id or not modules_with_old_case_type_exist:
                        for action in form.actions.get_open_actions():
                            if action.case_type == old_case_type:
                                action.case_type = case_type

    if should_edit("put_in_root"):
        module["put_in_root"] = json.loads(request.POST.get("put_in_root"))
    if should_edit("display_style"):
        module["display_style"] = request.POST.get("display_style")
    if should_edit("source_module_id"):
        module["source_module_id"] = request.POST.get("source_module_id")
    if should_edit("display_separately"):
        module["display_separately"] = json.loads(request.POST.get("display_separately"))
    if should_edit("parent_module"):
        parent_module = request.POST.get("parent_module")
        module.parent_select.module_id = parent_module
        if module_case_hierarchy_has_circular_reference(module):
            return HttpResponseBadRequest(_("The case hierarchy contains a circular reference."))
    if should_edit("auto_select_case"):
        module["auto_select_case"] = request.POST.get("auto_select_case") == 'true'

    if app.enable_module_filtering and should_edit('module_filter'):
        module['module_filter'] = request.POST.get('module_filter')

    if should_edit('case_list_form_id'):
        module.case_list_form.form_id = request.POST.get('case_list_form_id')
    if should_edit('case_list_form_label'):
        module.case_list_form.label[lang] = request.POST.get('case_list_form_label')
    if should_edit('case_list_post_form_workflow'):
        module.case_list_form.post_form_workflow = request.POST.get('case_list_post_form_workflow')

    if should_edit("name"):
        name = request.POST.get("name", None)
        module["name"][lang] = name
        resp['update'] = {'.variable-module_name': trans(module.name, [lang], use_delim=False)}
    if should_edit('comment'):
        module.comment = request.POST.get('comment')
    for SLUG in ('case_list', 'task_list'):
        show = '{SLUG}-show'.format(SLUG=SLUG)
        label = '{SLUG}-label'.format(SLUG=SLUG)
        if request.POST.get(show) == 'true' and (request.POST.get(label) == ''):
            # Show item, but empty label, was just getting ignored
            return HttpResponseBadRequest("A label is required for {SLUG}".format(SLUG=SLUG))
        if should_edit(SLUG):
            module[SLUG].show = json.loads(request.POST[show])
            module[SLUG].label[lang] = request.POST[label]

    if should_edit("root_module_id"):
        old_root = module['root_module_id']
        if not request.POST.get("root_module_id"):
            module["root_module_id"] = None
        else:
            module["root_module_id"] = request.POST.get("root_module_id")
        if not old_root and module['root_module_id']:
            track_workflow(request.couch_user.username, "User associated module with a parent")
        elif old_root and not module['root_module_id']:
            track_workflow(request.couch_user.username, "User orphaned a child module")

    if should_edit('excl_form_ids') and isinstance(module, ShadowModule):
        excl = request.POST.getlist('excl_form_ids')
        excl.remove('0')  # Placeholder value to make sure excl_form_ids is POSTed when no forms are excluded
        module.excluded_form_ids = excl

    handle_media_edits(request, module, should_edit, resp, lang)
    handle_media_edits(request, module.case_list_form, should_edit, resp, lang, prefix='case_list_form_')
    handle_media_edits(request, module.case_list, should_edit, resp, lang, prefix='case_list-menu_item_')


    app.save(resp)
    resp['case_list-show'] = module.requires_case_details()
    return HttpResponse(json.dumps(resp))
Example #25
0
def edit_module_attr(request, domain, app_id, module_id, attr):
    """
    Called to edit any (supported) module attribute, given by attr
    """
    attributes = {
        "all": None,
        "auto_select_case": None,
        "case_label": None,
        "case_list": ('case_list-show', 'case_list-label'),
        "case_list-menu_item_media_audio": None,
        "case_list-menu_item_media_image": None,
        "case_list_form_id": None,
        "case_list_form_label": None,
        "case_list_form_media_audio": None,
        "case_list_form_media_image": None,
        "case_type": None,
        'comment': None,
        "display_separately": None,
        "has_schedule": None,
        "media_audio": None,
        "media_image": None,
        "module_filter": None,
        "name": None,
        "parent_module": None,
        "put_in_root": None,
        "referral_label": None,
        "root_module_id": None,
        "source_module_id": None,
        "task_list": ('task_list-show', 'task_list-label'),
        "excl_form_ids": None,
        "display_style": None
    }

    if attr not in attributes:
        return HttpResponseBadRequest()

    def should_edit(attribute):
        if attribute == attr:
            return True
        if 'all' == attr:
            if attributes[attribute]:
                for param in attributes[attribute]:
                    if not request.POST.get(param):
                        return False
                return True
            else:
                return request.POST.get(attribute) is not None

    app = get_app(domain, app_id)
    module = app.get_module(module_id)
    lang = request.COOKIES.get('lang', app.langs[0])
    resp = {'update': {}, 'corrections': {}}
    if should_edit("case_type"):
        case_type = request.POST.get("case_type", None)
        if not case_type or is_valid_case_type(case_type, module):
            old_case_type = module["case_type"]
            module["case_type"] = case_type
            for cp_mod in (mod for mod in app.modules if isinstance(mod, CareplanModule)):
                if cp_mod.unique_id != module.unique_id and cp_mod.parent_select.module_id == module.unique_id:
                    cp_mod.case_type = case_type

            def rename_action_case_type(mod):
                for form in mod.forms:
                    for action in form.actions.get_all_actions():
                        if action.case_type == old_case_type:
                            action.case_type = case_type

            if isinstance(module, AdvancedModule):
                rename_action_case_type(module)
            for ad_mod in (mod for mod in app.modules if isinstance(mod, AdvancedModule)):
                if ad_mod.unique_id != module.unique_id and ad_mod.case_type != old_case_type:
                    # only apply change if the module's case_type does not reference the old value
                    rename_action_case_type(ad_mod)
        elif case_type == USERCASE_TYPE and not isinstance(module, AdvancedModule):
            return HttpResponseBadRequest('"{}" is a reserved case type'.format(USERCASE_TYPE))
        else:
            return HttpResponseBadRequest("case type is improperly formatted")
    if should_edit("put_in_root"):
        module["put_in_root"] = json.loads(request.POST.get("put_in_root"))
    if should_edit("display_style"):
        module["display_style"] = request.POST.get("display_style")
    if should_edit("source_module_id"):
        module["source_module_id"] = request.POST.get("source_module_id")
    if should_edit("display_separately"):
        module["display_separately"] = json.loads(request.POST.get("display_separately"))
    if should_edit("parent_module"):
        parent_module = request.POST.get("parent_module")
        module.parent_select.module_id = parent_module
        if module_case_hierarchy_has_circular_reference(module):
            return HttpResponseBadRequest(_("The case hierarchy contains a circular reference."))
    if should_edit("auto_select_case"):
        module["auto_select_case"] = request.POST.get("auto_select_case") == 'true'

    if app.enable_module_filtering and should_edit('module_filter'):
        module['module_filter'] = request.POST.get('module_filter')

    if should_edit('case_list_form_id'):
        module.case_list_form.form_id = request.POST.get('case_list_form_id')
    if should_edit('case_list_form_label'):
        module.case_list_form.label[lang] = request.POST.get('case_list_form_label')
    if should_edit('case_list_form_media_image'):
        new_path = process_media_attribute(
            'case_list_form_media_image',
            resp,
            request.POST.get('case_list_form_media_image')
        )
        module.case_list_form.set_icon(lang, new_path)

    if should_edit('case_list_form_media_audio'):
        new_path = process_media_attribute(
            'case_list_form_media_audio',
            resp,
            request.POST.get('case_list_form_media_audio')
        )
        module.case_list_form.set_audio(lang, new_path)

    if should_edit('case_list-menu_item_media_image'):
        val = process_media_attribute(
            'case_list-menu_item_media_image',
            resp,
            request.POST.get('case_list-menu_item_media_image')
        )
        module.case_list.set_icon(lang, val)
    if should_edit('case_list-menu_item_media_audio'):
        val = process_media_attribute(
            'case_list-menu_item_media_audio',
            resp,
            request.POST.get('case_list-menu_item_media_audio')
        )
        module.case_list.set_audio(lang, val)

    for attribute in ("name", "case_label", "referral_label"):
        if should_edit(attribute):
            name = request.POST.get(attribute, None)
            module[attribute][lang] = name
            if should_edit("name"):
                resp['update'] = {'.variable-module_name': trans(module.name, [lang], use_delim=False)}
    if should_edit('comment'):
        module.comment = request.POST.get('comment')
    for SLUG in ('case_list', 'task_list'):
        show = '{SLUG}-show'.format(SLUG=SLUG)
        label = '{SLUG}-label'.format(SLUG=SLUG)
        if request.POST.get(show) == 'true' and (request.POST.get(label) == ''):
            # Show item, but empty label, was just getting ignored
            return HttpResponseBadRequest("A label is required for {SLUG}".format(SLUG=SLUG))
        if should_edit(SLUG):
            module[SLUG].show = json.loads(request.POST[show])
            module[SLUG].label[lang] = request.POST[label]

    if should_edit("root_module_id"):
        if not request.POST.get("root_module_id"):
            module["root_module_id"] = None
        else:
            try:
                app.get_module(module_id)
                module["root_module_id"] = request.POST.get("root_module_id")
            except ModuleNotFoundException:
                messages.error(_("Unknown Menu"))

    if should_edit('excl_form_ids') and isinstance(module, ShadowModule):
        excl = request.POST.getlist('excl_form_ids')
        excl.remove('0')  # Placeholder value to make sure excl_form_ids is POSTed when no forms are excluded
        module.excluded_form_ids = excl

    handle_media_edits(request, module, should_edit, resp, lang)

    app.save(resp)
    resp['case_list-show'] = module.requires_case_details()
    return HttpResponse(json.dumps(resp))
Example #26
0
def schedule_detail_variables(module, detail, detail_column_infos):
    has_schedule_columns = any(ci.column.field_type == FIELD_TYPE_SCHEDULE
                               for ci in detail_column_infos)
    has_schedule = getattr(module, 'has_schedule', False)
    if (has_schedule and module.all_forms_require_a_case()
            and has_schedule_columns):
        yield DetailVariable(name=SCHEDULE_DATE_CASE_OPENED,
                             function="date_opened")  # date case is opened
        forms_due = []
        last_visit_dates = []
        for phase in module.get_schedule_phases():
            if not phase.anchor:
                raise ScheduleError(
                    _("Schedule Phase in module '{module_name}' is missing an anchor"
                      ).format(module_name=module.default_name()))

            for form in phase.get_forms():
                """
                Adds the following variables for each form:
                <anchor_{form_id} function="{anchor}"/>
                <last_visit_number_{form_id} function="{last_visit_number}"/>
                <last_visit_date_{form_id} function="{last_visit_date}"/>
                <next_{form_id} function={phase_set}/>
                """
                if not form.schedule_form_id:
                    raise ScheduleError(
                        _("'{form_name}' in '{module_name}' is missing an abbreviation"
                          ).format(form_name=trans(
                              form["name"],
                              langs=[module.get_app().default_language]),
                                   module_name=module.default_name()))
                form_xpath = ScheduleFormXPath(form, phase, module)
                name = u"next_{}".format(form.schedule_form_id)
                forms_due.append(u"${}".format(name))

                # Add an anchor and last_visit variables so we can reference it in the calculation
                yield DetailVariable(
                    name=form_xpath.anchor_detail_variable_name,
                    function=phase.anchor)
                yield DetailVariable(
                    name=form_xpath.last_visit_detail_variable_name,
                    function=SCHEDULE_LAST_VISIT.format(form.schedule_form_id))
                yield DetailVariable(
                    name=form_xpath.last_visit_date_detail_variable_name,
                    function=SCHEDULE_LAST_VISIT_DATE.format(
                        form.schedule_form_id))
                last_visit_dates.append(
                    form_xpath.last_visit_date_detail_variable_name)
                if phase.id == 1:
                    # If this is the first phase, `current_schedule_phase` and
                    # last_visit_num might not be set yet
                    yield DetailVariable(
                        name=name, function=form_xpath.first_visit_phase_set)
                else:
                    yield DetailVariable(name=name,
                                         function=form_xpath.xpath_phase_set)

        yield DetailVariable(name=SCHEDULE_GLOBAL_NEXT_VISIT_DATE,
                             function=u'date(min({}))'.format(
                                 ','.join(forms_due)))
        yield DetailVariable(
            name=SCHEDULE_NEXT_DUE,
            function=ScheduleFormXPath.next_visit_date(last_visit_dates))
        yield DetailVariable(
            name='is_late',
            function=u'${} < today()'.format(SCHEDULE_NEXT_DUE))

        if len(forms_due) != len(set(forms_due)):
            raise ScheduleError(
                _("Your app has multiple forms with the same schedule abbreviation"
                  ))
Example #27
0
def form_designer(request, domain, app_id, module_id=None, form_id=None):
    def _form_uses_case(module, form):
        return module and module.case_type and form.requires_case()

    def _form_is_basic(form):
        return form.doc_type == 'Form'

    def _form_too_large(app, form):
        # form less than 0.1MB, anything larger starts to have
        # performance issues with fullstory
        return app.blobs['{}.xml'.format(
            form.unique_id)]['content_length'] > 102400

    meta = get_meta(request)
    track_entered_form_builder_on_hubspot.delay(request.couch_user,
                                                request.COOKIES, meta)

    app = get_app(domain, app_id)
    module = None

    try:
        module = app.get_module(module_id)
    except ModuleNotFoundException:
        return bail(request, domain, app_id, not_found="module")
    try:
        form = module.get_form(form_id)
    except IndexError:
        return bail(request, domain, app_id, not_found="form")

    if form.no_vellum:
        messages.warning(
            request,
            _("You tried to edit this form in the Form Builder. "
              "However, your administrator has locked this form against editing "
              "in the form builder, so we have redirected you to "
              "the form's front page instead."))
        return back_to_main(request,
                            domain,
                            app_id=app_id,
                            unique_form_id=form.unique_id)

    include_fullstory = False
    vellum_plugins = ["modeliteration", "itemset", "atwho"]
    if (toggles.COMMTRACK.enabled(domain)):
        vellum_plugins.append("commtrack")
    if toggles.VELLUM_SAVE_TO_CASE.enabled(domain):
        vellum_plugins.append("saveToCase")
    if (_form_uses_case(module, form) and _form_is_basic(form)):
        vellum_plugins.append("databrowser")

    vellum_features = toggles.toggles_dict(username=request.user.username,
                                           domain=domain)
    vellum_features.update(feature_previews.previews_dict(domain))
    include_fullstory = not _form_too_large(app, form)
    vellum_features.update({
        'group_in_field_list':
        app.enable_group_in_field_list,
        'image_resize':
        app.enable_image_resize,
        'markdown_in_groups':
        app.enable_markdown_in_groups,
        'lookup_tables':
        domain_has_privilege(domain, privileges.LOOKUP_TABLES),
        'templated_intents':
        domain_has_privilege(domain, privileges.TEMPLATED_INTENTS),
        'custom_intents':
        domain_has_privilege(domain, privileges.CUSTOM_INTENTS),
        'rich_text':
        True,
    })

    has_schedule = (getattr(module, 'has_schedule', False)
                    and getattr(form, 'schedule', False)
                    and form.schedule.enabled)
    scheduler_data_nodes = []
    if has_schedule:
        scheduler_data_nodes = [
            SCHEDULE_CURRENT_VISIT_NUMBER,
            SCHEDULE_NEXT_DUE,
            SCHEDULE_UNSCHEDULED_VISIT,
            SCHEDULE_GLOBAL_NEXT_VISIT_DATE,
        ]
        scheduler_data_nodes.extend([
            u"next_{}".format(f.schedule_form_id)
            for f in form.get_phase().get_forms()
            if getattr(f, 'schedule', False) and f.schedule.enabled
        ])

    if tours.VELLUM_CASE_MANAGEMENT.is_enabled(
            request.user) and form.requires_case():
        request.guided_tour = tours.VELLUM_CASE_MANAGEMENT.get_tour_data()

    context = get_apps_base_context(request, domain, app)
    context.update(locals())
    context.update({
        'vellum_debug':
        settings.VELLUM_DEBUG,
        'nav_form':
        form,
        'formdesigner':
        True,
        'include_fullstory':
        include_fullstory,
        'notifications_enabled':
        request.user.is_superuser,
        'notify_facility':
        get_facility_for_form(domain, app_id, form.unique_id),
    })
    notify_form_opened(domain, request.couch_user, app_id, form.unique_id)

    domain_obj = Domain.get_by_name(domain)
    context.update({
        'show_live_preview':
        should_show_preview_app(
            request,
            app,
            request.couch_user.username,
        ),
        'can_preview_form':
        request.couch_user.has_permission(domain, 'edit_data'),
    })

    core = {
        'dataSourcesEndpoint':
        reverse('get_form_data_schema',
                kwargs={
                    'domain': domain,
                    'form_unique_id': form.get_unique_id()
                }),
        'dataSource': [
            # DEPRECATED. Use dataSourcesEndpoint
            {
                'key': 'fixture',
                'name': 'Fixtures',
                'endpoint': reverse('fixture_metadata',
                                    kwargs={'domain': domain}),
            },
        ],
        'form':
        form.source,
        'formId':
        form.get_unique_id(),
        'formName':
        trans(form.name, app.langs),
        'saveType':
        'patch',
        'saveUrl':
        reverse('edit_form_attr',
                args=[domain, app.id,
                      form.get_unique_id(), 'xform']),
        'patchUrl':
        reverse('patch_xform', args=[domain, app.id,
                                     form.get_unique_id()]),
        'allowedDataNodeReferences': [
            "meta/deviceID",
            "meta/instanceID",
            "meta/username",
            "meta/userID",
            "meta/timeStart",
            "meta/timeEnd",
            "meta/location",
        ] + scheduler_data_nodes,
        'activityUrl':
        reverse('ping'),
        'sessionid':
        request.COOKIES.get('sessionid'),
        'externalLinks': {
            'changeSubscription':
            reverse("domain_subscription_view", kwargs={'domain': domain}),
        },
        'invalidCaseProperties': ['name'],
    }

    if toggles.APP_MANAGER_V2.enabled(request.user.username):
        if form.get_action_type() == 'open':
            core.update({
                'defaultHelpTextTemplateId': '#fd-hq-helptext-registration',
                'formIconClass': 'fcc fcc-app-createform',
            })
        elif form.get_action_type() == 'close':
            core.update({
                'defaultHelpTextTemplateId': '#fd-hq-helptext-close',
                'formIconClass': 'fcc fcc-app-completeform',
            })
        elif form.get_action_type() == 'update':
            core.update({
                'defaultHelpTextTemplateId': '#fd-hq-helptext-followup',
                'formIconClass': 'fcc fcc-app-updateform',
            })
        else:
            core.update({
                'defaultHelpTextTemplateId': '#fd-hq-helptext-survey',
                'formIconClass': 'fa fa-file-o',
            })

    vellum_options = {
        'core': core,
        'plugins': vellum_plugins,
        'features': vellum_features,
        'intents': {
            'templates': next(app_callout_templates),
        },
        'javaRosa': {
            'langs': app.langs,
            'displayLanguage': context['lang'],
        },
        'uploader': {
            'uploadUrls': {
                'image': reverse("hqmedia_uploader_image",
                                 args=[domain, app.id]),
                'audio': reverse("hqmedia_uploader_audio",
                                 args=[domain, app.id]),
                'video': reverse("hqmedia_uploader_video",
                                 args=[domain, app.id]),
                'text': reverse("hqmedia_uploader_text", args=[domain,
                                                               app.id]),
            },
            'objectMap': app.get_object_map(),
            'sessionid': request.COOKIES.get('sessionid'),
        },
    }
    context.update({
        'vellum_options': vellum_options,
        'CKEDITOR_BASEPATH': "app_manager/js/vellum/lib/ckeditor/",
    })

    if not settings.VELLUM_DEBUG:
        context.update({'requirejs_url': "app_manager/js/vellum/src"})
    elif settings.VELLUM_DEBUG == "dev-min":
        context.update({'requirejs_url': "formdesigner/_build/src"})
    else:
        context.update({'requirejs_url': "formdesigner/src"})
    context.update({
        'requirejs_args':
        'version={}{}'.format(
            cachebuster("app_manager/js/vellum/src/main-components.js"),
            cachebuster("app_manager/js/vellum/src/local-deps.js")),
    })

    template = get_app_manager_template(
        request.user,
        'app_manager/v1/form_designer.html',
        'app_manager/v2/form_designer.html',
    )

    response = render(request, template, context)
    return response
Example #28
0
def _edit_form_attr(request, domain, app_id, unique_form_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(unique_form_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(unicode(e))
        else:
            messages.error(request, _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        xform = form.wrapped_xform()
        if xform.exists():
            xform.set_name(name)
            save_xform(app, form, xform.render())
        resp['update'] = {'.variable-form_name': trans(form.name, [lang], use_delim=False)}
    if should_edit('comment'):
        form.comment = request.POST['comment']
    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = unicode(xform, encoding="utf-8")
                except Exception:
                    raise Exception("Error uploading form: Please make sure your form is encoded in UTF-8")
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
                    prettyXml = text_re.sub('>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception, e:
            if ajax:
                return HttpResponseBadRequest(unicode(e))
            else:
                messages.error(request, unicode(e))
Example #29
0
def edit_module_attr(request, domain, app_id, module_id, attr):
    """
    Called to edit any (supported) module attribute, given by attr
    """
    attributes = {
        "all": None,
        "auto_select_case": None,
        "case_label": None,
        "case_list": ('case_list-show', 'case_list-label'),
        "case_list-menu_item_media_audio": None,
        "case_list-menu_item_media_image": None,
        "case_list_form_id": None,
        "case_list_form_label": None,
        "case_list_form_media_audio": None,
        "case_list_form_media_image": None,
        "case_type": None,
        'comment': None,
        "display_separately": None,
        "has_schedule": None,
        "media_audio": None,
        "media_image": None,
        "module_filter": None,
        "name": None,
        "parent_module": None,
        "put_in_root": None,
        "referral_label": None,
        "root_module_id": None,
        "source_module_id": None,
        "task_list": ('task_list-show', 'task_list-label'),
        "excl_form_ids": None,
        "display_style": None
    }

    if attr not in attributes:
        return HttpResponseBadRequest()

    def should_edit(attribute):
        if attribute == attr:
            return True
        if 'all' == attr:
            if attributes[attribute]:
                for param in attributes[attribute]:
                    if not request.POST.get(param):
                        return False
                return True
            else:
                return request.POST.get(attribute) is not None

    app = get_app(domain, app_id)
    module = app.get_module(module_id)
    lang = request.COOKIES.get('lang', app.langs[0])
    resp = {'update': {}, 'corrections': {}}
    if should_edit("case_type"):
        case_type = request.POST.get("case_type", None)
        if case_type == USERCASE_TYPE and not isinstance(
                module, AdvancedModule):
            return HttpResponseBadRequest(
                '"{}" is a reserved case type'.format(USERCASE_TYPE))
        elif case_type and not is_valid_case_type(case_type, module):
            return HttpResponseBadRequest("case type is improperly formatted")
        else:
            old_case_type = module["case_type"]
            module["case_type"] = case_type

            # rename other reference to the old case type
            other_careplan_modules = []
            all_advanced_modules = []
            modules_with_old_case_type_exist = False
            for mod in app.modules:
                if mod.unique_id != module_id:
                    if isinstance(mod, CareplanModule):
                        other_careplan_modules.append(mod)

                if isinstance(mod, AdvancedModule):
                    all_advanced_modules.append(mod)

                modules_with_old_case_type_exist |= mod.case_type == old_case_type

            for cp_mod in other_careplan_modules:
                if cp_mod.parent_select.module_id == module_id:
                    cp_mod.case_type = case_type

            for mod in all_advanced_modules:
                for form in mod.forms:
                    for action in form.actions.get_load_update_actions():
                        if action.case_type == old_case_type and action.details_module == module_id:
                            action.case_type = case_type

                    if mod.unique_id == module_id or not modules_with_old_case_type_exist:
                        for action in form.actions.get_open_actions():
                            if action.case_type == old_case_type:
                                action.case_type = case_type

    if should_edit("put_in_root"):
        module["put_in_root"] = json.loads(request.POST.get("put_in_root"))
    if should_edit("display_style"):
        module["display_style"] = request.POST.get("display_style")
    if should_edit("source_module_id"):
        module["source_module_id"] = request.POST.get("source_module_id")
    if should_edit("display_separately"):
        module["display_separately"] = json.loads(
            request.POST.get("display_separately"))
    if should_edit("parent_module"):
        parent_module = request.POST.get("parent_module")
        module.parent_select.module_id = parent_module
        if module_case_hierarchy_has_circular_reference(module):
            return HttpResponseBadRequest(
                _("The case hierarchy contains a circular reference."))
    if should_edit("auto_select_case"):
        module["auto_select_case"] = request.POST.get(
            "auto_select_case") == 'true'

    if app.enable_module_filtering and should_edit('module_filter'):
        module['module_filter'] = request.POST.get('module_filter')

    if should_edit('case_list_form_id'):
        module.case_list_form.form_id = request.POST.get('case_list_form_id')
    if should_edit('case_list_form_label'):
        module.case_list_form.label[lang] = request.POST.get(
            'case_list_form_label')
    if should_edit('case_list_form_media_image'):
        new_path = process_media_attribute(
            'case_list_form_media_image', resp,
            request.POST.get('case_list_form_media_image'))
        module.case_list_form.set_icon(lang, new_path)

    if should_edit('case_list_form_media_audio'):
        new_path = process_media_attribute(
            'case_list_form_media_audio', resp,
            request.POST.get('case_list_form_media_audio'))
        module.case_list_form.set_audio(lang, new_path)

    if should_edit('case_list-menu_item_media_image'):
        val = process_media_attribute(
            'case_list-menu_item_media_image', resp,
            request.POST.get('case_list-menu_item_media_image'))
        module.case_list.set_icon(lang, val)
    if should_edit('case_list-menu_item_media_audio'):
        val = process_media_attribute(
            'case_list-menu_item_media_audio', resp,
            request.POST.get('case_list-menu_item_media_audio'))
        module.case_list.set_audio(lang, val)

    for attribute in ("name", "case_label", "referral_label"):
        if should_edit(attribute):
            name = request.POST.get(attribute, None)
            module[attribute][lang] = name
            if should_edit("name"):
                resp['update'] = {
                    '.variable-module_name':
                    trans(module.name, [lang], use_delim=False)
                }
    if should_edit('comment'):
        module.comment = request.POST.get('comment')
    for SLUG in ('case_list', 'task_list'):
        show = '{SLUG}-show'.format(SLUG=SLUG)
        label = '{SLUG}-label'.format(SLUG=SLUG)
        if request.POST.get(show) == 'true' and (request.POST.get(label)
                                                 == ''):
            # Show item, but empty label, was just getting ignored
            return HttpResponseBadRequest(
                "A label is required for {SLUG}".format(SLUG=SLUG))
        if should_edit(SLUG):
            module[SLUG].show = json.loads(request.POST[show])
            module[SLUG].label[lang] = request.POST[label]

    if should_edit("root_module_id"):
        if not request.POST.get("root_module_id"):
            module["root_module_id"] = None
        else:
            try:
                app.get_module(module_id)
                module["root_module_id"] = request.POST.get("root_module_id")
            except ModuleNotFoundException:
                messages.error(_("Unknown Menu"))

    if should_edit('excl_form_ids') and isinstance(module, ShadowModule):
        excl = request.POST.getlist('excl_form_ids')
        excl.remove(
            '0'
        )  # Placeholder value to make sure excl_form_ids is POSTed when no forms are excluded
        module.excluded_form_ids = excl

    handle_media_edits(request, module, should_edit, resp, lang)

    app.save(resp)
    resp['case_list-show'] = module.requires_case_details()
    return HttpResponse(json.dumps(resp))
Example #30
0
def get_form_view_context_and_template(request, domain, form, langs, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append(u"Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append(u"Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append(u"Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append(u"Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
            pass
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request, _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append(u"Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append(u"Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachemnts) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled(request.user.username)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                messages.error(request, u"Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, unicode(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(unicode(e))
                messages.error(request, u"Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    form_has_schedule = isinstance(form, AdvancedForm) and form.get_module().has_schedule
    context = {
        'nav_form': form,
        'xform_languages': languages,
        "xform_questions": xform_questions,
        'case_reserved_words_json': load_case_reserved_words(),
        'module_case_types': module_case_types,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'xform_validation_missing': xform_validation_missing,
        'allow_cloudcare': isinstance(form, Form),
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': not isinstance(form, CareplanForm) and not form_has_schedule,
        'allow_form_workflow': not isinstance(form, CareplanForm),
        'uses_form_workflow': form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase': domain_has_privilege(request.domain, privileges.USER_CASE),
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': app.enable_module_filtering,
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'case_xpath_pattern_matches': CASE_XPATH_PATTERN_MATCHES,
        'case_xpath_substring_matches': CASE_XPATH_SUBSTRING_MATCHES,
        'user_case_xpath_pattern_matches': USER_CASE_XPATH_PATTERN_MATCHES,
        'user_case_xpath_substring_matches': USER_CASE_XPATH_SUBSTRING_MATCHES,
        'custom_instances': [
            {'instanceId': instance.instance_id, 'instancePath': instance.instance_path}
            for instance in form.custom_instances
        ],
    }

    if tours.NEW_APP.is_enabled(request.user):
        request.guided_tour = tours.NEW_APP.get_tour_data()

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(domain):
        module = form.get_module()

        def qualified_form_name(form, auto_link):
            module_name = trans(form.get_module().name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return u"{}{} -> {}".format(star, module_name, form_name)

        modules = filter(lambda m: m.case_type == module.case_type, all_modules)
        if getattr(module, 'root_module_id', None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(itertools.chain.from_iterable(list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, CareplanForm):
        context.update({
            'mode': form.mode,
            'fixed_questions': form.get_fixed_questions(),
            'custom_case_properties': [
                {'key': key, 'path': path} for key, path in form.custom_case_updates.items()
            ],
            'case_preload': [
                {'key': key, 'path': path} for key, path in form.case_preload.items()
            ],
        })
        return "app_manager/v1/form_view_careplan.html", context
    elif isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled(request.user.username),
            'commtrack_programs': all_programs + commtrack_programs(),
        })
        context.update(get_schedule_context(form))
        return "app_manager/v1/form_view_advanced.html", context
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled(request.user.username),
        })
        return "app_manager/v1/form_view.html", context
Example #31
0
 def _module_name(module):
     return trans(module.name, langs)
Example #32
0
 def qualified_form_name(form, auto_link):
     module_name = trans(form.get_module().name, langs)
     form_name = trans(form.name, langs)
     star = '* ' if auto_link else '  '
     return u"{}{} -> {}".format(star, module_name, form_name)
Example #33
0
def _edit_form_attr(request, domain, app_id, unique_form_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(unique_form_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(unicode(e))
        else:
            messages.error(request,
                           _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        xform = form.wrapped_xform()
        if xform.exists():
            xform.set_name(name)
            save_xform(app, form, xform.render())
        resp['update'] = {
            '.variable-form_name': trans(form.name, [lang], use_delim=False)
        }
    if should_edit('comment'):
        form.comment = request.POST['comment']
    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = unicode(xform, encoding="utf-8")
                except Exception:
                    raise Exception(
                        "Error uploading form: Please make sure your form is encoded in UTF-8"
                    )
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</',
                                         re.DOTALL)
                    prettyXml = text_re.sub('>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception as e:
            if ajax:
                return HttpResponseBadRequest(unicode(e))
            else:
                messages.error(request, unicode(e))
    if should_edit("references") or should_edit("case_references"):
        form.case_references = _get_case_references(request.POST)
    if should_edit("show_count"):
        show_count = request.POST['show_count']
        form.show_count = True if show_count == "True" else False
    if should_edit("put_in_root"):
        put_in_root = request.POST['put_in_root']
        form.put_in_root = True if put_in_root == "True" else False
    if should_edit('form_filter'):
        form.form_filter = request.POST['form_filter']
    if should_edit('post_form_workflow'):
        form.post_form_workflow = request.POST['post_form_workflow']
    if should_edit('auto_gps_capture'):
        form.auto_gps_capture = request.POST['auto_gps_capture'] == 'true'
    if should_edit('no_vellum'):
        form.no_vellum = request.POST['no_vellum'] == 'true'
    if (should_edit("form_links_xpath_expressions")
            and should_edit("form_links_form_ids")
            and toggles.FORM_LINK_WORKFLOW.enabled(domain)):
        form_links = zip(
            request.POST.getlist('form_links_xpath_expressions'),
            request.POST.getlist('form_links_form_ids'),
            [
                json.loads(datum_json) if datum_json else []
                for datum_json in request.POST.getlist('datums_json')
            ],
        )
        form.form_links = [
            FormLink(xpath=link[0],
                     form_id=link[1],
                     datums=[
                         FormDatum(name=datum['name'], xpath=datum['xpath'])
                         for datum in link[2]
                     ]) for link in form_links
        ]

    if should_edit('custom_instances'):
        instances = json.loads(request.POST.get('custom_instances'))
        try:  # validate that custom instances can be added into the XML
            for instance in instances:
                etree.fromstring("<instance id='{}' src='{}' />".format(
                    instance.get('instanceId'), instance.get('instancePath')))
        except etree.XMLSyntaxError as error:
            return json_response(
                {
                    'message':
                    _("There was an issue with your custom instances: {}").
                    format(error.message)
                },
                status_code=400)

        form.custom_instances = [
            CustomInstance(
                instance_id=instance.get("instanceId"),
                instance_path=instance.get("instancePath"),
            ) for instance in instances
        ]

    handle_media_edits(request, form, should_edit, resp, lang)

    app.save(resp)
    notify_form_changed(domain, request.couch_user, app_id, unique_form_id)
    if ajax:
        return HttpResponse(json.dumps(resp))
    else:
        return back_to_main(request,
                            domain,
                            app_id=app_id,
                            unique_form_id=unique_form_id)
Example #34
0
def get_form_view_context_and_template(request, domain, form, langs, current_lang, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append("Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append("Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append("Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append("Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request, _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append("Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append("Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachments) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled_for_request(request)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                messages.error(request, "Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, six.text_type(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(six.text_type(e))
                messages.error(request, "Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })
    module = form.get_module()

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USER_CASE)
    valid_index_names = list(DEFAULT_CASE_INDEX_IDENTIFIERS.values())
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])     # strip trailing slash

    form_has_schedule = isinstance(form, AdvancedForm) and module.has_schedule
    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': get_all_case_properties(app),
        'propertyDescriptions': get_case_property_description_dict(domain),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
        'usercasePropertiesMap': get_usercase_properties(app),
    }
    context = {
        'nav_form': form,
        'xform_languages': languages,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'xform_validation_missing': xform_validation_missing,
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': not form_has_schedule,
        'allow_form_workflow': True,
        'uses_form_workflow': form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase': allow_usercase,
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': app.enable_module_filtering,
        'is_training_module': module.is_training_module,
        'is_allowed_to_be_release_notes_form': form.is_allowed_to_be_release_notes_form,
        'root_requires_same_case': module.root_requires_same_case(),
        'is_case_list_form': form.is_case_list_form,
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'form_filter_patterns': {
            'case_substring': CASE_XPATH_SUBSTRING_MATCHES,
            'usercase_substring': USER_CASE_XPATH_SUBSTRING_MATCHES,
        },
        'custom_instances': [
            {'instanceId': instance.instance_id, 'instancePath': instance.instance_path}
            for instance in form.custom_instances
        ],
        'custom_assertions': [
            {'test': assertion.test, 'text': assertion.text.get(current_lang)}
            for assertion in form.custom_assertions
        ],
        'can_preview_form': request.couch_user.has_permission(domain, 'edit_data'),
        'form_icon': None,
    }

    if toggles.CUSTOM_ICON_BADGES.enabled(domain):
        context['form_icon'] = form.custom_icon if form.custom_icon else CustomIcon()

    if toggles.COPY_FORM_TO_APP.enabled_for_request(request):
        context['apps_modules'] = get_apps_modules(domain, app.id, module.unique_id)

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(domain):
        def qualified_form_name(form, auto_link):
            module_name = trans(module.name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return "{}{} -> {}".format(star, module_name, form_name)

        modules = [m for m in all_modules if m.case_type == module.case_type]
        if getattr(module, 'root_module_id', None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(itertools.chain.from_iterable(list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'commtrack_enabled': app.commtrack_enabled,
            'commtrack_programs': all_programs + commtrack_programs(),
            'module_id': module.unique_id,
            'save_url': reverse("edit_advanced_form_actions", args=[app.domain, app.id, form.unique_id]),
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })
        if getattr(module, 'has_schedule', False):
            schedule_options = get_schedule_context(form)
            schedule_options.update({
                'phase': schedule_options['schedule_phase'],
                'questions': xform_questions,
                'save_url': reverse("edit_visit_schedule", args=[app.domain, app.id, form.unique_id]),
                'schedule': form.schedule,
            })
            context.update({
                'schedule_options': schedule_options,
            })
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
        case_config_options.update({
            'actions': form.actions,
            'allowUsercase': allow_usercase,
            'save_url': reverse("edit_form_actions", args=[app.domain, app.id, form.unique_id]),
            'valid_index_names': valid_index_names,
        })

    context.update({'case_config_options': case_config_options})
    return "app_manager/form_view.html", context