mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-4538] Make folder-rules secondary entry point of aca-content (#3181)
* [ACS-4538] Make folder-rules secondary entry point of aca-content * [ACA-4538] Fix folder rules imports * [ACS-4538] Fix folder-rules unit tests * [ACS-4538] Fix package.json after folder rules transition * [ACS-4538] Remove duplicated peerDependency * [ACS-4538] Import fix
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"$schema": "../../../../extension.schema.json",
|
||||
"$id": "0455ca6c-cc7a-43ae-bbf7-35795413d2dd",
|
||||
"$name": "Folder Rules Plugin",
|
||||
"$version": "0.0.1",
|
||||
"$vendor": "Hyland Software, Inc. and its affiliates",
|
||||
"$license": "LGPL-3.0",
|
||||
|
||||
"features": {
|
||||
"toolbar": [
|
||||
{
|
||||
"id": "app.toolbar.more",
|
||||
"children": [
|
||||
{
|
||||
"id": "app.toolbar.rules.separator",
|
||||
"type": "separator",
|
||||
"rules": {
|
||||
"visible": "app.selection.folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.toolbar.rules.create",
|
||||
"title": "ACA_FOLDER_RULES.MENU.CREATE_RULES",
|
||||
"description": "ACA_FOLDER_RULES.MENU.CREATE_RULES_DESC",
|
||||
"icon": "add",
|
||||
"rules": {
|
||||
"visible": "rules.canCreateFolderRule"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.toolbar.rules.link",
|
||||
"title": "ACA_FOLDER_RULES.MENU.LINK_RULES",
|
||||
"description": "ACA_FOLDER_RULES.MENU.LINK_RULES_DESC",
|
||||
"icon": "link",
|
||||
"rules": {
|
||||
"visible": "rules.canLinkFolderRule"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
"contextMenu": [
|
||||
{
|
||||
"id": "app.toolbar.rules.separator",
|
||||
"type": "separator",
|
||||
"rules": {
|
||||
"visible": "app.selection.folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.toolbar.rules.create",
|
||||
"title": "ACA_FOLDER_RULES.MENU.CREATE_RULES",
|
||||
"description": "ACA_FOLDER_RULES.MENU.CREATE_RULES_DESC",
|
||||
"icon": "add",
|
||||
"rules": {
|
||||
"visible": "rules.canCreateFolderRule"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.toolbar.rules.link",
|
||||
"title": "ACA_FOLDER_RULES.MENU.LINK_RULES",
|
||||
"description": "ACA_FOLDER_RULES.MENU.LINK_RULES_DESC",
|
||||
"icon": "link",
|
||||
"rules": {
|
||||
"visible": "rules.canLinkFolderRule"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "إدارة القواعد"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "إلغاء",
|
||||
"CREATE": "إنشاء",
|
||||
"CREATE_TITLE": "إنشاء قاعدة",
|
||||
"UPDATE": "تحديث",
|
||||
"UPDATE_TITLE": "تحرير قاعدة"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "الاسم",
|
||||
"DESCRIPTION": "الوصف",
|
||||
"WHEN": "متي",
|
||||
"PERFORM_ACTIONS": "نفذ الإجراءات",
|
||||
"OPTIONS": "خيارات أخرى"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "أدخل اسم للقاعدة",
|
||||
"DESCRIPTION": "أدخل وصف للقاعدة",
|
||||
"NO_DESCRIPTION": "لا يوجد وصف",
|
||||
"VALUE": "القيمة",
|
||||
"CHOOSE_FOLDER": "اختر مجلد الوجهة"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "هذا الحقل مطلوب",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "مجموعة شرطية واحدة أو أكثر فارغة",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "مطلوب مشغل واحد على الأقل"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "يتم إنشاء العناصر أو الدخول إلى هذا المجلد",
|
||||
"UPDATE": "تم تحديث العناصر",
|
||||
"OUTBOUND": "تم حذف العناصر أو ترك هذا المجلد"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "تنطبق القاعدة على المجلدات الفرعية",
|
||||
"IS_ASYNCHRONOUS": "تشغيل القاعدة في الخلفية",
|
||||
"DISABLE_RULE": "تعطيل القاعدة",
|
||||
"ERROR_SCRIPT": "في حالة حدوث أخطاء قم بتشغيل البرنامج النصي",
|
||||
"NO_SCRIPT": "بلا"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) يساوي",
|
||||
"CONTAINS": "يتضمن",
|
||||
"STARTS_WITH": "يبدأ بـ",
|
||||
"ENDS_WITH": "ينتهي بـ",
|
||||
"GREATER_THAN": "(>) أكبر من",
|
||||
"LESS_THAN": "(<) أقل من",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) أكبر من أو يساوي",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) أقل من أو يساوي",
|
||||
"ON": "(=) في",
|
||||
"AFTER": "(>) بعد",
|
||||
"BEFORE": "(<) قبل",
|
||||
"ON_OR_AFTER": "(>=) في أو بعد",
|
||||
"ON_OR_BEFORE": "(<=) في أو قبل",
|
||||
"INSTANCE_OF": "(=) هو"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "الاسم",
|
||||
"SIZE": "الحجم",
|
||||
"MIMETYPE": "نوع Mime",
|
||||
"ENCODING": "ترميز",
|
||||
"HAS_CATEGORY": "لديه فئة",
|
||||
"HAS_TAG": "له علامة",
|
||||
"HAS_ASPECT": "له جانب"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "إذا",
|
||||
"NOT_IF": "ليس إذا",
|
||||
"AND": "و",
|
||||
"OR": "أو"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "أضف الشرط",
|
||||
"ADD_GROUP": "أضف مجموعة شرطية",
|
||||
"REMOVE": "إزالة"
|
||||
},
|
||||
"NO_CONDITIONS": "لا شروط",
|
||||
"NO_CONDITIONS_IN_GROUP": "لا توجد شروط في المجموعة",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "إضافة إجراء",
|
||||
"REMOVE": "إزالة"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "حدد إجراء"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "قواعد"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "إنشاء قاعدة",
|
||||
"LINK_RULES": "قواعد الارتباط",
|
||||
"EDIT_RULE": "تحرير",
|
||||
"SEE_IN_FOLDER": "انظر في المجلد",
|
||||
"INHERIT_RULES": "ترث القواعد"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "القائمة فارغة",
|
||||
"SUBTITLE": "لا توجد قواعد محددة لهذا المجلد حتى الآن."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "حذف القاعدة",
|
||||
"MESSAGE": "هل انت متأكد من رغبتك بحذف هذه القاعدة؟"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "حذف ارتباط مجموعة القواعد",
|
||||
"MESSAGE": "هل أنت متأكد أنك تريد حذف الارتباط لمجموعة القواعد هذه؟"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "القواعد من المجلد الحالي",
|
||||
"LINKED_RULES": "قواعد من مجلد مرتبط",
|
||||
"INHERITED_RULES": "القواعد الموروثة",
|
||||
"LOAD_MORE_RULE_SETS": "قواعد التحميل من المجلدات الأخرى",
|
||||
"LOAD_MORE_RULES": "تحميل المزيد من القواعد",
|
||||
"LOADING_RULES": "قواعد التحميل",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "سيتم تشغيل القواعد الموروثة أولاً",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "تم تعطيل جميع القواعد المرتبطة من مجموعة القواعد هذه"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "حدد مجلدًا لربط القواعد منه",
|
||||
"CANCEL": "إلغاء",
|
||||
"SUBMIT": "حدد مجلد",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "لا توجد قواعد مملوكة",
|
||||
"SUBTITLE": "لا يحتوي المجلد المحدد على أي قواعد خاصة به."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "قائمة القواعد التي سيتم ربطها",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "حدث خطأ أثناء محاولة إنشاء ارتباط لمجموعة قواعد"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "خطأ أثناء محاولة حذف ارتباط من مجموعة القواعد"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Správa pravidel"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Zrušit",
|
||||
"CREATE": "Vytvořit",
|
||||
"CREATE_TITLE": "Vytvořit pravidlo",
|
||||
"UPDATE": "Aktualizovat",
|
||||
"UPDATE_TITLE": "Edit a rule"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Název",
|
||||
"DESCRIPTION": "Popis",
|
||||
"WHEN": "Když",
|
||||
"PERFORM_ACTIONS": "Perform actions",
|
||||
"OPTIONS": "Other options"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Zadejte název pravidla",
|
||||
"DESCRIPTION": "Zadejte popis pravidla",
|
||||
"NO_DESCRIPTION": "Žádný popis",
|
||||
"VALUE": "Hodnota",
|
||||
"CHOOSE_FOLDER": "Vyberte cílovou složku"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Toto pole je povinné",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Nejméně jedna skupina podmínek je prázdná",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Je požadován alespoň jeden spouštěč"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Položky jsou vytvořeny nebo vstupují do této složky",
|
||||
"UPDATE": "Položky jsou aktualizovány",
|
||||
"OUTBOUND": "Položky jsou odstraněny nebo opouští tuto složku"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Rule applies to subfolders",
|
||||
"IS_ASYNCHRONOUS": "Run rule in the background",
|
||||
"DISABLE_RULE": "Disable rule",
|
||||
"ERROR_SCRIPT": "If errors occur run script",
|
||||
"NO_SCRIPT": "Žádný"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Rovná se",
|
||||
"CONTAINS": "Obsahuje",
|
||||
"STARTS_WITH": "Začíná na",
|
||||
"ENDS_WITH": "Končí na",
|
||||
"GREATER_THAN": "(>) Větší než",
|
||||
"LESS_THAN": "(<) Menší než",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Větší než nebo rovno",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Menší než nebo rovno",
|
||||
"ON": "(=) Na",
|
||||
"AFTER": "(>) Po",
|
||||
"BEFORE": "(<) Před",
|
||||
"ON_OR_AFTER": "(>=) Na nebo po",
|
||||
"ON_OR_BEFORE": "(<=) Na nebo před",
|
||||
"INSTANCE_OF": "(=) Je"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Název",
|
||||
"SIZE": "Velikost",
|
||||
"MIMETYPE": "Typ MIME",
|
||||
"ENCODING": "Kódování",
|
||||
"HAS_CATEGORY": "Má kategorii",
|
||||
"HAS_TAG": "Má značku",
|
||||
"HAS_ASPECT": "Má aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Pokud",
|
||||
"NOT_IF": "NE pokud",
|
||||
"AND": "A",
|
||||
"OR": "Nebo"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Add condition",
|
||||
"ADD_GROUP": "Add condition group",
|
||||
"REMOVE": "Odebrat"
|
||||
},
|
||||
"NO_CONDITIONS": "Žádné podmínky",
|
||||
"NO_CONDITIONS_IN_GROUP": "Žádné podmínky ve skupině",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Přidat akci",
|
||||
"REMOVE": "Odebrat"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Select an action"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "pravidla"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Vytvořit pravidlo",
|
||||
"LINK_RULES": "Pravidla pro odkazy",
|
||||
"EDIT_RULE": "Upravit",
|
||||
"SEE_IN_FOLDER": "Zobrazit ve složce",
|
||||
"INHERIT_RULES": "Zděděná pravidla"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Tento seznam je prázdný",
|
||||
"SUBTITLE": "Pro tuto složku ještě nejsou definována žádná pravidla."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Odstranit pravidlo",
|
||||
"MESSAGE": "Chcete skutečně smazat toto pravidlo?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Smazat odkaz na nastavené pravidlo",
|
||||
"MESSAGE": "Chcete skutečně smazat odkaz na toto nastavené pravidlo?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Pravidla z aktuální složky",
|
||||
"LINKED_RULES": "Pravidla z odkazované složky",
|
||||
"INHERITED_RULES": "Zděděná pravidla",
|
||||
"LOAD_MORE_RULE_SETS": "Načíst pravidla z jiných složek",
|
||||
"LOAD_MORE_RULES": "Načíst více pravidel",
|
||||
"LOADING_RULES": "Načítání pravidel",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Jako první budou spuštěna zděděná pravidla",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Všechna pravidla, na která byl vytvořen odkaz z tohoto souboru pravidel, jsou zakázaná"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Vyberte složku pro vytvoření odkazu na pravidla z",
|
||||
"CANCEL": "Zrušit",
|
||||
"SUBMIT": "Vybrat složku",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Žádná vlastní pravidla",
|
||||
"SUBTITLE": "Vybraná složka neobsahuje žádná vlastní pravidla."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Seznam pravidel, na která bude vytvořen odkaz",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Vyskytla se chyba při vytváření odkaz na nastavené pravidlo"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Vyskytla se chyba při pokusu o smazání odkazu z nastaveného pravidla"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Administrér regler"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Annuller",
|
||||
"CREATE": "Opret",
|
||||
"CREATE_TITLE": "Opret en regel",
|
||||
"UPDATE": "Opdater",
|
||||
"UPDATE_TITLE": "Redigér en regel"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Navn",
|
||||
"DESCRIPTION": "Beskrivelse",
|
||||
"WHEN": "Når",
|
||||
"PERFORM_ACTIONS": "Udfør handlinger",
|
||||
"OPTIONS": "Andre optioner"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Indtast regelnavn",
|
||||
"DESCRIPTION": "Indtast regelbeskrivelse",
|
||||
"NO_DESCRIPTION": "Ingen beskrivelse",
|
||||
"VALUE": "Værdi",
|
||||
"CHOOSE_FOLDER": "Vælg destinationsmappe"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Dette felt er påkrævet",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "En eller flere betingelsesgrupper tomme",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Der kræves mindst en udløser"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Der er oprettet elementer, eller indtast denne mappe",
|
||||
"UPDATE": "Elementer er opdateret",
|
||||
"OUTBOUND": "Elementer slettes, eller forlad denne mappe"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regel gælder for undermapper",
|
||||
"IS_ASYNCHRONOUS": "Kør regel i baggrunden",
|
||||
"DISABLE_RULE": "Deaktivér regel",
|
||||
"ERROR_SCRIPT": "Kør script, hvis der forekommer fejl",
|
||||
"NO_SCRIPT": "Ingen"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) lig med",
|
||||
"CONTAINS": "Indeholder",
|
||||
"STARTS_WITH": "Starter med",
|
||||
"ENDS_WITH": "Slutter med",
|
||||
"GREATER_THAN": "(>) større end",
|
||||
"LESS_THAN": "(<) mindre end",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) større end eller lig med",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) mindre end eller lig med",
|
||||
"ON": "(=) ved",
|
||||
"AFTER": "(>) efter",
|
||||
"BEFORE": "(<) før",
|
||||
"ON_OR_AFTER": "(>=) ved eller efter",
|
||||
"ON_OR_BEFORE": "(<=) ved eller før",
|
||||
"INSTANCE_OF": "(=) er"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Navn",
|
||||
"SIZE": "Størrelse",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"ENCODING": "Kodning",
|
||||
"HAS_CATEGORY": "Har kategori",
|
||||
"HAS_TAG": "Har tag",
|
||||
"HAS_ASPECT": "Har aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Hvis",
|
||||
"NOT_IF": "IKKE hvis",
|
||||
"AND": "Og",
|
||||
"OR": "Eller"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Tilføj betingelse",
|
||||
"ADD_GROUP": "Tilføj betingelsesgruppe",
|
||||
"REMOVE": "Fjern"
|
||||
},
|
||||
"NO_CONDITIONS": "Ingen betingelser",
|
||||
"NO_CONDITIONS_IN_GROUP": "Ingen betingelser i denne gruppe",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Tilføj handling",
|
||||
"REMOVE": "Fjern"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Vælg en handling"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regler"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Opret en regel",
|
||||
"LINK_RULES": "Forbind regler",
|
||||
"EDIT_RULE": "Rediger",
|
||||
"SEE_IN_FOLDER": "Se i mappe",
|
||||
"INHERIT_RULES": "Overtag regler"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Listen er tom",
|
||||
"SUBTITLE": "Der er ingen definerede regler for denne mappe endnu."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Slet regel",
|
||||
"MESSAGE": "Er du sikker på, at du vil slette denne regel?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Slet regelsætforbindelse",
|
||||
"MESSAGE": "Er du sikker på, at du vil slette forbindelsen til dette regelsæt?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regler fra aktuel mappe",
|
||||
"LINKED_RULES": "Regler fra forbundet mappe",
|
||||
"INHERITED_RULES": "Overtagede regler",
|
||||
"LOAD_MORE_RULE_SETS": "Indlæs regler fra andre mapper",
|
||||
"LOAD_MORE_RULES": "Indlæs flere regler",
|
||||
"LOADING_RULES": "Indlæser regler",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Overtagede regler køres først",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Alle regler, der er forbundet af dette indstillede regelsæt, er deaktiveret"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Vælg en mappe for at forbinde regler fra",
|
||||
"CANCEL": "Annuller",
|
||||
"SUBMIT": "Vælg mappe",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Ingen egne regler",
|
||||
"SUBTITLE": "Den valgte mappe har ikke sine egne regler."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Liste med regler, som forbindes",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Fejl, mens du prøvede at oprette en forbindelse til et regelsæt"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Fejl, mens du prøvede at slette en forbindelse fra et regelsæt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Regeln verwalten"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Abbrechen",
|
||||
"CREATE": "Erstellen",
|
||||
"CREATE_TITLE": "Eine Regel erstellen",
|
||||
"UPDATE": "Aktualisieren",
|
||||
"UPDATE_TITLE": "Eine Regel bearbeiten"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Name",
|
||||
"DESCRIPTION": "Beschreibung",
|
||||
"WHEN": "Wenn",
|
||||
"PERFORM_ACTIONS": "Aktionen durchführen",
|
||||
"OPTIONS": "Andere Optionen"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Regelname eingeben",
|
||||
"DESCRIPTION": "Regelbeschreibung eingeben",
|
||||
"NO_DESCRIPTION": "Keine Beschreibung",
|
||||
"VALUE": "Wert",
|
||||
"CHOOSE_FOLDER": "Zielordner auswählen"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Dieses Feld ist erforderlich",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Eine oder mehrere Konditionsgruppen sind leer",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Mindestens ein Auslöser ist erforderlich"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Elemente werden erstellt oder in diesen Ordner eingegeben",
|
||||
"UPDATE": "Elemente werden aktualisiert",
|
||||
"OUTBOUND": "Elemente werden gelöscht oder verlassen diesen Ordner"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regel gilt für Unterordner",
|
||||
"IS_ASYNCHRONOUS": "Regel im Hintergrund ausführen",
|
||||
"DISABLE_RULE": "Regel deaktivieren",
|
||||
"ERROR_SCRIPT": "Wenn Fehler auftreten, Skript ausführen",
|
||||
"NO_SCRIPT": "Keine"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Gleich",
|
||||
"CONTAINS": "Enthält",
|
||||
"STARTS_WITH": "Beginnt mit",
|
||||
"ENDS_WITH": "Endet mit",
|
||||
"GREATER_THAN": "(>) Größer als",
|
||||
"LESS_THAN": "(<) Kleiner als",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Größer als oder gleich",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Kleiner als oder gleich",
|
||||
"ON": "(=) Am",
|
||||
"AFTER": "(>) Nach",
|
||||
"BEFORE": "(<) Vor",
|
||||
"ON_OR_AFTER": "(>=) Am oder nach",
|
||||
"ON_OR_BEFORE": "(<=) Am oder vor",
|
||||
"INSTANCE_OF": "(=) Ist"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Name",
|
||||
"SIZE": "Größe",
|
||||
"MIMETYPE": "MIME-Type",
|
||||
"ENCODING": "Kodierung",
|
||||
"HAS_CATEGORY": "Hat Kategorie",
|
||||
"HAS_TAG": "Hat Tag",
|
||||
"HAS_ASPECT": "Hat Aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Wenn",
|
||||
"NOT_IF": "NICHT Wenn",
|
||||
"AND": "Und",
|
||||
"OR": "Oder"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Kondition hinzufügen",
|
||||
"ADD_GROUP": "Konditionsgruppe hinzufügen",
|
||||
"REMOVE": "Entfernen"
|
||||
},
|
||||
"NO_CONDITIONS": "Keine Kondition",
|
||||
"NO_CONDITIONS_IN_GROUP": "Keine Kondition in der Gruppe",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Aktion hinzufügen",
|
||||
"REMOVE": "Entfernen"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Eine Aktion auswählen"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "Regeln"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Eine Regel erstellen",
|
||||
"LINK_RULES": "Link-Regeln",
|
||||
"EDIT_RULE": "Bearbeiten",
|
||||
"SEE_IN_FOLDER": "Siehe im Ordner",
|
||||
"INHERIT_RULES": "Regeln vererben"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Diese Liste ist leer",
|
||||
"SUBTITLE": "Es sind noch keine Regeln für diesen Ordner definiert."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Regel löschen",
|
||||
"MESSAGE": "Möchten Sie diese Regel wirklich löschen?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Link für Regelsatz löschen",
|
||||
"MESSAGE": "Sind Sie sicher, dass Sie den Link zu diesem Regelsatz löschen möchten?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regeln aus aktuellem Ordner",
|
||||
"LINKED_RULES": "Regeln aus verknüpften Ordnern",
|
||||
"INHERITED_RULES": "Vererbte Regeln",
|
||||
"LOAD_MORE_RULE_SETS": "Regeln aus anderen Ordnern laden",
|
||||
"LOAD_MORE_RULES": "Weitere Regeln laden",
|
||||
"LOADING_RULES": "Regeln werden geladen",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Vererbte Regeln werden zuerst ausgeführt",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Alle von diesem Regelsatz verknüpften Regeln sind deaktiviert"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Wählen Sie einen Ordner für die Verknüpfung von Regeln aus",
|
||||
"CANCEL": "Abbrechen",
|
||||
"SUBMIT": "Ordner auswählen",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Keine eigenen Regeln",
|
||||
"SUBTITLE": "Der ausgewählte Ordner hat keine eigenen Regeln."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Liste der Regeln, die verknüpft werden sollen",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Fehler beim Versuch, einen Link zu einem Regelsatz zu erstellen, ist aufgetreten"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Fehler beim Versuch, einen Link aus einem Regelsatz zu löschen, ist aufgetreten"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Manage Rules"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Cancel",
|
||||
"CREATE": "Create",
|
||||
"CREATE_TITLE": "Create a rule",
|
||||
"UPDATE": "Update",
|
||||
"UPDATE_TITLE": "Edit a rule"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Name",
|
||||
"DESCRIPTION": "Description",
|
||||
"WHEN": "When",
|
||||
"PERFORM_ACTIONS": "Perform actions",
|
||||
"OPTIONS": "Other options"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Enter rule name",
|
||||
"DESCRIPTION": "Enter rule description",
|
||||
"NO_DESCRIPTION": "No description",
|
||||
"VALUE": "Value",
|
||||
"CHOOSE_FOLDER": "Choose destination folder"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "This field is required",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "One or more condition groups is empty",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "At least one trigger is required"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Items are created or enter this folder",
|
||||
"UPDATE": "Items are updated",
|
||||
"OUTBOUND": "Items are deleted or leave this folder"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Rule applies to subfolders",
|
||||
"IS_ASYNCHRONOUS": "Run rule in the background",
|
||||
"DISABLE_RULE": "Disable rule",
|
||||
"ERROR_SCRIPT": "If errors occur run script",
|
||||
"NO_SCRIPT": "None"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Equals",
|
||||
"CONTAINS": "Contains",
|
||||
"STARTS_WITH": "Starts with",
|
||||
"ENDS_WITH": "Ends with",
|
||||
"GREATER_THAN": "(>) Greater than",
|
||||
"LESS_THAN": "(<) Less than",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Greater than or equal",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Less than or equal",
|
||||
"ON": "(=) On",
|
||||
"AFTER": "(>) After",
|
||||
"BEFORE": "(<) Before",
|
||||
"ON_OR_AFTER": "(>=) On or after",
|
||||
"ON_OR_BEFORE": "(<=) On or before",
|
||||
"INSTANCE_OF": "(=) Is"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Name",
|
||||
"SIZE": "Size",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"ENCODING": "Encoding",
|
||||
"HAS_CATEGORY": "Has category",
|
||||
"HAS_TAG": "Has tag",
|
||||
"HAS_ASPECT": "Has aspect"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "If",
|
||||
"NOT_IF": "NOT If",
|
||||
"AND": "And",
|
||||
"OR": "Or"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Add condition",
|
||||
"ADD_GROUP": "Add condition group",
|
||||
"REMOVE": "Remove"
|
||||
},
|
||||
"NO_CONDITIONS": "No conditions",
|
||||
"NO_CONDITIONS_IN_GROUP": "No conditions in the group",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Add action",
|
||||
"REMOVE": "Remove"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Select an action"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "rules"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Create rule",
|
||||
"LINK_RULES": "Link rules",
|
||||
"EDIT_RULE": "Edit",
|
||||
"SEE_IN_FOLDER": "See in folder",
|
||||
"INHERIT_RULES": "Inherit rules"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "The list is empty",
|
||||
"SUBTITLE": "There are no rules defined for this folder yet."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Delete rule",
|
||||
"MESSAGE": "Are you sure you want to delete this rule?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Delete rule set link",
|
||||
"MESSAGE": "Are you sure you want to delete the link to this rule set?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Rules from current folder",
|
||||
"LINKED_RULES": "Rules from linked folder",
|
||||
"INHERITED_RULES": "Inherited rules",
|
||||
"LOAD_MORE_RULE_SETS": "Load rules from other folders",
|
||||
"LOAD_MORE_RULES": "Load more rules",
|
||||
"LOADING_RULES": "Loading rules",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Inherited rules will be run first",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "All rules linked from this rule set are disabled"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Select a folder to link rules from",
|
||||
"CANCEL": "Cancel",
|
||||
"SUBMIT": "Select folder",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "No owned rules",
|
||||
"SUBTITLE": "The selected folder doesn't have any rules of its own."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "List of rules that will be linked",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Error while trying to create a link to a rule set"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Error while trying to delete a link from a rule set"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Gestionar reglas"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Cancelar",
|
||||
"CREATE": "Crear",
|
||||
"CREATE_TITLE": "Crear una regla",
|
||||
"UPDATE": "Actualizar",
|
||||
"UPDATE_TITLE": "Editar la regla"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nombre",
|
||||
"DESCRIPTION": "Descripción",
|
||||
"WHEN": "Cuando",
|
||||
"PERFORM_ACTIONS": "Realizar acciones",
|
||||
"OPTIONS": "Otras opciones"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Introducir un nombre para la regla",
|
||||
"DESCRIPTION": "Introducir una descripción para la regla",
|
||||
"NO_DESCRIPTION": "Sin descripción",
|
||||
"VALUE": "Valor",
|
||||
"CHOOSE_FOLDER": "Seleccione la carpeta de destino"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Este campo es obligatorio",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Uno o más grupos de condiciones están vacíos",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Se requiere al menos un activador"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Los elementos se crean o ingresan a esta carpeta",
|
||||
"UPDATE": "Los elementos se actualizan",
|
||||
"OUTBOUND": "Los elementos se eliminan o abandonan esta carpeta"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "La regla se aplica a las subcarpetas",
|
||||
"IS_ASYNCHRONOUS": "Ejecutar la regla en segundo plano",
|
||||
"DISABLE_RULE": "Desactivar regla",
|
||||
"ERROR_SCRIPT": "Si se producen errores, ejecute el script",
|
||||
"NO_SCRIPT": "Ninguna"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Igual a",
|
||||
"CONTAINS": "Contiene",
|
||||
"STARTS_WITH": "Comienza con",
|
||||
"ENDS_WITH": "Termina con",
|
||||
"GREATER_THAN": "(>) Mayor que",
|
||||
"LESS_THAN": "(<) Menor que",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Mayor que o igual",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Menor que o igual",
|
||||
"ON": "(=) En",
|
||||
"AFTER": "(>) Después",
|
||||
"BEFORE": "(<) Antes",
|
||||
"ON_OR_AFTER": "(>=) En o después",
|
||||
"ON_OR_BEFORE": "(<=) En o antes",
|
||||
"INSTANCE_OF": "(=) Es"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nombre",
|
||||
"SIZE": "Tamaño",
|
||||
"MIMETYPE": "Tipo MIME",
|
||||
"ENCODING": "Codificación",
|
||||
"HAS_CATEGORY": "Tiene categoría",
|
||||
"HAS_TAG": "Tiene etiqueta",
|
||||
"HAS_ASPECT": "Tiene aspecto"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Si",
|
||||
"NOT_IF": "NO si es que",
|
||||
"AND": "Y",
|
||||
"OR": "O"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Añadir condición",
|
||||
"ADD_GROUP": "Añadir grupo de condiciones",
|
||||
"REMOVE": "Eliminar"
|
||||
},
|
||||
"NO_CONDITIONS": "Sin condiciones",
|
||||
"NO_CONDITIONS_IN_GROUP": "Sin condiciones en el grupo",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Añadir acción",
|
||||
"REMOVE": "Eliminar"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Seleccionar una acción"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "reglas"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Crear una regla",
|
||||
"LINK_RULES": "Reglas de enlace",
|
||||
"EDIT_RULE": "Editar",
|
||||
"SEE_IN_FOLDER": "Ver en carpeta",
|
||||
"INHERIT_RULES": "Heredar reglas"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Esta lista está vacía",
|
||||
"SUBTITLE": "Todavía no hay reglas definidas para esta carpeta."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Eliminar regla",
|
||||
"MESSAGE": "¿Está seguro de que desea eliminar esta regla?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Eliminar el enlace del conjunto de reglas",
|
||||
"MESSAGE": "¿Está seguro de que quiere eliminar el enlace a este conjunto de reglas?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Reglas de la carpeta actual",
|
||||
"LINKED_RULES": "Reglas de la carpeta vinculada",
|
||||
"INHERITED_RULES": "Reglas heredadas",
|
||||
"LOAD_MORE_RULE_SETS": "Cargar reglas de otras carpetas",
|
||||
"LOAD_MORE_RULES": "Cargar más reglas",
|
||||
"LOADING_RULES": "Cargando reglas",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Las reglas heredadas se ejecutarán primero",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Todas las reglas vinculadas a este conjunto de reglas están deshabilitadas"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Seleccione una carpeta desde la que vincular las reglas",
|
||||
"CANCEL": "Cancelar",
|
||||
"SUBMIT": "Seleccionar carpeta",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "No hay reglas de propiedad",
|
||||
"SUBTITLE": "La carpeta seleccionada no tiene reglas propias."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Lista de reglas que se vincularán",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Se produjo un error al intentar crear un enlace a un conjunto de reglas"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Se produjo un error al intentar eliminar un enlace de un conjunto de reglas"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Hallitse sääntöjä"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Peruuta",
|
||||
"CREATE": "Luo",
|
||||
"CREATE_TITLE": "Luo sääntö",
|
||||
"UPDATE": "Päivitä",
|
||||
"UPDATE_TITLE": "Muokkaa sääntöä"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nimi",
|
||||
"DESCRIPTION": "Kuvaus",
|
||||
"WHEN": "Kun",
|
||||
"PERFORM_ACTIONS": "Suorita toimintoja",
|
||||
"OPTIONS": "Muut valinnat"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Syötä säännön nimi",
|
||||
"DESCRIPTION": "Syötä säännön kuvaus",
|
||||
"NO_DESCRIPTION": "Ei kuvausta",
|
||||
"VALUE": "Arvo",
|
||||
"CHOOSE_FOLDER": "Valitse kohdekansio"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Tämä kenttä on pakollinen",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Yksi tai useampi ehtoryhmä on tyhjä",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Vähintään yksi laukaisija on pakollinen"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Kohteet luodaan tai syötetään tähän kansioon",
|
||||
"UPDATE": "Kohteet päivitetään",
|
||||
"OUTBOUND": "Kohteet poistetaan tai ne poistuvat tästä kansiosta"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Sääntö koskee alikansioita",
|
||||
"IS_ASYNCHRONOUS": "Suorita sääntö taustalla",
|
||||
"DISABLE_RULE": "Poista sääntö käytöstä",
|
||||
"ERROR_SCRIPT": "Suorita komentosarja, jos virheitä havaitaan",
|
||||
"NO_SCRIPT": "Ei mitään"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Yhtä suuri kuin",
|
||||
"CONTAINS": "Sisältää",
|
||||
"STARTS_WITH": "Alkaa merkillä",
|
||||
"ENDS_WITH": "Loppuu merkkiin",
|
||||
"GREATER_THAN": "(>) Suurempi kuin",
|
||||
"LESS_THAN": "(<) Pienempi kuin",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Suurempi tai yhtä suuri kuin",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Vähemmän tai yhtä paljon",
|
||||
"ON": "(=) Tänä ajankohtana",
|
||||
"AFTER": "(>) Jälkeen",
|
||||
"BEFORE": "(<) Ennen",
|
||||
"ON_OR_AFTER": "(>=) Tänä ajankohtana tai sen jälkeen",
|
||||
"ON_OR_BEFORE": "(<=) Tänä ajankohtana tai sitä ennen",
|
||||
"INSTANCE_OF": "(=) On"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nimi",
|
||||
"SIZE": "Koko",
|
||||
"MIMETYPE": "MIME-tyyppi",
|
||||
"ENCODING": "Koodaus",
|
||||
"HAS_CATEGORY": "Sisältää luokan",
|
||||
"HAS_TAG": "Sisältää tunnuksen",
|
||||
"HAS_ASPECT": "Sisältää ominaisuusjoukon"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Jos",
|
||||
"NOT_IF": "EI jos",
|
||||
"AND": "Ja",
|
||||
"OR": "Tai"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Lisää ehto",
|
||||
"ADD_GROUP": "Lisää ehtoryhmä",
|
||||
"REMOVE": "Poista"
|
||||
},
|
||||
"NO_CONDITIONS": "Ei ehtoja",
|
||||
"NO_CONDITIONS_IN_GROUP": "Ryhmässä ei ole ehtoja",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Lisää toiminto",
|
||||
"REMOVE": "Poista"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Valitse toiminto"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "säännöt"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Luo sääntö",
|
||||
"LINK_RULES": "Linkitä sääntöjä",
|
||||
"EDIT_RULE": "Muokkaa",
|
||||
"SEE_IN_FOLDER": "Katso kansiosta",
|
||||
"INHERIT_RULES": "Peri sääntöjä"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Luettelo on tyhjä",
|
||||
"SUBTITLE": "Tälle kansiolle ei ole vielä määritelty sääntöjä."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Poista sääntö",
|
||||
"MESSAGE": "Haluatko varmasti poistaa tämän säännön?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Poista sääntöjoukon linkki",
|
||||
"MESSAGE": "Haluatko varmasti poistaa tähän sääntöjoukkoon johtavan linkin?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Säännöt nykyisestä kansiosta",
|
||||
"LINKED_RULES": "Säännöt linkitetystä kansiosta",
|
||||
"INHERITED_RULES": "Perityt säännöt",
|
||||
"LOAD_MORE_RULE_SETS": "Lataa sääntöjä muista kansioista",
|
||||
"LOAD_MORE_RULES": "Lataa lisää sääntöjä",
|
||||
"LOADING_RULES": "Sääntöjä ladataan",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Perityt säännöt suoritetaan ensin",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Kaikki tästä sääntöjoukosta linkitetyt säännöt on poistettu käytöstä"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Valitse kansio, josta sääntöjä linkitetään",
|
||||
"CANCEL": "Peruuta",
|
||||
"SUBMIT": "Valitse kansio",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Ei omistettuja sääntöjä",
|
||||
"SUBTITLE": "Valitulla kansiolla ei ole omia sääntöjä."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Luettelo säännöistä, jotka linkitetään",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Virhe yritettäessä luoda linkki sääntöjoukkoon"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Virhe yritettäessä poistaa linkki sääntöjoukosta"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Gérer les règles"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Annuler",
|
||||
"CREATE": "Créer",
|
||||
"CREATE_TITLE": "Créer une règle",
|
||||
"UPDATE": "Mettre à jour",
|
||||
"UPDATE_TITLE": "Modifier une règle"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nom",
|
||||
"DESCRIPTION": "Description",
|
||||
"WHEN": "Quand",
|
||||
"PERFORM_ACTIONS": "Effectuer des actions",
|
||||
"OPTIONS": "Autres options"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Entrer le nom de la règle",
|
||||
"DESCRIPTION": "Entrer la description de la règle",
|
||||
"NO_DESCRIPTION": "Aucune description",
|
||||
"VALUE": "Valeur",
|
||||
"CHOOSE_FOLDER": "Choisir le dossier de destination"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Ce champ est obligatoire",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Un ou plusieurs groupes de conditions sont vides",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Au moins un déclencheur est requis"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Des éléments sont créés ou ajoutés à ce dossier",
|
||||
"UPDATE": "Des éléments sont mis à jour",
|
||||
"OUTBOUND": "Des éléments sont supprimés ou retirés de ce dossier"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "La règle s'applique aux sous-dossiers",
|
||||
"IS_ASYNCHRONOUS": "Exécuter la règle en arrière-plan",
|
||||
"DISABLE_RULE": "Désactiver la règle",
|
||||
"ERROR_SCRIPT": "Si des erreurs surviennent, exécuter le script",
|
||||
"NO_SCRIPT": "Aucun"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Est égal à",
|
||||
"CONTAINS": "Contient",
|
||||
"STARTS_WITH": "Commence par",
|
||||
"ENDS_WITH": "Finit par",
|
||||
"GREATER_THAN": "(>) Supérieur à",
|
||||
"LESS_THAN": "(<) Inférieur à",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Supérieur ou égal à",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Inférieur ou égal à",
|
||||
"ON": "(=) A cet instant",
|
||||
"AFTER": "(>) Après",
|
||||
"BEFORE": "(<) Avant",
|
||||
"ON_OR_AFTER": "(>=) A cet instant ou après",
|
||||
"ON_OR_BEFORE": "(<=) A cet instant ou avant",
|
||||
"INSTANCE_OF": "(=) Est"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nom",
|
||||
"SIZE": "Taille",
|
||||
"MIMETYPE": "Type MIME",
|
||||
"ENCODING": "Encodage",
|
||||
"HAS_CATEGORY": "A une catégorie",
|
||||
"HAS_TAG": "A un tag",
|
||||
"HAS_ASPECT": "A un aspect"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Si",
|
||||
"NOT_IF": "PAS Si",
|
||||
"AND": "Et",
|
||||
"OR": "Ou"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Ajouter une condition",
|
||||
"ADD_GROUP": "Ajouter un groupe de conditions",
|
||||
"REMOVE": "Supprimer"
|
||||
},
|
||||
"NO_CONDITIONS": "Aucune condition",
|
||||
"NO_CONDITIONS_IN_GROUP": "Aucune condition dans le groupe",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Ajouter une action",
|
||||
"REMOVE": "Supprimer"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Sélectionner une action"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "règles"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Créer une règle",
|
||||
"LINK_RULES": "Lier des règles",
|
||||
"EDIT_RULE": "Modifier",
|
||||
"SEE_IN_FOLDER": "Voir dans le dossier",
|
||||
"INHERIT_RULES": "Hériter des règles"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "La liste est vide",
|
||||
"SUBTITLE": "Aucune règle définie pour ce dossier pour l'instant."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Supprimez la règle",
|
||||
"MESSAGE": "Voulez-vous vraiment supprimer cette règle ?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Supprimer le lien vers l'ensemble de règles",
|
||||
"MESSAGE": "Voulez-vous vraiment supprimer le lien vers cet ensemble de règles ?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Règles du dossier actuel",
|
||||
"LINKED_RULES": "Règles du dossier lié",
|
||||
"INHERITED_RULES": "Règles héritées",
|
||||
"LOAD_MORE_RULE_SETS": "Charger les règles d'autres dossiers",
|
||||
"LOAD_MORE_RULES": "Charger plus de règles",
|
||||
"LOADING_RULES": "Chargement des règles en cours",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Les règles héritées seront exécutées en premier",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Toutes les règles liées à cet ensemble de règles sont désactivées"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Sélectionner le dossier à partir duquel lier les règles",
|
||||
"CANCEL": "Annuler",
|
||||
"SUBMIT": "Sélectionner le dossier",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Aucune règle acquise",
|
||||
"SUBTITLE": "Le dossier sélectionné n'a pas ses propres règles."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Liste des règles qui seront liées",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Erreur lors de la tentative de création d'un lien vers un ensemble de règles"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Erreur lors de la tentative de suppression d'un lien avec un ensemble de règles"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Gestisci regole"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Annulla",
|
||||
"CREATE": "Crea",
|
||||
"CREATE_TITLE": "Crea una regola",
|
||||
"UPDATE": "Aggiorna",
|
||||
"UPDATE_TITLE": "Modifica una regola"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nome",
|
||||
"DESCRIPTION": "Descrizione",
|
||||
"WHEN": "Quando",
|
||||
"PERFORM_ACTIONS": "Esegui azioni",
|
||||
"OPTIONS": "Altre opzioni"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Immetti nome regola",
|
||||
"DESCRIPTION": "Immetti descrizione regola",
|
||||
"NO_DESCRIPTION": "Nessuna descrizione",
|
||||
"VALUE": "Valore",
|
||||
"CHOOSE_FOLDER": "Scegli cartella di destinazione"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Questo campo è obbligatorio",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Uno o più gruppi di condizioni sono vuoti.",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "È necessario almeno un trigger"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Vengono creati gli elementi o si accede alla cartella",
|
||||
"UPDATE": "Vengono aggiornati gli elementi",
|
||||
"OUTBOUND": "Vengono eliminati gli elementi o si esce dalla cartella"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regola applicabile alle sottocartelle",
|
||||
"IS_ASYNCHRONOUS": "Esegui regola in background",
|
||||
"DISABLE_RULE": "Disabilita regola",
|
||||
"ERROR_SCRIPT": "Esegui script in caso di errori",
|
||||
"NO_SCRIPT": "Nessuna"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Uguale a",
|
||||
"CONTAINS": "Contiene",
|
||||
"STARTS_WITH": "Inizia con",
|
||||
"ENDS_WITH": "Finisce con",
|
||||
"GREATER_THAN": "(>) Maggiore di",
|
||||
"LESS_THAN": "(<) Minore di",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Maggiore di o uguale a",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Minore di o uguale a",
|
||||
"ON": "(=) Il",
|
||||
"AFTER": "(>) Dopo",
|
||||
"BEFORE": "(<) Prima",
|
||||
"ON_OR_AFTER": "(>=) Il o dopo",
|
||||
"ON_OR_BEFORE": "(<=) Il o prima",
|
||||
"INSTANCE_OF": "(=) È"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nome",
|
||||
"SIZE": "Dimensione",
|
||||
"MIMETYPE": "Tipo MIME",
|
||||
"ENCODING": "Codifica",
|
||||
"HAS_CATEGORY": "Con categoria",
|
||||
"HAS_TAG": "Con tag",
|
||||
"HAS_ASPECT": "Con aspetto"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "If",
|
||||
"NOT_IF": "NOT If",
|
||||
"AND": "And",
|
||||
"OR": "Or"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Aggiungi condizione",
|
||||
"ADD_GROUP": "Aggiungi gruppo di condizioni",
|
||||
"REMOVE": "Rimuovi"
|
||||
},
|
||||
"NO_CONDITIONS": "Nessuna condizione",
|
||||
"NO_CONDITIONS_IN_GROUP": "Nessuna condizione nel gruppo",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Aggiungi azione",
|
||||
"REMOVE": "Rimuovi"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Seleziona un'azione"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regole"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Crea una regola",
|
||||
"LINK_RULES": "Collega le regole",
|
||||
"EDIT_RULE": "Modifica",
|
||||
"SEE_IN_FOLDER": "Visualizza nella cartella",
|
||||
"INHERIT_RULES": "Eredita le regole"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "L'elenco è vuoto",
|
||||
"SUBTITLE": "Non sono ancora state definite delle regole per questa cartella."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Elimina regola",
|
||||
"MESSAGE": "Eliminare questa regola?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Elimina collegamento al set di regole",
|
||||
"MESSAGE": "Eliminare il collegamento a questo set di regole?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regole dalla cartella corrente",
|
||||
"LINKED_RULES": "Regole dalla cartella collegata",
|
||||
"INHERITED_RULES": "Regole ereditate",
|
||||
"LOAD_MORE_RULE_SETS": "Carica regole da altre cartelle",
|
||||
"LOAD_MORE_RULES": "Carica altre regole",
|
||||
"LOADING_RULES": "Caricamento delle regole in corso",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Le regole ereditate verranno eseguite per prime",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Tutte le regole collegate a questo set di regole verranno disabilitate"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Seleziona una cartella da cui collegare le regole",
|
||||
"CANCEL": "Annulla",
|
||||
"SUBMIT": "Seleziona cartella",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Nessuna regola di proprietà",
|
||||
"SUBTITLE": "La cartella selezionata non ha regole proprie."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Elenco delle regole che verranno collegate",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Errore durante il tentativo di creare un collegamento a un set di regole"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Errore durante il tentativo di eliminare un collegamento da un set di regole"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "ルールの管理"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "キャンセル",
|
||||
"CREATE": "作成",
|
||||
"CREATE_TITLE": "ルールの作成",
|
||||
"UPDATE": "更新",
|
||||
"UPDATE_TITLE": "ルールの編集"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "名前",
|
||||
"DESCRIPTION": "説明",
|
||||
"WHEN": "日時",
|
||||
"PERFORM_ACTIONS": "アクションの実行",
|
||||
"OPTIONS": "他のオプション"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "ルールの名前を入力します",
|
||||
"DESCRIPTION": "ルールの説明を入力します",
|
||||
"NO_DESCRIPTION": "説明なし",
|
||||
"VALUE": "値",
|
||||
"CHOOSE_FOLDER": "移動先フォルダの選択"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "このフィールドは必須です",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "1つ以上の条件グループが空です",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "少なくとも1つのトリガーが必要です"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "アイタムがこのフォルダに作成または入力される",
|
||||
"UPDATE": "アイタムが更新される",
|
||||
"OUTBOUND": "アイタムがこのフォルダから削除または送り出される"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "ルールはサブフォルダに適用",
|
||||
"IS_ASYNCHRONOUS": "ルールを背景で実行",
|
||||
"DISABLE_RULE": "ルールの無効化",
|
||||
"ERROR_SCRIPT": "エラーが発生した場合、スクリプトを実行",
|
||||
"NO_SCRIPT": "なし"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) 等しい",
|
||||
"CONTAINS": "含む",
|
||||
"STARTS_WITH": "で始まる",
|
||||
"ENDS_WITH": "で終わる",
|
||||
"GREATER_THAN": "(>) より大きい",
|
||||
"LESS_THAN": "(<) より小さい",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) より大きいか等しい",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) より小さいか等しい",
|
||||
"ON": "(=) その日",
|
||||
"AFTER": "(>) その日の後",
|
||||
"BEFORE": "(<) その日の前",
|
||||
"ON_OR_AFTER": "(>=) その日か後",
|
||||
"ON_OR_BEFORE": "(<=) その日か前",
|
||||
"INSTANCE_OF": "(=) 等しい"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "名前",
|
||||
"SIZE": "サイズ",
|
||||
"MIMETYPE": "MIME タイプ",
|
||||
"ENCODING": "エンコーディング",
|
||||
"HAS_CATEGORY": "カテゴリがない",
|
||||
"HAS_TAG": "タグがある",
|
||||
"HAS_ASPECT": "アスペクトがある"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "次の場合",
|
||||
"NOT_IF": "次でない場合",
|
||||
"AND": "および",
|
||||
"OR": "または"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "条件の追加",
|
||||
"ADD_GROUP": "条件グループの追加",
|
||||
"REMOVE": "削除"
|
||||
},
|
||||
"NO_CONDITIONS": "条件なし",
|
||||
"NO_CONDITIONS_IN_GROUP": "グループに条件なし",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "処理の追加",
|
||||
"REMOVE": "削除"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "アクションの選択"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "ルール"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "ルールの作成",
|
||||
"LINK_RULES": "リンクルール",
|
||||
"EDIT_RULE": "編集",
|
||||
"SEE_IN_FOLDER": "フォルダで見る",
|
||||
"INHERIT_RULES": "ルールの継承"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "このリストは空です",
|
||||
"SUBTITLE": "このフォルダーにはまだルールが定義されていません。"
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "ルールの削除",
|
||||
"MESSAGE": "このルールを削除しますか?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "ルールセットリンクの削除",
|
||||
"MESSAGE": "このルールセットへのリンクを削除しますか?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "現在のフォルダのルール",
|
||||
"LINKED_RULES": "リンク先フォルダのルール",
|
||||
"INHERITED_RULES": "継承されたルール",
|
||||
"LOAD_MORE_RULE_SETS": "他のフォルダからユールを読み込む",
|
||||
"LOAD_MORE_RULES": "さらにルールを読み込む",
|
||||
"LOADING_RULES": "ルールの読み込み中",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "継承されたルールが最初に実行されます",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "このルールセットからリンクされているすべてのルールは無効です"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "ルールをリンクさせるフォルダを選択",
|
||||
"CANCEL": "キャンセル",
|
||||
"SUBMIT": "フォルダの選択",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "所有ルールがありません",
|
||||
"SUBTITLE": "選択したフォルダには、それ自身のルールがありません。"
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "リンク先のルールリスト",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "ルールセットへのリンクを作成中にエラーが発生しました"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "ルールセットからリンクを削除中にエラーが発生しました"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Administrer regler"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Avbryt",
|
||||
"CREATE": "Opprett",
|
||||
"CREATE_TITLE": "Opprett en regel",
|
||||
"UPDATE": "Oppdater",
|
||||
"UPDATE_TITLE": "Rediger en regel"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Navn",
|
||||
"DESCRIPTION": "Beskrivelse",
|
||||
"WHEN": "Når",
|
||||
"PERFORM_ACTIONS": "Utfør handlinger",
|
||||
"OPTIONS": "Andre alternativer"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Tast inn et navn på regelen",
|
||||
"DESCRIPTION": "Tast inn en beskrivelse av regelen",
|
||||
"NO_DESCRIPTION": "Ingen beskrivelse",
|
||||
"VALUE": "Verdi",
|
||||
"CHOOSE_FOLDER": "Velg målmappe"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Dette feltet er påkrevd",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "En eller flere av betingelsesgruppene er tomme",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Er det obligatorisk med minst en utløser"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Enheter blir opprettet etter lagt til i denne mappen",
|
||||
"UPDATE": "Enhetene er oppdaterte",
|
||||
"OUTBOUND": "Enhetene er slettet eller har forlatt denne mappen"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regel brukt på undermapper",
|
||||
"IS_ASYNCHRONOUS": "Kjør regel i bakgrunnen",
|
||||
"DISABLE_RULE": "Deaktiver regel",
|
||||
"ERROR_SCRIPT": "Kjør skript hvis feil oppstår",
|
||||
"NO_SCRIPT": "Ingen"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Er lik",
|
||||
"CONTAINS": "Inneholder",
|
||||
"STARTS_WITH": "Starter med",
|
||||
"ENDS_WITH": "Slutter med",
|
||||
"GREATER_THAN": "(>) Større enn",
|
||||
"LESS_THAN": "(>) Mindre enn",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Større enn eller lik",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Mindre enn eller lik",
|
||||
"ON": "(=) På",
|
||||
"AFTER": "(>) Etter",
|
||||
"BEFORE": "(<) Før",
|
||||
"ON_OR_AFTER": "(>=) På eller etter",
|
||||
"ON_OR_BEFORE": "(<=) På eller før",
|
||||
"INSTANCE_OF": "(=) Er"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Navn",
|
||||
"SIZE": "Størrelse",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"ENCODING": "Koding",
|
||||
"HAS_CATEGORY": "Ingen kategori",
|
||||
"HAS_TAG": "Har tag",
|
||||
"HAS_ASPECT": "Har aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Hvis",
|
||||
"NOT_IF": "IKKE hvis",
|
||||
"AND": "Og",
|
||||
"OR": "Eller"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Legg til betingelse",
|
||||
"ADD_GROUP": "Legg til betingelsesgruppe",
|
||||
"REMOVE": "Fjern"
|
||||
},
|
||||
"NO_CONDITIONS": "Ingen betingelser",
|
||||
"NO_CONDITIONS_IN_GROUP": "Ingen betingelse i gruppen",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Legg til handling",
|
||||
"REMOVE": "Fjern"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Velg en handling"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regler"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Opprett en regel",
|
||||
"LINK_RULES": "Koble regler",
|
||||
"EDIT_RULE": "Rediger",
|
||||
"SEE_IN_FOLDER": "Se i mappe",
|
||||
"INHERIT_RULES": "Arve regler"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Denne listen er tom",
|
||||
"SUBTITLE": "Det er ingen regler definert for denne mappen enda."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Slett regel",
|
||||
"MESSAGE": "Er du sikker på at du vil slette denne regelen?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Slett regelsettkobling",
|
||||
"MESSAGE": "Er du sikker på at du vil slette koblingen til dette regelsettet?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regler fra gjeldende mappe",
|
||||
"LINKED_RULES": "Regler fra koblet mappe",
|
||||
"INHERITED_RULES": "Arvede regler",
|
||||
"LOAD_MORE_RULE_SETS": "Last regler fra andre mapper",
|
||||
"LOAD_MORE_RULES": "Last flere regler",
|
||||
"LOADING_RULES": "Laster regler",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Arvede regler kjøres først",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Alle regler som er stammer fra dette regelsettet, er deaktiverte"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Velg en mappe å koble regler fra",
|
||||
"CANCEL": "Avbryt",
|
||||
"SUBMIT": "Velg mappe",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Ingen eide regler",
|
||||
"SUBTITLE": "Den valgte mappen har ingen egne regler."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Liste over regler som vil bli koblet",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Feil ved forsøk på å opprette en kobling til et regelsett"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Feil ved forsøk på å slette en kobling fra et regelsett"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Regels beheren"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Annuleren",
|
||||
"CREATE": "Maken",
|
||||
"CREATE_TITLE": "Een regel maken",
|
||||
"UPDATE": "Bijwerken",
|
||||
"UPDATE_TITLE": "Een regel bewerken"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Naam",
|
||||
"DESCRIPTION": "Beschrijving",
|
||||
"WHEN": "Wanneer",
|
||||
"PERFORM_ACTIONS": "Acties uitvoeren",
|
||||
"OPTIONS": "Andere opties"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Regelnaam invoeren",
|
||||
"DESCRIPTION": "Regelbeschrijving invoeren",
|
||||
"NO_DESCRIPTION": "Geen beschrijving",
|
||||
"VALUE": "Waarde",
|
||||
"CHOOSE_FOLDER": "Doelmap kiezen"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Dit veld is vereist",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Eén of meer voorwaardengroepen zijn leeg",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Er is minstens één trigger vereist"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Items worden gemaakt of gaan naar deze map",
|
||||
"UPDATE": "Items worden bijgewerkt",
|
||||
"OUTBOUND": "Items worden verwijderd of verlaten deze map"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regel is van toepassing op submappen",
|
||||
"IS_ASYNCHRONOUS": "Regel op de achtergrond uitvoeren",
|
||||
"DISABLE_RULE": "Regel uitschakelen",
|
||||
"ERROR_SCRIPT": "Script uitvoeren wanneer er fouten optreden",
|
||||
"NO_SCRIPT": "Geen"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Gelijk aan",
|
||||
"CONTAINS": "Bevat",
|
||||
"STARTS_WITH": "Begint met",
|
||||
"ENDS_WITH": "Eindigt met",
|
||||
"GREATER_THAN": "(>) Groter dan",
|
||||
"LESS_THAN": "(<) Kleiner dan",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Groter dan of gelijk aan",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Kleiner dan of gelijk aan",
|
||||
"ON": "(=) Op",
|
||||
"AFTER": "(>) Na",
|
||||
"BEFORE": "(<) Voor",
|
||||
"ON_OR_AFTER": "(>=) Op of na",
|
||||
"ON_OR_BEFORE": "(<=) Op of voor",
|
||||
"INSTANCE_OF": "(=) Is"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Naam",
|
||||
"SIZE": "Grootte",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"ENCODING": "Codering",
|
||||
"HAS_CATEGORY": "Heeft categorie",
|
||||
"HAS_TAG": "Heeft tag",
|
||||
"HAS_ASPECT": "Heeft aspect"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Als",
|
||||
"NOT_IF": "NIET Als",
|
||||
"AND": "En",
|
||||
"OR": "Of"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Voorwaarde toevoegen",
|
||||
"ADD_GROUP": "Voorwaardengroep toevoegen",
|
||||
"REMOVE": "Verwijderen"
|
||||
},
|
||||
"NO_CONDITIONS": "Geen voorwaarden",
|
||||
"NO_CONDITIONS_IN_GROUP": "Geen voorwaarden in de groep",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Actie toevoegen",
|
||||
"REMOVE": "Verwijderen"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Selecteer een actie"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regels"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Een regel maken",
|
||||
"LINK_RULES": "Regels koppelen",
|
||||
"EDIT_RULE": "Bewerken",
|
||||
"SEE_IN_FOLDER": "Zien in map",
|
||||
"INHERIT_RULES": "Regels overnemen"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "De lijst is leeg",
|
||||
"SUBTITLE": "Er zijn nog geen regels gedefinieerd voor deze map."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Regel verwijderen",
|
||||
"MESSAGE": "Weet u zeker dat u deze regel wilt verwijderen?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Koppeling regelset verwijderen",
|
||||
"MESSAGE": "Weet u zeker dat u de koppeling naar deze regelset wilt verwijderen?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regels uit huidige map",
|
||||
"LINKED_RULES": "Regels uit gekoppelde map",
|
||||
"INHERITED_RULES": "Overgenomen regels",
|
||||
"LOAD_MORE_RULE_SETS": "Regels laden uit andere mappen",
|
||||
"LOAD_MORE_RULES": "Meer regels laden",
|
||||
"LOADING_RULES": "Regels worden geladen",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Overgenomen regels worden als eerste uitgevoerd",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Alle regels die vanuit deze regelset zijn gekoppeld worden uitgeschakeld"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Selecteer een map van waaruit regels moeten worden gekoppeld",
|
||||
"CANCEL": "Annuleren",
|
||||
"SUBMIT": "Map selecteren",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Geen regels in eigendom",
|
||||
"SUBTITLE": "De geselecteerde map bevat geen eigen regels."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Lijst met regels die worden gekoppeld",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Fout bij het maken van een koppeling naar een regelset"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Fout bij het verwijderen van een koppeling uit een regelset"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Zarządzaj regułami"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Anuluj",
|
||||
"CREATE": "Utwórz",
|
||||
"CREATE_TITLE": "Utwórz regułę",
|
||||
"UPDATE": "Aktualizuj",
|
||||
"UPDATE_TITLE": "Edytuj regułę"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nazwa",
|
||||
"DESCRIPTION": "Opis",
|
||||
"WHEN": "Kiedy",
|
||||
"PERFORM_ACTIONS": "Wykonaj czynności",
|
||||
"OPTIONS": "Inne opcje"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Wprowadź nazwę reguły",
|
||||
"DESCRIPTION": "Wprowadź opis reguły",
|
||||
"NO_DESCRIPTION": "Brak opisu",
|
||||
"VALUE": "Wartość",
|
||||
"CHOOSE_FOLDER": "Wybierz folder docelowy"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "To pole jest wymagane.",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Co najmniej jedna grupa warunków jest pusta",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Co najmniej jeden wyzwalacz jest wymagany"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Pozycje są utworzone lub wejdź do tego folderu",
|
||||
"UPDATE": "Pozycje są zaktualizowane",
|
||||
"OUTBOUND": "Pozycje są usunięte lub opuść ten folder"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Reguła ma zastosowanie do podfolderów",
|
||||
"IS_ASYNCHRONOUS": "Uruchom regułę w tle",
|
||||
"DISABLE_RULE": "Wyłącz regułę",
|
||||
"ERROR_SCRIPT": "Jeśli wystąpią błędy, uruchom skrypt",
|
||||
"NO_SCRIPT": "Brak"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Równa się",
|
||||
"CONTAINS": "Zawiera",
|
||||
"STARTS_WITH": "Zaczyna się na",
|
||||
"ENDS_WITH": "Kończy się na",
|
||||
"GREATER_THAN": "(>) Większe od",
|
||||
"LESS_THAN": "(<) Mniejsze od",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Większe od lub równe",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Mniejsze od lub równe",
|
||||
"ON": "(=) W dniu",
|
||||
"AFTER": "(>) Po",
|
||||
"BEFORE": "(<) Przed",
|
||||
"ON_OR_AFTER": "(>=) W dniu lub po",
|
||||
"ON_OR_BEFORE": "(<=) W dniu lub przed",
|
||||
"INSTANCE_OF": "(=) Jest"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nazwa",
|
||||
"SIZE": "Rozmiar",
|
||||
"MIMETYPE": "Typ MIME",
|
||||
"ENCODING": "Kodowanie",
|
||||
"HAS_CATEGORY": "Posiada kategorię",
|
||||
"HAS_TAG": "Posiada znacznik",
|
||||
"HAS_ASPECT": "Posiada aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Jeśli",
|
||||
"NOT_IF": "NIE jeśli",
|
||||
"AND": "Oraz",
|
||||
"OR": "Lub"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Dodaj warunek",
|
||||
"ADD_GROUP": "Dodaj grupę warunków",
|
||||
"REMOVE": "Usuń"
|
||||
},
|
||||
"NO_CONDITIONS": "Brak warunków",
|
||||
"NO_CONDITIONS_IN_GROUP": "Brak warunków w grupie",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Dodaj czynność",
|
||||
"REMOVE": "Usuń"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Wybierz czynność"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "reguły"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Utwórz regułę",
|
||||
"LINK_RULES": "Połącz reguły",
|
||||
"EDIT_RULE": "Edytuj",
|
||||
"SEE_IN_FOLDER": "Zobacz w folderze",
|
||||
"INHERIT_RULES": "Dziedzicz reguły"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Ta lista jest pusta",
|
||||
"SUBTITLE": "Nie zdefiniowano jeszcze reguł dla tego folderu."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Usuń regułę",
|
||||
"MESSAGE": "Czy na pewno chcesz usunąć tę regułę?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Usuń łącze zbioru reguł",
|
||||
"MESSAGE": "Czy na pewno usunąć łącze do tego zbioru reguł?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Reguły z bieżącego folderu",
|
||||
"LINKED_RULES": "Reguły z połączonego folderu",
|
||||
"INHERITED_RULES": "Odziedziczone reguły",
|
||||
"LOAD_MORE_RULE_SETS": "Wczytaj reguły z innych folderów",
|
||||
"LOAD_MORE_RULES": "Wczytaj więcej reguł",
|
||||
"LOADING_RULES": "Wczytywanie reguł",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Odziedziczone reguły zostaną wykonane najpierw",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Wszystkie reguły z łączem z tego zbioru reguł są wyłączone"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Wybierz folder, z którego mają być łączone reguły",
|
||||
"CANCEL": "Anuluj",
|
||||
"SUBMIT": "Wybierz folder",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Brak własnych reguł",
|
||||
"SUBTITLE": "Wybrany folder nie ma żadnych własnych reguł."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Lista reguł, które zostaną połączone",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Błąd podczas próby utworzenia łącza do zbioru reguł"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Błąd podczas próby usunięcia łącza ze zbioru reguł"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Gerenciar regras"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Cancelar",
|
||||
"CREATE": "Criar",
|
||||
"CREATE_TITLE": "Criar uma regra",
|
||||
"UPDATE": "Atualizar",
|
||||
"UPDATE_TITLE": "Editar uma regra"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Nome",
|
||||
"DESCRIPTION": "Descrição",
|
||||
"WHEN": "Quando",
|
||||
"PERFORM_ACTIONS": "Realizar ações",
|
||||
"OPTIONS": "Outras opções"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Inserir um nome para a regra",
|
||||
"DESCRIPTION": "Inserir uma descrição para a regra",
|
||||
"NO_DESCRIPTION": "Nenhuma descrição",
|
||||
"VALUE": "Valor",
|
||||
"CHOOSE_FOLDER": "Escolher pasta de destino"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Este campo é obrigatório",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Um ou mais grupos de condições estiver vazio",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Pelo menos um gatilho será necessário"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Itens foram criados ou inseridos nesta pasta",
|
||||
"UPDATE": "Itens foram atualizados",
|
||||
"OUTBOUND": "Itens foram excluídos ou saíram desta pasta"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "A regra é aplicável a subpastas",
|
||||
"IS_ASYNCHRONOUS": "Executar regra no plano de fundo",
|
||||
"DISABLE_RULE": "Desativar regra",
|
||||
"ERROR_SCRIPT": "Se ocorrerem erros, execute o script",
|
||||
"NO_SCRIPT": "Nenhum"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Igual a",
|
||||
"CONTAINS": "Contém",
|
||||
"STARTS_WITH": "Inicia com",
|
||||
"ENDS_WITH": "Termina com",
|
||||
"GREATER_THAN": "Maior que",
|
||||
"LESS_THAN": "Menor que",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Maior que ou igual a",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Menor que ou igual a",
|
||||
"ON": "(=) Em",
|
||||
"AFTER": "(>) Após",
|
||||
"BEFORE": "(<) Antes",
|
||||
"ON_OR_AFTER": "(>=) Em ou após",
|
||||
"ON_OR_BEFORE": "(<=) Em ou antes",
|
||||
"INSTANCE_OF": "(=) É"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Nome",
|
||||
"SIZE": "Tamanho",
|
||||
"MIMETYPE": "Tipo mime",
|
||||
"ENCODING": "Codificação",
|
||||
"HAS_CATEGORY": "Tem categoria",
|
||||
"HAS_TAG": "Tem marca",
|
||||
"HAS_ASPECT": "Tem aspecto"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Se",
|
||||
"NOT_IF": "Não se",
|
||||
"AND": "E",
|
||||
"OR": "Ou"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Adicionar condição",
|
||||
"ADD_GROUP": "Adicionar grupo de condição",
|
||||
"REMOVE": "Excluir"
|
||||
},
|
||||
"NO_CONDITIONS": "Sem condições",
|
||||
"NO_CONDITIONS_IN_GROUP": "Sem condições no grupo",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Adicionar ação",
|
||||
"REMOVE": "Excluir"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Selecionar uma ação"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regras"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Criar uma regra",
|
||||
"LINK_RULES": "Regras de link",
|
||||
"EDIT_RULE": "Editar",
|
||||
"SEE_IN_FOLDER": "Consulte na pasta",
|
||||
"INHERIT_RULES": "Herdar regras"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "A lista está vazia",
|
||||
"SUBTITLE": "Não há regras definidas para esta pasta ainda."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Excluir regra",
|
||||
"MESSAGE": "Tem certeza de que deseja excluir esta regra?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Excluir link do conjunto de regras",
|
||||
"MESSAGE": "Tem certeza de que deseja excluir o link deste conjunto de regras?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regras da pasta atual",
|
||||
"LINKED_RULES": "Regras da pasta vinculada",
|
||||
"INHERITED_RULES": "Regras herdadas",
|
||||
"LOAD_MORE_RULE_SETS": "Carregar regras de outras pastas",
|
||||
"LOAD_MORE_RULES": "Carregar mais regras",
|
||||
"LOADING_RULES": "Carregando regras",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "As regras herdadas serão executadas primeiro",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Todas as regras vinculadas deste conjunto de regras estão desativadas"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Selecione uma pasta da qual vincular regras",
|
||||
"CANCEL": "Cancelar",
|
||||
"SUBMIT": "Selecionar pasta",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Nenhuma regra própria",
|
||||
"SUBTITLE": "A pasta selecionada não possui regras próprias."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Lista das regras que serão vinculadas",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Erro ao tentar criar um link a um conjunto de regras"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Erro ao tentar excluir um link de um conjunto de regras"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Управление правилами"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Отмена",
|
||||
"CREATE": "Создать",
|
||||
"CREATE_TITLE": "Создать правило",
|
||||
"UPDATE": "Обновить",
|
||||
"UPDATE_TITLE": "Редактировать правило"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Имя",
|
||||
"DESCRIPTION": "Описание",
|
||||
"WHEN": "Когда",
|
||||
"PERFORM_ACTIONS": "Выполнить действия",
|
||||
"OPTIONS": "Другие параметры"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Введите имя правила",
|
||||
"DESCRIPTION": "Введите описание правила",
|
||||
"NO_DESCRIPTION": "Нет описания",
|
||||
"VALUE": "Значение",
|
||||
"CHOOSE_FOLDER": "Выбрать расположение"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Поле обязательно для заполнения",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "Одна или несколько групп условий пустые",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Требуется как минимум один триггер"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Элементы создаются или попадают в эту папку",
|
||||
"UPDATE": "Элементы обновлены",
|
||||
"OUTBOUND": "Элементы удаляются или покидают эту папку"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Правило применяется к вложенным папкам",
|
||||
"IS_ASYNCHRONOUS": "Выполнить правило в фоновом режиме",
|
||||
"DISABLE_RULE": "Отключить правило",
|
||||
"ERROR_SCRIPT": "Запустите скрипт при возникновении ошибок",
|
||||
"NO_SCRIPT": "Нет"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Равно",
|
||||
"CONTAINS": "Содержит",
|
||||
"STARTS_WITH": "Начинается с",
|
||||
"ENDS_WITH": "Завершается с",
|
||||
"GREATER_THAN": "(>) Больше чем",
|
||||
"LESS_THAN": "(<) Меньше чем",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Больше или равно",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Меньше или равно",
|
||||
"ON": "(=) В",
|
||||
"AFTER": "(>) После",
|
||||
"BEFORE": "(<) До",
|
||||
"ON_OR_AFTER": "(>=) В или после",
|
||||
"ON_OR_BEFORE": "(<=) В или ранее",
|
||||
"INSTANCE_OF": "(=) Есть"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Имя",
|
||||
"SIZE": "Размер",
|
||||
"MIMETYPE": "Тип MIME",
|
||||
"ENCODING": "Кодировка",
|
||||
"HAS_CATEGORY": "Имеет категорию",
|
||||
"HAS_TAG": "Имеется метка",
|
||||
"HAS_ASPECT": "Имеет аспект"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Если",
|
||||
"NOT_IF": "Не если",
|
||||
"AND": "И",
|
||||
"OR": "Или"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Добавить условие",
|
||||
"ADD_GROUP": "Добавить группу условий",
|
||||
"REMOVE": "Удалить"
|
||||
},
|
||||
"NO_CONDITIONS": "Нет условий",
|
||||
"NO_CONDITIONS_IN_GROUP": "В группе нет условий",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Добавить действие",
|
||||
"REMOVE": "Удалить"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Выбрать действие"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "правила"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Создать правило",
|
||||
"LINK_RULES": "Привязать правила",
|
||||
"EDIT_RULE": "Редактировать",
|
||||
"SEE_IN_FOLDER": "Показать в папке",
|
||||
"INHERIT_RULES": "Наследовать правила"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Список пуст",
|
||||
"SUBTITLE": "Для этой папки еще не определены правила."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Удалить правило",
|
||||
"MESSAGE": "Вы действительно хотите удалить это правило?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Удалить ссылку на набор правил",
|
||||
"MESSAGE": "Вы действительно хотите удалить ссылку на этот набор правил?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Правила из текущей папки",
|
||||
"LINKED_RULES": "Правила из привязанной папки",
|
||||
"INHERITED_RULES": "Унаследованные правила",
|
||||
"LOAD_MORE_RULE_SETS": "Загрузить правила из других папок",
|
||||
"LOAD_MORE_RULES": "Загрузить другие правила",
|
||||
"LOADING_RULES": "Загрузка правил",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Унаследованные правила будут выполняться в первую очередь",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Все правила, привязанные из этого набора правил, отключены"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Выберите папку, из которой будут взяты правила",
|
||||
"CANCEL": "Отмена",
|
||||
"SUBMIT": "Выбрать папку",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Нет приобретенных правил",
|
||||
"SUBTITLE": "Выбранная папка не имеет собственных правил."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Список правил, которые будут привязаны",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Ошибка при попытке создать ссылку на набор правил"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Ошибка при попытке удалить ссылку из набора правил"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "Hantera regler"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "Avbryt",
|
||||
"CREATE": "Skapa",
|
||||
"CREATE_TITLE": "Skapa en regel",
|
||||
"UPDATE": "Uppdatera",
|
||||
"UPDATE_TITLE": "Redigera en regel"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "Namn",
|
||||
"DESCRIPTION": "Beskrivning",
|
||||
"WHEN": "När",
|
||||
"PERFORM_ACTIONS": "Utför åtgärder",
|
||||
"OPTIONS": "Andra alternativ"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "Ange regelnamn",
|
||||
"DESCRIPTION": "Ange regelbeskrivning",
|
||||
"NO_DESCRIPTION": "Ingen beskrivning",
|
||||
"VALUE": "Värde",
|
||||
"CHOOSE_FOLDER": "Välj destinationsmapp"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "Det här fältet krävs",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "En eller flera villkorsgrupper är tomma",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "Minst en utlösare krävs"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "Objekt skapas eller gå in i den här mappen",
|
||||
"UPDATE": "Objekt uppdateras",
|
||||
"OUTBOUND": "Objekt tas bort eller lämna den här mappen"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "Regel för undermappar",
|
||||
"IS_ASYNCHRONOUS": "Kör regel i bakgrunden",
|
||||
"DISABLE_RULE": "Inaktivera regel",
|
||||
"ERROR_SCRIPT": "Kör skript om fel inträffar",
|
||||
"NO_SCRIPT": "Ingen"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) Är lika med",
|
||||
"CONTAINS": "Innehåller",
|
||||
"STARTS_WITH": "Börjar med",
|
||||
"ENDS_WITH": "Slutar med",
|
||||
"GREATER_THAN": "(>) Större än",
|
||||
"LESS_THAN": "(<) Mindre än",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) Större än eller lika med",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) Mindre än eller lika",
|
||||
"ON": "(=) På",
|
||||
"AFTER": "(>) Efter",
|
||||
"BEFORE": "(<) Innan",
|
||||
"ON_OR_AFTER": "(>=) På eller efter",
|
||||
"ON_OR_BEFORE": "(<=) På eller innan",
|
||||
"INSTANCE_OF": "(=) Är"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "Namn",
|
||||
"SIZE": "Storlek",
|
||||
"MIMETYPE": "Mime-typ",
|
||||
"ENCODING": "Kodning",
|
||||
"HAS_CATEGORY": "Har en kategori",
|
||||
"HAS_TAG": "Har en tagg",
|
||||
"HAS_ASPECT": "Har en aspekt"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "Om",
|
||||
"NOT_IF": "INTE om",
|
||||
"AND": "Och",
|
||||
"OR": "Eller"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "Lägg till villkor",
|
||||
"ADD_GROUP": "Lägg till villkorsgrupp",
|
||||
"REMOVE": "Ta bort"
|
||||
},
|
||||
"NO_CONDITIONS": "Inga villkor",
|
||||
"NO_CONDITIONS_IN_GROUP": "Inga villkor i gruppen",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "Lägg till åtgärd",
|
||||
"REMOVE": "Ta bort"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "Välj en åtgärd"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "regler"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "Skapa en regel",
|
||||
"LINK_RULES": "Länkregler",
|
||||
"EDIT_RULE": "Redigera",
|
||||
"SEE_IN_FOLDER": "Se i mappen",
|
||||
"INHERIT_RULES": "Ärv regler"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Listan är tom",
|
||||
"SUBTITLE": "Det finns inga regler definierade för den här mappen ännu."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Radera regel",
|
||||
"MESSAGE": "Vill du verkligen radera den här regeln?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "Ta bort regeluppsättningslänk",
|
||||
"MESSAGE": "Är du säker på att du vill ta bort länken till denna regeluppsättning?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "Regler från aktuell mapp",
|
||||
"LINKED_RULES": "Regler från länkad mapp",
|
||||
"INHERITED_RULES": "Ärvda regler",
|
||||
"LOAD_MORE_RULE_SETS": "Läs in regler från andra mappar",
|
||||
"LOAD_MORE_RULES": "Ladda fler regler",
|
||||
"LOADING_RULES": "Laddar regler",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Ärvda regler körs först",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "Alla regler som är länkade från den här regeluppsättningen är inaktiverade"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "Välj en mapp att länka regler från",
|
||||
"CANCEL": "Avbryt",
|
||||
"SUBMIT": "Välj mapp",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "Inga ägda regler",
|
||||
"SUBTITLE": "Den valda mappen har inga egna regler."
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "Lista över regler som kommer att länkas",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "Det gick inte att skapa en länk till en regeluppsättning"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "Det gick inte att ta bort en länk från en regeluppsättning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"ACA_FOLDER_RULES": {
|
||||
"ACTIONS": {
|
||||
"MANAGE_RULES": "管理规则"
|
||||
},
|
||||
"EDIT_RULE_DIALOG": {
|
||||
"CANCEL": "取消",
|
||||
"CREATE": "创建",
|
||||
"CREATE_TITLE": "创建规则",
|
||||
"UPDATE": "更新",
|
||||
"UPDATE_TITLE": "编辑一个规则"
|
||||
},
|
||||
"RULE_DETAILS": {
|
||||
"LABEL": {
|
||||
"NAME": "名称",
|
||||
"DESCRIPTION": "说明",
|
||||
"WHEN": "什么时候",
|
||||
"PERFORM_ACTIONS": "执行操作",
|
||||
"OPTIONS": "其他选项"
|
||||
},
|
||||
"PLACEHOLDER": {
|
||||
"NAME": "输入规则名称",
|
||||
"DESCRIPTION": "输入规则描述",
|
||||
"NO_DESCRIPTION": "没有描述",
|
||||
"VALUE": "值",
|
||||
"CHOOSE_FOLDER": "选择目标文件夹"
|
||||
},
|
||||
"ERROR": {
|
||||
"REQUIRED": "此字段为必填字段",
|
||||
"RULE_COMPOSITE_CONDITION_INVALID": "一个或多个条件组为空",
|
||||
"INSUFFICIENT_TRIGGERS_SELECTED": "至少需要一个触发器"
|
||||
},
|
||||
"TRIGGERS": {
|
||||
"INBOUND": "项目被创建或进入此文件夹",
|
||||
"UPDATE": "项目被更新",
|
||||
"OUTBOUND": "项目被删除或离开此文件夹"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"IS_INHERITABLE": "规则适用于子文件夹",
|
||||
"IS_ASYNCHRONOUS": "在后台运行规则",
|
||||
"DISABLE_RULE": "禁用规则",
|
||||
"ERROR_SCRIPT": "如果发生错误则运行脚本",
|
||||
"NO_SCRIPT": "无"
|
||||
},
|
||||
"COMPARATORS": {
|
||||
"EQUALS": "(=) 等于",
|
||||
"CONTAINS": "包含",
|
||||
"STARTS_WITH": "开始于",
|
||||
"ENDS_WITH": "结束于",
|
||||
"GREATER_THAN": "(>) 大于",
|
||||
"LESS_THAN": "(<) 小于",
|
||||
"GREATER_THAN_OR_EQUAL": "(>=) 大于或等于",
|
||||
"LESS_THAN_OR_EQUAL": "(<=) 小于或等于",
|
||||
"ON": "(=) 当天",
|
||||
"AFTER": "(>) 之后",
|
||||
"BEFORE": "(<) 之后",
|
||||
"ON_OR_AFTER": "(>=) 当天或之后",
|
||||
"ON_OR_BEFORE": "(<=) 当天或之前",
|
||||
"INSTANCE_OF": "(=) 是"
|
||||
},
|
||||
"FIELDS": {
|
||||
"NAME": "名称",
|
||||
"SIZE": "大小",
|
||||
"MIMETYPE": "MIME 类型",
|
||||
"ENCODING": "编码",
|
||||
"HAS_CATEGORY": "有类别",
|
||||
"HAS_TAG": "有标签",
|
||||
"HAS_ASPECT": "有纵横比"
|
||||
},
|
||||
"LOGIC_OPERATORS": {
|
||||
"IF": "如果",
|
||||
"NOT_IF": "除非",
|
||||
"AND": "与",
|
||||
"OR": "或"
|
||||
},
|
||||
"CONDITION_BUTTONS": {
|
||||
"ADD_CONDITION": "添加条件",
|
||||
"ADD_GROUP": "添加条件组",
|
||||
"REMOVE": "删除"
|
||||
},
|
||||
"NO_CONDITIONS": "无条件",
|
||||
"NO_CONDITIONS_IN_GROUP": "组中无条件",
|
||||
"ACTION_BUTTONS": {
|
||||
"ADD_ACTION": "添加操作",
|
||||
"REMOVE": "删除"
|
||||
},
|
||||
"ACTION_SELECT_PLACEHOLDER": "选择一个操作"
|
||||
},
|
||||
"MANAGE_RULES": {
|
||||
"TOOLBAR": {
|
||||
"BREADCRUMB": {
|
||||
"RULES": "规则"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"CREATE_RULE": "创建规则",
|
||||
"LINK_RULES": "链接规则",
|
||||
"EDIT_RULE": "编辑",
|
||||
"SEE_IN_FOLDER": "在文件夹中查看",
|
||||
"INHERIT_RULES": "继承规则"
|
||||
}
|
||||
},
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "此列表为空",
|
||||
"SUBTITLE": "尚未为此文件夹定义规则。"
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "删除规则",
|
||||
"MESSAGE": "是否确定要删除此规则?"
|
||||
},
|
||||
"DELETE_RULE_SET_LINK": {
|
||||
"TITLE": "删除规则集链接",
|
||||
"MESSAGE": "是否确定要删除此规则集的链接?"
|
||||
}
|
||||
},
|
||||
"RULE_LIST": {
|
||||
"OWNED_RULES": "来自当前文件夹的规则",
|
||||
"LINKED_RULES": "来自链接文件夹的规则",
|
||||
"INHERITED_RULES": "继承的规则",
|
||||
"LOAD_MORE_RULE_SETS": "加载来自其他文件夹的规则",
|
||||
"LOAD_MORE_RULES": "加载更多规则",
|
||||
"LOADING_RULES": "正在加载规则",
|
||||
"INHERITED_RULES_WILL_BE_RUN_FIRST": "首先运行继承的规则",
|
||||
"ALL_LINKED_RULES_ARE_DISABLED": "禁用从此规则集链接的所有规则"
|
||||
},
|
||||
"LINK_RULES_DIALOG": {
|
||||
"TITLE": "选择要从中链接规则的文件夹",
|
||||
"CANCEL": "取消",
|
||||
"SUBMIT": "选择文件夹",
|
||||
"EMPTY_RULES_LIST": {
|
||||
"TITLE": "无自定义规则",
|
||||
"SUBTITLE": "所选文件夹不包含任何自定义规则。"
|
||||
},
|
||||
"LIST_OF_RULES_TO_LINK": "将链接的规则列表",
|
||||
"ERRORS": {
|
||||
"REQUEST_FAILED": "创建规则集链接时出错"
|
||||
}
|
||||
},
|
||||
"ERRORS": {
|
||||
"DELETE_RULE_SET_LINK_FAILED": "删除规则集链接时出错"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { CoreModule, TranslationService } from '@alfresco/adf-core';
|
||||
import { ExtensionService, ExtensionsModule, provideExtensionConfig } from '@alfresco/adf-extensions';
|
||||
import { NgModule } from '@angular/core';
|
||||
import * as rules from './folder-rules.rules';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { EditRuleDialogUiComponent } from './rule-details/edit-rule-dialog.ui-component';
|
||||
import { ManageRulesSmartComponent } from './manage-rules/manage-rules.smart-component';
|
||||
import { RuleCompositeConditionUiComponent } from './rule-details/conditions/rule-composite-condition.ui-component';
|
||||
import { RuleDetailsUiComponent } from './rule-details/rule-details.ui-component';
|
||||
import { RuleSimpleConditionUiComponent } from './rule-details/conditions/rule-simple-condition.ui-component';
|
||||
import { GenericErrorComponent, PageLayoutModule } from '@alfresco/aca-shared';
|
||||
import { BreadcrumbModule, ContentNodeSelectorModule, DocumentListModule } from '@alfresco/adf-content-services';
|
||||
import { RuleListItemUiComponent } from './rule-list/rule-list-item/rule-list-item.ui-component';
|
||||
import { RuleListGroupingUiComponent } from './rule-list/rule-list-grouping/rule-list-grouping.ui-component';
|
||||
import { RuleTriggersUiComponent } from './rule-details/triggers/rule-triggers.ui-component';
|
||||
import { RuleOptionsUiComponent } from './rule-details/options/rule-options.ui-component';
|
||||
import { RuleActionListUiComponent } from './rule-details/actions/rule-action-list.ui-component';
|
||||
import { RuleActionUiComponent } from './rule-details/actions/rule-action.ui-component';
|
||||
import { RuleListUiComponent } from './rule-list/rule-list/rule-list.ui-component';
|
||||
import { RuleSetPickerSmartComponent } from './rule-set-picker/rule-set-picker.smart-component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'rules',
|
||||
component: ManageRulesSmartComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
providers: [provideExtensionConfig(['folder-rules.plugin.json'])],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild(routes),
|
||||
CoreModule.forChild(),
|
||||
PageLayoutModule,
|
||||
BreadcrumbModule,
|
||||
DocumentListModule,
|
||||
ExtensionsModule,
|
||||
ContentNodeSelectorModule,
|
||||
GenericErrorComponent
|
||||
],
|
||||
declarations: [
|
||||
EditRuleDialogUiComponent,
|
||||
ManageRulesSmartComponent,
|
||||
RuleActionListUiComponent,
|
||||
RuleActionUiComponent,
|
||||
RuleCompositeConditionUiComponent,
|
||||
RuleDetailsUiComponent,
|
||||
RuleListGroupingUiComponent,
|
||||
RuleListItemUiComponent,
|
||||
RuleListUiComponent,
|
||||
RuleSetPickerSmartComponent,
|
||||
RuleSimpleConditionUiComponent,
|
||||
RuleTriggersUiComponent,
|
||||
RuleOptionsUiComponent
|
||||
]
|
||||
})
|
||||
export class AcaFolderRulesModule {
|
||||
constructor(translation: TranslationService, extensions: ExtensionService) {
|
||||
translation.addTranslationFolder('folder-rules', 'assets/folder-rules');
|
||||
|
||||
extensions.setEvaluators({
|
||||
'rules.canManageFolderRules': rules.canManageFolderRules
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AcaRuleContext } from '@alfresco/aca-shared/rules';
|
||||
import { isFolderRulesEnabled, canManageFolderRules } from './folder-rules.rules';
|
||||
|
||||
describe('Folder Rules Visibility Rules', () => {
|
||||
describe('isFolderRulesEnabled', () => {
|
||||
it('should have the feature enabled', () => {
|
||||
const context: any = {
|
||||
appConfig: {
|
||||
get: () => true
|
||||
}
|
||||
};
|
||||
|
||||
const result = isFolderRulesEnabled(context);
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should have the feature disabled', () => {
|
||||
const context: any = {
|
||||
appConfig: {
|
||||
get: () => false
|
||||
}
|
||||
};
|
||||
|
||||
const result = isFolderRulesEnabled(context);
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canManageFolderRules', () => {
|
||||
let context: AcaRuleContext;
|
||||
|
||||
beforeEach(() => {
|
||||
context = {
|
||||
appConfig: {
|
||||
get: () => true
|
||||
},
|
||||
selection: {
|
||||
folder: {} as any
|
||||
},
|
||||
navigation: {
|
||||
url: '/personal-files'
|
||||
},
|
||||
permissions: {
|
||||
check: () => true
|
||||
}
|
||||
} as any;
|
||||
});
|
||||
|
||||
it('should allow creating a rule for the selected folder', () => {
|
||||
const result = canManageFolderRules(context);
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not allow creating a rule if no folder selected', () => {
|
||||
context.selection.folder = null;
|
||||
|
||||
const result = canManageFolderRules(context);
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AcaRuleContext, hasFolderSelected, canEditFolder, isNotFavorites } from '@alfresco/aca-shared/rules';
|
||||
|
||||
export const isFolderRulesEnabled = (context: AcaRuleContext) => context.appConfig.get<boolean>('plugins.folderRules', false);
|
||||
export const isFolderRulesAllowed = (context: AcaRuleContext) =>
|
||||
isFolderRulesEnabled(context) && canEditFolder(context) && hasFolderSelected(context) && isNotFavorites(context);
|
||||
|
||||
export const canManageFolderRules = (context: AcaRuleContext): boolean => isFolderRulesAllowed(context);
|
||||
@@ -0,0 +1,140 @@
|
||||
<aca-page-layout>
|
||||
|
||||
<aca-page-layout-header>
|
||||
<adf-toolbar class="adf-toolbar--inline">
|
||||
<button mat-icon-button (click)="goBack()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
</adf-toolbar>
|
||||
<adf-breadcrumb root="{{'ACA_FOLDER_RULES.ACTIONS.MANAGE_RULES' | translate}}"></adf-breadcrumb>
|
||||
</aca-page-layout-header>
|
||||
|
||||
<aca-page-layout-content>
|
||||
<div class="main-content">
|
||||
|
||||
<ng-container *ngIf="((ruleSetsLoading$ | async) && (inheritedRuleSets$ | async).length === 0) || (actionsLoading$ | async); else onLoaded">
|
||||
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #onLoaded>
|
||||
<ng-container *ngIf="folderInfo$ | async; else genericError">
|
||||
<adf-toolbar class="adf-toolbar--inline aca-manage-rules__actions-bar">
|
||||
|
||||
<adf-toolbar-title class="aca-manage-rules__actions-bar__title">
|
||||
<mat-icon class="icon-aligner">folder</mat-icon>
|
||||
<adf-breadcrumb root="{{ (folderInfo$ | async).name }}:{{'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.BREADCRUMB.RULES' | translate}}"
|
||||
class="aca-manage-rules__actions-bar__title__breadcrumb"></adf-breadcrumb>
|
||||
</adf-toolbar-title>
|
||||
|
||||
<mat-slide-toggle
|
||||
data-automation-id="manage-rules-inheritance-toggle-button"
|
||||
[checked]="isInheritanceEnabled"
|
||||
(change)="onInheritanceToggleChange($event)"
|
||||
[disabled]="isInheritanceToggleDisabled"
|
||||
[labelPosition]="'before'">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.INHERIT_RULES' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<mat-divider vertical class="vertical-divider"></mat-divider>
|
||||
|
||||
<div class="aca-manage-rules__actions-bar__buttons">
|
||||
<button
|
||||
*ngIf="!(mainRuleSet$ | async)"
|
||||
data-automation-id="manage-rules-link-button"
|
||||
mat-stroked-button
|
||||
(click)="openLinkRulesDialog()">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.LINK_RULES' | translate }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="canEditMainRule"
|
||||
data-automation-id="manage-rules-create-button"
|
||||
mat-flat-button color="primary"
|
||||
(click)="openCreateUpdateRuleDialog()">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.CREATE_RULE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</adf-toolbar>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="aca-manage-rules__container" *ngIf="isMainRuleSetNotEmpty || isInheritedRuleSetsNotEmpty; else emptyContent">
|
||||
<aca-rule-list
|
||||
[mainRuleSet]="mainRuleSet$ | async"
|
||||
[folderId]="nodeId"
|
||||
[inheritedRuleSets]="inheritedRuleSets$ | async"
|
||||
[hasMoreRuleSets]="hasMoreRuleSets$ | async"
|
||||
[ruleSetsLoading]="ruleSetsLoading$ | async"
|
||||
[selectedRule]="selectedRule$ | async"
|
||||
(loadMoreRuleSets)="onLoadMoreRuleSets()"
|
||||
(loadMoreRules)="onLoadMoreRules($event)"
|
||||
(selectRule)="onSelectRule($event)"
|
||||
(ruleEnabledChanged)="onRuleEnabledToggle($event[0], $event[1])"
|
||||
(ruleSetEditLinkClicked)="openLinkRulesDialog($event)"
|
||||
(ruleSetUnlinkClicked)="onRuleSetUnlinkClicked($event)">
|
||||
</aca-rule-list>
|
||||
|
||||
<div class="aca-manage-rules__container__rule-details">
|
||||
|
||||
<div class="aca-manage-rules__container__rule-details__header" *ngIf="(selectedRule$ | async) as selectedRule">
|
||||
<div class="aca-manage-rules__container__rule-details__header__title">
|
||||
<div class="aca-manage-rules__container__rule-details__header__title__name">
|
||||
{{ selectedRule.name }}
|
||||
</div>
|
||||
<div class="aca-manage-rules__container__rule-details__header__title__description">
|
||||
{{ selectedRule.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-manage-rules__container__rule-details__header__buttons">
|
||||
<ng-container *ngIf="canEditSelectedRule; else goToFolderButton">
|
||||
<button mat-stroked-button (click)="onRuleDeleteButtonClicked(selectedRule)" id="delete-rule-btn">
|
||||
<mat-icon>delete_outline</mat-icon>
|
||||
</button>
|
||||
<button mat-stroked-button (click)="openCreateUpdateRuleDialog(selectedRule)" id="edit-rule-btn">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.EDIT_RULE' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #goToFolderButton>
|
||||
<button mat-stroked-button [routerLink]="['/nodes', (selectedRuleSet$ | async).owningFolder.id, 'rules']">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.SEE_IN_FOLDER' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-manage-rules__container__rule-details__content" *ngIf="(selectedRule$ | async) as selectedRule">
|
||||
<aca-rule-details
|
||||
[actionDefinitions]="actionDefinitions$ | async"
|
||||
[readOnly]="true"
|
||||
[preview]="true"
|
||||
[value]="selectedRule"
|
||||
[nodeId]="nodeId">
|
||||
</aca-rule-details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #emptyContent>
|
||||
<adf-empty-content
|
||||
icon="library_books"
|
||||
[title]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.TITLE' | translate"
|
||||
[subtitle]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.SUBTITLE' | translate"
|
||||
>
|
||||
</adf-empty-content>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #genericError>
|
||||
<aca-page-layout-error>
|
||||
<aca-generic-error></aca-generic-error>
|
||||
</aca-page-layout-error>
|
||||
</ng-template>
|
||||
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</aca-page-layout-content>
|
||||
|
||||
</aca-page-layout>
|
||||
@@ -0,0 +1,103 @@
|
||||
.aca-manage-rules {
|
||||
&__actions-bar {
|
||||
padding: 0 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__title {
|
||||
align-items: center;
|
||||
width: unset !important;
|
||||
flex: 1;
|
||||
|
||||
&__breadcrumb {
|
||||
margin-left: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mat-slide-toggle-label {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--adf-breadcrumb-item-active-color);
|
||||
}
|
||||
|
||||
.vertical-divider {
|
||||
height: 50%;
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(250px, 1fr) 3fr;
|
||||
padding: 20px;
|
||||
gap: 12px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
&__rule-details {
|
||||
border: 1px solid var(--theme-border-color);
|
||||
border-radius: 12px;
|
||||
overflow-y: auto;
|
||||
|
||||
&__header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
overflow-x: auto;
|
||||
justify-content: space-between;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid var(--theme-border-color);
|
||||
background-color: var(--theme-background-color);
|
||||
column-gap: 20px;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
word-wrap: break-word;
|
||||
min-width: 100px;
|
||||
|
||||
&__name {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
gap: 4px;
|
||||
|
||||
button {
|
||||
color: var(--theme-text-color);
|
||||
|
||||
&#delete-rule-btn {
|
||||
padding: 0 8px;
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
// Something pops out of this button for some reason so this is necessary
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AcaFolderRulesModule } from '../folder-rules.module';
|
||||
import { ManageRulesSmartComponent } from './manage-rules.smart-component';
|
||||
import { DebugElement, Predicate } from '@angular/core';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { FolderRulesService } from '../services/folder-rules.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BehaviorSubject, of, Subject } from 'rxjs';
|
||||
import {
|
||||
inheritedRuleSetMock,
|
||||
inheritedRuleSetWithEmptyRulesMock,
|
||||
inheritedRuleSetWithOnlyDisabledRulesMock,
|
||||
ownedRuleSetMock,
|
||||
ruleSetWithLinkMock
|
||||
} from '../mock/rule-sets.mock';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { getOwningFolderEntryMock, owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActionsService } from '../services/actions.service';
|
||||
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
||||
import { ruleMock, ruleSettingsMock } from '../mock/rules.mock';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppService } from '@alfresco/aca-shared';
|
||||
|
||||
describe('ManageRulesSmartComponent', () => {
|
||||
let fixture: ComponentFixture<ManageRulesSmartComponent>;
|
||||
let component: ManageRulesSmartComponent;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
let folderRuleSetsService: FolderRuleSetsService;
|
||||
let folderRulesService: FolderRulesService;
|
||||
let actionsService: ActionsService;
|
||||
let callApiSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule, AcaFolderRulesModule],
|
||||
providers: [
|
||||
{
|
||||
provide: AppService,
|
||||
useValue: {
|
||||
appNavNarMode$: new BehaviorSubject('expanded'),
|
||||
toggleAppNavBar$: new Subject()
|
||||
}
|
||||
},
|
||||
FolderRuleSetsService,
|
||||
FolderRulesService,
|
||||
{ provide: Store, useValue: { dispatch: () => {} } },
|
||||
{ provide: ActivatedRoute, useValue: { params: of({ nodeId: owningFolderIdMock }) } }
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(ManageRulesSmartComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
|
||||
folderRuleSetsService = TestBed.inject(FolderRuleSetsService);
|
||||
folderRulesService = TestBed.inject(FolderRulesService);
|
||||
actionsService = TestBed.inject(ActionsService);
|
||||
|
||||
spyOn(actionsService, 'loadActionDefinitions').and.stub();
|
||||
spyOn(folderRulesService, 'getRuleSettings').and.returnValue(Promise.resolve(ruleSettingsMock));
|
||||
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi');
|
||||
callApiSpy
|
||||
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET')
|
||||
.and.returnValue(Promise.resolve(ownedRuleSetMock))
|
||||
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')
|
||||
.and.returnValue(Promise.resolve(ownedRuleSetMock))
|
||||
.withArgs(`/nodes/${owningFolderIdMock}?include=path%2Cproperties%2CallowableOperations%2Cpermissions`, 'GET')
|
||||
.and.returnValue(Promise.resolve(getOwningFolderEntryMock));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should show a list of rule sets and rules', () => {
|
||||
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.stub();
|
||||
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
expect(loadRuleSetsSpy).toHaveBeenCalledOnceWith(component.nodeId);
|
||||
|
||||
const ruleGroupingSections = debugElement.queryAll(By.css(`[data-automation-id="rule-list-item"]`));
|
||||
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
||||
|
||||
expect(ruleGroupingSections.length).toBe(2, 'unexpected number of rule sections');
|
||||
expect(rules.length).toBe(4, 'unexpected number of aca-rule-list-item');
|
||||
expect(ruleDetails).toBeTruthy('aca-rule-details was not rendered');
|
||||
expect(deleteRuleBtn).toBeTruthy('no delete rule button');
|
||||
});
|
||||
|
||||
it('should show adf-empty-content if node has no rules defined yet', () => {
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetWithEmptyRulesMock]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
const adfEmptyContent = debugElement.query(By.css('adf-empty-content'));
|
||||
const ruleSets = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
|
||||
expect(adfEmptyContent).toBeTruthy();
|
||||
expect(ruleSets.length).toBe(0);
|
||||
expect(ruleDetails).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show adf-empty-content if there are only inherited disabled rules', () => {
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetWithOnlyDisabledRulesMock]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
const adfEmptyContent = debugElement.query(By.css('adf-empty-content'));
|
||||
const ruleSets = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
|
||||
expect(adfEmptyContent).toBeTruthy();
|
||||
expect(ruleSets.length).toBe(0);
|
||||
expect(ruleDetails).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should only show aca-generic-error if the non-existing node was provided', () => {
|
||||
folderRuleSetsService.folderInfo$ = of(null);
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
const acaGenericError = debugElement.query(By.css('aca-generic-error'));
|
||||
const rules = debugElement.query(By.css('.aca-rule-list-item'));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
|
||||
expect(acaGenericError).toBeTruthy();
|
||||
expect(rules).toBeFalsy();
|
||||
expect(ruleDetails).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should only show progress bar while loading', async () => {
|
||||
folderRuleSetsService.folderInfo$ = of(null);
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||
folderRuleSetsService.isLoading$ = of(true);
|
||||
actionsService.loading$ = of(true);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
const matProgressBar = debugElement.query(By.css('mat-progress-bar'));
|
||||
const rules = debugElement.query(By.css('.aca-rule-list-item'));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
|
||||
expect(matProgressBar).toBeTruthy();
|
||||
expect(rules).toBeFalsy();
|
||||
expect(ruleDetails).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should call deleteRule() if confirmation dialog returns true', () => {
|
||||
const dialog = TestBed.inject(MatDialog);
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
const onRuleDeleteButtonClickedSpy = spyOn(component, 'onRuleDeleteButtonClicked').and.callThrough();
|
||||
|
||||
const dialogResult: any = {
|
||||
afterClosed: () => of(true)
|
||||
};
|
||||
const dialogOpenSpy = spyOn(dialog, 'open').and.returnValue(dialogResult);
|
||||
const deleteRuleSpy = spyOn(folderRulesService, 'deleteRule');
|
||||
const onRuleDeleteSpy = spyOn(component, 'onRuleDelete').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(component).toBeTruthy('expected component');
|
||||
|
||||
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
const deleteRuleBtn = fixture.debugElement.nativeElement.querySelector('#delete-rule-btn');
|
||||
|
||||
deleteRuleBtn.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
folderRulesService.deletedRuleId$ = of('owned-rule-1-id');
|
||||
|
||||
expect(onRuleDeleteButtonClickedSpy).toHaveBeenCalled();
|
||||
expect(dialogOpenSpy).toHaveBeenCalled();
|
||||
expect(deleteRuleSpy).toHaveBeenCalled();
|
||||
expect(onRuleDeleteSpy).toHaveBeenCalledTimes(1);
|
||||
expect(rules).toBeTruthy('expected rules');
|
||||
expect(ruleDetails).toBeTruthy('expected ruleDetails');
|
||||
expect(deleteRuleBtn).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Create rule & link rules buttons visibility', () => {
|
||||
let createButtonPredicate: Predicate<DebugElement>;
|
||||
let linkButtonPredicate: Predicate<DebugElement>;
|
||||
|
||||
beforeEach(() => {
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
actionsService.loading$ = of(false);
|
||||
|
||||
createButtonPredicate = By.css(`[data-automation-id="manage-rules-create-button"]`);
|
||||
linkButtonPredicate = By.css(`[data-automation-id="manage-rules-link-button"]`);
|
||||
});
|
||||
|
||||
it('should show the create rule button if there is no main rule set', () => {
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const createButton = debugElement.query(createButtonPredicate);
|
||||
expect(createButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show the link rules button if there is no main rule set', () => {
|
||||
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const linkButton = debugElement.query(linkButtonPredicate);
|
||||
expect(linkButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show the create rule button if the main rule set is owned', () => {
|
||||
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
const createButton = debugElement.query(createButtonPredicate);
|
||||
expect(createButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not show the create rule button if the main rule set is linked', () => {
|
||||
folderRuleSetsService.mainRuleSet$ = of(ruleSetWithLinkMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
const createButton = debugElement.query(createButtonPredicate);
|
||||
expect(createButton).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not show the link rules button if the folder has a main rule set', () => {
|
||||
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
const linkButton = debugElement.query(linkButtonPredicate);
|
||||
expect(linkButton).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rule inheritance toggle button', () => {
|
||||
beforeEach(() => {
|
||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||
folderRuleSetsService.isLoading$ = of(false);
|
||||
actionsService.loading$ = of(false);
|
||||
});
|
||||
|
||||
it('should show inherit rules toggle button, and disable it when isInheritanceToggleDisabled = true', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-inheritance-toggle-button"]`));
|
||||
expect(createButton).toBeTruthy();
|
||||
|
||||
component.isInheritanceToggleDisabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(createButton.nativeNode.classList).toContain('mat-disabled');
|
||||
});
|
||||
|
||||
it('should call onInheritanceToggleChange() on change', () => {
|
||||
const onInheritanceToggleChangeSpy = spyOn(component, 'onInheritanceToggleChange').and.callThrough();
|
||||
const updateRuleSettingsSpy = spyOn(folderRulesService, 'updateRuleSettings').and.returnValue(Promise.resolve(ruleSettingsMock));
|
||||
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const inheritanceToggleBtn = fixture.debugElement.query(By.css(`[data-automation-id="manage-rules-inheritance-toggle-button"]`));
|
||||
|
||||
inheritanceToggleBtn.nativeElement.dispatchEvent(new Event('change'));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(onInheritanceToggleChangeSpy).toHaveBeenCalled();
|
||||
expect(updateRuleSettingsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(loadRuleSetsSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,272 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { FolderRulesService } from '../services/folder-rules.service';
|
||||
import { Observable, Subject, Subscription } from 'rxjs';
|
||||
import { Rule } from '../model/rule.model';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||
import { delay, takeUntil } from 'rxjs/operators';
|
||||
import { EditRuleDialogUiComponent } from '../rule-details/edit-rule-dialog.ui-component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||
import { ActionsService } from '../services/actions.service';
|
||||
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
||||
import { RuleSet } from '../model/rule-set.model';
|
||||
import { RuleSetPickerSmartComponent } from '../rule-set-picker/rule-set-picker.smart-component';
|
||||
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-manage-rules',
|
||||
templateUrl: 'manage-rules.smart-component.html',
|
||||
styleUrls: ['manage-rules.smart-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
host: { class: 'aca-manage-rules' }
|
||||
})
|
||||
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
||||
nodeId = '';
|
||||
isInheritanceEnabled = true;
|
||||
isInheritanceToggleDisabled = false;
|
||||
|
||||
mainRuleSet$: Observable<RuleSet>;
|
||||
inheritedRuleSets$: Observable<RuleSet[]>;
|
||||
selectedRule$: Observable<Rule>;
|
||||
selectedRuleSet$: Observable<RuleSet>;
|
||||
hasMoreRuleSets$: Observable<boolean>;
|
||||
ruleSetsLoading$: Observable<boolean>;
|
||||
folderInfo$: Observable<NodeInfo>;
|
||||
|
||||
actionsLoading$: Observable<boolean>;
|
||||
actionDefinitions$: Observable<ActionDefinitionTransformed[]>;
|
||||
parameterConstraints$: Observable<ActionParameterConstraint[]>;
|
||||
canEditMainRule = false;
|
||||
canEditSelectedRule = false;
|
||||
isMainRuleSetNotEmpty = false;
|
||||
isInheritedRuleSetsNotEmpty = false;
|
||||
|
||||
private destroyed$ = new Subject<void>();
|
||||
private _actionDefinitionsSub: Subscription;
|
||||
|
||||
constructor(
|
||||
private location: Location,
|
||||
private folderRulesService: FolderRulesService,
|
||||
private route: ActivatedRoute,
|
||||
private matDialogService: MatDialog,
|
||||
private notificationService: NotificationService,
|
||||
private actionsService: ActionsService,
|
||||
private folderRuleSetsService: FolderRuleSetsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.mainRuleSet$ = this.folderRuleSetsService.mainRuleSet$;
|
||||
this.inheritedRuleSets$ = this.folderRuleSetsService.inheritedRuleSets$;
|
||||
this.selectedRule$ = this.folderRulesService.selectedRule$;
|
||||
this.selectedRuleSet$ = this.folderRuleSetsService.selectedRuleSet$;
|
||||
this.hasMoreRuleSets$ = this.folderRuleSetsService.hasMoreRuleSets$;
|
||||
this.ruleSetsLoading$ = this.folderRuleSetsService.isLoading$;
|
||||
this.folderInfo$ = this.folderRuleSetsService.folderInfo$;
|
||||
|
||||
this.actionsLoading$ = this.actionsService.loading$.pipe(delay(0));
|
||||
this.actionDefinitions$ = this.actionsService.actionDefinitionsListing$;
|
||||
this.parameterConstraints$ = this.actionsService.parameterConstraints$;
|
||||
|
||||
this.folderRulesService.deletedRuleId$.pipe(takeUntil(this.destroyed$)).subscribe((deletedRuleId) => this.onRuleDelete(deletedRuleId));
|
||||
|
||||
this.actionsService.loadActionDefinitions();
|
||||
|
||||
this.route.params.subscribe((params) => {
|
||||
this.nodeId = params.nodeId;
|
||||
if (this.nodeId) {
|
||||
this.folderRulesService.getRuleSettings(this.nodeId).then((ruleSettings) => {
|
||||
this.isInheritanceEnabled = ruleSettings.value;
|
||||
});
|
||||
|
||||
this.folderRuleSetsService.loadRuleSets(this.nodeId);
|
||||
}
|
||||
});
|
||||
|
||||
this._actionDefinitionsSub = this.actionDefinitions$.subscribe((actionDefinitions: ActionDefinitionTransformed[]) =>
|
||||
this.actionsService.loadActionParameterConstraints(actionDefinitions)
|
||||
);
|
||||
|
||||
this.mainRuleSet$.pipe(takeUntil(this.destroyed$)).subscribe((ruleSet) => {
|
||||
this.canEditMainRule = this.canEditRule(ruleSet);
|
||||
this.isMainRuleSetNotEmpty = !!ruleSet;
|
||||
});
|
||||
|
||||
this.inheritedRuleSets$.pipe(takeUntil(this.destroyed$)).subscribe((inheritedRuleSet) => {
|
||||
this.isInheritedRuleSetsNotEmpty = inheritedRuleSet.some((ruleSet) => ruleSet.rules.some((rule: Rule) => rule.isEnabled));
|
||||
});
|
||||
|
||||
this.selectedRuleSet$.pipe(takeUntil(this.destroyed$)).subscribe((ruleSet) => {
|
||||
this.canEditSelectedRule = this.canEditRule(ruleSet);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroyed$.next();
|
||||
this.destroyed$.complete();
|
||||
this._actionDefinitionsSub.unsubscribe();
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
onSelectRule(rule: Rule) {
|
||||
this.folderRulesService.selectRule(rule);
|
||||
}
|
||||
|
||||
openCreateUpdateRuleDialog(model = {}) {
|
||||
const dialogRef = this.matDialogService.open(EditRuleDialogUiComponent, {
|
||||
width: '90%',
|
||||
panelClass: 'aca-edit-rule-dialog-container',
|
||||
data: {
|
||||
model,
|
||||
nodeId: this.nodeId,
|
||||
parameterConstraints$: this.parameterConstraints$,
|
||||
actionDefinitions$: this.actionDefinitions$
|
||||
}
|
||||
});
|
||||
|
||||
this.onSubmitRuleDialog(dialogRef);
|
||||
}
|
||||
|
||||
onSubmitRuleDialog(dialogRef) {
|
||||
dialogRef.componentInstance.submitted.subscribe(async (rule) => {
|
||||
try {
|
||||
if (rule.id) {
|
||||
await this.onRuleUpdate(rule);
|
||||
} else {
|
||||
await this.onRuleCreate(rule);
|
||||
}
|
||||
dialogRef.close();
|
||||
} catch (error) {
|
||||
this.notificationService.showError(error.response.body.error.errorKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async onRuleUpdate(rule: Rule) {
|
||||
const newRule = await this.folderRulesService.updateRule(this.nodeId, rule.id, rule);
|
||||
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
|
||||
}
|
||||
|
||||
async onRuleCreate(ruleCreateParams: Partial<Rule>) {
|
||||
const newRule = await this.folderRulesService.createRule(this.nodeId, ruleCreateParams);
|
||||
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
|
||||
}
|
||||
|
||||
async onRuleEnabledToggle(rule: Rule, isEnabled: boolean) {
|
||||
await this.folderRulesService.updateRule(this.nodeId, rule.id, { ...rule, isEnabled });
|
||||
}
|
||||
|
||||
async onInheritanceToggleChange(event: MatSlideToggleChange) {
|
||||
this.isInheritanceToggleDisabled = true;
|
||||
const ruleSettings = await this.folderRulesService.updateRuleSettings(this.nodeId, '-isInheritanceEnabled-', { value: event.checked });
|
||||
this.isInheritanceEnabled = ruleSettings.value;
|
||||
this.folderRuleSetsService.loadRuleSets(this.nodeId);
|
||||
this.isInheritanceToggleDisabled = false;
|
||||
}
|
||||
|
||||
onRuleDeleteButtonClicked(rule: Rule) {
|
||||
this.matDialogService
|
||||
.open(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.TITLE',
|
||||
message: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.MESSAGE'
|
||||
},
|
||||
minWidth: '346px'
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
this.folderRulesService.deleteRule(this.nodeId, rule.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRuleDelete(deletedRuleId: string) {
|
||||
this.folderRuleSetsService.removeRuleFromMainRuleSet(deletedRuleId);
|
||||
}
|
||||
|
||||
onLoadMoreRuleSets() {
|
||||
this.folderRuleSetsService.loadMoreInheritedRuleSets();
|
||||
}
|
||||
|
||||
onLoadMoreRules(ruleSet: RuleSet) {
|
||||
this.folderRulesService.loadRules(ruleSet);
|
||||
}
|
||||
|
||||
canEditRule(ruleSet: RuleSet): boolean {
|
||||
return !ruleSet || FolderRuleSetsService.isOwnedRuleSet(ruleSet, this.nodeId);
|
||||
}
|
||||
|
||||
openLinkRulesDialog(existingRuleSet?: RuleSet) {
|
||||
this.matDialogService
|
||||
.open(RuleSetPickerSmartComponent, {
|
||||
width: '90%',
|
||||
panelClass: 'aca-rule-set-picker-container',
|
||||
data: {
|
||||
nodeId: this.nodeId,
|
||||
defaultNodeId: this.nodeId,
|
||||
existingRuleSet
|
||||
}
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
this.folderRuleSetsService.refreshMainRuleSet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRuleSetUnlinkClicked(linkedRuleSet: RuleSet) {
|
||||
this.matDialogService
|
||||
.open(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE_SET_LINK.TITLE',
|
||||
message: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE_SET_LINK.MESSAGE'
|
||||
},
|
||||
minWidth: '346px'
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(async (result) => {
|
||||
if (result) {
|
||||
try {
|
||||
await this.folderRuleSetsService.deleteRuleSetLink(this.nodeId, linkedRuleSet.id);
|
||||
this.folderRuleSetsService.refreshMainRuleSet();
|
||||
} catch (error) {
|
||||
this.notificationService.showError('ACA_FOLDER_RULES.ERRORS.DELETE_RULE_SET_LINK_FAILED');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
|
||||
|
||||
export const dummyConstraints: ActionParameterConstraint[] = [
|
||||
{
|
||||
name: 'aspect-name',
|
||||
constraints: [
|
||||
{
|
||||
value: 'cm:aspect1',
|
||||
label: 'Label 1'
|
||||
},
|
||||
{
|
||||
value: 'cm:aspect2',
|
||||
label: 'Label 2'
|
||||
},
|
||||
{
|
||||
value: 'cm:aspect3',
|
||||
label: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const rawConstraints = {
|
||||
entry: {
|
||||
constraintValues: [
|
||||
{
|
||||
value: 'cm:aspect1',
|
||||
label: 'Label 1'
|
||||
},
|
||||
{
|
||||
value: 'cm:aspect2',
|
||||
label: 'Label 2'
|
||||
},
|
||||
{
|
||||
value: 'cm:aspect3'
|
||||
}
|
||||
],
|
||||
constraintName: 'ac-aspects'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,221 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ActionDefinitionTransformed, ActionParameterDefinitionTransformed, RuleAction } from '../model/rule-action.model';
|
||||
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
|
||||
|
||||
export const actionDefListMock = {
|
||||
list: {
|
||||
pagination: {
|
||||
count: 2,
|
||||
hasMoreItems: false,
|
||||
totalItems: 2,
|
||||
skipCount: 0,
|
||||
maxItems: 100
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
applicableTypes: [],
|
||||
parameterDefinitions: [
|
||||
{
|
||||
name: 'mock-action-parameter-text',
|
||||
type: 'd:text',
|
||||
multiValued: false,
|
||||
mandatory: true,
|
||||
displayLabel: 'Mock action parameter text'
|
||||
},
|
||||
{
|
||||
name: 'mock-action-parameter-boolean',
|
||||
type: 'd:boolean',
|
||||
multiValued: false,
|
||||
mandatory: false
|
||||
},
|
||||
{
|
||||
name: 'aspect-name',
|
||||
type: 'd:qname',
|
||||
multiValued: false,
|
||||
mandatory: false,
|
||||
parameterConstraintName: 'ac-aspects'
|
||||
},
|
||||
{
|
||||
name: 'mock-action-parameter-noderef',
|
||||
type: 'd:noderef',
|
||||
multiValued: false,
|
||||
mandatory: false
|
||||
},
|
||||
{
|
||||
name: 'aspect-name',
|
||||
type: 'd:noderef',
|
||||
multiValued: false,
|
||||
mandatory: false
|
||||
}
|
||||
],
|
||||
name: 'mock-action-1-definition',
|
||||
trackStatus: false,
|
||||
description: 'This is a mock action',
|
||||
id: 'mock-action-1-definition',
|
||||
title: 'Action 1 title'
|
||||
},
|
||||
{
|
||||
applicableTypes: [],
|
||||
name: 'mock-action-2-definition',
|
||||
trackStatus: false,
|
||||
id: 'mock-action-2-definition'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const actionParam1TransformedMock: ActionParameterDefinitionTransformed = {
|
||||
name: 'mock-action-parameter-text',
|
||||
type: 'd:text',
|
||||
multiValued: false,
|
||||
mandatory: true,
|
||||
displayLabel: 'Mock action parameter text',
|
||||
parameterConstraintName: ''
|
||||
};
|
||||
|
||||
const actionParam2TransformedMock: ActionParameterDefinitionTransformed = {
|
||||
name: 'mock-action-parameter-boolean',
|
||||
type: 'd:boolean',
|
||||
multiValued: false,
|
||||
mandatory: false,
|
||||
displayLabel: 'mock-action-parameter-boolean',
|
||||
parameterConstraintName: ''
|
||||
};
|
||||
|
||||
const actionParam3TransformedMock: ActionParameterDefinitionTransformed = {
|
||||
name: 'aspect-name',
|
||||
type: 'd:qname',
|
||||
multiValued: false,
|
||||
mandatory: false,
|
||||
displayLabel: 'aspect-name',
|
||||
parameterConstraintName: 'ac-aspects'
|
||||
};
|
||||
|
||||
const actionParam4TransformedMock: ActionParameterDefinitionTransformed = {
|
||||
name: 'mock-action-parameter-noderef',
|
||||
type: 'd:noderef',
|
||||
multiValued: false,
|
||||
mandatory: false,
|
||||
displayLabel: 'mock-action-parameter-noderef',
|
||||
parameterConstraintName: ''
|
||||
};
|
||||
|
||||
const actionParam5TransformedMock: ActionParameterDefinitionTransformed = {
|
||||
name: 'aspect-name',
|
||||
type: 'd:noderef',
|
||||
multiValued: false,
|
||||
mandatory: false,
|
||||
displayLabel: 'aspect-name',
|
||||
parameterConstraintName: ''
|
||||
};
|
||||
|
||||
const actionParamLinkToCategoryTransformedMock = {
|
||||
name: 'category-value',
|
||||
type: 'd:noderef',
|
||||
multiValued: false,
|
||||
mandatory: true,
|
||||
displayLabel: 'Category value'
|
||||
};
|
||||
|
||||
const action1TransformedMock: ActionDefinitionTransformed = {
|
||||
id: 'mock-action-1-definition',
|
||||
name: 'mock-action-1-definition',
|
||||
description: 'This is a mock action',
|
||||
title: 'Action 1 title',
|
||||
applicableTypes: [],
|
||||
trackStatus: false,
|
||||
parameterDefinitions: [
|
||||
actionParam1TransformedMock,
|
||||
actionParam2TransformedMock,
|
||||
actionParam3TransformedMock,
|
||||
actionParam4TransformedMock,
|
||||
actionParam5TransformedMock
|
||||
]
|
||||
};
|
||||
|
||||
const action2TransformedMock: ActionDefinitionTransformed = {
|
||||
id: 'mock-action-2-definition',
|
||||
name: 'mock-action-2-definition',
|
||||
description: '',
|
||||
title: 'mock-action-2-definition',
|
||||
applicableTypes: [],
|
||||
trackStatus: false,
|
||||
parameterDefinitions: []
|
||||
};
|
||||
|
||||
export const actionLinkToCategoryTransformedMock: ActionDefinitionTransformed = {
|
||||
id: 'mock-action-3-definition',
|
||||
name: 'mock-action-3-definition',
|
||||
description: '',
|
||||
title: 'mock-action-3-definition',
|
||||
applicableTypes: [],
|
||||
trackStatus: false,
|
||||
parameterDefinitions: [actionParamLinkToCategoryTransformedMock]
|
||||
};
|
||||
|
||||
export const actionsTransformedListMock: ActionDefinitionTransformed[] = [action1TransformedMock, action2TransformedMock];
|
||||
|
||||
export const validActionMock: RuleAction = {
|
||||
actionDefinitionId: 'mock-action-1-definition',
|
||||
params: {
|
||||
'mock-action-parameter-text': 'mock'
|
||||
}
|
||||
};
|
||||
export const nonExistentActionDefinitionIdMock: RuleAction = {
|
||||
actionDefinitionId: 'non-existent-action-definition-id',
|
||||
params: {}
|
||||
};
|
||||
export const missingMandatoryParameterMock: RuleAction = {
|
||||
actionDefinitionId: 'mock-action-1-definition',
|
||||
params: {}
|
||||
};
|
||||
export const incompleteMandatoryParameterMock: RuleAction = {
|
||||
actionDefinitionId: 'mock-action-1-definition',
|
||||
params: {
|
||||
'mock-action-parameter-text': ''
|
||||
}
|
||||
};
|
||||
export const validActionsMock: RuleAction[] = [
|
||||
validActionMock,
|
||||
{
|
||||
actionDefinitionId: 'mock-action-2-definition',
|
||||
params: {}
|
||||
}
|
||||
];
|
||||
|
||||
export const errorScriptConstraintMock: ActionParameterConstraint = {
|
||||
name: 'script-ref',
|
||||
constraints: [
|
||||
{
|
||||
value: 'script-1-value',
|
||||
label: 'Script 1'
|
||||
},
|
||||
{
|
||||
value: 'script-2-value',
|
||||
label: 'Script 2'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleCompositeCondition } from '../model/rule-composite-condition.model';
|
||||
import { RuleSimpleCondition } from '../model/rule-simple-condition.model';
|
||||
|
||||
const simpleConditionMock: RuleSimpleCondition = {
|
||||
field: 'cm:name',
|
||||
comparator: 'equals',
|
||||
parameter: ''
|
||||
};
|
||||
|
||||
export const mimeTypeMock: RuleSimpleCondition = {
|
||||
field: 'mimetype',
|
||||
comparator: 'equals',
|
||||
parameter: ''
|
||||
};
|
||||
|
||||
export const categoryMock: RuleSimpleCondition = {
|
||||
field: 'category',
|
||||
comparator: 'equals',
|
||||
parameter: ''
|
||||
};
|
||||
|
||||
export const simpleConditionUnknownFieldMock: RuleSimpleCondition = {
|
||||
field: 'unknown-field',
|
||||
comparator: 'equals',
|
||||
parameter: ''
|
||||
};
|
||||
|
||||
const emptyCompositeConditionMock: RuleCompositeCondition = {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
compositeConditions: [],
|
||||
simpleConditions: []
|
||||
};
|
||||
|
||||
const compositeConditionWithOneConditionMock: RuleCompositeCondition = {
|
||||
...emptyCompositeConditionMock,
|
||||
simpleConditions: [{ ...simpleConditionMock }]
|
||||
};
|
||||
|
||||
export const compositeConditionWithOneGroupMock: RuleCompositeCondition = {
|
||||
...emptyCompositeConditionMock,
|
||||
compositeConditions: [{ ...compositeConditionWithOneConditionMock }]
|
||||
};
|
||||
|
||||
export const compositeConditionWithNestedGroupsMock: RuleCompositeCondition = {
|
||||
...emptyCompositeConditionMock,
|
||||
compositeConditions: [
|
||||
{
|
||||
...emptyCompositeConditionMock,
|
||||
compositeConditions: [{ ...compositeConditionWithOneConditionMock }]
|
||||
},
|
||||
{ ...compositeConditionWithOneConditionMock }
|
||||
]
|
||||
};
|
||||
|
||||
export const compositeConditionWithThreeConditionMock: RuleCompositeCondition = {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
compositeConditions: [],
|
||||
simpleConditions: [{ ...simpleConditionMock }, { ...simpleConditionMock }, { ...simpleConditionMock }]
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||
import { Node, NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
export const getOwningFolderEntryMock: NodeEntry = {
|
||||
entry: {
|
||||
id: 'owning-folder-id',
|
||||
name: 'owning-folder-name'
|
||||
}
|
||||
} as NodeEntry;
|
||||
|
||||
export const getOtherFolderEntryMock: NodeEntry = {
|
||||
entry: {
|
||||
id: 'other-folder-id',
|
||||
name: 'other-folder-name'
|
||||
}
|
||||
} as NodeEntry;
|
||||
|
||||
export const owningFolderIdMock = 'owning-folder-id';
|
||||
export const otherFolderIdMock = 'other-folder-id';
|
||||
|
||||
export const owningFolderMock: NodeInfo = {
|
||||
id: owningFolderIdMock,
|
||||
name: 'owning-folder-name'
|
||||
};
|
||||
|
||||
export const otherFolderMock: NodeInfo = {
|
||||
id: otherFolderIdMock,
|
||||
name: 'other-folder-name'
|
||||
};
|
||||
|
||||
export const folderToLinkMock: Node = {
|
||||
id: 'folder-1-id',
|
||||
name: 'folder-1-name',
|
||||
nodeType: 'folder',
|
||||
isFolder: true,
|
||||
isFile: false,
|
||||
modifiedAt: new Date(),
|
||||
modifiedByUser: {
|
||||
id: 'user-id',
|
||||
displayName: 'user-name'
|
||||
},
|
||||
createdAt: new Date(),
|
||||
createdByUser: {
|
||||
id: 'user-id',
|
||||
displayName: 'user-name'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleSet } from '../model/rule-set.model';
|
||||
import { folderToLinkMock, otherFolderIdMock, otherFolderMock, owningFolderIdMock, owningFolderMock } from './node.mock';
|
||||
import { Rule } from '../model/rule.model';
|
||||
import { inheritedRulesMock, linkedRulesMock, ownedRulesMock, ruleMock } from './rules.mock';
|
||||
|
||||
export const getRuleSetsResponseMock = {
|
||||
list: {
|
||||
pagination: {
|
||||
count: 3,
|
||||
hasMoreItems: false,
|
||||
totalItems: 3,
|
||||
skipCount: 0,
|
||||
maxItems: 100
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
linkedToBy: [],
|
||||
owningFolder: otherFolderIdMock,
|
||||
isLinkedTo: false,
|
||||
id: 'inherited-rule-set'
|
||||
}
|
||||
},
|
||||
{
|
||||
entry: {
|
||||
linkedToBy: [],
|
||||
owningFolder: owningFolderIdMock,
|
||||
isLinkedTo: false,
|
||||
id: 'rule-set-no-links'
|
||||
}
|
||||
},
|
||||
{
|
||||
entry: {
|
||||
linkedToBy: [owningFolderIdMock],
|
||||
owningFolder: otherFolderIdMock,
|
||||
isLinkedTo: true,
|
||||
id: 'rule-set-with-link'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultRuleSetResponseMock = {
|
||||
entry: {
|
||||
linkedToBy: [],
|
||||
owningFolder: owningFolderIdMock,
|
||||
isLinkedTo: false,
|
||||
id: 'rule-set-no-links'
|
||||
}
|
||||
};
|
||||
|
||||
export const ruleSetMock = (rules: Rule[] = []): RuleSet => ({
|
||||
id: 'rule-set-id',
|
||||
isLinkedTo: false,
|
||||
owningFolder: owningFolderMock,
|
||||
linkedToBy: [],
|
||||
rules: [...rules],
|
||||
hasMoreRules: true,
|
||||
loadingRules: false
|
||||
});
|
||||
|
||||
export const ownedRuleSetMock: RuleSet = {
|
||||
id: 'rule-set-no-links',
|
||||
isLinkedTo: false,
|
||||
owningFolder: owningFolderMock,
|
||||
linkedToBy: [],
|
||||
rules: ownedRulesMock,
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const ruleSetWithLinkMock: RuleSet = {
|
||||
id: 'rule-set-with-link',
|
||||
isLinkedTo: true,
|
||||
owningFolder: otherFolderMock,
|
||||
linkedToBy: [owningFolderIdMock],
|
||||
rules: linkedRulesMock,
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const inheritedRuleSetMock: RuleSet = {
|
||||
id: 'inherited-rule-set',
|
||||
isLinkedTo: false,
|
||||
owningFolder: otherFolderMock,
|
||||
linkedToBy: [],
|
||||
rules: inheritedRulesMock,
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const inheritedRuleSetWithEmptyRulesMock: RuleSet = {
|
||||
id: 'inherited-rule-set',
|
||||
isLinkedTo: false,
|
||||
owningFolder: otherFolderMock,
|
||||
linkedToBy: [],
|
||||
rules: [],
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const inheritedRuleSetWithOnlyDisabledRulesMock: RuleSet = {
|
||||
id: 'inherited-rule-set',
|
||||
isLinkedTo: false,
|
||||
owningFolder: otherFolderMock,
|
||||
linkedToBy: [],
|
||||
rules: [
|
||||
{ ...ruleMock('rule1'), isEnabled: false },
|
||||
{ ...ruleMock('rule2'), isEnabled: false }
|
||||
],
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const ruleSetsMock: RuleSet[] = [inheritedRuleSetMock, ownedRuleSetMock, ruleSetWithLinkMock];
|
||||
|
||||
export const ruleSetWithNoRulesToLinkMock: RuleSet = {
|
||||
id: 'rule-set-to-link-with-no-rules',
|
||||
isLinkedTo: false,
|
||||
owningFolder: folderToLinkMock,
|
||||
linkedToBy: [],
|
||||
rules: [],
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
|
||||
export const ruleSetWithOwnedRulesToLinkMock: RuleSet = {
|
||||
id: 'rule-set-to-link-with-no-rules',
|
||||
isLinkedTo: false,
|
||||
owningFolder: folderToLinkMock,
|
||||
linkedToBy: [],
|
||||
rules: ownedRulesMock,
|
||||
hasMoreRules: false,
|
||||
loadingRules: false
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Rule, RuleSettings } from '../model/rule.model';
|
||||
import { RuleGroupingItem } from '../model/rule-grouping-item.model';
|
||||
|
||||
export const getRulesResponseMock = {
|
||||
list: {
|
||||
pagination: {
|
||||
count: 2,
|
||||
hasMoreItems: false,
|
||||
totalItems: 2,
|
||||
skipCount: 0,
|
||||
maxItems: 100
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
isShared: false,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
name: 'rule1-name',
|
||||
id: 'rule1-id',
|
||||
triggers: ['inbound'],
|
||||
actions: [
|
||||
{
|
||||
actionDefinitionId: 'counter',
|
||||
params: {}
|
||||
}
|
||||
],
|
||||
isEnabled: true
|
||||
}
|
||||
},
|
||||
{
|
||||
entry: {
|
||||
isShared: false,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
name: 'rule2-name',
|
||||
id: 'rule2-id',
|
||||
triggers: ['inbound'],
|
||||
actions: [
|
||||
{
|
||||
actionDefinitionId: 'counter',
|
||||
params: {}
|
||||
}
|
||||
],
|
||||
isEnabled: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export const getMoreRulesResponseMock = {
|
||||
list: {
|
||||
pagination: {
|
||||
count: 2,
|
||||
hasMoreItems: false,
|
||||
totalItems: 2,
|
||||
skipCount: 0,
|
||||
maxItems: 100
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
isShared: false,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
name: 'rule3-name',
|
||||
id: 'rule3-id',
|
||||
triggers: ['inbound'],
|
||||
actions: [
|
||||
{
|
||||
actionDefinitionId: 'counter',
|
||||
params: {}
|
||||
}
|
||||
],
|
||||
isEnabled: true
|
||||
}
|
||||
},
|
||||
{
|
||||
entry: {
|
||||
isShared: false,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
name: 'rule4-name',
|
||||
id: 'rule4-id',
|
||||
triggers: ['inbound'],
|
||||
actions: [
|
||||
{
|
||||
actionDefinitionId: 'counter',
|
||||
params: {}
|
||||
}
|
||||
],
|
||||
isEnabled: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const genericRuleMock: Rule = {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
isEnabled: true,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
errorScript: '',
|
||||
isShared: false,
|
||||
triggers: ['inbound'],
|
||||
conditions: {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
simpleConditions: [],
|
||||
compositeConditions: []
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
actionDefinitionId: 'counter',
|
||||
params: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const ruleMock = (unique: string): Rule => ({
|
||||
...genericRuleMock,
|
||||
id: `${unique}-id`,
|
||||
name: `${unique}-name`
|
||||
});
|
||||
|
||||
export const rulesMock: Rule[] = [ruleMock('rule1'), ruleMock('rule2')];
|
||||
export const moreRulesMock: Rule[] = [ruleMock('rule3'), ruleMock('rule4')];
|
||||
export const manyRulesMock: Rule[] = [ruleMock('rule1'), ruleMock('rule2'), ruleMock('rule3'), ruleMock('rule4'), ruleMock('rule5')];
|
||||
|
||||
export const ownedRulesMock: Rule[] = [ruleMock('owned-rule-1'), ruleMock('owned-rule-2')];
|
||||
export const linkedRulesMock: Rule[] = [ruleMock('linked-rule-1'), ruleMock('linked-rule-2')];
|
||||
export const inheritedRulesMock: Rule[] = [ruleMock('inherited-rule-1'), ruleMock('inherited-rule-2')];
|
||||
|
||||
export const ruleListGroupingItemsMock: RuleGroupingItem[] = [
|
||||
{
|
||||
type: 'rule',
|
||||
rule: ruleMock('rule1')
|
||||
},
|
||||
{
|
||||
type: 'rule',
|
||||
rule: ruleMock('rule2')
|
||||
}
|
||||
];
|
||||
|
||||
export const ruleSettingsMock: RuleSettings = {
|
||||
value: false,
|
||||
key: '-parameter-'
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface ActionParameterConstraint {
|
||||
name: string;
|
||||
constraints: ConstraintValue[];
|
||||
}
|
||||
|
||||
export interface ConstraintValue {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface RuleAction {
|
||||
actionDefinitionId: string;
|
||||
params?: { [key: string]: unknown };
|
||||
}
|
||||
|
||||
export const isRuleAction = (obj): obj is RuleAction =>
|
||||
typeof obj === 'object' && typeof obj.actionDefinitionId === 'string' && (obj.params === undefined || typeof obj.params === 'object');
|
||||
export const isRuleActions = (obj): obj is RuleAction[] =>
|
||||
typeof obj === 'object' && obj instanceof Array && obj.reduce((acc, curr) => acc && isRuleAction(curr), true);
|
||||
|
||||
export interface ActionDefinitionTransformed {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
title: string;
|
||||
applicableTypes: string[];
|
||||
trackStatus: boolean;
|
||||
parameterDefinitions: ActionParameterDefinitionTransformed[];
|
||||
}
|
||||
|
||||
export interface ActionParameterDefinitionTransformed {
|
||||
name: string;
|
||||
type: string;
|
||||
multiValued: boolean;
|
||||
mandatory: boolean;
|
||||
displayLabel: string;
|
||||
parameterConstraintName?: string;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleSimpleCondition } from './rule-simple-condition.model';
|
||||
|
||||
export interface RuleCompositeCondition {
|
||||
inverted: boolean;
|
||||
booleanMode: 'and' | 'or';
|
||||
compositeConditions: RuleCompositeCondition[];
|
||||
simpleConditions: RuleSimpleCondition[];
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Rule } from './rule.model';
|
||||
import { RuleSet } from './rule-set.model';
|
||||
|
||||
export type RuleGroupingItem =
|
||||
| {
|
||||
type: 'rule';
|
||||
rule: Rule;
|
||||
}
|
||||
| {
|
||||
type: 'load-more-rules';
|
||||
ruleSet: RuleSet;
|
||||
}
|
||||
| {
|
||||
type: 'loading' | 'load-more-rule-sets';
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Rule } from './rule.model';
|
||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||
|
||||
export interface RuleSet {
|
||||
id: string;
|
||||
isLinkedTo: boolean;
|
||||
owningFolder: NodeInfo;
|
||||
linkedToBy: string[];
|
||||
rules: Rule[];
|
||||
hasMoreRules: boolean;
|
||||
loadingRules: boolean;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface RuleSimpleCondition {
|
||||
field: string;
|
||||
comparator: string;
|
||||
parameter: string;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleCompositeCondition } from './rule-composite-condition.model';
|
||||
import { RuleAction } from './rule-action.model';
|
||||
|
||||
export type RuleTrigger = 'inbound' | 'update' | 'outbound';
|
||||
|
||||
export interface Rule {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
isEnabled: boolean;
|
||||
isInheritable: boolean;
|
||||
isAsynchronous: boolean;
|
||||
errorScript: string;
|
||||
isShared: boolean;
|
||||
triggers: RuleTrigger[];
|
||||
conditions: RuleCompositeCondition;
|
||||
actions: RuleAction[];
|
||||
}
|
||||
|
||||
export interface RuleOptions {
|
||||
isEnabled: boolean;
|
||||
isInheritable: boolean;
|
||||
isAsynchronous: boolean;
|
||||
errorScript: string;
|
||||
}
|
||||
|
||||
export interface RuleForForm {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
triggers: RuleTrigger[];
|
||||
conditions: RuleCompositeCondition;
|
||||
actions: RuleAction[];
|
||||
options: RuleOptions;
|
||||
}
|
||||
|
||||
export interface RuleSettings {
|
||||
value: boolean;
|
||||
key?: string;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export * from './folder-rules.module';
|
||||
export { ManageRulesSmartComponent } from './manage-rules/manage-rules.smart-component';
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<div class="aca-rule-action-list__item " *ngFor="let control of formControls">
|
||||
<aca-rule-action
|
||||
[actionDefinitions]="actionDefinitions"
|
||||
[parameterConstraints]="parameterConstraints"
|
||||
[readOnly]="readOnly"
|
||||
[formControl]="control"
|
||||
[nodeId]="nodeId">
|
||||
</aca-rule-action>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
data-automation-id="rule-action-list-action-menu"
|
||||
*ngIf="!readOnly" [matMenuTriggerFor]="menu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #menu>
|
||||
<button
|
||||
mat-menu-item
|
||||
data-automation-id="rule-action-list-remove-action-button"
|
||||
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.REMOVE' | translate"
|
||||
[disabled]="formControls.length <= 1"
|
||||
(click)="removeAction(control)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.REMOVE' | translate }}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
<button mat-button data-automation-id="rule-action-list-add-action-button" (click)="addAction()" *ngIf="!readOnly">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.ADD_ACTION' | translate }}</span>
|
||||
</button>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
.aca-rule-action-list {
|
||||
&__item {
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
|
||||
& > .aca-rule-action {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:nth-child(2n) {
|
||||
background-color: hsl(0deg, 0%, 95%);
|
||||
}
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { RuleActionListUiComponent } from './rule-action-list.ui-component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { RuleActionUiComponent } from './rule-action.ui-component';
|
||||
|
||||
describe('RuleActionListUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleActionListUiComponent>;
|
||||
let component: RuleActionListUiComponent;
|
||||
|
||||
const getByDataAutomationId = (dataAutomationId: string, index = 0): DebugElement =>
|
||||
fixture.debugElement.queryAll(By.css(`[data-automation-id="${dataAutomationId}"]`))[index];
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleActionListUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.writeValue([]);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should default to 1 empty action when an empty array of actions is written', () => {
|
||||
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||
expect(acaRuleActions.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should add a new action when the "add action" button is clicked', () => {
|
||||
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||
addActionButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||
expect(acaRuleActions.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should disable the remove button if there is only one action', () => {
|
||||
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||
expect(removeActionButton.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
it('should enable the remove button if there is more than one action', () => {
|
||||
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||
addActionButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||
expect(removeActionButton.disabled).toBeFalse();
|
||||
});
|
||||
|
||||
it('should remove an action when the remove action button is clicked', () => {
|
||||
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||
addActionButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||
removeActionButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||
expect(acaRuleActions.length).toBe(1);
|
||||
});
|
||||
});
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, forwardRef, Input, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||
import { ControlValueAccessor, FormArray, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import { ActionDefinitionTransformed, RuleAction } from '../../model/rule-action.model';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ruleActionValidator } from '../validators/rule-actions.validator';
|
||||
import { ActionParameterConstraint } from '../../model/action-parameter-constraint.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-action-list',
|
||||
templateUrl: './rule-action-list.ui-component.html',
|
||||
styleUrls: ['./rule-action-list.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-action-list' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleActionListUiComponent)
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RuleActionListUiComponent implements ControlValueAccessor, OnDestroy {
|
||||
@Input()
|
||||
actionDefinitions: ActionDefinitionTransformed[] = [];
|
||||
@Input()
|
||||
readOnly = false;
|
||||
@Input()
|
||||
parameterConstraints: ActionParameterConstraint[] = [];
|
||||
@Input()
|
||||
nodeId = '';
|
||||
|
||||
formArray = new FormArray([]);
|
||||
private formArraySubscription: Subscription;
|
||||
|
||||
formControls: FormControl[] = [];
|
||||
|
||||
onChange: (actions: RuleAction[]) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
writeValue(actions: RuleAction[]) {
|
||||
if (actions.length === 0) {
|
||||
actions = [
|
||||
{
|
||||
actionDefinitionId: null,
|
||||
params: {}
|
||||
}
|
||||
];
|
||||
}
|
||||
this.formArray = new FormArray(actions.map((action: RuleAction) => new FormControl(action)));
|
||||
this.formControls = this.formArray.controls as FormControl[];
|
||||
this.formArraySubscription?.unsubscribe();
|
||||
this.formArraySubscription = this.formArray.valueChanges.subscribe((value: any) => {
|
||||
this.onChange(value);
|
||||
this.onTouch();
|
||||
});
|
||||
}
|
||||
|
||||
registerOnChange(fn: (actions: RuleAction[]) => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
addAction() {
|
||||
const newAction: RuleAction = {
|
||||
actionDefinitionId: null,
|
||||
params: {}
|
||||
};
|
||||
this.formArray.push(new FormControl(newAction, [Validators.required, ruleActionValidator(this.actionDefinitions)]));
|
||||
this.formControls = this.formArray.controls as FormControl[];
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.formArraySubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
removeAction(control: FormControl) {
|
||||
const index = this.formArray.value.indexOf(control.value);
|
||||
this.formArray.removeAt(index);
|
||||
this.formControls = this.formArray.controls as FormControl[];
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<form class="aca-rule-action__form" [formGroup]="form">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select
|
||||
formControlName="actionDefinitionId"
|
||||
data-automation-id="rule-action-select"
|
||||
[placeholder]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_SELECT_PLACEHOLDER' | translate">
|
||||
<mat-option
|
||||
*ngFor="let actionDefinition of actionDefinitions"
|
||||
[value]="actionDefinition.id">
|
||||
{{ actionDefinition.title }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<adf-card-view
|
||||
data-automation-id="rule-action-card-view"
|
||||
[properties]="cardViewItems"
|
||||
[ngClass]="{ 'aca-rule-action-full-width': isFullWidth }"
|
||||
[editable]="!readOnly">
|
||||
</adf-card-view>
|
||||
|
||||
</form>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
.aca-rule-action {
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
|
||||
adf-card-view .adf-property-list .adf-property adf-card-view-item-dispatcher adf-card-view-textitem .mat-form-field .mat-form-field-infix {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
&-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CardViewBoolItemModel, CardViewComponent, CardViewSelectItemModel, CardViewTextItemModel, CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { RuleActionUiComponent } from './rule-action.ui-component';
|
||||
import { actionLinkToCategoryTransformedMock, actionsTransformedListMock } from '../../mock/actions.mock';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { dummyConstraints } from '../../mock/action-parameter-constraints.mock';
|
||||
|
||||
describe('RuleActionUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleActionUiComponent>;
|
||||
let component: RuleActionUiComponent;
|
||||
|
||||
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||
|
||||
const changeMatSelectValue = (dataAutomationId: string, value: string) => {
|
||||
const matSelect = getByDataAutomationId(dataAutomationId).nativeElement;
|
||||
matSelect.click();
|
||||
fixture.detectChanges();
|
||||
const matOption = fixture.debugElement.query(By.css(`.mat-option[ng-reflect-value="${value}"]`)).nativeElement;
|
||||
matOption.click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleActionUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should populate the dropdown selector with the action definitions', () => {
|
||||
component.actionDefinitions = actionsTransformedListMock;
|
||||
fixture.detectChanges();
|
||||
|
||||
const matSelect = getByDataAutomationId('rule-action-select').nativeElement;
|
||||
matSelect.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const matOptions = fixture.debugElement.queryAll(By.css(`mat-option`));
|
||||
expect(matOptions.length).toBe(2);
|
||||
expect(matOptions[0].nativeElement.innerText).toBe('Action 1 title');
|
||||
expect(matOptions[1].nativeElement.innerText).toBe('mock-action-2-definition');
|
||||
});
|
||||
|
||||
it('should populate the card view with parameters when an action is selected', () => {
|
||||
component.actionDefinitions = actionsTransformedListMock;
|
||||
component.parameterConstraints = dummyConstraints;
|
||||
fixture.detectChanges();
|
||||
|
||||
const cardView = getByDataAutomationId('rule-action-card-view').componentInstance as CardViewComponent;
|
||||
expect(cardView.properties.length).toBe(0);
|
||||
|
||||
changeMatSelectValue('rule-action-select', 'mock-action-1-definition');
|
||||
|
||||
expect(cardView.properties.length).toBe(5);
|
||||
expect(cardView.properties[0]).toBeInstanceOf(CardViewTextItemModel);
|
||||
expect(cardView.properties[1]).toBeInstanceOf(CardViewBoolItemModel);
|
||||
expect(cardView.properties[2]).toBeInstanceOf(CardViewSelectItemModel);
|
||||
expect(cardView.properties[3]).toBeInstanceOf(CardViewTextItemModel);
|
||||
expect(cardView.properties[4]).toBeInstanceOf(CardViewSelectItemModel);
|
||||
|
||||
changeMatSelectValue('rule-action-select', 'mock-action-2-definition');
|
||||
expect(cardView.properties.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should create category-value action parameter as a text box rather than node picker', () => {
|
||||
component.actionDefinitions = [actionLinkToCategoryTransformedMock];
|
||||
component.parameterConstraints = dummyConstraints;
|
||||
fixture.detectChanges();
|
||||
|
||||
const cardView = getByDataAutomationId('rule-action-card-view').componentInstance as CardViewComponent;
|
||||
expect(cardView.properties.length).toBe(0);
|
||||
|
||||
changeMatSelectValue('rule-action-select', 'mock-action-3-definition');
|
||||
|
||||
expect(cardView.properties[0].icon).toBeFalsy();
|
||||
expect(cardView.properties[0].value).toBeFalsy();
|
||||
expect(cardView.properties[0]).toBeInstanceOf(CardViewTextItemModel);
|
||||
});
|
||||
});
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, forwardRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import { ActionDefinitionTransformed, RuleAction } from '../../model/rule-action.model';
|
||||
import { CardViewItem } from '@alfresco/adf-core/lib/card-view/interfaces/card-view-item.interface';
|
||||
import {
|
||||
CardViewBoolItemModel,
|
||||
CardViewSelectItemModel,
|
||||
CardViewSelectItemOption,
|
||||
CardViewTextItemModel,
|
||||
CardViewUpdateService,
|
||||
UpdateNotification
|
||||
} from '@alfresco/adf-core';
|
||||
import { ActionParameterDefinition, Node } from '@alfresco/js-api';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ActionParameterConstraint, ConstraintValue } from '../../model/action-parameter-constraint.model';
|
||||
import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData, NodeAction } from '@alfresco/adf-content-services';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-action',
|
||||
templateUrl: './rule-action.ui-component.html',
|
||||
styleUrls: ['./rule-action.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-action' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleActionUiComponent)
|
||||
},
|
||||
CardViewUpdateService
|
||||
]
|
||||
})
|
||||
export class RuleActionUiComponent implements ControlValueAccessor, OnInit, OnChanges, OnDestroy {
|
||||
@Input()
|
||||
nodeId = '';
|
||||
|
||||
@Input()
|
||||
actionDefinitions: ActionDefinitionTransformed[];
|
||||
|
||||
@Input()
|
||||
readOnly = false;
|
||||
|
||||
private _parameterConstraints = [];
|
||||
@Input()
|
||||
get parameterConstraints(): ActionParameterConstraint[] {
|
||||
return this._parameterConstraints;
|
||||
}
|
||||
set parameterConstraints(value) {
|
||||
this._parameterConstraints = value.map((obj) => ({ ...obj, constraints: this.parseConstraintsToSelectOptions(obj.constraints) }));
|
||||
}
|
||||
|
||||
isFullWidth = false;
|
||||
|
||||
form = new FormGroup({
|
||||
actionDefinitionId: new FormControl('', Validators.required)
|
||||
});
|
||||
|
||||
cardViewItems: CardViewItem[] = [];
|
||||
parameters: { [key: string]: unknown } = {};
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
|
||||
get selectedActionDefinitionId(): string {
|
||||
return this.form.get('actionDefinitionId').value;
|
||||
}
|
||||
|
||||
get selectedActionDefinition(): ActionDefinitionTransformed {
|
||||
return this.actionDefinitions.find((actionDefinition: ActionDefinitionTransformed) => actionDefinition.id === this.selectedActionDefinitionId);
|
||||
}
|
||||
|
||||
get cardViewStyle() {
|
||||
return this.isFullWidth ? { width: '100%' } : {};
|
||||
}
|
||||
|
||||
onChange: (action: RuleAction) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
constructor(private cardViewUpdateService: CardViewUpdateService, private dialog: MatDialog, private translate: TranslateService) {}
|
||||
|
||||
writeValue(action: RuleAction) {
|
||||
this.form.setValue({
|
||||
actionDefinitionId: action.actionDefinitionId
|
||||
});
|
||||
this.parameters = {
|
||||
...this.parameters,
|
||||
...action.params
|
||||
};
|
||||
this.setCardViewProperties();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (action: RuleAction) => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.actionDefinitions = this.actionDefinitions.sort((firstActionDefinition, secondActionDefinition) =>
|
||||
firstActionDefinition.title.localeCompare(secondActionDefinition.title)
|
||||
);
|
||||
|
||||
this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||
this.setDefaultParameters();
|
||||
this.setCardViewProperties();
|
||||
this.onChange({
|
||||
actionDefinitionId: this.selectedActionDefinitionId,
|
||||
params: this.parameters
|
||||
});
|
||||
this.onTouch();
|
||||
});
|
||||
|
||||
this.cardViewUpdateService.itemUpdated$.pipe(takeUntil(this.onDestroy$)).subscribe((updateNotification: UpdateNotification) => {
|
||||
this.parameters = {
|
||||
...this.parameters,
|
||||
...updateNotification.changed
|
||||
};
|
||||
this.onChange({
|
||||
actionDefinitionId: this.selectedActionDefinitionId,
|
||||
params: this.parameters
|
||||
});
|
||||
this.onTouch();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const readOnly = changes['readOnly']?.currentValue;
|
||||
if (readOnly !== undefined && readOnly !== null) {
|
||||
this.readOnly = readOnly;
|
||||
this.setDisabledState(readOnly);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
setCardViewProperties() {
|
||||
this.cardViewItems = (this.selectedActionDefinition?.parameterDefinitions ?? []).map((paramDef) => {
|
||||
this.isFullWidth = false;
|
||||
const constraintsForDropdownBox = this._parameterConstraints.find((obj) => obj.name === paramDef.name);
|
||||
const cardViewPropertiesModel = {
|
||||
label: paramDef.displayLabel + (paramDef.mandatory ? ' *' : ''),
|
||||
key: paramDef.name,
|
||||
editable: true,
|
||||
...(paramDef.mandatory
|
||||
? {
|
||||
validators: [
|
||||
{
|
||||
message: 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.REQUIRED',
|
||||
isValid: (value: unknown) => !!value
|
||||
}
|
||||
]
|
||||
}
|
||||
: {})
|
||||
};
|
||||
switch (paramDef.type) {
|
||||
case 'd:boolean':
|
||||
return new CardViewBoolItemModel({
|
||||
...cardViewPropertiesModel,
|
||||
value: this.parameters[paramDef.name] ?? false
|
||||
});
|
||||
case 'd:noderef':
|
||||
if (!constraintsForDropdownBox && !this.readOnly && paramDef.name !== 'category-value') {
|
||||
return new CardViewTextItemModel({
|
||||
...cardViewPropertiesModel,
|
||||
icon: 'folder',
|
||||
default: '',
|
||||
clickable: true,
|
||||
clickCallBack: this.openSelectorDialog.bind(this, paramDef.name),
|
||||
value: this.parameters[paramDef.name]
|
||||
});
|
||||
}
|
||||
// falls through
|
||||
default:
|
||||
if (constraintsForDropdownBox && !this.readOnly) {
|
||||
this.isFullWidth = true;
|
||||
return new CardViewSelectItemModel({
|
||||
...cardViewPropertiesModel,
|
||||
value: (this.parameters[paramDef.name] as string) ?? '',
|
||||
options$: of(constraintsForDropdownBox.constraints)
|
||||
});
|
||||
}
|
||||
return new CardViewTextItemModel({
|
||||
...cardViewPropertiesModel,
|
||||
value: this.parameters[paramDef.name] ?? ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private openSelectorDialog(paramDefName) {
|
||||
const data: ContentNodeSelectorComponentData = {
|
||||
title: this.translate.instant('ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.CHOOSE_FOLDER'),
|
||||
actionName: NodeAction.CHOOSE,
|
||||
currentFolderId: this.nodeId,
|
||||
select: new Subject<Node[]>()
|
||||
};
|
||||
|
||||
this.dialog.open(ContentNodeSelectorComponent, {
|
||||
data,
|
||||
panelClass: 'adf-content-node-selector-dialog',
|
||||
width: '630px'
|
||||
});
|
||||
|
||||
data.select.subscribe(
|
||||
(selections: Node[]) => {
|
||||
if (selections[0].id) {
|
||||
this.writeValue({
|
||||
actionDefinitionId: this.selectedActionDefinitionId,
|
||||
params: {
|
||||
...this.parameters,
|
||||
[paramDefName]: selections[0].id
|
||||
}
|
||||
});
|
||||
this.onChange({
|
||||
actionDefinitionId: this.selectedActionDefinitionId,
|
||||
params: this.parameters
|
||||
});
|
||||
this.onTouch();
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
() => {
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
setDefaultParameters() {
|
||||
this.parameters = {};
|
||||
(this.selectedActionDefinition?.parameterDefinitions ?? []).forEach((paramDef: ActionParameterDefinition) => {
|
||||
switch (paramDef.type) {
|
||||
case 'd:boolean':
|
||||
this.parameters[paramDef.name] = false;
|
||||
break;
|
||||
default:
|
||||
this.parameters[paramDef.name] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
this.readOnly = true;
|
||||
this.form.disable();
|
||||
} else {
|
||||
this.readOnly = false;
|
||||
this.form.enable();
|
||||
}
|
||||
}
|
||||
|
||||
private parseConstraintsToSelectOptions(constraints: ConstraintValue[]): CardViewSelectItemOption<unknown>[] {
|
||||
return constraints
|
||||
.sort((a, b) => {
|
||||
if (!a.label && b.label) {
|
||||
return 1;
|
||||
}
|
||||
if (!b.label && a.label) {
|
||||
return -1;
|
||||
}
|
||||
return a.label?.localeCompare(b.label) ?? -1;
|
||||
})
|
||||
.map((constraint) => ({
|
||||
key: constraint.value,
|
||||
label: constraint.label ? `${constraint.label} [${constraint.value}]` : constraint.value
|
||||
}));
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<form class="aca-rule-composite-condition__form" [formGroup]="form">
|
||||
<div *ngIf="hasNoConditions" class="aca-rule-composite-condition__form__no-conditions" data-automation-id="no-conditions">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.' + (childCondition ? 'NO_CONDITIONS_IN_GROUP' : 'NO_CONDITIONS') | translate }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="aca-rule-composite-condition__form__row"
|
||||
*ngFor="let control of conditionFormControls; let i = index">
|
||||
|
||||
<mat-form-field *ngIf="i === 0">
|
||||
<mat-select
|
||||
[formControl]="invertedControl"
|
||||
[disabled]="readOnly">
|
||||
<mat-option [value]="false">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.IF' | translate }}</mat-option>
|
||||
<mat-option [value]="true">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.NOT_IF' | translate }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field *ngIf="i > 0">
|
||||
<mat-select
|
||||
[disabled]="!isOrImplemented || i > 1 || readOnly"
|
||||
[formControl]="booleanModeControl">
|
||||
<mat-option value="and">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.AND' | translate }}</mat-option>
|
||||
<mat-option value="or">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.OR' | translate }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<aca-rule-composite-condition
|
||||
*ngIf="!isFormControlSimpleCondition(control)"
|
||||
[secondaryBackground]="!secondaryBackground"
|
||||
[childCondition]="true"
|
||||
[formControl]="control"
|
||||
[readOnly]="readOnly">
|
||||
</aca-rule-composite-condition>
|
||||
|
||||
<aca-rule-simple-condition
|
||||
*ngIf="isFormControlSimpleCondition(control)"
|
||||
[formControl]="control"
|
||||
[readOnly]="readOnly">
|
||||
</aca-rule-simple-condition>
|
||||
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" *ngIf="!readOnly" data-automation-id="condition-actions-button">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button
|
||||
mat-menu-item
|
||||
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.REMOVE' | translate"
|
||||
(click)="removeCondition(control)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.REMOVE' | translate }}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-composite-condition__form__actions" *ngIf="!readOnly" data-automation-id="add-actions">
|
||||
<button mat-button (click)="addSimpleCondition()" data-automation-id="add-condition-button">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.ADD_CONDITION' | translate }}</span>
|
||||
</button>
|
||||
<button mat-button (click)="addCompositeCondition()" data-automation-id="add-group-button">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.ADD_GROUP' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
.aca-rule-composite-condition {
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
|
||||
&.childCompositeCondition {
|
||||
padding: 8px 16px;
|
||||
background-color: hsl(0deg, 0%, 100%);
|
||||
|
||||
&.secondaryBackground {
|
||||
background-color: hsl(0deg, 0%, 95%);
|
||||
}
|
||||
}
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
&__no-conditions {
|
||||
color: var(--theme-disabled-text-color);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
& > :nth-child(1) {
|
||||
width: 5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
& > :nth-child(2) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RuleCompositeConditionUiComponent } from './rule-composite-condition.ui-component';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import {
|
||||
compositeConditionWithNestedGroupsMock,
|
||||
compositeConditionWithOneGroupMock,
|
||||
compositeConditionWithThreeConditionMock
|
||||
} from '../../mock/conditions.mock';
|
||||
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||
|
||||
describe('RuleCompositeConditionUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleCompositeConditionUiComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [RuleCompositeConditionUiComponent, RuleSimpleConditionUiComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleCompositeConditionUiComponent);
|
||||
});
|
||||
|
||||
describe('No conditions', () => {
|
||||
let noConditionsElement: DebugElement;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture.detectChanges();
|
||||
noConditionsElement = fixture.debugElement.query(By.css(`[data-automation-id="no-conditions"]`));
|
||||
});
|
||||
|
||||
it('should default to no conditions', () => {
|
||||
const rowElements = fixture.debugElement.queryAll(By.css(`.aca-rule-composite-condition__form__row`));
|
||||
|
||||
expect(rowElements.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should show a message if there are no conditions', () => {
|
||||
fixture.componentInstance.childCondition = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect((noConditionsElement.nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.NO_CONDITIONS');
|
||||
});
|
||||
|
||||
it('should show a different message if the component is not a root condition group', () => {
|
||||
fixture.componentInstance.childCondition = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect((noConditionsElement.nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.NO_CONDITIONS_IN_GROUP');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read only mode', () => {
|
||||
it('should hide the add buttons in read only mode', () => {
|
||||
fixture.componentInstance.setDisabledState(true);
|
||||
fixture.detectChanges();
|
||||
const actionsElement = fixture.debugElement.query(By.css(`[data-automation-id="add-actions"]`));
|
||||
|
||||
expect(actionsElement).toBeNull();
|
||||
});
|
||||
|
||||
it('should hide the more actions button on the right side of the condition', () => {
|
||||
fixture.componentInstance.writeValue(compositeConditionWithOneGroupMock);
|
||||
fixture.componentInstance.setDisabledState(true);
|
||||
fixture.detectChanges();
|
||||
const actionsButtonElements = fixture.debugElement.queryAll(By.css(`[data-automation-id="condition-actions-button"]`));
|
||||
|
||||
expect(actionsButtonElements.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have as many simple condition components as defined in the simpleConditions array', () => {
|
||||
fixture.componentInstance.writeValue(compositeConditionWithThreeConditionMock);
|
||||
fixture.detectChanges();
|
||||
const simpleConditionComponents = fixture.debugElement.queryAll(By.css(`.aca-rule-simple-condition`));
|
||||
|
||||
expect(simpleConditionComponents.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should have as many composite condition components as defined in the compositeConditions array, including nested', () => {
|
||||
fixture.componentInstance.writeValue(compositeConditionWithNestedGroupsMock);
|
||||
fixture.detectChanges();
|
||||
const compositeConditionComponents = fixture.debugElement.queryAll(By.css(`.aca-rule-composite-condition`));
|
||||
|
||||
expect(compositeConditionComponents.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should add a simple condition component on click of add condition button', () => {
|
||||
fixture.detectChanges();
|
||||
const predicate = By.css(`.aca-rule-simple-condition`);
|
||||
const simpleConditionComponentsBeforeClick = fixture.debugElement.queryAll(predicate);
|
||||
|
||||
expect(simpleConditionComponentsBeforeClick.length).toBe(0);
|
||||
|
||||
const addConditionButton = fixture.debugElement.query(By.css(`[data-automation-id="add-condition-button"]`));
|
||||
(addConditionButton.nativeElement as HTMLButtonElement).click();
|
||||
fixture.detectChanges();
|
||||
const simpleConditionComponentsAfterClick = fixture.debugElement.queryAll(predicate);
|
||||
|
||||
expect(simpleConditionComponentsAfterClick.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should add a composite condition component on click of add group button', () => {
|
||||
fixture.detectChanges();
|
||||
const predicate = By.css(`.aca-rule-composite-condition`);
|
||||
const compositeConditionComponentsBeforeClick = fixture.debugElement.queryAll(predicate);
|
||||
|
||||
expect(compositeConditionComponentsBeforeClick.length).toBe(0);
|
||||
|
||||
const addGroupButton = fixture.debugElement.query(By.css(`[data-automation-id="add-group-button"]`));
|
||||
(addGroupButton.nativeElement as HTMLButtonElement).click();
|
||||
fixture.detectChanges();
|
||||
const compositeConditionComponentsAfterClick = fixture.debugElement.queryAll(predicate);
|
||||
|
||||
expect(compositeConditionComponentsAfterClick.length).toBe(1);
|
||||
});
|
||||
});
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, forwardRef, HostBinding, Input, OnChanges, OnDestroy, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
||||
import { ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-composite-condition',
|
||||
templateUrl: './rule-composite-condition.ui-component.html',
|
||||
styleUrls: ['./rule-composite-condition.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-composite-condition' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleCompositeConditionUiComponent)
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RuleCompositeConditionUiComponent implements ControlValueAccessor, OnDestroy, OnChanges {
|
||||
@HostBinding('class.secondaryBackground')
|
||||
@Input()
|
||||
secondaryBackground = false;
|
||||
@HostBinding('class.childCompositeCondition')
|
||||
@Input()
|
||||
childCondition = false;
|
||||
|
||||
form = new FormGroup({
|
||||
inverted: new FormControl(),
|
||||
booleanMode: new FormControl(),
|
||||
compositeConditions: new FormArray([]),
|
||||
simpleConditions: new FormArray([])
|
||||
});
|
||||
|
||||
readonly isOrImplemented = false;
|
||||
|
||||
@Input()
|
||||
public readOnly = false;
|
||||
|
||||
private formSubscription = this.form.valueChanges.subscribe((value: any) => {
|
||||
this.onChange(value);
|
||||
this.onTouch();
|
||||
});
|
||||
|
||||
public invertedControl = this.form.get('inverted') as FormControl;
|
||||
public booleanModeControl = this.form.get('booleanMode') as FormControl;
|
||||
|
||||
get compositeConditionsFormArray(): FormArray {
|
||||
return this.form.get('compositeConditions') as FormArray;
|
||||
}
|
||||
get simpleConditionsFormArray(): FormArray {
|
||||
return this.form.get('simpleConditions') as FormArray;
|
||||
}
|
||||
get conditionFormControls(): FormControl[] {
|
||||
return [...(this.compositeConditionsFormArray.controls as FormControl[]), ...(this.simpleConditionsFormArray.controls as FormControl[])];
|
||||
}
|
||||
get hasNoConditions(): boolean {
|
||||
return this.conditionFormControls.length === 0;
|
||||
}
|
||||
|
||||
onChange: (condition: RuleCompositeCondition) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
writeValue(value: RuleCompositeCondition) {
|
||||
this.form.get('inverted').setValue(value.inverted);
|
||||
this.form.get('booleanMode').setValue(value.booleanMode);
|
||||
this.form.setControl('compositeConditions', new FormArray(value.compositeConditions.map((condition) => new FormControl(condition))));
|
||||
this.form.setControl('simpleConditions', new FormArray(value.simpleConditions.map((condition) => new FormControl(condition))));
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
this.readOnly = true;
|
||||
this.form.disable();
|
||||
} else {
|
||||
this.readOnly = false;
|
||||
this.form.enable();
|
||||
}
|
||||
}
|
||||
|
||||
isFormControlSimpleCondition(control: FormControl): boolean {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
return control.value.hasOwnProperty('field');
|
||||
}
|
||||
|
||||
removeCondition(control: FormControl) {
|
||||
const formArray = this.isFormControlSimpleCondition(control) ? this.simpleConditionsFormArray : this.compositeConditionsFormArray;
|
||||
const index = (formArray.value as FormControl[]).indexOf(control.value);
|
||||
formArray.removeAt(index);
|
||||
}
|
||||
|
||||
addSimpleCondition() {
|
||||
const newCondition: RuleSimpleCondition = {
|
||||
field: 'cm:name',
|
||||
comparator: 'equals',
|
||||
parameter: ''
|
||||
};
|
||||
this.simpleConditionsFormArray.push(new FormControl(newCondition));
|
||||
}
|
||||
|
||||
addCompositeCondition() {
|
||||
const newCondition: RuleCompositeCondition = {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
compositeConditions: [],
|
||||
simpleConditions: []
|
||||
};
|
||||
this.compositeConditionsFormArray.push(new FormControl(newCondition));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.formSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const readOnly = changes['readOnly'].currentValue;
|
||||
if (readOnly !== undefined && readOnly !== null) {
|
||||
this.readOnly = readOnly;
|
||||
this.setDisabledState(readOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface RuleConditionComparator {
|
||||
name: string;
|
||||
labels: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const ruleConditionComparators: RuleConditionComparator[] = [
|
||||
{
|
||||
name: 'equals',
|
||||
labels: {
|
||||
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.EQUALS',
|
||||
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.EQUALS',
|
||||
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON',
|
||||
special: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'contains',
|
||||
labels: {
|
||||
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.CONTAINS'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'begins',
|
||||
labels: {
|
||||
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.STARTS_WITH'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'ends',
|
||||
labels: {
|
||||
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ENDS_WITH'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'greater_than',
|
||||
labels: {
|
||||
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.GREATER_THAN',
|
||||
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.AFTER'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'less_than',
|
||||
labels: {
|
||||
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.LESS_THAN',
|
||||
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.BEFORE'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'greater_than_equal',
|
||||
labels: {
|
||||
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.GREATER_THAN_OR_EQUAL',
|
||||
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON_OR_AFTER'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'less_than_equal',
|
||||
labels: {
|
||||
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.LESS_THAN_OR_EQUAL',
|
||||
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON_OR_BEFORE'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'instance_of',
|
||||
labels: {
|
||||
type: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.INSTANCE_OF'
|
||||
}
|
||||
}
|
||||
];
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type RuleConditionFieldType = 'string' | 'number' | 'date' | 'type' | 'special' | 'mimeType';
|
||||
|
||||
export interface RuleConditionField {
|
||||
name: string;
|
||||
label: string;
|
||||
type: RuleConditionFieldType;
|
||||
}
|
||||
|
||||
export const comparatorHiddenForConditionFieldType: string[] = ['special', 'mimeType'];
|
||||
|
||||
export const ruleConditionFields: RuleConditionField[] = [
|
||||
{
|
||||
name: 'cm:name',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.NAME',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.SIZE',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'mimetype',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.MIMETYPE',
|
||||
type: 'mimeType'
|
||||
},
|
||||
{
|
||||
name: 'encoding',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.ENCODING',
|
||||
type: 'special'
|
||||
},
|
||||
{
|
||||
name: 'category',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_CATEGORY',
|
||||
type: 'special'
|
||||
},
|
||||
{
|
||||
name: 'tag',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_TAG',
|
||||
type: 'special'
|
||||
},
|
||||
{
|
||||
name: 'aspect',
|
||||
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_ASPECT',
|
||||
type: 'special'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface MimeType {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<form class="aca-rule-simple-condition__form" [formGroup]="form">
|
||||
<mat-form-field class="aca-rule-simple-condition__form__field-input">
|
||||
<mat-select formControlName="field" data-automation-id="field-select"
|
||||
(selectionChange)="onChangeField()">
|
||||
<mat-option *ngIf="!isSelectedFieldKnown" [value]="selectedField.name" data-automation-id="unknown-field-option">
|
||||
{{ selectedField.label }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let field of fields" [value]="field.name">
|
||||
{{ field.label | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="aca-rule-simple-condition__form__comparator-input" [class]="{ hidden: isComparatorHidden }" data-automation-id="comparator-form-field">
|
||||
<mat-select formControlName="comparator" data-automation-id="comparator-select">
|
||||
<mat-option
|
||||
*ngFor="let comparator of selectedFieldComparators"
|
||||
[value]="comparator.name">
|
||||
{{ comparator.labels[this.selectedField?.type || 'equals'] | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="aca-rule-simple-condition__form__parameter-input">
|
||||
<mat-select formControlName="parameter" data-automation-id="simple-condition-value-select" *ngIf="selectedField?.type === 'mimeType'; else valueInput">
|
||||
<mat-option *ngFor="let mimeType of mimeTypes"
|
||||
[value]="mimeType.value">
|
||||
{{ mimeType.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<ng-template #valueInput>
|
||||
<input matInput placeholder="{{ 'ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.VALUE' | translate }}" type="text" formControlName="parameter" data-automation-id="value-input">
|
||||
</ng-template>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
.aca-rule-simple-condition {
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
|
||||
mat-form-field {
|
||||
flex: 2;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
&__comparator-input {
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { categoryMock, mimeTypeMock, simpleConditionUnknownFieldMock } from '../../mock/conditions.mock';
|
||||
import { MimeType } from './rule-mime-types';
|
||||
|
||||
describe('RuleSimpleConditionUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleSimpleConditionUiComponent>;
|
||||
|
||||
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||
|
||||
const changeMatSelectValue = (dataAutomationId: string, value: string) => {
|
||||
const matSelect = getByDataAutomationId(dataAutomationId).nativeElement;
|
||||
matSelect.click();
|
||||
fixture.detectChanges();
|
||||
const matOption = fixture.debugElement.query(By.css(`.mat-option[ng-reflect-value="${value}"]`)).nativeElement;
|
||||
matOption.click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [RuleSimpleConditionUiComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleSimpleConditionUiComponent);
|
||||
});
|
||||
|
||||
it('should default the field to the name, the comparator to equals and the value empty', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('field-select').componentInstance.value).toBe('cm:name');
|
||||
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals');
|
||||
expect(getByDataAutomationId('value-input').nativeElement.value).toBe('');
|
||||
});
|
||||
|
||||
it('should hide the comparator select box if the type of the field is special', () => {
|
||||
fixture.detectChanges();
|
||||
const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement;
|
||||
|
||||
expect(fixture.componentInstance.isComparatorHidden).toBeFalsy();
|
||||
expect(getComputedStyle(comparatorFormField).display).not.toBe('none');
|
||||
|
||||
changeMatSelectValue('field-select', 'category');
|
||||
|
||||
expect(fixture.componentInstance.isComparatorHidden).toBeTruthy();
|
||||
expect(getComputedStyle(comparatorFormField).display).toBe('none');
|
||||
});
|
||||
|
||||
it('should hide the comparator select box if the type of the field is mimeType', () => {
|
||||
fixture.detectChanges();
|
||||
const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement;
|
||||
|
||||
expect(fixture.componentInstance.isComparatorHidden).toBeFalsy();
|
||||
expect(getComputedStyle(comparatorFormField).display).not.toBe('none');
|
||||
|
||||
changeMatSelectValue('field-select', 'mimetype');
|
||||
|
||||
expect(fixture.componentInstance.isComparatorHidden).toBeTruthy();
|
||||
expect(getComputedStyle(comparatorFormField).display).toBe('none');
|
||||
});
|
||||
|
||||
it('should set the comparator to equals if the field is set to a type with different comparators', () => {
|
||||
const onChangeFieldSpy = spyOn(fixture.componentInstance, 'onChangeField').and.callThrough();
|
||||
fixture.detectChanges();
|
||||
changeMatSelectValue('comparator-select', 'contains');
|
||||
|
||||
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('contains');
|
||||
changeMatSelectValue('field-select', 'mimetype');
|
||||
|
||||
expect(onChangeFieldSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals');
|
||||
changeMatSelectValue('field-select', 'size');
|
||||
|
||||
expect(onChangeFieldSpy).toHaveBeenCalledTimes(2);
|
||||
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals');
|
||||
});
|
||||
|
||||
it('should display an additional option for a currently selected unknown field', () => {
|
||||
fixture.componentInstance.writeValue(simpleConditionUnknownFieldMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('field-select').componentInstance.value).toBe(simpleConditionUnknownFieldMock.field);
|
||||
const matSelect = getByDataAutomationId('field-select').nativeElement;
|
||||
matSelect.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unknownOptionMatOption = getByDataAutomationId('unknown-field-option');
|
||||
expect(unknownOptionMatOption).not.toBeNull();
|
||||
expect((unknownOptionMatOption.nativeElement as HTMLElement).innerText.trim()).toBe(simpleConditionUnknownFieldMock.field);
|
||||
});
|
||||
|
||||
it('should remove the option for the unknown field as soon as another option is selected', () => {
|
||||
fixture.componentInstance.writeValue(simpleConditionUnknownFieldMock);
|
||||
fixture.detectChanges();
|
||||
changeMatSelectValue('field-select', 'cm:name');
|
||||
const matSelect = getByDataAutomationId('field-select').nativeElement;
|
||||
matSelect.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unknownOptionMatOption = getByDataAutomationId('unknown-field-option');
|
||||
expect(unknownOptionMatOption).toBeNull();
|
||||
});
|
||||
|
||||
it('should provide select option when mimeType is selected and value filled', () => {
|
||||
const mockMimeTypes: MimeType[] = [
|
||||
{
|
||||
value: 'video/3gpp',
|
||||
label: '3G Video'
|
||||
},
|
||||
{
|
||||
value: 'video/3gpp2',
|
||||
label: '3G2 Video'
|
||||
},
|
||||
{
|
||||
value: 'application/vnd.alfresco.ai.features.v1+json',
|
||||
label: 'AI-Features'
|
||||
},
|
||||
{
|
||||
value: 'application/vnd.alfresco.ai.labels.v1+json',
|
||||
label: 'AI-Labels'
|
||||
}
|
||||
];
|
||||
|
||||
fixture.componentInstance.writeValue(mimeTypeMock);
|
||||
fixture.componentInstance.mimeTypes = mockMimeTypes;
|
||||
|
||||
fixture.componentInstance.onChangeField();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('simple-condition-value-select')).toBeTruthy();
|
||||
expect(fixture.componentInstance.form.get('parameter').value).toEqual(mockMimeTypes[0].value);
|
||||
});
|
||||
|
||||
it('should set value to empty when any condition is selected after mimeType', () => {
|
||||
fixture.componentInstance.writeValue(mimeTypeMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('simple-condition-value-select')).toBeTruthy();
|
||||
|
||||
fixture.componentInstance.writeValue(categoryMock);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('value-input').nativeElement.value).toBe('');
|
||||
});
|
||||
});
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, forwardRef, Input, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||
import { AbstractControl, ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
||||
import { comparatorHiddenForConditionFieldType, RuleConditionField, ruleConditionFields } from './rule-condition-fields';
|
||||
import { RuleConditionComparator, ruleConditionComparators } from './rule-condition-comparators';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { MimeType } from './rule-mime-types';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-simple-condition',
|
||||
templateUrl: './rule-simple-condition.ui-component.html',
|
||||
styleUrls: ['./rule-simple-condition.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-simple-condition' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleSimpleConditionUiComponent)
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RuleSimpleConditionUiComponent implements ControlValueAccessor, OnDestroy {
|
||||
readonly fields = ruleConditionFields;
|
||||
|
||||
form = new FormGroup({
|
||||
field: new FormControl('cm:name'),
|
||||
comparator: new FormControl('equals'),
|
||||
parameter: new FormControl()
|
||||
});
|
||||
|
||||
mimeTypes: MimeType[] = [];
|
||||
|
||||
private _readOnly = false;
|
||||
@Input()
|
||||
get readOnly(): boolean {
|
||||
return this._readOnly;
|
||||
}
|
||||
set readOnly(isReadOnly: boolean) {
|
||||
this.setDisabledState(isReadOnly);
|
||||
}
|
||||
|
||||
constructor(private config: AppConfigService) {
|
||||
this.mimeTypes = this.config.get<Array<MimeType>>('mimeTypes');
|
||||
}
|
||||
|
||||
private formSubscription = this.form.valueChanges.subscribe((value: any) => {
|
||||
this.onChange(value);
|
||||
this.onTouch();
|
||||
});
|
||||
|
||||
get isSelectedFieldKnown(): boolean {
|
||||
const selectedFieldName = this.form.get('field').value;
|
||||
return this.fields.findIndex((field: RuleConditionField) => selectedFieldName === field.name) > -1;
|
||||
}
|
||||
get selectedField(): RuleConditionField {
|
||||
const selectedFieldName = this.form.get('field').value;
|
||||
if (!this.isSelectedFieldKnown) {
|
||||
return {
|
||||
name: selectedFieldName,
|
||||
label: selectedFieldName,
|
||||
type: 'special'
|
||||
};
|
||||
}
|
||||
return this.fields.find((field) => field.name === selectedFieldName);
|
||||
}
|
||||
|
||||
get selectedFieldComparators(): RuleConditionComparator[] {
|
||||
return ruleConditionComparators.filter((comparator) => Object.keys(comparator.labels).includes(this.selectedField.type));
|
||||
}
|
||||
get isComparatorHidden(): boolean {
|
||||
return comparatorHiddenForConditionFieldType.includes(this.selectedField?.type);
|
||||
}
|
||||
get comparatorControl(): AbstractControl {
|
||||
return this.form.get('comparator');
|
||||
}
|
||||
|
||||
private get parameterControl(): AbstractControl<string> {
|
||||
return this.form.get('parameter');
|
||||
}
|
||||
|
||||
onChange: (condition: RuleSimpleCondition) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
writeValue(value: RuleSimpleCondition) {
|
||||
this.form.setValue(value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
this._readOnly = true;
|
||||
this.form.disable();
|
||||
} else {
|
||||
this._readOnly = false;
|
||||
this.form.enable();
|
||||
}
|
||||
}
|
||||
|
||||
onChangeField() {
|
||||
if (!this.selectedFieldComparators.find((comparator) => comparator.name === this.comparatorControl.value)) {
|
||||
this.comparatorControl.setValue('equals');
|
||||
}
|
||||
if (!this.parameterControl.value && this.selectedField?.type === 'mimeType') {
|
||||
this.parameterControl.setValue(this.mimeTypes[0]?.value);
|
||||
} else if (this.parameterControl.value) {
|
||||
this.parameterControl.setValue('');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.formSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { EditRuleDialogOptions, EditRuleDialogUiComponent } from './edit-rule-dialog.ui-component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-condition.ui-component';
|
||||
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
||||
import { RuleActionListUiComponent } from './actions/rule-action-list.ui-component';
|
||||
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
||||
import { RuleOptionsUiComponent } from './options/rule-options.ui-component';
|
||||
import { of, timer } from 'rxjs';
|
||||
|
||||
describe('EditRuleDialogSmartComponent', () => {
|
||||
let fixture: ComponentFixture<EditRuleDialogUiComponent>;
|
||||
|
||||
const dialogRef = {
|
||||
close: jasmine.createSpy('close'),
|
||||
open: jasmine.createSpy('open')
|
||||
};
|
||||
|
||||
const setupBeforeEach = (dialogOptions: EditRuleDialogOptions = { actionDefinitions$: of([]), parameterConstraints$: of([]) }) => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [
|
||||
EditRuleDialogUiComponent,
|
||||
RuleCompositeConditionUiComponent,
|
||||
RuleDetailsUiComponent,
|
||||
RuleTriggersUiComponent,
|
||||
RuleActionListUiComponent,
|
||||
RuleActionUiComponent,
|
||||
RuleOptionsUiComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions }
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(EditRuleDialogUiComponent);
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
describe('No dialog options passed / indifferent', () => {
|
||||
beforeEach(() => {
|
||||
setupBeforeEach();
|
||||
});
|
||||
|
||||
it('should activate the submit button only when a valid state is received', async () => {
|
||||
const submitButton = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||
const ruleDetails = fixture.debugElement.query(By.directive(RuleDetailsUiComponent)).componentInstance as RuleDetailsUiComponent;
|
||||
ruleDetails.formValidationChanged.emit(true);
|
||||
|
||||
fixture.detectChanges();
|
||||
// timer needed to wait for the next tick to avoid ExpressionChangedAfterItHasBeenCheckedError
|
||||
await timer(1).toPromise();
|
||||
fixture.detectChanges();
|
||||
expect(submitButton.disabled).toBeFalsy();
|
||||
ruleDetails.formValidationChanged.emit(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
await timer(1).toPromise();
|
||||
fixture.detectChanges();
|
||||
expect(submitButton.disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a "create" label in the title', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
||||
|
||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE_TITLE');
|
||||
});
|
||||
|
||||
it('should show a "create" label in the submit button', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||
|
||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('With dialog options passed', () => {
|
||||
const dialogOptions: EditRuleDialogOptions = {
|
||||
model: {
|
||||
id: 'rule-id'
|
||||
},
|
||||
actionDefinitions$: of([]),
|
||||
parameterConstraints$: of([])
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupBeforeEach(dialogOptions);
|
||||
});
|
||||
|
||||
it('should show an "update" label in the title', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
||||
|
||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE_TITLE');
|
||||
});
|
||||
|
||||
it('should show an "update" label in the submit button', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||
|
||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
<div mat-dialog-title class="aca-edit-rule-dialog__header">
|
||||
<div class="aca-edit-rule-dialog__header__title" data-automation-id="edit-rule-dialog-title">
|
||||
{{ title | translate }}
|
||||
</div>
|
||||
<button mat-icon-button mat-dialog-close class="aca-edit-rule-dialog__header__close" tabindex="-1">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-dialog-content class="aca-edit-rule-dialog__content">
|
||||
<aca-rule-details
|
||||
[actionDefinitions]="actionDefinitions$ | async"
|
||||
[parameterConstraints]="parameterConstraints$ | async"
|
||||
[value]="model"
|
||||
[nodeId]="nodeId"
|
||||
(formValueChanged)="formValue = $event"
|
||||
(formValidationChanged)="onFormValidChange($event)">
|
||||
</aca-rule-details>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end" class="aca-edit-rule-dialog__footer">
|
||||
<button mat-flat-button mat-dialog-close>{{ 'ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CANCEL' | translate }}</button>
|
||||
<button mat-flat-button color="primary" [disabled]="!formValid" data-automation-id="edit-rule-dialog-submit" (click)="onSubmit()">{{ submitLabel | translate }}</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,49 @@
|
||||
.aca-edit-rule-dialog-container {
|
||||
--edit-rule-dialog-padding: 8px 20px;
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.aca-edit-rule-dialog {
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: var(--edit-rule-dialog-padding);
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--theme-border-color);
|
||||
|
||||
&__title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&__close {
|
||||
& mat-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
&__spinner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin: 0;
|
||||
padding: var(--edit-rule-dialog-padding);
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid var(--theme-border-color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Inject, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Rule } from '../model/rule.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
|
||||
|
||||
export interface EditRuleDialogOptions {
|
||||
model?: Partial<Rule>;
|
||||
nodeId?: string;
|
||||
actionDefinitions$?: Observable<ActionDefinitionTransformed[]>;
|
||||
parameterConstraints$?: Observable<ActionParameterConstraint[]>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'aca-edit-rule-dialog',
|
||||
templateUrl: './edit-rule-dialog.ui-component.html',
|
||||
styleUrls: ['./edit-rule-dialog.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-edit-rule-dialog' }
|
||||
})
|
||||
export class EditRuleDialogUiComponent {
|
||||
formValid = false;
|
||||
model: Partial<Rule>;
|
||||
nodeId = '';
|
||||
actionDefinitions$;
|
||||
parameterConstraints$;
|
||||
formValue: Partial<Rule>;
|
||||
@Output() submitted = new EventEmitter<Partial<Rule>>();
|
||||
|
||||
title = 'ACA_FOLDER_RULES.EDIT_RULE_DIALOG.' + (this.isUpdateMode ? 'UPDATE_TITLE' : 'CREATE_TITLE');
|
||||
submitLabel = 'ACA_FOLDER_RULES.EDIT_RULE_DIALOG.' + (this.isUpdateMode ? 'UPDATE' : 'CREATE');
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: EditRuleDialogOptions) {
|
||||
this.model = this.data?.model || {};
|
||||
this.nodeId = this.data?.nodeId;
|
||||
this.actionDefinitions$ = this.data?.actionDefinitions$;
|
||||
this.parameterConstraints$ = this.data?.parameterConstraints$;
|
||||
}
|
||||
|
||||
get isUpdateMode(): boolean {
|
||||
return !!this.data?.model?.id;
|
||||
}
|
||||
onSubmit() {
|
||||
this.submitted.emit(this.formValue);
|
||||
}
|
||||
|
||||
onFormValidChange(isValid: boolean) {
|
||||
// setTimeout needed to avoid ExpressionChangedAfterItHasBeenCheckedError
|
||||
setTimeout(() => {
|
||||
this.formValid = isValid;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<ng-container [formGroup]="form">
|
||||
<div class="aca-rule-options__option" *ngIf="!readOnly || isAsynchronousChecked">
|
||||
<mat-checkbox
|
||||
formControlName="isAsynchronous"
|
||||
(change)="toggleErrorScriptDropdown($event)"
|
||||
data-automation-id="rule-option-checkbox-asynchronous">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_ASYNCHRONOUS' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-form-field
|
||||
data-automation-id="rule-option-form-field-errorScript"
|
||||
floatLabel="always"
|
||||
[ngClass]="{ 'hide-error-script-dropdown': hideErrorScriptDropdown }">
|
||||
|
||||
<mat-select
|
||||
formControlName="errorScript"
|
||||
placeholder="{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.ERROR_SCRIPT' | translate}}"
|
||||
data-automation-id="rule-option-select-errorScript">
|
||||
|
||||
<mat-option value="">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.NO_SCRIPT' | translate }}</mat-option>
|
||||
<mat-option *ngFor="let option of errorScriptOptions"
|
||||
[value]="option.value">
|
||||
{{ option.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="aca-rule-options__option" *ngIf="!readOnly || isInheritableChecked">
|
||||
<mat-checkbox
|
||||
formControlName="isInheritable"
|
||||
data-automation-id="rule-option-checkbox-inheritable">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_INHERITABLE' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-options__option" *ngIf="!readOnly">
|
||||
<mat-checkbox
|
||||
formControlName="isDisabled"
|
||||
data-automation-id="rule-option-checkbox-disabled">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.DISABLE_RULE' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
.aca-rule-options {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24px;
|
||||
|
||||
&__option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.read-only .mat-checkbox-inner-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hide-error-script-dropdown {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RuleOptionsUiComponent } from './rule-options.ui-component';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { errorScriptConstraintMock } from '../../mock/actions.mock';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
|
||||
describe('RuleOptionsUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleOptionsUiComponent>;
|
||||
let component: RuleOptionsUiComponent;
|
||||
|
||||
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||
|
||||
const toggleMatCheckbox = (dataAutomationId: string) => {
|
||||
((getByDataAutomationId(dataAutomationId).nativeElement as HTMLElement).children[0] as HTMLElement).click();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [FormsModule, ReactiveFormsModule, CoreTestingModule],
|
||||
declarations: [RuleOptionsUiComponent],
|
||||
providers: [{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { floatLabel: 'never' } }]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleOptionsUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.writeValue({
|
||||
isEnabled: true,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
errorScript: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to write to the component', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable').componentInstance.checked).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-disabled').componentInstance.checked).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeTruthy();
|
||||
|
||||
component.writeValue({
|
||||
isEnabled: false,
|
||||
isInheritable: true,
|
||||
isAsynchronous: true,
|
||||
errorScript: ''
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-disabled').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should enable selector when async checkbox is truthy', () => {
|
||||
fixture.detectChanges();
|
||||
toggleMatCheckbox('rule-option-checkbox-asynchronous');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should hide disabled checkbox and unselected checkboxes in read-only mode', () => {
|
||||
component.readOnly = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable')).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-enabled')).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-select-errorScript')).toBeFalsy();
|
||||
|
||||
component.writeValue({
|
||||
isEnabled: false,
|
||||
isInheritable: true,
|
||||
isAsynchronous: true,
|
||||
errorScript: ''
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable')).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-option-checkbox-enabled')).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-option-select-errorScript')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should populate the error script dropdown with scripts', () => {
|
||||
component.writeValue({
|
||||
isEnabled: true,
|
||||
isInheritable: false,
|
||||
isAsynchronous: true,
|
||||
errorScript: ''
|
||||
});
|
||||
component.errorScriptConstraint = errorScriptConstraintMock;
|
||||
fixture.detectChanges();
|
||||
|
||||
(getByDataAutomationId('rule-option-select-errorScript').nativeElement as HTMLElement).click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const matOptions = fixture.debugElement.queryAll(By.css(`.mat-option`));
|
||||
expect(matOptions.length).toBe(3);
|
||||
expect((matOptions[0].nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.NO_SCRIPT');
|
||||
expect((matOptions[1].nativeElement as HTMLElement).innerText.trim()).toBe('Script 1');
|
||||
expect((matOptions[2].nativeElement as HTMLElement).innerText.trim()).toBe('Script 2');
|
||||
});
|
||||
|
||||
it('should always show a label for the error script dropdown even when MAT_FORM_FIELD_DEFAULT_OPTIONS sets floatLabel to never', () => {
|
||||
component.writeValue({
|
||||
isEnabled: true,
|
||||
isInheritable: false,
|
||||
isAsynchronous: true,
|
||||
errorScript: ''
|
||||
});
|
||||
component.errorScriptConstraint = errorScriptConstraintMock;
|
||||
fixture.detectChanges();
|
||||
|
||||
const matFormField = fixture.debugElement.query(By.css(`[data-automation-id="rule-option-form-field-errorScript"] .mat-form-field-label`));
|
||||
fixture.detectChanges();
|
||||
expect(matFormField).not.toBeNull();
|
||||
expect(matFormField.componentInstance['floatLabel']).toBe('always');
|
||||
});
|
||||
});
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, forwardRef, HostBinding, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { AbstractControl, ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { RuleOptions } from '../../model/rule.model';
|
||||
import { ActionParameterConstraint, ConstraintValue } from '../../model/action-parameter-constraint.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-options',
|
||||
templateUrl: 'rule-options.ui-component.html',
|
||||
styleUrls: ['rule-options.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-options' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleOptionsUiComponent)
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RuleOptionsUiComponent implements ControlValueAccessor, OnInit, OnDestroy {
|
||||
form = new FormGroup({
|
||||
isDisabled: new FormControl(),
|
||||
isInheritable: new FormControl(),
|
||||
isAsynchronous: new FormControl(),
|
||||
errorScript: new FormControl()
|
||||
});
|
||||
|
||||
formSubscription = this.form.valueChanges.subscribe((value: any) => {
|
||||
this.isAsynchronousChecked = value.isAsynchronous;
|
||||
this.isInheritableChecked = value.isInheritable;
|
||||
this.onChange({
|
||||
isEnabled: !value.isDisabled,
|
||||
isInheritable: value.isInheritable,
|
||||
isAsynchronous: value.isAsynchronous,
|
||||
errorScript: value.errorScript ?? ''
|
||||
});
|
||||
this.onTouch();
|
||||
});
|
||||
|
||||
hideErrorScriptDropdown = true;
|
||||
|
||||
@Input()
|
||||
errorScriptConstraint: ActionParameterConstraint;
|
||||
|
||||
@HostBinding('class.read-only')
|
||||
readOnly = false;
|
||||
|
||||
onChange: (options: RuleOptions) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
isAsynchronousChecked = false;
|
||||
isInheritableChecked = false;
|
||||
|
||||
errorScriptOptions: ConstraintValue[] = [];
|
||||
|
||||
writeValue(options: RuleOptions) {
|
||||
const isAsynchronousFormControl = this.form.get('isAsynchronous');
|
||||
const errorScriptFormControl = this.form.get('errorScript');
|
||||
this.form.get('isDisabled').setValue(!options.isEnabled);
|
||||
this.form.get('isInheritable').setValue(options.isInheritable);
|
||||
this.form.get('isAsynchronous').setValue(options.isAsynchronous);
|
||||
errorScriptFormControl.setValue(options.errorScript ?? '');
|
||||
if (isAsynchronousFormControl.value) {
|
||||
this.hideErrorScriptDropdown = false;
|
||||
errorScriptFormControl.enable();
|
||||
} else {
|
||||
this.hideErrorScriptDropdown = true;
|
||||
errorScriptFormControl.disable();
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
this.form.disable();
|
||||
this.readOnly = true;
|
||||
} else {
|
||||
this.form.enable();
|
||||
this.readOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.errorScriptOptions = this.errorScriptConstraint?.constraints ?? [];
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.formSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
toggleErrorScriptDropdown(value: MatCheckboxChange) {
|
||||
const formControl: AbstractControl = this.form.get('errorScript');
|
||||
if (value.checked) {
|
||||
this.hideErrorScriptDropdown = false;
|
||||
formControl.enable();
|
||||
} else {
|
||||
this.hideErrorScriptDropdown = true;
|
||||
formControl.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<form class="aca-rule-details__form" [ngClass]="{ 'read-only': readOnly }" [formGroup]="form">
|
||||
|
||||
<ng-container *ngIf="!preview">
|
||||
<div class="aca-rule-details__form__row aca-rule-details__form__name">
|
||||
<label for="rule-details-name-input">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.NAME' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<input
|
||||
id="rule-details-name-input"
|
||||
matInput type="text" formControlName="name" data-automation-id="rule-details-name-input"
|
||||
[placeholder]="'ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.NAME' | translate">
|
||||
<mat-error>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.REQUIRED' | translate }}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__row aca-rule-details__form__description">
|
||||
<label for="rule-details-description-textarea">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.DESCRIPTION' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<textarea
|
||||
id="rule-details-description-textarea"
|
||||
matInput formControlName="description" data-automation-id="rule-details-description-textarea"
|
||||
[placeholder]="descriptionPlaceHolder | translate">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="aca-rule-details__form__row aca-rule-details__form__triggers">
|
||||
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.WHEN' | translate }}</div>
|
||||
<div>
|
||||
<aca-rule-triggers formControlName="triggers" data-automation-id="rule-details-triggers-component"></aca-rule-triggers>
|
||||
<mat-error class="rule-details-error" *ngIf="triggers.hasError('required')">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.INSUFFICIENT_TRIGGERS_SELECTED' | translate }}</mat-error>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__conditions">
|
||||
<aca-rule-composite-condition formControlName="conditions"></aca-rule-composite-condition>
|
||||
<mat-error class="rule-details-error" *ngIf="conditions.hasError('ruleCompositeConditionInvalid')">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.RULE_COMPOSITE_CONDITION_INVALID' | translate }}</mat-error>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__row aca-rule-details__form__actions">
|
||||
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.PERFORM_ACTIONS' | translate }}</div>
|
||||
<aca-rule-action-list
|
||||
formControlName="actions"
|
||||
[actionDefinitions]="actionDefinitions"
|
||||
[parameterConstraints]="parameterConstraints"
|
||||
[readOnly]="readOnly"
|
||||
[nodeId]="nodeId">
|
||||
</aca-rule-action-list>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__row" *ngIf="showOptionsSection">
|
||||
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.OPTIONS' | translate }}</div>
|
||||
<aca-rule-options
|
||||
formControlName="options"
|
||||
data-automation-id="rule-details-options-component"
|
||||
[errorScriptConstraint]="errorScriptConstraint">
|
||||
</aca-rule-options>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,92 @@
|
||||
.aca-rule-details {
|
||||
&__form {
|
||||
position: relative;
|
||||
|
||||
& > div {
|
||||
padding: 8px 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: auto;
|
||||
|
||||
&.aca-rule-details__form__name {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.aca-rule-details__form__description {
|
||||
padding-top: 0;
|
||||
border: none;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
|
||||
& > label,
|
||||
& > .label {
|
||||
font-weight: bold;
|
||||
width: 20%;
|
||||
min-width: 100px;
|
||||
max-width: 150px;
|
||||
padding: 0.75em 0;
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex-grow: 1;
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
font-size: inherit;
|
||||
|
||||
.mat-form-field-infix {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:nth-child(1)) {
|
||||
border-top: 1px solid var(--theme-border-color);
|
||||
}
|
||||
|
||||
&__conditions {
|
||||
width: 100%;
|
||||
|
||||
& > .rule-details-error {
|
||||
margin-left: 16px;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 4em;
|
||||
}
|
||||
|
||||
.rule-details-error {
|
||||
font-size: 75%;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
.aca-rule-action-list {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.read-only,
|
||||
.mat-form-field-disabled {
|
||||
.mat-form-field-underline,
|
||||
.mat-select-arrow-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*:disabled,
|
||||
.mat-select-disabled .mat-select-value {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
||||
import { Rule } from '../model/rule.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-condition.ui-component';
|
||||
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
||||
import { RuleOptionsUiComponent } from './options/rule-options.ui-component';
|
||||
import { RuleActionListUiComponent } from './actions/rule-action-list.ui-component';
|
||||
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
||||
|
||||
describe('RuleDetailsUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleDetailsUiComponent>;
|
||||
let component: RuleDetailsUiComponent;
|
||||
|
||||
const testValue: Partial<Rule> = {
|
||||
id: 'rule-id',
|
||||
name: 'Rule name',
|
||||
description: 'This is the description of the rule',
|
||||
triggers: ['update', 'outbound'],
|
||||
isAsynchronous: true,
|
||||
isInheritable: true,
|
||||
isEnabled: true,
|
||||
errorScript: ''
|
||||
};
|
||||
|
||||
const getHtmlElement = <T>(dataAutomationId: string) =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`))?.nativeElement as T;
|
||||
|
||||
const getComponentInstance = <T>(dataAutomationId: string) =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`))?.componentInstance as T;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [
|
||||
RuleCompositeConditionUiComponent,
|
||||
RuleDetailsUiComponent,
|
||||
RuleTriggersUiComponent,
|
||||
RuleOptionsUiComponent,
|
||||
RuleActionListUiComponent,
|
||||
RuleActionUiComponent
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleDetailsUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should fill the form out with initial values', () => {
|
||||
component.value = testValue;
|
||||
fixture.detectChanges();
|
||||
|
||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||
|
||||
expect(nameInput.value).toBe(testValue.name);
|
||||
expect(descriptionTextarea.value).toBe(testValue.description);
|
||||
expect(ruleTriggersComponent.value).toEqual(testValue.triggers);
|
||||
expect(ruleOptionsComponent.form.value).toEqual({
|
||||
isDisabled: !testValue.isEnabled,
|
||||
isInheritable: testValue.isInheritable,
|
||||
isAsynchronous: testValue.isAsynchronous,
|
||||
errorScript: testValue.errorScript
|
||||
});
|
||||
});
|
||||
|
||||
it('should modify the form if the value input property is modified', () => {
|
||||
fixture.detectChanges();
|
||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||
|
||||
expect(nameInput.value).toBe('');
|
||||
expect(descriptionTextarea.value).toBe('');
|
||||
expect(ruleTriggersComponent.value).toEqual(['inbound']);
|
||||
component.value = testValue;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(nameInput.value).toBe(testValue.name);
|
||||
expect(descriptionTextarea.value).toBe(testValue.description);
|
||||
expect(ruleTriggersComponent.value).toEqual(testValue.triggers);
|
||||
});
|
||||
|
||||
it('should be editable if not read-only', () => {
|
||||
component.value = testValue;
|
||||
component.readOnly = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||
|
||||
expect(nameInput.disabled).toBeFalsy();
|
||||
expect(descriptionTextarea.disabled).toBeFalsy();
|
||||
expect(ruleTriggersComponent.readOnly).toBeFalsy();
|
||||
expect(ruleOptionsComponent.readOnly).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not be editable if read-only', () => {
|
||||
component.value = testValue;
|
||||
component.readOnly = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||
|
||||
expect(nameInput.disabled).toBeTruthy();
|
||||
expect(descriptionTextarea.disabled).toBeTruthy();
|
||||
expect(ruleTriggersComponent.readOnly).toBeTruthy();
|
||||
expect(ruleOptionsComponent.readOnly).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should hide the options section entirely in read-only mode if it has no selected options', () => {
|
||||
component.value = {
|
||||
...testValue,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false
|
||||
};
|
||||
component.readOnly = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
||||
import { Rule, RuleForForm } from '../model/rule.model';
|
||||
import { ruleCompositeConditionValidator } from './validators/rule-composite-condition.validator';
|
||||
import { FolderRulesService } from '../services/folder-rules.service';
|
||||
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||
import { ruleActionsValidator } from './validators/rule-actions.validator';
|
||||
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-details',
|
||||
templateUrl: './rule-details.ui-component.html',
|
||||
styleUrls: ['./rule-details.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-details' }
|
||||
})
|
||||
export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
readOnly: boolean;
|
||||
|
||||
descriptionPlaceHolder: string;
|
||||
|
||||
private _initialValue: RuleForForm = FolderRulesService.emptyRuleForForm;
|
||||
@Input()
|
||||
get value(): Partial<Rule> {
|
||||
let value = this.form ? this.form.value : this._initialValue;
|
||||
if (value.options) {
|
||||
value = {
|
||||
...value,
|
||||
...(value.options ?? FolderRulesService.emptyRuleOptions)
|
||||
};
|
||||
delete value.options;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
set value(newValue: Partial<Rule>) {
|
||||
const newValueForForm: RuleForForm = {
|
||||
id: newValue.id || FolderRulesService.emptyRule.id,
|
||||
name: newValue.name || FolderRulesService.emptyRule.name,
|
||||
description: newValue.description || FolderRulesService.emptyRule.description,
|
||||
triggers: newValue.triggers || FolderRulesService.emptyRule.triggers,
|
||||
conditions: newValue.conditions || FolderRulesService.emptyRule.conditions,
|
||||
actions: newValue.actions || FolderRulesService.emptyRule.actions,
|
||||
options: {
|
||||
isEnabled: typeof newValue.isInheritable == 'boolean' ? newValue.isEnabled : FolderRulesService.emptyRule.isEnabled,
|
||||
isInheritable: newValue.isInheritable || FolderRulesService.emptyRule.isInheritable,
|
||||
isAsynchronous: newValue.isAsynchronous || FolderRulesService.emptyRule.isAsynchronous,
|
||||
errorScript: newValue.errorScript || FolderRulesService.emptyRule.errorScript
|
||||
}
|
||||
};
|
||||
if (this.form) {
|
||||
this.form.setValue(newValueForForm);
|
||||
} else {
|
||||
this._initialValue = newValueForForm;
|
||||
}
|
||||
}
|
||||
@Input()
|
||||
preview: boolean;
|
||||
@Input()
|
||||
actionDefinitions: ActionDefinitionTransformed[] = [];
|
||||
@Input()
|
||||
parameterConstraints: ActionParameterConstraint[] = [];
|
||||
@Input()
|
||||
nodeId = '';
|
||||
|
||||
@Output()
|
||||
formValidationChanged = new EventEmitter<boolean>();
|
||||
@Output()
|
||||
formValueChanged = new EventEmitter<Partial<Rule>>();
|
||||
|
||||
private onDestroy$ = new Subject();
|
||||
form: UntypedFormGroup;
|
||||
|
||||
errorScriptConstraint: ActionParameterConstraint;
|
||||
get name(): UntypedFormControl {
|
||||
return this.form.get('name') as UntypedFormControl;
|
||||
}
|
||||
get description(): UntypedFormControl {
|
||||
return this.form.get('description') as UntypedFormControl;
|
||||
}
|
||||
get triggers(): UntypedFormControl {
|
||||
return this.form.get('triggers') as UntypedFormControl;
|
||||
}
|
||||
|
||||
get conditions(): UntypedFormControl {
|
||||
return this.form.get('conditions') as UntypedFormControl;
|
||||
}
|
||||
|
||||
get showOptionsSection(): boolean {
|
||||
return !this.readOnly || this.value.isAsynchronous || this.value.isInheritable;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.form = new UntypedFormGroup({
|
||||
id: new UntypedFormControl(this.value.id),
|
||||
name: new UntypedFormControl(this.value.name || '', Validators.required),
|
||||
description: new UntypedFormControl(this.value.description || ''),
|
||||
triggers: new UntypedFormControl(this.value.triggers || ['inbound'], Validators.required),
|
||||
conditions: new UntypedFormControl(
|
||||
this.value.conditions || {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
compositeConditions: [],
|
||||
simpleConditions: []
|
||||
},
|
||||
ruleCompositeConditionValidator()
|
||||
),
|
||||
actions: new UntypedFormControl(this.value.actions, [Validators.required, ruleActionsValidator(this.actionDefinitions)]),
|
||||
options: new UntypedFormControl({
|
||||
isEnabled: this.value.isEnabled,
|
||||
isInheritable: this.value.isInheritable,
|
||||
isAsynchronous: this.value.isAsynchronous,
|
||||
errorScript: this.value.errorScript
|
||||
})
|
||||
});
|
||||
|
||||
this.form.statusChanges
|
||||
.pipe(
|
||||
map(() => this.form.valid),
|
||||
distinctUntilChanged(),
|
||||
takeUntil(this.onDestroy$)
|
||||
)
|
||||
.subscribe((value: boolean) => {
|
||||
this.formValidationChanged.emit(value);
|
||||
});
|
||||
this.formValidationChanged.emit(this.form.valid);
|
||||
|
||||
this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||
this.formValueChanged.emit(this.value);
|
||||
});
|
||||
|
||||
if (this.readOnly) {
|
||||
this.form.disable();
|
||||
} else {
|
||||
this.form.enable();
|
||||
}
|
||||
|
||||
this.descriptionPlaceHolder = this.readOnly
|
||||
? 'ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.NO_DESCRIPTION'
|
||||
: 'ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.DESCRIPTION';
|
||||
|
||||
this.errorScriptConstraint = this.parameterConstraints.find(
|
||||
(parameterConstraint: ActionParameterConstraint) => parameterConstraint.name === 'script-ref'
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<div *ngFor="let trigger of triggerOptions">
|
||||
<ng-container *ngIf="readOnly; else checkbox">
|
||||
<div
|
||||
*ngIf="selectedTriggers[trigger]"
|
||||
[attr.data-automation-id]="'rule-trigger-value-' + trigger | lowercase">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.TRIGGERS.' + trigger | uppercase | translate }}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #checkbox>
|
||||
<mat-checkbox
|
||||
[attr.data-automation-id]="'rule-trigger-checkbox-' + trigger | lowercase"
|
||||
[checked]="selectedTriggers[trigger]"
|
||||
(change)="onTriggerChange(trigger, $event.checked)">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.TRIGGERS.' + trigger | uppercase | translate }}
|
||||
</mat-checkbox>
|
||||
</ng-template>
|
||||
</div>
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleTriggersUiComponent } from './rule-triggers.ui-component';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('RuleTriggerUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleTriggersUiComponent>;
|
||||
let component: RuleTriggersUiComponent;
|
||||
|
||||
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||
|
||||
const toggleMatCheckbox = (dataAutomationId: string) => {
|
||||
((getByDataAutomationId(dataAutomationId).nativeElement as HTMLElement).children[0] as HTMLElement).click();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [RuleTriggersUiComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleTriggersUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should default to only the inbound checkbox', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.value).toEqual(['inbound']);
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-inbound').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-update').componentInstance.checked).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-outbound').componentInstance.checked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should change the checked boxes when the value is written to', () => {
|
||||
fixture.detectChanges();
|
||||
component.writeValue(['update', 'outbound']);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.value).toEqual(['update', 'outbound']);
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-inbound').componentInstance.checked).toBeFalsy();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-update').componentInstance.checked).toBeTruthy();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-outbound').componentInstance.checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should update the value when a checkbox is checked', () => {
|
||||
const onChangeSpy = spyOn(component, 'onChange');
|
||||
fixture.detectChanges();
|
||||
toggleMatCheckbox('rule-trigger-checkbox-update');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.value).toEqual(['inbound', 'update']);
|
||||
expect(onChangeSpy).toHaveBeenCalledWith(['inbound', 'update']);
|
||||
});
|
||||
|
||||
it('should update the value when a checkbox is unchecked', () => {
|
||||
const onChangeSpy = spyOn(component, 'onChange');
|
||||
fixture.detectChanges();
|
||||
toggleMatCheckbox('rule-trigger-checkbox-inbound');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.value).toEqual([]);
|
||||
expect(onChangeSpy).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
it('should only show the correct triggers in read only mode', () => {
|
||||
component.setDisabledState(true);
|
||||
component.writeValue(['update', 'outbound']);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-inbound')).toBeNull();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-update')).toBeNull();
|
||||
expect(getByDataAutomationId('rule-trigger-checkbox-outbound')).toBeNull();
|
||||
|
||||
expect(getByDataAutomationId('rule-trigger-value-inbound')).toBeNull();
|
||||
expect(getByDataAutomationId('rule-trigger-value-update')).not.toBeNull();
|
||||
expect(getByDataAutomationId('rule-trigger-value-outbound')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { Component, forwardRef, ViewEncapsulation } from '@angular/core';
|
||||
import { RuleTrigger } from '../../model/rule.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-triggers',
|
||||
templateUrl: './rule-triggers.ui-component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-triggers' },
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => RuleTriggersUiComponent)
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RuleTriggersUiComponent implements ControlValueAccessor {
|
||||
readonly triggerOptions: RuleTrigger[] = ['inbound', 'update', 'outbound'];
|
||||
|
||||
public selectedTriggers: { [key: string]: boolean } = {
|
||||
inbound: true
|
||||
};
|
||||
|
||||
value: RuleTrigger[] = ['inbound'];
|
||||
readOnly = false;
|
||||
|
||||
onChange: (triggers: RuleTrigger[]) => void = () => undefined;
|
||||
onTouch: () => void = () => undefined;
|
||||
|
||||
writeValue(triggers: RuleTrigger[]) {
|
||||
this.value = triggers;
|
||||
this.selectedTriggers = {};
|
||||
this.value.forEach((trigger) => (this.selectedTriggers[trigger] = true));
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouch = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
this.readOnly = isDisabled;
|
||||
}
|
||||
|
||||
onTriggerChange(trigger: RuleTrigger, checked: boolean) {
|
||||
if (checked) {
|
||||
this.value.push(trigger);
|
||||
} else {
|
||||
this.value.splice(
|
||||
this.value.findIndex((t) => t === trigger),
|
||||
1
|
||||
);
|
||||
}
|
||||
this.selectedTriggers[trigger] = checked;
|
||||
this.onTouch();
|
||||
this.onChange([...this.value]);
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FormControl, ValidatorFn } from '@angular/forms';
|
||||
import { ruleActionsValidator, ruleActionValidator } from './rule-actions.validator';
|
||||
import {
|
||||
actionsTransformedListMock,
|
||||
incompleteMandatoryParameterMock,
|
||||
missingMandatoryParameterMock,
|
||||
nonExistentActionDefinitionIdMock,
|
||||
validActionMock,
|
||||
validActionsMock
|
||||
} from '../../mock/actions.mock';
|
||||
|
||||
describe('ruleActionsValidator', () => {
|
||||
let validatorFn: ValidatorFn;
|
||||
|
||||
beforeEach(() => {
|
||||
validatorFn = ruleActionValidator(actionsTransformedListMock);
|
||||
});
|
||||
|
||||
it('should return null for a valid action', () => {
|
||||
const control = new FormControl(validActionMock);
|
||||
expect(validatorFn(control)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return a validation error for an non-existent action definition ID', () => {
|
||||
const control = new FormControl(nonExistentActionDefinitionIdMock);
|
||||
expect(validatorFn(control)).toEqual({ ruleActionInvalid: true });
|
||||
});
|
||||
|
||||
it('should return a validation error for an missing mandatory parameter', () => {
|
||||
const control = new FormControl(missingMandatoryParameterMock);
|
||||
expect(validatorFn(control)).toEqual({ ruleActionInvalid: true });
|
||||
});
|
||||
|
||||
it('should return a validation error for an incomplete mandatory parameter', () => {
|
||||
const control = new FormControl(incompleteMandatoryParameterMock);
|
||||
expect(validatorFn(control)).toEqual({ ruleActionInvalid: true });
|
||||
});
|
||||
|
||||
it('should return null for valid actions', () => {
|
||||
const multipleActionsValidatorFn = ruleActionsValidator(actionsTransformedListMock);
|
||||
const control = new FormControl(validActionsMock);
|
||||
expect(multipleActionsValidatorFn(control)).toBeNull();
|
||||
});
|
||||
});
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
|
||||
import {
|
||||
ActionDefinitionTransformed,
|
||||
ActionParameterDefinitionTransformed,
|
||||
isRuleAction,
|
||||
isRuleActions,
|
||||
RuleAction
|
||||
} from '../../model/rule-action.model';
|
||||
|
||||
const isRuleActionValid = (value: unknown, actionDefinitions: ActionDefinitionTransformed[]): boolean => {
|
||||
const actionDefinition = isRuleAction(value)
|
||||
? actionDefinitions.find((actionDef: ActionDefinitionTransformed) => value.actionDefinitionId === actionDef.id)
|
||||
: undefined;
|
||||
return (
|
||||
isRuleAction(value) &&
|
||||
actionDefinition &&
|
||||
actionDefinition.parameterDefinitions.reduce(
|
||||
(isValid: boolean, paramDef: ActionParameterDefinitionTransformed) => isValid && (!paramDef.mandatory || !!value.params[paramDef.name]),
|
||||
true
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const isRuleActionsValid = (value: unknown, actionDefinitions: ActionDefinitionTransformed[]): boolean =>
|
||||
isRuleActions(value) &&
|
||||
value.reduce((isValid: boolean, currentAction: RuleAction) => isValid && isRuleActionValid(currentAction, actionDefinitions), true);
|
||||
|
||||
export const ruleActionValidator =
|
||||
(actionDefinitions: ActionDefinitionTransformed[]): ValidatorFn =>
|
||||
(control: AbstractControl): ValidationErrors | null =>
|
||||
isRuleActionValid(control.value, actionDefinitions) ? null : { ruleActionInvalid: true };
|
||||
|
||||
export const ruleActionsValidator =
|
||||
(actionDefinitions: ActionDefinitionTransformed[]): ValidatorFn =>
|
||||
(control: AbstractControl): ValidationErrors | null =>
|
||||
isRuleActionsValid(control.value, actionDefinitions) ? null : { ruleActionsInvalid: true };
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
||||
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
|
||||
|
||||
const isCompositeConditionValid = (value: RuleCompositeCondition, isRootCondition = true): boolean => {
|
||||
if (value.compositeConditions.length > 0) {
|
||||
return value.compositeConditions.reduce(
|
||||
(arrayValid: boolean, nestedCondition: RuleCompositeCondition) => arrayValid && isCompositeConditionValid(nestedCondition, false),
|
||||
true
|
||||
);
|
||||
}
|
||||
return !!value.simpleConditions.length || isRootCondition;
|
||||
};
|
||||
|
||||
export const ruleCompositeConditionValidator =
|
||||
(): ValidatorFn =>
|
||||
(control: AbstractControl): ValidationErrors | null =>
|
||||
isCompositeConditionValid(control.value) ? null : { ruleCompositeConditionInvalid: true };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<ng-container *ngFor="let item of items">
|
||||
|
||||
<aca-rule-list-item
|
||||
*ngIf="item.type === 'rule'; else loadMoreRules"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||
tabindex="0"
|
||||
[rule]="item.rule"
|
||||
[isSelected]="item.rule.id === this.selectedRule?.id"
|
||||
[showEnabledToggle]="showEnabledToggles"
|
||||
(click)="onRuleClicked(item.rule)"
|
||||
(enabledChanged)="onEnabledChanged(item.rule, $event)">
|
||||
</aca-rule-list-item>
|
||||
|
||||
<ng-template #loadMoreRules>
|
||||
<div
|
||||
*ngIf="item.type === 'load-more-rules'; else loadMoreRuleSets"
|
||||
tabindex="0"
|
||||
class="aca-rule-list-grouping__non-rule-item load-more"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||
(click)="onClickLoadMoreRules(item.ruleSet)"
|
||||
(keyup.enter)="onClickLoadMoreRules(item.ruleSet)">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULES' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #loadMoreRuleSets>
|
||||
<div
|
||||
*ngIf="item.type === 'load-more-rule-sets'; else loadingRules"
|
||||
tabindex="0"
|
||||
class="aca-rule-list-grouping__non-rule-item load-more"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||
(click)="onClickLoadMoreRuleSets()"
|
||||
(keyup.enter)="onClickLoadMoreRuleSets()">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULE_SETS' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #loadingRules>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="aca-rule-list-grouping__non-rule-item"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)">
|
||||
<mat-spinner mode="indeterminate" [diameter]="16"></mat-spinner>
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOADING_RULES' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</ng-container>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
.aca-rule-list-grouping {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__non-rule-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--theme-disabled-text-color);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
padding: 0.5em 0;
|
||||
|
||||
&.load-more {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--theme-border-color);
|
||||
}
|
||||
|
||||
.mat-spinner {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RuleListGroupingUiComponent } from './rule-list-grouping.ui-component';
|
||||
import { ruleListGroupingItemsMock, rulesMock } from '../../mock/rules.mock';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { AcaFolderRulesModule } from '../../folder-rules.module';
|
||||
|
||||
describe('RuleListGroupingUiComponent', () => {
|
||||
let component: RuleListGroupingUiComponent;
|
||||
let fixture: ComponentFixture<RuleListGroupingUiComponent>;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule, AcaFolderRulesModule],
|
||||
declarations: [RuleListGroupingUiComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleListGroupingUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
});
|
||||
|
||||
it('should display the list of rules', () => {
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
component.items = ruleListGroupingItemsMock;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||
|
||||
expect(rules).toBeTruthy('Could not find rules');
|
||||
expect(rules.length).toBe(2, 'Unexpected number of rules');
|
||||
|
||||
const rule = debugElement.query(By.css('.aca-rule-list-item:first-child'));
|
||||
const name = rule.query(By.css('.aca-rule-list-item__header__name'));
|
||||
const description = rule.query(By.css('.aca-rule-list-item__description'));
|
||||
|
||||
expect(name.nativeElement.textContent).toBe(rulesMock[0].name);
|
||||
expect(description.nativeElement.textContent).toBe(rulesMock[0].description);
|
||||
});
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { Rule } from '../../model/rule.model';
|
||||
import { RuleGroupingItem } from '../../model/rule-grouping-item.model';
|
||||
import { RuleSet } from '../../model/rule-set.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-list-grouping',
|
||||
templateUrl: 'rule-list-grouping.ui-component.html',
|
||||
styleUrls: ['rule-list-grouping.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-list-grouping' }
|
||||
})
|
||||
export class RuleListGroupingUiComponent {
|
||||
@Input()
|
||||
items: RuleGroupingItem[] = [];
|
||||
@Input()
|
||||
selectedRule: Rule = null;
|
||||
@Input()
|
||||
showEnabledToggles = false;
|
||||
|
||||
@Output()
|
||||
selectRule = new EventEmitter<Rule>();
|
||||
@Output()
|
||||
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
||||
@Output()
|
||||
loadMoreRules = new EventEmitter<RuleSet>();
|
||||
@Output()
|
||||
loadMoreRuleSets = new EventEmitter<void>();
|
||||
|
||||
onRuleClicked(rule: Rule): void {
|
||||
this.selectRule.emit(rule);
|
||||
}
|
||||
|
||||
isSelected(rule): boolean {
|
||||
return rule.id === this.selectedRule?.id;
|
||||
}
|
||||
|
||||
onEnabledChanged(rule: Rule, isEnabled: boolean) {
|
||||
this.ruleEnabledChanged.emit([rule, isEnabled]);
|
||||
}
|
||||
|
||||
onClickLoadMoreRules(ruleSet: RuleSet) {
|
||||
this.loadMoreRules.emit(ruleSet);
|
||||
}
|
||||
|
||||
onClickLoadMoreRuleSets() {
|
||||
this.loadMoreRuleSets.emit();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<div class="aca-rule-list-item__header">
|
||||
|
||||
<span class="aca-rule-list-item__header__name">{{ rule.name }}</span>
|
||||
|
||||
<mat-slide-toggle
|
||||
*ngIf="showEnabledToggle"
|
||||
[checked]="rule.isEnabled"
|
||||
(click)="onToggleClick(!rule.isEnabled, $event)">
|
||||
</mat-slide-toggle>
|
||||
|
||||
</div>
|
||||
<div class="aca-rule-list-item__description">{{ rule.description }}</div>
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
.aca-rule-list-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
margin: 6px 0 0;
|
||||
color: rgba(33, 35, 40, 0.7);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
|
||||
&__name {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: var(--theme-selected-button-bg-color);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, HostBinding, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { Rule } from '../../model/rule.model';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-list-item',
|
||||
templateUrl: 'rule-list-item.ui-component.html',
|
||||
styleUrls: ['rule-list-item.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-list-item' }
|
||||
})
|
||||
export class RuleListItemUiComponent {
|
||||
@Input()
|
||||
rule: Rule;
|
||||
@Input()
|
||||
@HostBinding('class.selected')
|
||||
isSelected: boolean;
|
||||
@Input()
|
||||
showEnabledToggle = false;
|
||||
|
||||
@Output()
|
||||
enabledChanged = new EventEmitter<boolean>();
|
||||
|
||||
onToggleClick(isEnabled: boolean, event: Event) {
|
||||
event.stopPropagation();
|
||||
this.rule.isEnabled = !this.rule.isEnabled;
|
||||
this.enabledChanged.emit(isEnabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<div
|
||||
*ngIf="inheritedRuleSetGroupingItems.length > 0"
|
||||
class="aca-rule-list__item"
|
||||
data-automation-id="rule-list-item"
|
||||
[ngClass]="{ expanded: inheritedRuleSetsExpanded }">
|
||||
|
||||
<div
|
||||
tabindex="0"
|
||||
class="aca-rule-list__item__header"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||
(click)="inheritedRuleSetsExpanded = !inheritedRuleSetsExpanded"
|
||||
(keyup.enter)="inheritedRuleSetsExpanded = !inheritedRuleSetsExpanded">
|
||||
|
||||
<span class="aca-rule-list__item__header__title">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.INHERITED_RULES' | translate }}
|
||||
<mat-icon [matTooltip]="'ACA_FOLDER_RULES.RULE_LIST.INHERITED_RULES_WILL_BE_RUN_FIRST' | translate">
|
||||
info
|
||||
</mat-icon>
|
||||
</span>
|
||||
<mat-icon class="aca-rule-list__item__header__icon">
|
||||
{{ inheritedRuleSetsExpanded ? 'expand_more' : 'chevron_right' }}
|
||||
</mat-icon>
|
||||
</div>
|
||||
|
||||
<aca-rule-list-grouping
|
||||
*ngIf="inheritedRuleSetsExpanded"
|
||||
[items]="inheritedRuleSetGroupingItems"
|
||||
[selectedRule]="selectedRule"
|
||||
(selectRule)="onSelectRule($event)"
|
||||
(ruleEnabledChanged)="onRuleEnabledChanged($event)"
|
||||
(loadMoreRules)="onLoadMoreRules($event)"
|
||||
(loadMoreRuleSets)="onLoadMoreRuleSets()">
|
||||
</aca-rule-list-grouping>
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="mainRuleSetGroupingItems.length > 0 || isMainRuleSetLinked"
|
||||
class="aca-rule-list__item"
|
||||
data-automation-id="rule-list-item"
|
||||
[ngClass]="{ expanded: mainRuleSetExpanded }">
|
||||
|
||||
<div
|
||||
tabindex="0"
|
||||
class="aca-rule-list__item__header"
|
||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||
(click)="mainRuleSetExpanded = !mainRuleSetExpanded"
|
||||
(keyup.enter)="mainRuleSetExpanded = !mainRuleSetExpanded">
|
||||
|
||||
<span class="aca-rule-list__item__header__title" data-automation-id="main-rule-set-title">
|
||||
<ng-container *ngIf="isMainRuleSetOwned; else linkedRuleSet">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.OWNED_RULES' | translate }}
|
||||
</ng-container>
|
||||
<ng-template #linkedRuleSet>
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LINKED_RULES' | translate }}
|
||||
</ng-template>
|
||||
</span>
|
||||
|
||||
<ng-container *ngIf="!isMainRuleSetOwned">
|
||||
<mat-icon class="aca-rule-list__item__header__icon" (click)="onRuleSetEditLinkClicked($event)">edit</mat-icon>
|
||||
<mat-icon class="aca-rule-list__item__header__icon" (click)="onRuleSetUnlinkClicked($event)">link_off</mat-icon>
|
||||
</ng-container>
|
||||
|
||||
<mat-icon class="aca-rule-list__item__header__icon">
|
||||
{{ mainRuleSetExpanded ? 'expand_more' : 'chevron_right' }}
|
||||
</mat-icon>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="mainRuleSetExpanded">
|
||||
<aca-rule-list-grouping
|
||||
*ngIf="mainRuleSetGroupingItems.length > 0; else emptyLinkedRuleSet"
|
||||
[items]="mainRuleSetGroupingItems"
|
||||
[selectedRule]="selectedRule"
|
||||
[showEnabledToggles]="isMainRuleSetOwned"
|
||||
(selectRule)="onSelectRule($event)"
|
||||
(ruleEnabledChanged)="onRuleEnabledChanged($event)"
|
||||
(loadMoreRules)="onLoadMoreRules($event)">
|
||||
</aca-rule-list-grouping>
|
||||
|
||||
<ng-template #emptyLinkedRuleSet>
|
||||
<div class="aca-rule-list__item__all-linked-rules-are-disabled">
|
||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.ALL_LINKED_RULES_ARE_DISABLED' | translate }}
|
||||
<button mat-stroked-button [routerLink]="['/nodes', mainRuleSet.owningFolder.id, 'rules']">
|
||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.SEE_IN_FOLDER' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,63 @@
|
||||
.aca-rule-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
gap: 8px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--theme-border-color);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
align-items: stretch;
|
||||
cursor: pointer;
|
||||
color: var(--theme-text-color);
|
||||
user-select: none;
|
||||
font-size: 0.9em;
|
||||
padding: 0.5em 1em;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex: 1;
|
||||
|
||||
.mat-icon {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.aca-rule-list__item__header {
|
||||
border-bottom: 1px solid var(--theme-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
&__all-linked-rules-are-disabled {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: var(--theme-disabled-text-color);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
padding: 0.5em 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RuleListUiComponent } from './rule-list.ui-component';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { RuleListGroupingUiComponent } from '../rule-list-grouping/rule-list-grouping.ui-component';
|
||||
import { RuleListItemUiComponent } from '../rule-list-item/rule-list-item.ui-component';
|
||||
import { ownedRuleSetMock, ruleSetsMock, ruleSetWithLinkMock } from '../../mock/rule-sets.mock';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { owningFolderIdMock } from '../../mock/node.mock';
|
||||
|
||||
describe('RuleListUiComponent', () => {
|
||||
let fixture: ComponentFixture<RuleListUiComponent>;
|
||||
let component: RuleListUiComponent;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
declarations: [RuleListUiComponent, RuleListGroupingUiComponent, RuleListItemUiComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(RuleListUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
|
||||
component.folderId = owningFolderIdMock;
|
||||
component.inheritedRuleSets = ruleSetsMock;
|
||||
});
|
||||
|
||||
it('should show "Rules from current folder" as a title if the main rule set is owned', () => {
|
||||
component.mainRuleSet = ownedRuleSetMock;
|
||||
fixture.detectChanges();
|
||||
|
||||
const mainRuleSetTitleElement = debugElement.query(By.css(`[data-automation-id="main-rule-set-title"]`));
|
||||
expect((mainRuleSetTitleElement.nativeElement as HTMLDivElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_LIST.OWNED_RULES');
|
||||
});
|
||||
|
||||
it('should show "Rules from linked folder" as a title if the main rule set is linked', () => {
|
||||
component.mainRuleSet = ruleSetWithLinkMock;
|
||||
fixture.detectChanges();
|
||||
|
||||
const mainRuleSetTitleElement = debugElement.query(By.css(`[data-automation-id="main-rule-set-title"]`));
|
||||
expect((mainRuleSetTitleElement.nativeElement as HTMLDivElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_LIST.LINKED_RULES');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { RuleSet } from '../../model/rule-set.model';
|
||||
import { Rule } from '../../model/rule.model';
|
||||
import { RuleGroupingItem } from '../../model/rule-grouping-item.model';
|
||||
import { FolderRuleSetsService } from '../../services/folder-rule-sets.service';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-list',
|
||||
templateUrl: './rule-list.ui-component.html',
|
||||
styleUrls: ['./rule-list.ui-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-list' }
|
||||
})
|
||||
export class RuleListUiComponent {
|
||||
@Input()
|
||||
mainRuleSet: RuleSet = null;
|
||||
@Input()
|
||||
folderId: string;
|
||||
@Input()
|
||||
inheritedRuleSets: RuleSet[] = [];
|
||||
@Input()
|
||||
hasMoreRuleSets = false;
|
||||
@Input()
|
||||
ruleSetsLoading = false;
|
||||
@Input()
|
||||
selectedRule = null;
|
||||
|
||||
@Output()
|
||||
loadMoreRuleSets = new EventEmitter<void>();
|
||||
@Output()
|
||||
loadMoreRules = new EventEmitter<RuleSet>();
|
||||
@Output()
|
||||
selectRule = new EventEmitter<Rule>();
|
||||
@Output()
|
||||
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
||||
@Output()
|
||||
ruleSetEditLinkClicked = new EventEmitter<RuleSet>();
|
||||
@Output()
|
||||
ruleSetUnlinkClicked = new EventEmitter<RuleSet>();
|
||||
|
||||
inheritedRuleSetsExpanded = true;
|
||||
mainRuleSetExpanded = true;
|
||||
|
||||
get isMainRuleSetOwned(): boolean {
|
||||
return FolderRuleSetsService.isOwnedRuleSet(this.mainRuleSet, this.folderId);
|
||||
}
|
||||
get isMainRuleSetLinked(): boolean {
|
||||
return FolderRuleSetsService.isLinkedRuleSet(this.mainRuleSet, this.folderId);
|
||||
}
|
||||
|
||||
get mainRuleSetGroupingItems(): RuleGroupingItem[] {
|
||||
return this.mainRuleSet ? this.getRuleSetGroupingItems(this.mainRuleSet, !this.isMainRuleSetOwned) : [];
|
||||
}
|
||||
|
||||
get inheritedRuleSetGroupingItems(): RuleGroupingItem[] {
|
||||
const items = this.inheritedRuleSets.reduce((accumulator: RuleGroupingItem[], currentRuleSet: RuleSet) => {
|
||||
accumulator.push(...this.getRuleSetGroupingItems(currentRuleSet, true));
|
||||
return accumulator;
|
||||
}, []);
|
||||
if (this.ruleSetsLoading || this.hasMoreRuleSets) {
|
||||
items.push({
|
||||
type: this.ruleSetsLoading ? 'loading' : 'load-more-rule-sets'
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
getRuleSetGroupingItems(ruleSet: RuleSet, filterOutDisabledRules: boolean): RuleGroupingItem[] {
|
||||
const items: RuleGroupingItem[] = ruleSet.rules
|
||||
.filter((rule: Rule) => rule.isEnabled || !filterOutDisabledRules)
|
||||
.map((rule: Rule) => ({
|
||||
type: 'rule',
|
||||
rule
|
||||
}));
|
||||
if (ruleSet.loadingRules || ruleSet.hasMoreRules) {
|
||||
items.push(
|
||||
ruleSet.loadingRules
|
||||
? {
|
||||
type: 'loading'
|
||||
}
|
||||
: {
|
||||
type: 'load-more-rules',
|
||||
ruleSet
|
||||
}
|
||||
);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
onLoadMoreRuleSets() {
|
||||
this.loadMoreRuleSets.emit();
|
||||
}
|
||||
|
||||
onLoadMoreRules(ruleSet: RuleSet) {
|
||||
this.loadMoreRules.emit(ruleSet);
|
||||
}
|
||||
|
||||
onSelectRule(rule: Rule) {
|
||||
this.selectRule.emit(rule);
|
||||
}
|
||||
|
||||
onRuleEnabledChanged(event: [Rule, boolean]) {
|
||||
this.ruleEnabledChanged.emit(event);
|
||||
}
|
||||
|
||||
onRuleSetEditLinkClicked(event: Event) {
|
||||
event.stopPropagation();
|
||||
this.ruleSetEditLinkClicked.emit(this.mainRuleSet);
|
||||
}
|
||||
|
||||
onRuleSetUnlinkClicked(event: Event) {
|
||||
event.stopPropagation();
|
||||
this.ruleSetUnlinkClicked.emit(this.mainRuleSet);
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<div mat-dialog-title class="aca-rule-set-picker__header">
|
||||
<div class="aca-rule-set-picker__header__title">
|
||||
{{ 'ACA_FOLDER_RULES.LINK_RULES_DIALOG.TITLE' | translate }}
|
||||
</div>
|
||||
<button mat-icon-button mat-dialog-close class="aca-rule-set-picker__header__close" tabindex="-1">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-dialog-content class="aca-rule-set-picker__content">
|
||||
<adf-content-node-selector-panel
|
||||
class="aca-rule-set-picker__content__node-selector"
|
||||
[currentFolderId]="defaultNodeId"
|
||||
(select)="onNodeSelect($event)"
|
||||
(folderLoaded)="setFolderLoading(false)"
|
||||
(navigationChange)="setFolderLoading(true)"
|
||||
(siteChange)="setFolderLoading(true)">
|
||||
</adf-content-node-selector-panel>
|
||||
|
||||
<div class="aca-rule-set-picker__content__rule-list" [ngClass]="{ justify: rulesLoading$ | async }">
|
||||
<ng-container *ngIf="rulesLoading$ | async; else rulesLoaded">
|
||||
<mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #rulesLoaded>
|
||||
<ng-container *ngIf="hasOwnedRules; else noOwnedRules">
|
||||
<div class="aca-rule-set-picker__content__rule-list__header">
|
||||
{{ 'ACA_FOLDER_RULES.LINK_RULES_DIALOG.LIST_OF_RULES_TO_LINK' | translate }}
|
||||
</div>
|
||||
|
||||
<aca-rule-list-item
|
||||
*ngFor="let rule of (mainRuleSet$ | async).rules"
|
||||
[rule]="rule">
|
||||
</aca-rule-list-item>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #noOwnedRules>
|
||||
<adf-empty-content
|
||||
icon="library_books"
|
||||
[title]="'ACA_FOLDER_RULES.LINK_RULES_DIALOG.EMPTY_RULES_LIST.TITLE' | translate"
|
||||
[subtitle]="'ACA_FOLDER_RULES.LINK_RULES_DIALOG.EMPTY_RULES_LIST.SUBTITLE' | translate">
|
||||
</adf-empty-content>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end" class="aca-rule-set-picker__footer">
|
||||
<button mat-flat-button mat-dialog-close>
|
||||
{{ 'ACA_FOLDER_RULES.LINK_RULES_DIALOG.CANCEL' | translate }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
mat-flat-button color="primary"
|
||||
[disabled]="!hasOwnedRules || isBusy"
|
||||
(click)="onSubmit()">
|
||||
{{ 'ACA_FOLDER_RULES.LINK_RULES_DIALOG.SUBMIT' | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
.aca-rule-set-picker-container {
|
||||
--rule-set-picker-padding: 8px 20px;
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.aca-rule-set-picker {
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: var(--rule-set-picker-padding);
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--theme-border-color);
|
||||
|
||||
&__title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&__close {
|
||||
& mat-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr minmax(250px, 1fr);
|
||||
|
||||
&__node-selector {
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__rule-list {
|
||||
padding: 0 20px 0 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.justify {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__header {
|
||||
color: var(--theme-text-color);
|
||||
font-size: 0.9em;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.aca-rule-list-item {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin: 0;
|
||||
padding: var(--rule-set-picker-padding);
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid var(--theme-border-color);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RuleSetPickerOptions, RuleSetPickerSmartComponent } from './rule-set-picker.smart-component';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { folderToLinkMock, otherFolderMock } from '../mock/node.mock';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
||||
import { of } from 'rxjs';
|
||||
import { ownedRuleSetMock, ruleSetWithLinkMock, ruleSetWithNoRulesToLinkMock, ruleSetWithOwnedRulesToLinkMock } from '../mock/rule-sets.mock';
|
||||
import { ContentApiService } from '@alfresco/aca-shared';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('RuleSetPickerSmartComponent', () => {
|
||||
let fixture: ComponentFixture<RuleSetPickerSmartComponent>;
|
||||
let component: RuleSetPickerSmartComponent;
|
||||
let folderRuleSetsService: FolderRuleSetsService;
|
||||
|
||||
let loadRuleSetsSpy: jasmine.Spy;
|
||||
let callApiSpy: jasmine.Spy;
|
||||
|
||||
const dialogRef = {
|
||||
close: jasmine.createSpy('close'),
|
||||
open: jasmine.createSpy('open')
|
||||
};
|
||||
|
||||
const dialogOptions: RuleSetPickerOptions = {
|
||||
nodeId: 'folder-1-id',
|
||||
defaultNodeId: 'folder-1-id'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions },
|
||||
{
|
||||
provide: ContentApiService,
|
||||
useValue: {
|
||||
getNode: () => {
|
||||
return of({ entry: folderToLinkMock });
|
||||
},
|
||||
getNodeInfo: () => {
|
||||
return of(otherFolderMock);
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
folderRuleSetsService = TestBed.inject(FolderRuleSetsService);
|
||||
fixture = TestBed.createComponent(RuleSetPickerSmartComponent);
|
||||
component = fixture.componentInstance;
|
||||
component['folderRuleSetsService'] = folderRuleSetsService;
|
||||
|
||||
loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.callThrough();
|
||||
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi');
|
||||
callApiSpy
|
||||
.withArgs(`/nodes/${dialogOptions.nodeId}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET')
|
||||
.and.returnValue(Promise.resolve(ownedRuleSetMock))
|
||||
.withArgs(`/nodes/${dialogOptions.nodeId}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')
|
||||
.and.returnValue(Promise.resolve(ownedRuleSetMock))
|
||||
.withArgs(`/nodes/${folderToLinkMock.id}?include=path%2Cproperties%2CallowableOperations%2Cpermissions`, 'GET')
|
||||
.and.returnValue(Promise.resolve({ entry: folderToLinkMock }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should load the rule sets of a node once it has been selected', () => {
|
||||
expect(loadRuleSetsSpy).not.toHaveBeenCalled();
|
||||
component.onNodeSelect([folderToLinkMock]);
|
||||
expect(loadRuleSetsSpy).toHaveBeenCalledWith(folderToLinkMock.id, false);
|
||||
component.onNodeSelect([folderToLinkMock]);
|
||||
expect(loadRuleSetsSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should show an empty list message if a selected folder has no rules', () => {
|
||||
component.mainRuleSet$ = of(ruleSetWithNoRulesToLinkMock);
|
||||
component.rulesLoading$ = of(false);
|
||||
component.onNodeSelect([folderToLinkMock]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
|
||||
expect(items.length).toBe(0);
|
||||
|
||||
const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
|
||||
expect(emptyList).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should show an empty list message if a selected folder has linked rules', () => {
|
||||
component.mainRuleSet$ = of(ruleSetWithLinkMock);
|
||||
component.rulesLoading$ = of(false);
|
||||
component.onNodeSelect([folderToLinkMock]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
|
||||
expect(items.length).toBe(0);
|
||||
|
||||
const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
|
||||
expect(emptyList).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should show a list of items if a selected folder has owned rules', () => {
|
||||
component.mainRuleSet$ = of(ruleSetWithOwnedRulesToLinkMock);
|
||||
component.rulesLoading$ = of(false);
|
||||
component.onNodeSelect([folderToLinkMock]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
|
||||
expect(items.length).toBe(2);
|
||||
|
||||
const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
|
||||
expect(emptyList).toBeNull();
|
||||
});
|
||||
});
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
import { RuleSet } from '../model/rule-set.model';
|
||||
import { BehaviorSubject, combineLatest, from, of, Subject } from 'rxjs';
|
||||
import { finalize, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
|
||||
export interface RuleSetPickerOptions {
|
||||
nodeId: string;
|
||||
defaultNodeId: string;
|
||||
existingRuleSet?: RuleSet;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'aca-rule-set-picker',
|
||||
templateUrl: './rule-set-picker.smart-component.html',
|
||||
styleUrls: ['./rule-set-picker.smart-component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-rule-set-picker' },
|
||||
providers: [FolderRuleSetsService]
|
||||
})
|
||||
export class RuleSetPickerSmartComponent implements OnInit, OnDestroy {
|
||||
nodeId = '-root-';
|
||||
defaultNodeId = '-root-';
|
||||
isBusy = false;
|
||||
existingRuleSet: RuleSet = null;
|
||||
hasOwnedRules = false;
|
||||
|
||||
private selectedNodeId = '';
|
||||
private folderLoading$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
mainRuleSet$ = this.folderRuleSetsService.mainRuleSet$;
|
||||
rulesLoading$ = combineLatest(this.folderRuleSetsService.isLoading$, this.folderLoading$).pipe(
|
||||
map(([rulesLoading, folderLoading]) => rulesLoading || folderLoading)
|
||||
);
|
||||
|
||||
onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public data: RuleSetPickerOptions,
|
||||
private folderRuleSetsService: FolderRuleSetsService,
|
||||
private dialogRef: MatDialogRef<RuleSetPickerSmartComponent>,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
this.nodeId = this.data?.nodeId ?? '-root-';
|
||||
this.defaultNodeId = this.data?.defaultNodeId ?? '-root-';
|
||||
this.existingRuleSet = this.data?.existingRuleSet ?? null;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.mainRuleSet$.pipe(takeUntil(this.onDestroy$)).subscribe((mainRuleSet) => {
|
||||
this.hasOwnedRules = mainRuleSet?.rules.length > 0 && FolderRuleSetsService.isOwnedRuleSet(mainRuleSet, this.selectedNodeId);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
onNodeSelect(nodes: Node[]) {
|
||||
if (nodes?.length && nodes[0].isFolder && nodes[0].id !== this.selectedNodeId) {
|
||||
this.selectedNodeId = nodes[0].id;
|
||||
this.folderRuleSetsService.loadRuleSets(this.selectedNodeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
setFolderLoading(isLoading: boolean) {
|
||||
this.folderLoading$.next(isLoading);
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.isBusy = true;
|
||||
from(this.existingRuleSet ? this.folderRuleSetsService.deleteRuleSetLink(this.nodeId, this.existingRuleSet.id) : of(null))
|
||||
.pipe(
|
||||
switchMap(() => from(this.folderRuleSetsService.createRuleSetLink(this.nodeId, this.selectedNodeId))),
|
||||
finalize(() => {
|
||||
this.isBusy = false;
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
() => {
|
||||
this.handleError();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private handleError() {
|
||||
this.notificationService.showError('ACA_FOLDER_RULES.LINK_RULES_DIALOG.ERRORS.REQUEST_FAILED');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ActionsService } from './actions.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { ActionsApi } from '@alfresco/js-api';
|
||||
import { actionDefListMock, actionsTransformedListMock } from '../mock/actions.mock';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { dummyConstraints, rawConstraints } from '../mock/action-parameter-constraints.mock';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('ActionsService', () => {
|
||||
let actionsService: ActionsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [ActionsService]
|
||||
});
|
||||
|
||||
actionsService = TestBed.inject(ActionsService);
|
||||
});
|
||||
|
||||
it('should load the data into the observable', async () => {
|
||||
spyOn(ActionsApi.prototype, 'listActions').and.returnValue(Promise.resolve(actionDefListMock));
|
||||
const actionsPromise = actionsService.actionDefinitionsListing$.pipe(take(2)).toPromise();
|
||||
|
||||
actionsService.loadActionDefinitions();
|
||||
|
||||
const actionsList = await actionsPromise;
|
||||
expect(actionsList).toEqual(actionsTransformedListMock);
|
||||
});
|
||||
|
||||
it('should set loading to true while the request is being sent', async () => {
|
||||
spyOn(ActionsApi.prototype, 'listActions').and.returnValue(Promise.resolve(actionDefListMock));
|
||||
const loadingTruePromise = actionsService.loading$.pipe(take(2)).toPromise();
|
||||
const loadingFalsePromise = actionsService.loading$.pipe(take(3)).toPromise();
|
||||
|
||||
actionsService.loadActionDefinitions();
|
||||
|
||||
expect(await loadingTruePromise).toBeTrue();
|
||||
expect(await loadingFalsePromise).toBeFalse();
|
||||
});
|
||||
|
||||
it('loadParameterConstraints should send GET request and return formatted observable', async () => {
|
||||
const params = [{}, {}, {}, {}, {}, ['application/json'], ['application/json']];
|
||||
const constraintName = dummyConstraints[0].name;
|
||||
const formattedConstraints = dummyConstraints[0].constraints;
|
||||
|
||||
const apiCallSpy = spyOn<any>(actionsService, 'publicApiCall')
|
||||
.withArgs(`/action-parameter-constraints/${constraintName}`, 'GET', params)
|
||||
.and.returnValue(of(rawConstraints));
|
||||
|
||||
actionsService.getParameterConstraints(constraintName).subscribe((result) => expect(result).toEqual(formattedConstraints));
|
||||
|
||||
expect(apiCallSpy).toHaveBeenCalled();
|
||||
expect(apiCallSpy).toHaveBeenCalledWith(`/action-parameter-constraints/${constraintName}`, 'GET', params);
|
||||
});
|
||||
|
||||
it('loadActionParameterConstraints should load the data into the observable', async () => {
|
||||
spyOn(actionsService, 'getParameterConstraints').and.returnValue(of(dummyConstraints[0].constraints));
|
||||
|
||||
const constraintsPromise = actionsService.parameterConstraints$.pipe(take(2)).toPromise();
|
||||
|
||||
actionsService.loadActionParameterConstraints(actionsTransformedListMock);
|
||||
|
||||
const parameterConstraints = await constraintsPromise;
|
||||
|
||||
expect(parameterConstraints).toEqual(dummyConstraints);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionDefinition, ActionDefinitionEntry, ActionDefinitionList, ActionsApi } from '@alfresco/js-api';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, forkJoin, from, Observable, of } from 'rxjs';
|
||||
import { finalize, map, switchMap } from 'rxjs/operators';
|
||||
import { ActionDefinitionTransformed, ActionParameterDefinitionTransformed } from '../model/rule-action.model';
|
||||
import { ActionParameterConstraint, ConstraintValue } from '../model/action-parameter-constraint.model';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ActionsService {
|
||||
private actionDefinitionsListingSource = new BehaviorSubject<ActionDefinitionTransformed[]>([]);
|
||||
actionDefinitionsListing$ = this.actionDefinitionsListingSource.asObservable();
|
||||
private loadingSource = new BehaviorSubject<boolean>(false);
|
||||
loading$ = this.loadingSource.asObservable();
|
||||
private parameterConstraintsSource = new BehaviorSubject<ActionParameterConstraint[]>([]);
|
||||
parameterConstraints$: Observable<ActionParameterConstraint[]> = this.parameterConstraintsSource.asObservable();
|
||||
|
||||
private _actionsApi: ActionsApi;
|
||||
get actionsApi(): ActionsApi {
|
||||
if (!this._actionsApi) {
|
||||
this._actionsApi = new ActionsApi(this.apiService.getInstance());
|
||||
}
|
||||
return this._actionsApi;
|
||||
}
|
||||
|
||||
constructor(private apiService: AlfrescoApiService) {}
|
||||
|
||||
loadActionDefinitions() {
|
||||
this.loadingSource.next(true);
|
||||
from(this.actionsApi.listActions())
|
||||
.pipe(
|
||||
map((list: ActionDefinitionList) => list.list.entries.map((entry) => this.transformActionDefinition(entry))),
|
||||
finalize(() => this.loadingSource.next(false))
|
||||
)
|
||||
.subscribe((obj: ActionDefinitionTransformed[]) => {
|
||||
this.actionDefinitionsListingSource.next(obj);
|
||||
});
|
||||
}
|
||||
|
||||
getParameterConstraints(constraintName): Observable<ConstraintValue[]> {
|
||||
return from(
|
||||
this.publicApiCall(`/action-parameter-constraints/${constraintName}`, 'GET', [{}, {}, {}, {}, {}, ['application/json'], ['application/json']])
|
||||
).pipe(map((res) => res.entry.constraintValues.map((entry) => this.formatConstraint(entry))));
|
||||
}
|
||||
|
||||
private transformActionDefinition(obj: ActionDefinition | ActionDefinitionEntry): ActionDefinitionTransformed {
|
||||
if (this.isActionDefinitionEntry(obj)) {
|
||||
obj = obj.entry;
|
||||
}
|
||||
return {
|
||||
id: obj.id,
|
||||
name: obj.name ?? '',
|
||||
description: obj.description ?? '',
|
||||
title: obj.title ?? obj.name ?? '',
|
||||
applicableTypes: obj.applicableTypes ?? [],
|
||||
trackStatus: obj.trackStatus ?? false,
|
||||
parameterDefinitions: (obj.parameterDefinitions ?? []).map((paramDef: ActionParameterDefinitionTransformed) =>
|
||||
this.transformActionParameterDefinition(paramDef)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private transformActionParameterDefinition(obj: ActionParameterDefinitionTransformed): ActionParameterDefinitionTransformed {
|
||||
return {
|
||||
name: obj.name ?? '',
|
||||
type: obj.type ?? '',
|
||||
multiValued: obj.multiValued ?? false,
|
||||
mandatory: obj.mandatory ?? false,
|
||||
displayLabel: obj.displayLabel ?? obj.name ?? '',
|
||||
parameterConstraintName: obj.parameterConstraintName ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
private isActionDefinitionEntry(obj): obj is ActionDefinitionEntry {
|
||||
return typeof obj.entry !== 'undefined';
|
||||
}
|
||||
|
||||
private publicApiCall(path: string, httpMethod: string, params?: any[]): Promise<any> {
|
||||
return this.apiService.getInstance().contentClient.callApi(path, httpMethod, ...params);
|
||||
}
|
||||
|
||||
private formatConstraint(constraint): ConstraintValue {
|
||||
return {
|
||||
value: constraint.value ?? '',
|
||||
label: constraint.label ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
loadActionParameterConstraints(actionDefinitions: ActionDefinitionTransformed[]): void {
|
||||
of(actionDefinitions)
|
||||
.pipe(
|
||||
map((actionDefinition) =>
|
||||
actionDefinition
|
||||
.map((obj) => obj.parameterDefinitions)
|
||||
.flat()
|
||||
.filter((parameterDefinition) => parameterDefinition.parameterConstraintName.length > 0)
|
||||
.map((parameterDefinition) => ({
|
||||
name: parameterDefinition.name,
|
||||
parameterConstraintName: parameterDefinition.parameterConstraintName,
|
||||
constraints: null
|
||||
}))
|
||||
),
|
||||
switchMap((parameterDefinitions) =>
|
||||
forkJoin(
|
||||
...parameterDefinitions.map((parameterDefinition) =>
|
||||
this.getParameterConstraints(parameterDefinition.parameterConstraintName).pipe(
|
||||
map((constraints) => ({ name: parameterDefinition.name, constraints }))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe((res) => this.parameterConstraintsSource.next(res));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FolderRuleSetsService } from './folder-rule-sets.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { FolderRulesService } from './folder-rules.service';
|
||||
import { ContentApiService } from '@alfresco/aca-shared';
|
||||
import { getOtherFolderEntryMock, getOwningFolderEntryMock, otherFolderIdMock, owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
||||
import { of } from 'rxjs';
|
||||
import { getDefaultRuleSetResponseMock, getRuleSetsResponseMock, inheritedRuleSetMock, ownedRuleSetMock } from '../mock/rule-sets.mock';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { inheritedRulesMock, linkedRulesMock, ownedRulesMock, ruleMock } from '../mock/rules.mock';
|
||||
|
||||
describe('FolderRuleSetsService', () => {
|
||||
let folderRuleSetsService: FolderRuleSetsService;
|
||||
let folderRulesService: FolderRulesService;
|
||||
let contentApiService: ContentApiService;
|
||||
|
||||
let callApiSpy: jasmine.Spy;
|
||||
let getRulesSpy: jasmine.Spy;
|
||||
let getNodeSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [FolderRuleSetsService, FolderRulesService, ContentApiService]
|
||||
});
|
||||
|
||||
folderRuleSetsService = TestBed.inject(FolderRuleSetsService);
|
||||
folderRulesService = TestBed.inject(FolderRulesService);
|
||||
contentApiService = TestBed.inject(ContentApiService);
|
||||
|
||||
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi')
|
||||
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')
|
||||
.and.returnValue(of(getDefaultRuleSetResponseMock))
|
||||
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET')
|
||||
.and.returnValue(of(getRuleSetsResponseMock));
|
||||
getRulesSpy = spyOn<any>(folderRulesService, 'getRules')
|
||||
.withArgs(jasmine.anything(), 'rule-set-no-links')
|
||||
.and.returnValue(of({ rules: ownedRulesMock, hasMoreRules: false }))
|
||||
.withArgs(jasmine.anything(), 'rule-set-with-link')
|
||||
.and.returnValue(of({ rules: linkedRulesMock, hasMoreRules: false }))
|
||||
.withArgs(jasmine.anything(), 'inherited-rule-set')
|
||||
.and.returnValue(of({ rules: inheritedRulesMock, hasMoreRules: false }));
|
||||
|
||||
getNodeSpy = spyOn(contentApiService, 'getNode')
|
||||
.withArgs(owningFolderIdMock)
|
||||
.and.returnValue(of(getOwningFolderEntryMock))
|
||||
.withArgs(otherFolderIdMock)
|
||||
.and.returnValue(of(getOtherFolderEntryMock));
|
||||
});
|
||||
|
||||
it(`should load node info when loading the node's rule sets`, async () => {
|
||||
// take(2), because: 1 = init of the BehaviourSubject, 2 = in subscribe
|
||||
const folderInfoPromise = folderRuleSetsService.folderInfo$.pipe(take(2)).toPromise();
|
||||
|
||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||
const folderInfo = await folderInfoPromise;
|
||||
|
||||
expect(getNodeSpy).toHaveBeenCalledWith(owningFolderIdMock);
|
||||
expect(folderInfo).toEqual(owningFolderMock);
|
||||
});
|
||||
|
||||
it('should load the main rule set (main or linked)', async () => {
|
||||
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||
const mainRuleSetPromise = folderRuleSetsService.mainRuleSet$.pipe(take(3)).toPromise();
|
||||
|
||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||
const ruleSet = await mainRuleSetPromise;
|
||||
|
||||
expect(callApiSpy).toHaveBeenCalledWith(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET');
|
||||
expect(ruleSet).toEqual(ownedRuleSetMock);
|
||||
});
|
||||
|
||||
it('should load inherited rule sets of a node and filter out owned or inherited rule sets', async () => {
|
||||
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||
const inheritedRuleSetsPromise = folderRuleSetsService.inheritedRuleSets$.pipe(take(3)).toPromise();
|
||||
const hasMoreRuleSetsPromise = folderRuleSetsService.hasMoreRuleSets$.pipe(take(3)).toPromise();
|
||||
|
||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||
const ruleSets = await inheritedRuleSetsPromise;
|
||||
const hasMoreRuleSets = await hasMoreRuleSetsPromise;
|
||||
|
||||
expect(callApiSpy).toHaveBeenCalledWith(
|
||||
`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`,
|
||||
'GET'
|
||||
);
|
||||
expect(ruleSets).toEqual([inheritedRuleSetMock]);
|
||||
expect(getRulesSpy).toHaveBeenCalledWith(owningFolderIdMock, jasmine.anything());
|
||||
expect(hasMoreRuleSets).toEqual(false);
|
||||
});
|
||||
|
||||
it('should select the first rule of the owned rule set of the folder', async () => {
|
||||
const selectRuleSpy = spyOn(folderRulesService, 'selectRule');
|
||||
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||
const ruleSetListingPromise = folderRuleSetsService.inheritedRuleSets$.pipe(take(3)).toPromise();
|
||||
|
||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||
await ruleSetListingPromise;
|
||||
|
||||
expect(selectRuleSpy).toHaveBeenCalledWith(ruleMock('owned-rule-1'));
|
||||
});
|
||||
|
||||
it('should select a different rule when removing a rule', () => {
|
||||
const selectRuleSpy = spyOn(folderRulesService, 'selectRule');
|
||||
folderRuleSetsService['mainRuleSet'] = JSON.parse(JSON.stringify(ownedRuleSetMock));
|
||||
folderRuleSetsService['inheritedRuleSets'] = JSON.parse(JSON.stringify([inheritedRuleSetMock]));
|
||||
|
||||
folderRuleSetsService.removeRuleFromMainRuleSet('owned-rule-1-id');
|
||||
|
||||
expect(selectRuleSpy).toHaveBeenCalledWith(ruleMock('owned-rule-2'));
|
||||
|
||||
selectRuleSpy.calls.reset();
|
||||
folderRuleSetsService.removeRuleFromMainRuleSet('owned-rule-2-id');
|
||||
|
||||
expect(selectRuleSpy).toHaveBeenCalledWith(ruleMock('inherited-rule-1'));
|
||||
});
|
||||
|
||||
it('should send a POST request to create a new link between two folders', () => {
|
||||
folderRuleSetsService.createRuleSetLink('folder-1-id', 'folder-2-id');
|
||||
expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links', 'POST', {
|
||||
id: 'folder-2-id'
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a DELETE request to delete a link between two folders', () => {
|
||||
folderRuleSetsService.deleteRuleSetLink('folder-1-id', 'rule-set-1-id');
|
||||
expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links/rule-set-1-id', 'DELETE');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,255 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, combineLatest, from, Observable, of } from 'rxjs';
|
||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||
import { catchError, finalize, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { RuleSet } from '../model/rule-set.model';
|
||||
import { ContentApiService } from '@alfresco/aca-shared';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { FolderRulesService } from './folder-rules.service';
|
||||
import { Rule } from '../model/rule.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FolderRuleSetsService {
|
||||
public static MAX_RULE_SETS_PER_GET = 100;
|
||||
|
||||
static isOwnedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||
return ruleSet?.owningFolder?.id === nodeId;
|
||||
}
|
||||
static isLinkedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||
return ruleSet?.linkedToBy.indexOf(nodeId) > -1;
|
||||
}
|
||||
static isMainRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||
return this.isOwnedRuleSet(ruleSet, nodeId) || this.isLinkedRuleSet(ruleSet, nodeId);
|
||||
}
|
||||
static isInheritedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||
return !this.isMainRuleSet(ruleSet, nodeId);
|
||||
}
|
||||
|
||||
private currentFolder: NodeInfo = null;
|
||||
private mainRuleSet: RuleSet = null;
|
||||
private inheritedRuleSets: RuleSet[] = [];
|
||||
private hasMoreRuleSets = true;
|
||||
|
||||
private mainRuleSetSource = new BehaviorSubject<RuleSet>(null);
|
||||
private inheritedRuleSetsSource = new BehaviorSubject<RuleSet[]>([]);
|
||||
private hasMoreRuleSetsSource = new BehaviorSubject<boolean>(true);
|
||||
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
|
||||
private isLoadingSource = new BehaviorSubject<boolean>(false);
|
||||
|
||||
mainRuleSet$: Observable<RuleSet> = this.mainRuleSetSource.asObservable();
|
||||
inheritedRuleSets$: Observable<RuleSet[]> = this.inheritedRuleSetsSource.asObservable();
|
||||
hasMoreRuleSets$: Observable<boolean> = this.hasMoreRuleSetsSource.asObservable();
|
||||
folderInfo$: Observable<NodeInfo> = this.folderInfoSource.asObservable();
|
||||
isLoading$: Observable<boolean> = this.isLoadingSource.asObservable();
|
||||
|
||||
selectedRuleSet$ = this.folderRulesService.selectedRule$.pipe(
|
||||
map((rule: Rule) => {
|
||||
if (rule === null) {
|
||||
return null;
|
||||
}
|
||||
if (this.mainRuleSet?.rules.findIndex((r: Rule) => r.id === rule.id) > -1) {
|
||||
return this.mainRuleSet;
|
||||
}
|
||||
return this.inheritedRuleSets.find((ruleSet: RuleSet) => ruleSet.rules.findIndex((r: Rule) => r.id === rule.id) > -1) ?? null;
|
||||
})
|
||||
);
|
||||
|
||||
constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService, private folderRulesService: FolderRulesService) {}
|
||||
|
||||
private callApi(path: string, httpMethod: string, body: object = {}): Promise<any> {
|
||||
// APIs used by this service are still private and not yet available for public use
|
||||
const params = [{}, {}, {}, {}, body, ['application/json'], ['application/json']];
|
||||
return this.apiService.getInstance().contentPrivateClient.callApi(path, httpMethod, ...params);
|
||||
}
|
||||
|
||||
private getMainRuleSet(nodeId: string): Observable<RuleSet> {
|
||||
return from(this.callApi(`/nodes/${nodeId}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')).pipe(
|
||||
catchError((error) => {
|
||||
if (error.status === 404) {
|
||||
return of({ entry: null });
|
||||
}
|
||||
return of(error);
|
||||
}),
|
||||
switchMap((res) => this.formatRuleSet(res.entry))
|
||||
);
|
||||
}
|
||||
|
||||
private getInheritedRuleSets(nodeId: string, skipCount = 0): Observable<RuleSet[]> {
|
||||
return from(
|
||||
this.callApi(
|
||||
`/nodes/${nodeId}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=${skipCount}&maxItems=${FolderRuleSetsService.MAX_RULE_SETS_PER_GET}`,
|
||||
'GET'
|
||||
)
|
||||
).pipe(
|
||||
tap((res) => {
|
||||
if (res?.list?.pagination) {
|
||||
this.hasMoreRuleSets = res.list.pagination.hasMoreItems;
|
||||
}
|
||||
}),
|
||||
switchMap((res) => this.formatRuleSets(res)),
|
||||
map((ruleSets: RuleSet[]) => ruleSets.filter((ruleSet) => FolderRuleSetsService.isInheritedRuleSet(ruleSet, this.currentFolder.id)))
|
||||
);
|
||||
}
|
||||
|
||||
loadRuleSets(nodeId: string, loadInheritedRuleSets = true) {
|
||||
this.isLoadingSource.next(true);
|
||||
this.mainRuleSet = null;
|
||||
this.inheritedRuleSets = [];
|
||||
this.hasMoreRuleSets = true;
|
||||
this.currentFolder = null;
|
||||
this.mainRuleSetSource.next(this.mainRuleSet);
|
||||
this.inheritedRuleSetsSource.next(this.inheritedRuleSets);
|
||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||
this.getNodeInfo(nodeId)
|
||||
.pipe(
|
||||
tap((nodeInfo: NodeInfo) => {
|
||||
this.currentFolder = nodeInfo;
|
||||
this.folderInfoSource.next(this.currentFolder);
|
||||
}),
|
||||
switchMap(() => combineLatest(this.getMainRuleSet(nodeId), loadInheritedRuleSets ? this.getInheritedRuleSets(nodeId) : of([]))),
|
||||
finalize(() => this.isLoadingSource.next(false))
|
||||
)
|
||||
.subscribe(([mainRuleSet, inheritedRuleSets]) => {
|
||||
this.mainRuleSet = mainRuleSet;
|
||||
this.inheritedRuleSets = inheritedRuleSets;
|
||||
this.mainRuleSetSource.next(mainRuleSet);
|
||||
this.inheritedRuleSetsSource.next(inheritedRuleSets);
|
||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||
const ruleToSelect =
|
||||
mainRuleSet?.rules.find((r: Rule) => FolderRuleSetsService.isOwnedRuleSet(mainRuleSet, nodeId) || r.isEnabled) ??
|
||||
inheritedRuleSets.reduce((foundRule: Rule, ruleSet: RuleSet) => foundRule ?? ruleSet.rules.find((r: Rule) => r.isEnabled), null);
|
||||
this.folderRulesService.selectRule(ruleToSelect);
|
||||
});
|
||||
}
|
||||
|
||||
loadMoreInheritedRuleSets() {
|
||||
this.isLoadingSource.next(true);
|
||||
this.getInheritedRuleSets(this.currentFolder.id, this.inheritedRuleSets.length)
|
||||
.pipe(finalize(() => this.isLoadingSource.next(false)))
|
||||
.subscribe((ruleSets) => {
|
||||
this.inheritedRuleSets.push(...ruleSets);
|
||||
this.inheritedRuleSetsSource.next(this.inheritedRuleSets);
|
||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||
});
|
||||
}
|
||||
|
||||
private getNodeInfo(nodeId: string): Observable<NodeInfo> {
|
||||
if (nodeId) {
|
||||
return this.contentApi.getNode(nodeId).pipe(
|
||||
catchError((error) => {
|
||||
if (error.status === 404) {
|
||||
return of({ entry: null });
|
||||
}
|
||||
return of(error);
|
||||
}),
|
||||
map((entry: NodeEntry) => entry.entry)
|
||||
);
|
||||
} else {
|
||||
return of(null);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRuleSets(res: any): Observable<RuleSet[]> {
|
||||
return res?.list?.entries && res.list.entries instanceof Array
|
||||
? combineLatest((res.list.entries as Array<any>).map((entry) => this.formatRuleSet(entry.entry)))
|
||||
: of([]);
|
||||
}
|
||||
|
||||
private formatRuleSet(entry: any): Observable<RuleSet> {
|
||||
if (!entry) {
|
||||
return of(null);
|
||||
}
|
||||
return combineLatest(
|
||||
this.currentFolder?.id === entry.owningFolder ? of(this.currentFolder) : this.getNodeInfo(entry.owningFolder || ''),
|
||||
this.folderRulesService.getRules(this.currentFolder.id || '', entry.id)
|
||||
).pipe(
|
||||
map(([owningFolderNodeInfo, getRulesRes]) => ({
|
||||
id: entry.id,
|
||||
isLinkedTo: entry.isLinkedTo || false,
|
||||
owningFolder: owningFolderNodeInfo,
|
||||
linkedToBy: entry.linkedToBy || [],
|
||||
rules: getRulesRes.rules,
|
||||
hasMoreRules: getRulesRes.hasMoreRules,
|
||||
loadingRules: false
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
removeRuleFromMainRuleSet(ruleId: string) {
|
||||
if (this.mainRuleSet) {
|
||||
const index = this.mainRuleSet.rules.findIndex((rule: Rule) => rule.id === ruleId);
|
||||
if (index > -1) {
|
||||
if (this.mainRuleSet.rules.length > 1) {
|
||||
this.mainRuleSet.rules.splice(index, 1);
|
||||
} else {
|
||||
this.mainRuleSet = null;
|
||||
this.mainRuleSetSource.next(this.mainRuleSet);
|
||||
}
|
||||
this.folderRulesService.selectRule(this.mainRuleSet?.rules[0] ?? this.inheritedRuleSets[0]?.rules[0] ?? null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addOrUpdateRuleInMainRuleSet(newRule: Rule) {
|
||||
if (this.mainRuleSet) {
|
||||
const index = this.mainRuleSet.rules.findIndex((rule: Rule) => rule.id === newRule.id);
|
||||
if (index > -1) {
|
||||
this.mainRuleSet.rules.splice(index, 1, newRule);
|
||||
} else {
|
||||
this.mainRuleSet.rules.push(newRule);
|
||||
}
|
||||
this.folderRulesService.selectRule(newRule);
|
||||
} else {
|
||||
this.refreshMainRuleSet(newRule);
|
||||
}
|
||||
}
|
||||
|
||||
refreshMainRuleSet(ruleToSelect: Rule = null) {
|
||||
this.getMainRuleSet(this.currentFolder.id).subscribe((mainRuleSet: RuleSet) => {
|
||||
this.mainRuleSet = { ...mainRuleSet };
|
||||
this.mainRuleSetSource.next(this.mainRuleSet);
|
||||
if (mainRuleSet) {
|
||||
const ruleToSelectInRuleSet = ruleToSelect ? mainRuleSet.rules.find((rule: Rule) => rule.id === ruleToSelect.id) : mainRuleSet.rules[0];
|
||||
this.folderRulesService.selectRule(ruleToSelectInRuleSet);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async createRuleSetLink(folderIdToCreateLink: string, folderIdToLinkFrom: string): Promise<unknown> {
|
||||
const data = {
|
||||
id: folderIdToLinkFrom
|
||||
};
|
||||
return this.callApi(`/nodes/${folderIdToCreateLink}/rule-set-links`, 'POST', data);
|
||||
}
|
||||
|
||||
async deleteRuleSetLink(folderIdToDeleteLink: string, ruleSetIdToDelete: string): Promise<unknown> {
|
||||
return this.callApi(`/nodes/${folderIdToDeleteLink}/rule-set-links/${ruleSetIdToDelete}`, 'DELETE');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { FolderRulesService } from './folder-rules.service';
|
||||
import {
|
||||
getMoreRulesResponseMock,
|
||||
getRulesResponseMock,
|
||||
manyRulesMock,
|
||||
moreRulesMock,
|
||||
ruleMock,
|
||||
ruleSettingsMock,
|
||||
rulesMock
|
||||
} from '../mock/rules.mock';
|
||||
import { ruleSetMock } from '../mock/rule-sets.mock';
|
||||
import { owningFolderIdMock } from '../mock/node.mock';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
describe('FolderRulesService', () => {
|
||||
let folderRulesService: FolderRulesService;
|
||||
|
||||
let callApiSpy: jasmine.Spy;
|
||||
|
||||
const nodeId = owningFolderIdMock;
|
||||
const ruleSetId = 'rule-set-id';
|
||||
const mockedRule = ruleMock('rule-mock');
|
||||
const { ...mockedRuleWithoutId } = mockedRule;
|
||||
const mockedRuleEntry = { entry: mockedRule };
|
||||
const ruleId = mockedRule.id;
|
||||
const mockedRuleSettingsEntry = { entry: ruleSettingsMock };
|
||||
const key = ruleSettingsMock.key;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [FolderRulesService]
|
||||
});
|
||||
|
||||
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
|
||||
|
||||
callApiSpy = spyOn<any>(folderRulesService, 'callApi');
|
||||
});
|
||||
|
||||
it('should load some rules into a rule set', () => {
|
||||
const ruleSet = ruleSetMock();
|
||||
callApiSpy.and.returnValue(of(getRulesResponseMock));
|
||||
|
||||
expect(ruleSet.rules.length).toBe(0);
|
||||
expect(ruleSet.hasMoreRules).toBeTrue();
|
||||
expect(ruleSet.loadingRules).toBeFalse();
|
||||
|
||||
folderRulesService.loadRules(ruleSet);
|
||||
|
||||
expect(callApiSpy).toHaveBeenCalledWith(`/nodes/${ruleSet.owningFolder.id}/rule-sets/${ruleSet.id}/rules?skipCount=0&maxItems=100`, 'GET');
|
||||
expect(ruleSet.rules.length).toBe(2);
|
||||
expect(ruleSet.rules).toEqual(rulesMock);
|
||||
expect(ruleSet.hasMoreRules).toBeFalse();
|
||||
});
|
||||
|
||||
it('should load more rules if it still has some more to load', () => {
|
||||
const ruleSet = ruleSetMock(rulesMock);
|
||||
callApiSpy.and.returnValue(of(getMoreRulesResponseMock));
|
||||
|
||||
expect(ruleSet.rules.length).toBe(2);
|
||||
expect(ruleSet.hasMoreRules).toBeTrue();
|
||||
|
||||
folderRulesService.loadRules(ruleSet);
|
||||
|
||||
expect(callApiSpy).toHaveBeenCalledWith(`/nodes/${ruleSet.owningFolder.id}/rule-sets/${ruleSet.id}/rules?skipCount=2&maxItems=100`, 'GET');
|
||||
expect(ruleSet.rules.length).toBe(4);
|
||||
expect(ruleSet.rules).toEqual([...rulesMock, ...moreRulesMock]);
|
||||
expect(ruleSet.hasMoreRules).toBeFalse();
|
||||
});
|
||||
|
||||
it('should select the right rule rule after loading', () => {
|
||||
const ruleSet = ruleSetMock(rulesMock);
|
||||
spyOn(folderRulesService, 'getRules').and.returnValue(of({ rules: manyRulesMock, hasMoreRules: false }));
|
||||
const selectedRuleSourceSpy = spyOn(folderRulesService['selectedRuleSource'], 'next');
|
||||
|
||||
folderRulesService.loadRules(ruleSet, 0);
|
||||
expect(selectedRuleSourceSpy).not.toHaveBeenCalled();
|
||||
|
||||
folderRulesService.loadRules(ruleSet, 0, 'first');
|
||||
expect(selectedRuleSourceSpy).toHaveBeenCalledWith(ruleMock('rule1'));
|
||||
selectedRuleSourceSpy.calls.reset();
|
||||
|
||||
folderRulesService.loadRules(ruleSet, 0, 'last');
|
||||
expect(selectedRuleSourceSpy).toHaveBeenCalledWith(ruleMock('rule5'));
|
||||
selectedRuleSourceSpy.calls.reset();
|
||||
selectedRuleSourceSpy.calls.reset();
|
||||
|
||||
folderRulesService.loadRules(ruleSet, 0, ruleMock('rule3'));
|
||||
expect(selectedRuleSourceSpy).toHaveBeenCalledWith(ruleMock('rule3'));
|
||||
});
|
||||
|
||||
it('should delete a rule and return its id', async () => {
|
||||
callApiSpy.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE').and.returnValue(ruleId);
|
||||
const deletedRulePromise = folderRulesService.deletedRuleId$.pipe(take(2)).toPromise();
|
||||
|
||||
folderRulesService.deleteRule(nodeId, ruleId, ruleSetId);
|
||||
const deletedRule = await deletedRulePromise;
|
||||
|
||||
expect(deletedRule).toBeTruthy('rule has not been deleted');
|
||||
expect(deletedRule).toBe(ruleId, 'wrong id of deleted rule');
|
||||
expect(callApiSpy).toHaveBeenCalledTimes(1);
|
||||
expect(callApiSpy).toHaveBeenCalledWith(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE');
|
||||
});
|
||||
|
||||
it('should send correct POST request and return created rule', async () => {
|
||||
callApiSpy
|
||||
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', mockedRuleWithoutId)
|
||||
.and.returnValue(Promise.resolve(mockedRuleEntry));
|
||||
const result = await folderRulesService.createRule(nodeId, mockedRuleWithoutId, ruleSetId);
|
||||
|
||||
expect(result).toEqual(mockedRule);
|
||||
});
|
||||
|
||||
it('should send correct PUT request to update rule and return it', async () => {
|
||||
callApiSpy
|
||||
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', mockedRule)
|
||||
.and.returnValue(Promise.resolve(mockedRuleEntry));
|
||||
|
||||
const result = await folderRulesService.updateRule(nodeId, ruleId, mockedRule, ruleSetId);
|
||||
expect(result).toEqual(mockedRule);
|
||||
});
|
||||
|
||||
it('should send correct GET request and return rule settings', async () => {
|
||||
callApiSpy.withArgs(`/nodes/${nodeId}/rule-settings/${key}`, 'GET').and.returnValue(Promise.resolve(mockedRuleSettingsEntry));
|
||||
|
||||
const result = await folderRulesService.getRuleSettings(nodeId, key);
|
||||
expect(result).toEqual(ruleSettingsMock);
|
||||
});
|
||||
|
||||
it('should send correct PUT request to update rule settings and return them', async () => {
|
||||
callApiSpy.withArgs(`/nodes/${nodeId}/rule-settings/${key}`, 'PUT', ruleSettingsMock).and.returnValue(Promise.resolve(mockedRuleSettingsEntry));
|
||||
|
||||
const result = await folderRulesService.updateRuleSettings(nodeId, key, ruleSettingsMock);
|
||||
expect(result).toEqual(ruleSettingsMock);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,225 @@
|
||||
/*!
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||
import { finalize, map } from 'rxjs/operators';
|
||||
import { Rule, RuleForForm, RuleOptions, RuleSettings } from '../model/rule.model';
|
||||
import { RuleCompositeCondition } from '../model/rule-composite-condition.model';
|
||||
import { RuleSimpleCondition } from '../model/rule-simple-condition.model';
|
||||
import { RuleSet } from '../model/rule-set.model';
|
||||
|
||||
interface GetRulesResult {
|
||||
rules: Rule[];
|
||||
hasMoreRules: boolean;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FolderRulesService {
|
||||
public static MAX_RULES_PER_GET = 100;
|
||||
|
||||
public static get emptyCompositeCondition(): RuleCompositeCondition {
|
||||
return {
|
||||
inverted: false,
|
||||
booleanMode: 'and',
|
||||
compositeConditions: [],
|
||||
simpleConditions: []
|
||||
};
|
||||
}
|
||||
|
||||
public static get emptyRuleOptions(): RuleOptions {
|
||||
return {
|
||||
isEnabled: true,
|
||||
isInheritable: false,
|
||||
isAsynchronous: false,
|
||||
errorScript: ''
|
||||
};
|
||||
}
|
||||
|
||||
public static get emptyRule(): Rule {
|
||||
return {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
isShared: false,
|
||||
triggers: ['inbound'],
|
||||
conditions: FolderRulesService.emptyCompositeCondition,
|
||||
actions: [],
|
||||
...FolderRulesService.emptyRuleOptions
|
||||
};
|
||||
}
|
||||
|
||||
public static get emptyRuleForForm(): RuleForForm {
|
||||
const value = {
|
||||
...FolderRulesService.emptyRule,
|
||||
options: FolderRulesService.emptyRuleOptions
|
||||
};
|
||||
Object.keys(value.options).forEach((key: string) => {
|
||||
delete value[key];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
private selectedRuleSource = new BehaviorSubject<Rule>(null);
|
||||
private deletedRuleIdSource = new BehaviorSubject<string>(null);
|
||||
|
||||
selectedRule$ = this.selectedRuleSource.asObservable();
|
||||
deletedRuleId$: Observable<string> = this.deletedRuleIdSource.asObservable();
|
||||
|
||||
constructor(private apiService: AlfrescoApiService) {}
|
||||
|
||||
private callApi(path: string, httpMethod: string, body: object = {}): Promise<any> {
|
||||
// APIs used by this service are still private and not yet available for public use
|
||||
const params = [{}, {}, {}, {}, body, ['application/json'], ['application/json']];
|
||||
return this.apiService.getInstance().contentPrivateClient.callApi(path, httpMethod, ...params);
|
||||
}
|
||||
|
||||
getRules(owningFolderId: string, ruleSetId: string, skipCount = 0): Observable<GetRulesResult> {
|
||||
return from(
|
||||
this.callApi(
|
||||
`/nodes/${owningFolderId}/rule-sets/${ruleSetId}/rules?skipCount=${skipCount}&maxItems=${FolderRulesService.MAX_RULES_PER_GET}`,
|
||||
'GET'
|
||||
)
|
||||
).pipe(
|
||||
map((res) => ({
|
||||
rules: this.formatRules(res),
|
||||
hasMoreRules: !!res?.list?.pagination?.hasMoreItems
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
loadRules(ruleSet: RuleSet, skipCount = ruleSet.rules.length, selectRule: 'first' | 'last' | Rule = null) {
|
||||
if (ruleSet && !ruleSet.loadingRules) {
|
||||
ruleSet.loadingRules = true;
|
||||
this.getRules(ruleSet.owningFolder.id, ruleSet.id, skipCount)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
ruleSet.loadingRules = false;
|
||||
})
|
||||
)
|
||||
.subscribe((res: GetRulesResult) => {
|
||||
ruleSet.hasMoreRules = res.hasMoreRules;
|
||||
ruleSet.rules.splice(skipCount);
|
||||
ruleSet.rules.push(...res.rules);
|
||||
this.selectRuleInRuleSet(ruleSet, selectRule);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async createRule(nodeId: string, rule: Partial<Rule>, ruleSetId: string = '-default-'): Promise<Rule> {
|
||||
const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', { ...rule });
|
||||
return this.formatRule(response.entry);
|
||||
}
|
||||
|
||||
async updateRule(nodeId: string, ruleId: string, rule: Rule, ruleSetId: string = '-default-'): Promise<Rule> {
|
||||
const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', { ...rule });
|
||||
return this.formatRule(response.entry);
|
||||
}
|
||||
|
||||
deleteRule(nodeId: string, ruleId: string, ruleSetId: string = '-default-') {
|
||||
from(this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE')).subscribe(
|
||||
() => {
|
||||
this.deletedRuleIdSource.next(ruleId);
|
||||
},
|
||||
(error) => {
|
||||
this.deletedRuleIdSource.next(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async getRuleSettings(nodeId: string, key: string = '-isInheritanceEnabled-'): Promise<RuleSettings> {
|
||||
const response = await this.callApi(`/nodes/${nodeId}/rule-settings/${key}`, 'GET');
|
||||
return response.entry;
|
||||
}
|
||||
|
||||
async updateRuleSettings(nodeId: string, key: string, body: RuleSettings): Promise<RuleSettings> {
|
||||
const response = await this.callApi(`/nodes/${nodeId}/rule-settings/${key}`, 'PUT', { ...body });
|
||||
return response.entry;
|
||||
}
|
||||
|
||||
private formatRules(res): Rule[] {
|
||||
return [...res.list.entries.map((entry) => this.formatRule(entry.entry))];
|
||||
}
|
||||
|
||||
private formatRule(obj): Rule {
|
||||
return {
|
||||
id: obj.id,
|
||||
name: obj.name ?? FolderRulesService.emptyRule.name,
|
||||
description: obj.description ?? FolderRulesService.emptyRule.description,
|
||||
isEnabled: obj.isEnabled ?? FolderRulesService.emptyRule.isEnabled,
|
||||
isInheritable: obj.isInheritable ?? FolderRulesService.emptyRule.isInheritable,
|
||||
isAsynchronous: obj.isAsynchronous ?? FolderRulesService.emptyRule.isAsynchronous,
|
||||
errorScript: obj.errorScript ?? FolderRulesService.emptyRule.errorScript,
|
||||
isShared: obj.isShared ?? FolderRulesService.emptyRule.isShared,
|
||||
triggers: obj.triggers ?? FolderRulesService.emptyRule.triggers,
|
||||
conditions: this.formatCompositeCondition(obj.conditions ?? { ...FolderRulesService.emptyRule.conditions }),
|
||||
actions: obj.actions ?? FolderRulesService.emptyRule.actions
|
||||
};
|
||||
}
|
||||
|
||||
private formatCompositeCondition(obj): RuleCompositeCondition {
|
||||
return {
|
||||
inverted: obj.inverted ?? false,
|
||||
booleanMode: obj.booleanMode ?? 'and',
|
||||
compositeConditions: (obj.compositeConditions || []).map((condition) => this.formatCompositeCondition(condition)),
|
||||
simpleConditions: this.parseSimpleCondition(obj.simpleConditions)
|
||||
};
|
||||
}
|
||||
|
||||
private parseSimpleCondition(arr) {
|
||||
if (arr) {
|
||||
if (arr.every((element) => element === null)) {
|
||||
return [];
|
||||
}
|
||||
return arr.map((condition) => this.formatSimpleCondition(condition));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private formatSimpleCondition(obj): RuleSimpleCondition {
|
||||
return {
|
||||
field: obj.field || 'cm:name',
|
||||
comparator: obj.comparator || 'equals',
|
||||
parameter: obj.parameter || ''
|
||||
};
|
||||
}
|
||||
|
||||
selectRule(rule: Rule) {
|
||||
this.selectedRuleSource.next(rule);
|
||||
}
|
||||
|
||||
selectRuleInRuleSet(ruleSet: RuleSet, selectRule: 'first' | 'last' | Rule = null) {
|
||||
if (selectRule === 'first') {
|
||||
this.selectRule(ruleSet.rules[0]);
|
||||
} else if (selectRule === 'last') {
|
||||
this.selectRule(ruleSet.rules[ruleSet.rules.length - 1]);
|
||||
} else if (selectRule) {
|
||||
this.selectRule(selectRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"input": "./src/lib/ui/",
|
||||
"output": "ui"
|
||||
},
|
||||
"folder-rules/assets",
|
||||
"about/assets",
|
||||
"ms-office/assets",
|
||||
"assets"
|
||||
|
||||
@@ -4,7 +4,18 @@
|
||||
"license": "LGPL-3.0",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^14.1.0",
|
||||
"@angular/core": "^14.1.0"
|
||||
"@angular/core": "^14.1.0",
|
||||
"@alfresco/adf-core": "^6.1.0-0",
|
||||
"@alfresco/adf-content-services": "^6.1.0-0",
|
||||
"@alfresco/adf-extensions": "^6.1.0-0",
|
||||
"@alfresco/js-api": "^6.1.0-0",
|
||||
"@angular/animations": "^14.1.3",
|
||||
"@angular/cdk": "^14.1.3",
|
||||
"@angular/forms": "^14.1.3",
|
||||
"@angular/material": "^14.1.3",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"rxjs": "6.6.6",
|
||||
"zone.js": "0.11.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user