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()] }
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
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
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
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
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 }
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) }
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
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
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"))
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, }
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
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
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
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
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()] }
def _form_name(form): module_name = _module_name(form.get_module()) form_name = trans(form.name, langs) return "{} > {}".format(module_name, form_name)
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
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)
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)
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)
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
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))
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))
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))
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" ))
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
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))
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
def _module_name(module): return trans(module.name, langs)
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)
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)
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