Script: Copy List Settings

Copies the list settings of folders, link containers, and resource planning views

Copies the list setting of a folder, LinkContainer, or Resource Planning view.

a dialog appears where one of the following actions can be selected:

  • Transfer settings from another list: Copy the list settings of another list to the current one. The object from which the list settings are to be transferred can be selected in a Vertec browser.
  • Transfer this list settings: Copy the list settings of the current list to another. The object to which the list settings should be transferred can be selected in a Vertec browser.
  • Load settings from file: The list settings are loaded from a previously saved JSON file from the file system. A file browser appears where the file can be selected.
  • Save settings to file: Saves the current list settings to a JSON file on the file system. A save dialog appears with the default file name: YYYYMMDD-Containername-ObjID. This can also be overwritten if necessary.

CAUTION: It is important to pay careful attention to what is being transferred. Transfer List settings to locations where they do not fit can cause Vertec to crash.

Therefore, we recommend that you do not register the script, but use it via the Script Editor.

You can also define list settings that you want to use for all lists of a class (e.g. all address lists) as default. See the article about list settings – button Als Standard speichern.

Versions

Vertec version Specificity version Script
From Vertec 6.8 Uses the english data model Transferlistsettings.py

Execute

Folders and LinkContainers

The script must be run on the folder/LinkContainer. Select it and then open the Script Editor. Run the script in it.

Resource planning views

Copying list settings is done on the Resource Planning View definition. Select it and then open the Script Editor. Run the script in it.

Script Text

# coding: windows-1252
#
# Name: Transfer list settings
# Classes: Entry
# CondExpression: None
# Selectedobjectsscript: Y
# Condexpression: self->size = 1
# ExtendedRights: N
#
# Description: Copies list settings in Vertec or to / from a file.
#
# 2025-03-06, CBR: Created.
# 2026-08-21, CBR: Updated for concept.
#
# Version = 101


import vtcapp
import traceback
import json
import re


# region constants

LANGUAGES = vtcapp.evalocl("Language.allInstances.languagename->asSet") + ["NV"]
TITLE = vtcapp.translate("Transfer list settings")

MEMBERS_SOURCE = ["tabsPerClass", "controllerClassName"]
MEMBERS_GRIDDEF = ["rowHeight", "ghostrow", "fontColor", "color", "editing", "class"]
MEMBERS_GRIDCOL = ["writeableExpression", "alignment", "orderIdx", "color", "fontColor", "controlName", "rendererName", "totals", "controlDefinition", "expression", "readOnly", "sortOrder", "tabstop", "width", "isFixed", "remarks", "isDynamic", "enableSpellCheck", "spellCheckLanguageExpression", "visibleExpression", "title"]
MEMBERS_ML = ["title"]


# region action functions

def transfer_from_list(target):
    "Copy another lists settings to this list."

    # Select source
    source = select_folder(target, False)
    if not source:
        return

    # Source to dict
    source_settings = to_dict(source)
    if not source_settings:
        return

    # Dict to target
    from_dict(target, source_settings)


def transfer_to_list(source):
    "Copy this lists settings to another list."

    # Select target
    target = select_folder(source, True)
    if not target:
        return

    # Source to dict
    source_settings = to_dict(source)
    if not source_settings:
        return

    # Dict to target
    from_dict(target, source_settings)


def load_from_file(target):
    "Updates the current list settings from a file."

    # Select file
    try:
        filename, file = vtcapp.requestfilefromclient(TITLE, "", "JSON|*.json")
    except RuntimeError:
        return

    # Source to dict
    source_settings = json.loads(file.decode('utf-8'))
    if not source_settings:
        return

    # Dict to target
    from_dict(target, source_settings)


def save_to_file(source):
    "Saves the current list setttings to a file."

    # Source to dict
    settings_dict = to_dict(source)

    # Save file
    filename = settings_dict.get('filename', '')
    file = json.dumps(settings_dict).encode('utf-8')

    vtcapp.sendfile(file, filename, True, False)


# region internal functions

def _save_getattr(vtc_object, member, settings):
    "Savely gets an attribute."

    normalized_member = member.lower()

    if hasattr(vtc_object, normalized_member):

        try:
            if normalized_member in MEMBERS_ML:
                settings[normalized_member] = {}
                for language in LANGUAGES:
                    settings[normalized_member][language] = vtc_object.getmlvalue(normalized_member, language)
            else:
                settings[normalized_member] = getattr(vtc_object, normalized_member)
        except Exception:
            traceback.print_exc()

    return settings


def _save_setattr(vtc_object, member, settings):
    "Savely sets an attribute."

    normalized_member = member.lower()

    if normalized_member not in settings.keys():
        return

    try:
        if normalized_member in MEMBERS_ML:
            for language in LANGUAGES:
                vtc_object.setmlvalue(normalized_member, settings[normalized_member][language], language)
        else:
            setattr(vtc_object, normalized_member, settings[normalized_member])
    except Exception:
        traceback.print_exc()


def _get_filename(source):
    "Returns the filename for the given source."

    date_string = vtcapp.currentdate().strftime('%Y%m%d')
    source_string = source.evalocl("asstring")
    source_reference = source.evalocl("objid")

    filename = "{}_{}_{}.json".format(date_string, source_string, source_reference)

    # Replace invalid characters with underscore and replace consecutive underscores.
    filename = re.sub(r'[\\/:*?"<>|]', '_', filename)
    filename = re.sub(r'_+', '_', filename)

    return filename


