(request, domain, app_id, module_unique_id)
return json_response(resp)
Overwrite module case details. Only overwrites components that have been
    provided in the request. Components are short, long, filter, parent_select,
    fixture_select and sort_elements.
Overwrite module case details. Only overwrites components that have been
    provided in the request. Components are short, long, filter, parent_select,
    fixture_select and sort_elements.
Overwrite module case details . Only overwrites components that have been provided in the request . Components are short long filter parent_select fixture_select and sort_elements .
def edit_module_detail_screens(request, domain, app_id, module_unique_id):
    """
    Overwrite module case details. Only overwrites components that have been
    provided in the request. Components are short, long, filter, parent_select,
    fixture_select and sort_elements.
    """
    params = json_request(request.POST)
    detail_type = params.get('type')
    short = params.get('short', None)
    long_ = params.get('long', None)
    tabs = params.get('tabs', None)
    filter = params.get('filter', ())
    custom_xml = params.get('custom_xml', None)
    parent_select = params.get('parent_select', None)
    fixture_select = params.get('fixture_select', None)
    sort_elements = params.get('sort_elements', None)
    persist_case_context = params.get('persistCaseContext', None)
    persistent_case_context_xml = params.get('persistentCaseContextXML', None)
    use_case_tiles = params.get('useCaseTiles', None)
    persist_tile_on_forms = params.get("persistTileOnForms", None)
    persistent_case_tile_from_module = params.get("persistentCaseTileFromModule", None)
    pull_down_tile = params.get("enableTilePullDown", None)
    print_template = params.get('printTemplate', None)
    case_list_lookup = params.get("case_list_lookup", None)
    search_properties = params.get("search_properties")
    custom_variables = {
        'short': params.get("short_custom_variables", None),
        'long': params.get("long_custom_variables", 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)

    if detail_type == 'case':
        detail = module.case_details
    else:
        try:
            detail = getattr(module, '{0}_details'.format(detail_type))
        except AttributeError:
            return HttpResponseBadRequest("Unknown detail type '%s'" % detail_type)

    lang = request.COOKIES.get('lang', app.langs[0])
    if short is not None:
        detail.short.columns = list(map(DetailColumn.from_json, short))
        if persist_case_context is not None:
            detail.short.persist_case_context = persist_case_context
            detail.short.persistent_case_context_xml = persistent_case_context_xml
        if use_case_tiles is not None:
            detail.short.use_case_tiles = use_case_tiles
        if persist_tile_on_forms is not None:
            detail.short.persist_tile_on_forms = persist_tile_on_forms
        if persistent_case_tile_from_module is not None:
            detail.short.persistent_case_tile_from_module = persistent_case_tile_from_module
        if pull_down_tile is not None:
            detail.short.pull_down_tile = pull_down_tile
        if case_list_lookup is not None:
            _save_case_list_lookup_params(detail.short, case_list_lookup, lang)

    if long_ is not None:
        detail.long.columns = list(map(DetailColumn.from_json, long_))
        if tabs is not None:
            detail.long.tabs = list(map(DetailTab.wrap, tabs))
        if print_template is not None:
            detail.long.print_template = print_template
    if filter != ():
        # Note that we use the empty tuple as the sentinel because a filter
        # value of None represents clearing the filter.
        detail.short.filter = filter
    if custom_xml is not None:
        detail.short.custom_xml = custom_xml

    if custom_variables['short'] is not None:
        try:
            etree.fromstring("<variables>{}</variables>".format(custom_variables['short']))
        except etree.XMLSyntaxError as error:
            return HttpResponseBadRequest(
                "There was an issue with your custom variables: {}".format(error)
            )
        detail.short.custom_variables = custom_variables['short']

    if custom_variables['long'] is not None:
        try:
            etree.fromstring("<variables>{}</variables>".format(custom_variables['long']))
        except etree.XMLSyntaxError as error:
            return HttpResponseBadRequest(
                "There was an issue with your custom variables: {}".format(error)
            )
        detail.long.custom_variables = custom_variables['long']

    if sort_elements is not None:
        # Attempt to map new elements to old so we don't lose translations
        # Imperfect because the same field may be used multiple times, or user may change field
        old_elements_by_field = {e['field']: e for e in detail.short.sort_elements}

        detail.short.sort_elements = []
        for sort_element in sort_elements:
            item = SortElement()
            item.field = sort_element['field']
            item.type = sort_element['type']
            item.direction = sort_element['direction']
            item.blanks = sort_element['blanks']
            if item.field in old_elements_by_field:
                item.display = old_elements_by_field[item.field].display
            item.display[lang] = sort_element['display']
            if toggles.SORT_CALCULATION_IN_CASE_LIST.enabled(domain):
                item.sort_calculation = sort_element['sort_calculation']
            else:
                item.sort_calculation = ""
            detail.short.sort_elements.append(item)
    if parent_select is not None:
        module.parent_select = ParentSelect.wrap(parent_select)
        if module_case_hierarchy_has_circular_reference(module):
            return HttpResponseBadRequest(_("The case hierarchy contains a circular reference."))
    if fixture_select is not None:
        module.fixture_select = FixtureSelect.wrap(fixture_select)
    if search_properties is not None:
        if (
                search_properties.get('properties') is not None
                or search_properties.get('default_properties') is not None
        ):
            search_label = module.search_config.search_label
            search_label.label[lang] = search_properties.get('search_label', '')
            if search_properties.get('search_label_image_for_all'):
                search_label.use_default_image_for_all = (
                    search_properties.get('search_label_image_for_all') == 'true')
            if search_properties.get('search_label_audio_for_all'):
                search_label.use_default_audio_for_all = (
                    search_properties.get('search_label_audio_for_all') == 'true')
            search_label.set_media("media_image", lang, search_properties.get('search_label_image'))
            search_label.set_media("media_audio", lang, search_properties.get('search_label_audio'))

            search_again_label = module.search_config.search_again_label
            search_again_label.label[lang] = search_properties.get('search_again_label', '')
            if search_properties.get('search_again_label_image_for_all'):
                search_again_label.use_default_image_for_all = (
                    search_properties.get('search_again_label_image_for_all') == 'true')
            if search_properties.get('search_again_label_audio_for_all'):
                search_again_label.use_default_audio_for_all = (
                    search_properties.get('search_again_label_audio_for_all') == 'true')
            search_again_label.set_media("media_image", lang, search_properties.get('search_again_label_image'))
            search_again_label.set_media("media_audio", lang, search_properties.get('search_again_label_audio'))

            try:
                properties = [
                    CaseSearchProperty.wrap(p)
                    for p in _update_search_properties(
                        module,
                        search_properties.get('properties'), lang
                    )
                ]
            except CaseSearchConfigError as e:
                return HttpResponseBadRequest(e)
            xpath_props = [
                "search_filter", "blacklisted_owner_ids_expression",
                "search_button_display_condition", "search_additional_relevant"
            ]

            def _check_xpath(xpath, location):
                is_valid, message = validate_xpath(xpath)
                if not is_valid:
                    raise ValueError(
                        f"Please fix the errors in xpath expression '{xpath}' "
                        f"in {location}. The error is {message}"
                    )

            for prop in xpath_props:
                xpath = search_properties.get(prop, "")
                if xpath:
                    try:
                        _check_xpath(xpath, "Search and Claim Options")
                    except ValueError as e:
                        return HttpResponseBadRequest(str(e))

            additional_registry_cases = []
            for case_id_xpath in search_properties.get('additional_registry_cases', []):
                if not case_id_xpath:
                    continue

                try:
                    _check_xpath(case_id_xpath, "the Case ID of Additional Data Registry Query")
                except ValueError as e:
                    return HttpResponseBadRequest(str(e))

                additional_registry_cases.append(case_id_xpath)

            data_registry_slug = search_properties.get('data_registry', "")
            data_registry_workflow = search_properties.get('data_registry_workflow', "")
            # force auto launch when data registry load case workflow selected
            force_auto_launch = data_registry_slug and data_registry_workflow == REGISTRY_WORKFLOW_LOAD_CASE

            module.search_config = CaseSearch(
                search_label=search_label,
                search_again_label=search_again_label,
                properties=properties,
                additional_case_types=module.search_config.additional_case_types,
                default_relevant=bool(search_properties.get('search_default_relevant')),
                additional_relevant=search_properties.get('search_additional_relevant', ''),
                auto_launch=force_auto_launch or bool(search_properties.get('auto_launch')),
                default_search=bool(search_properties.get('default_search')),
                search_filter=search_properties.get('search_filter', ""),
                search_button_display_condition=search_properties.get('search_button_display_condition', ""),
                blacklisted_owner_ids_expression=search_properties.get('blacklisted_owner_ids_expression', ""),
                default_properties=[
                    DefaultCaseSearchProperty.wrap(p)
                    for p in search_properties.get('default_properties')
                ],
                data_registry=data_registry_slug,
                data_registry_workflow=data_registry_workflow,
                additional_registry_cases=additional_registry_cases,
                custom_related_case_property=search_properties.get('custom_related_case_property', ""),
            )

    resp = {}
    app.save(resp)
    return json_response(resp)
def edit_module_detail_screens ( request , domain , app_id , module_unique_id ) : params = json_request ( request . POST ) detail_type = params . get ( 'type' ) short = params . get ( 'short' , None ) long_ = params . get ( 'long' , None ) tabs = params . get ( 'tabs' , None ) filter = params . get ( 'filter' , ( ) ) custom_xml = params . get ( 'custom_xml' , None ) parent_select = params . get ( 'parent_select' , None ) fixture_select = params . get ( 'fixture_select' , None ) sort_elements = params . get ( 'sort_elements' , None ) persist_case_context = params . get ( 'persistCaseContext' , None ) persistent_case_context_xml = params . get ( 'persistentCaseContextXML' , None ) use_case_tiles = params . get ( 'useCaseTiles' , None ) persist_tile_on_forms = params . get ( "persistTileOnForms" , None ) persistent_case_tile_from_module = params . get ( "persistentCaseTileFromModule" , None ) pull_down_tile = params . get ( "enableTilePullDown" , None ) print_template = params . get ( 'printTemplate' , None ) case_list_lookup = params . get ( "case_list_lookup" , None ) search_properties = params . get ( "search_properties" ) custom_variables = { 'short' : params . get ( "short_custom_variables" , None ) , 'long' : params . get ( "long_custom_variables" , 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 ) if detail_type == 'case' : detail = module . case_details else : try : detail = getattr ( module , '{0}_details' . format ( detail_type ) ) except AttributeError : return HttpResponseBadRequest ( "Unknown detail type '%s'" % detail_type ) lang = request . COOKIES . get ( 'lang' , app . langs [ 0 ] ) if short is not None : detail . short . columns = list ( map ( DetailColumn . from_json , short ) ) if persist_case_context is not None : detail . short . persist_case_context = persist_case_context detail . short . persistent_case_context_xml = persistent_case_context_xml if use_case_tiles is not None : detail . short . use_case_tiles = use_case_tiles if persist_tile_on_forms is not None : detail . short . persist_tile_on_forms = persist_tile_on_forms if persistent_case_tile_from_module is not None : detail . short . persistent_case_tile_from_module = persistent_case_tile_from_module if pull_down_tile is not None : detail . short . pull_down_tile = pull_down_tile if case_list_lookup is not None : _save_case_list_lookup_params ( detail . short , case_list_lookup , lang ) if long_ is not None : detail . long . columns = list ( map ( DetailColumn . from_json , long_ ) ) if tabs is not None : detail . long . tabs = list ( map ( DetailTab . wrap , tabs ) ) if print_template is not None : detail . long . print_template = print_template if filter != ( ) : # Note that we use the empty tuple as the sentinel because a filter # value of None represents clearing the filter. detail . short . filter = filter if custom_xml is not None : detail . short . custom_xml = custom_xml if custom_variables [ 'short' ] is not None : try : etree . fromstring ( "<variables>{}</variables>" . format ( custom_variables [ 'short' ] ) ) except etree . XMLSyntaxError as error : return HttpResponseBadRequest ( "There was an issue with your custom variables: {}" . format ( error ) ) detail . short . custom_variables = custom_variables [ 'short' ] if custom_variables [ 'long' ] is not None : try : etree . fromstring ( "<variables>{}</variables>" . format ( custom_variables [ 'long' ] ) ) except etree . XMLSyntaxError as error : return HttpResponseBadRequest ( "There was an issue with your custom variables: {}" . format ( error ) ) detail . long . custom_variables = custom_variables [ 'long' ] if sort_elements is not None : # Attempt to map new elements to old so we don't lose translations # Imperfect because the same field may be used multiple times, or user may change field old_elements_by_field = { e [ 'field' ] : e for e in detail . short . sort_elements } detail . short . sort_elements = [ ] for sort_element in sort_elements : item = SortElement ( ) item . field = sort_element [ 'field' ] item . type = sort_element [ 'type' ] item . direction = sort_element [ 'direction' ] item . blanks = sort_element [ 'blanks' ] if item . field in old_elements_by_field : item . display = old_elements_by_field [ item . field ] . display item . display [ lang ] = sort_element [ 'display' ] if toggles . SORT_CALCULATION_IN_CASE_LIST . enabled ( domain ) : item . sort_calculation = sort_element [ 'sort_calculation' ] else : item . sort_calculation = "" detail . short . sort_elements . append ( item ) if parent_select is not None : module . parent_select = ParentSelect . wrap ( parent_select ) if module_case_hierarchy_has_circular_reference ( module ) : return HttpResponseBadRequest ( _ ( "The case hierarchy contains a circular reference." ) ) if fixture_select is not None : module . fixture_select = FixtureSelect . wrap ( fixture_select ) if search_properties is not None : if ( search_properties . get ( 'properties' ) is not None or search_properties . get ( 'default_properties' ) is not None ) : search_label = module . search_config . search_label search_label . label [ lang ] = search_properties . get ( 'search_label' , '' ) if search_properties . get ( 'search_label_image_for_all' ) : search_label . use_default_image_for_all = ( search_properties . get ( 'search_label_image_for_all' ) == 'true' ) if search_properties . get ( 'search_label_audio_for_all' ) : search_label . use_default_audio_for_all = ( search_properties . get ( 'search_label_audio_for_all' ) == 'true' ) search_label . set_media ( "media_image" , lang , search_properties . get ( 'search_label_image' ) ) search_label . set_media ( "media_audio" , lang , search_properties . get ( 'search_label_audio' ) ) search_again_label = module . search_config . search_again_label search_again_label . label [ lang ] = search_properties . get ( 'search_again_label' , '' ) if search_properties . get ( 'search_again_label_image_for_all' ) : search_again_label . use_default_image_for_all = ( search_properties . get ( 'search_again_label_image_for_all' ) == 'true' ) if search_properties . get ( 'search_again_label_audio_for_all' ) : search_again_label . use_default_audio_for_all = ( search_properties . get ( 'search_again_label_audio_for_all' ) == 'true' ) search_again_label . set_media ( "media_image" , lang , search_properties . get ( 'search_again_label_image' ) ) search_again_label . set_media ( "media_audio" , lang , search_properties . get ( 'search_again_label_audio' ) ) try : properties = [ CaseSearchProperty . wrap ( p ) for p in _update_search_properties ( module , search_properties . get ( 'properties' ) , lang ) ] except CaseSearchConfigError as e : return HttpResponseBadRequest ( e ) xpath_props = [ "search_filter" , "blacklisted_owner_ids_expression" , "search_button_display_condition" , "search_additional_relevant" ] def _check_xpath ( xpath , location ) : is_valid , message = validate_xpath ( xpath ) if not is_valid : raise ValueError ( f"Please fix the errors in xpath expression '{xpath}' " f"in {location}. The error is {message}" ) for prop in xpath_props : xpath = search_properties . get ( prop , "" ) if xpath : try : _check_xpath ( xpath , "Search and Claim Options" ) except ValueError as e : return HttpResponseBadRequest ( str ( e ) ) additional_registry_cases = [ ] for case_id_xpath in search_properties . get ( 'additional_registry_cases' , [ ] ) : if not case_id_xpath : continue try : _check_xpath ( case_id_xpath , "the Case ID of Additional Data Registry Query" ) except ValueError as e : return HttpResponseBadRequest ( str ( e ) ) additional_registry_cases . append ( case_id_xpath ) data_registry_slug = search_properties . get ( 'data_registry' , "" ) data_registry_workflow = search_properties . get ( 'data_registry_workflow' , "" ) # force auto launch when data registry load case workflow selected force_auto_launch = data_registry_slug and data_registry_workflow == REGISTRY_WORKFLOW_LOAD_CASE module . search_config = CaseSearch ( search_label = search_label , search_again_label = search_again_label , properties = properties , additional_case_types = module . search_config . additional_case_types , default_relevant = bool ( search_properties . get ( 'search_default_relevant' ) ) , additional_relevant = search_properties . get ( 'search_additional_relevant' , '' ) , auto_launch = force_auto_launch or bool ( search_properties . get ( 'auto_launch' ) ) , default_search = bool ( search_properties . get ( 'default_search' ) ) , search_filter = search_properties . get ( 'search_filter' , "" ) , search_button_display_condition = search_properties . get ( 'search_button_display_condition' , "" ) , blacklisted_owner_ids_expression = search_properties . get ( 'blacklisted_owner_ids_expression' , "" ) , default_properties = [ DefaultCaseSearchProperty . wrap ( p ) for p in search_properties . get ( 'default_properties' ) ] , data_registry = data_registry_slug , data_registry_workflow = data_registry_workflow , additional_registry_cases = additional_registry_cases , custom_related_case_property = search_properties . get ( 'custom_related_case_property' , "" ) , ) resp = { } app . save ( resp ) return json_response ( resp )