def _source_to_dict(source):
    "Translate the source to a settings dict."

    source_settings = {
        'griddefs': [],
        'filename': _get_filename(source),
    }

    for member in MEMBERS_SOURCE:
        source_settings = _save_getattr(source, member, source_settings)

    return source_settings


def _dict_to_target(target, target_setting):
    "Translate the settings dict to the target"

    for member in MEMBERS_SOURCE:
        _save_setattr(target, member, target_setting)


def _griddef_to_dict(griddef):
    "Translate the grid def to a settings dict."

    griddef_settings = {
        'gridcols': []
    }

    for member in MEMBERS_GRIDDEF:
        griddef_settings = _save_getattr(griddef, member, griddef_settings)

    for gridcol in griddef.evalocl("gridcols->orderby(orderidx)"):
        griddef_settings['gridcols'].append(_gridcol_to_dict(gridcol))

    return griddef_settings


def _dict_to_griddef(griddef, griddef_setting):
    "Translate the settings dict to a grid def ."

    for member in MEMBERS_GRIDDEF:
        _save_setattr(griddef, member, griddef_setting)

    for gridcol_setting in griddef_setting.get('gridcols', []):

        gridcol = vtcapp.createobject('GridColDef')
        griddef.gridcols.append(gridcol)
        _dict_to_gridcol(gridcol, gridcol_setting)


def _gridcol_to_dict(gridcol):
    "Translate the grid col def to a dict."

    gridcol_settings = {}

    for member in MEMBERS_GRIDCOL:
        gridcol_settings = _save_getattr(gridcol, member, gridcol_settings)

    return gridcol_settings


def _dict_to_gridcol(gridcol, gridcol_setting):
    "Translate the grid col def to a settings dict."

    for member in MEMBERS_GRIDCOL:
        _save_setattr(gridcol, member, gridcol_setting)


# region helper functions

def to_dict(source):
    "Transfers the list settings to a dict."

    if not source:
        return None

    if source.evalocl("oclIsKindOf(LinkContainer)"):
        source = source.rolle

    # Save global settings
    source_settings = _source_to_dict(source)

    # Save griddefs
    for griddef in source.evalocl("griddefs"):
        source_settings['griddefs'].append(_griddef_to_dict(griddef))

    return source_settings


def from_dict(target, target_setting):
    "Transfers the list settings from a dict."

    if not target:
        return

    if target.evalocl("oclIsKindOf(LinkContainer)"):
        target = target.rolle

    # Restore global settings
    _dict_to_target(target, target_setting)

    # Remove old grids
    for griddef in list(target.evalocl("griddefs")):
        target.griddefs.remove(griddef)

    # Create new griddefs
    for griddef_setting in target_setting.get('griddefs', []):

        griddef = vtcapp.createobject("GridDef")
        target.griddefs.append(griddef)
        _dict_to_griddef(griddef, griddef_setting)


def select_folder(base, base_is_source=True):
    "Lets the user select a destination or target."

    title = "{} {}: '{}'".format(TITLE, vtcapp.translate("from" if base_is_source else "to"), base)
    return vtcapp.selectobjectintree(title, [], "", "AbstractFolder, LinkContainer, ViewType")


def select_action():
    "Lets the user select which action should be performed."
    dlgDefinition="""
    <Dialog Title="Transfer list settings" Width="500">
        <Group Label="Select action" ShowLabel="True">
            <ListBox Name="AuswahlBox" Lines="4"/>
        </Group>
        <Dialog.Buttons>
            <Button Text="OK" IsAccept="True" Command="{Binding OkCommand}" />
            <Button Text="Cancel" IsCancel="True" Command="{Binding CancelCommand}" />
        </Dialog.Buttons>
    </Dialog>
    """
    initValues = {}
    action = None
    initValues["AuswahlBox.Items"] = (vtcapp.translate("Transfer settings from another list"), vtcapp.translate("Transfer this list settings"), vtcapp.translate("Load settings from file"), vtcapp.translate("Save settings to file"))
    ok, values = vtcapp.showcustomdialog(dlgDefinition, initValues)
    if ok:
        actions = {0: 'transfer_from_list', 1:'transfer_to_list', 2: 'load_from_file', 3: 'save_to_file'}
        action = actions.get(values["AuswahlBox"])

    return action

# region main function

# We cannot use normal main entry, because we need locals()
source = argobject  # type: ignore # noqa

# Check conditions
if not source.eval("oclisKindOf(AbstractFolder) or oclisKindOf(LinkContainer) or oclisKindOf(ViewType)"):
    raise Exception(vtcapp.translate("This script only works for Folders, Link Containers and Ressource Planning views."))

# Select action
action = select_action()
if action:

    # Execute action
    try:
        locals()[action](source)

    # Catch error. Display short message and print long message.
    except Exception as e:
        traceback.print_exc()
        message = "{}\n\n{}\n\n({})".format(vtcapp.translate("An error occurred."),
                                            e,
                                            vtcapp.translate("See Python console for more information"))
        vtcapp.msgbox(message)

Netherlands

United Kingdom