Module tree - strom modulů

Module tree - strom modulů

V administračním rozhraní Edee.one se používá navigace pomocí stromů umístěných v různých částech systému. Specifické stromy zobrazují strukturu stránek, souborů nebo kategorií produktů. Ostatní pak jsou položky získané z jednotlivých modulů.

Strom modulu je získán z běžícího modulu (modul implementující ModuleDescriptorProvider)

Obecně modul poskytuje:

  • security descriptor - ten umožní nastavení oprávnění na uzly ve stromu (zaregistruje SecurityModule odpovídající modulu v sitemap)
  • tree descriptor - jeden nebo více stromů zařazených do konkrétních sekcí (slouží také pro definici objektů pro nastavení oprávnění)
  • každá sekce modulu pak strukturu uzlů
  • jednotlivé uzly specifikují unikátní identifikátor, lokalizovaný titulek, akce pro klik, nebo do konktextového menu, požadavky na oprávnění (definují položky na které lze nastavit oprávnění) a ikonu
  • součástí definice uzlu může být i seznam podřízených uzlů

V administraci existují následující sekce (stav od verze 6):

  • content - doplňkový strom pro funkce přímo spojené s editací obsahu, zobrazený pod stromem stránek a souborů v záložce "Obsah"
  • extensions - hlavní strom modulů obsahuje jednotlivé moduly a jejich hlavní funkce - v záložce "Rozšíření"
  • system - strom obsahující systémové funkce, doplňkové nástroje nebo přehledy - v záložce "Nastavení"
  • internal - speciální sekce primárně určena pro doplňkové nastavení v administraci zabezpečení
  • eshop - separátní sekce modulů specifických pro záložku "E-shop"

Zkopírovat odkaz na sekciPostup pro specifikaci vlastního stromu

  • modul musí implementovat ModuleDescriptorProvider
  • instance descriptoru je typicky dostupná v kontextu modulu jako vlastní třída extendující AbstractModuleDescriptor
  • abstratní předek zajistí registraci do security CPS, existuje rozšíření RefreshableModuleDescriptor pro modifikaci stromu za běhu, např po odstranění vybraných entit
  • descriptor vrací třídu ModuleDescription, které drží instance SecurityDescriptor a TreeDescriptor

Volitelně lze definovat položku ve stromě pouze na úrovni stránky modulu a metadata <moduleTree> (viz dále)

Zkopírovat odkaz na sekciDefinice stromu modulu

Strom lze definovat v XML Spring konfiguraci kontextu spojeného s modulem pomocí mtd rozšíření.

Reálný příklad (zápis getModuleName() lze použít pokud je předpoklad vícenásobného použití stejného modulu):

xml
1 <?xml version="1.0" encoding="UTF-8"?>2<beans xmlns="http://www.springframework.org/schema/beans"3       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4       xmlns:mtd="http://www.fg.cz/schema/mtd"5       xmlns:context="http://www.springframework.org/schema/context"6       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-5.3.xsd7		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd8		http://www.fg.cz/schema/mtd http://www.fg.cz/schema/mtd/mtd.xsd">9
10    <context:annotation-config/>11
12    <!-- třída implementující ModuleDescriptor -->13    <bean id="moduleDescriptor" class="com.fg.export.cps.ExportModuleTree"/>14
15    <bean id="moduleDescription" class="com.fg.edee.integration.descriptor.schema.ModuleDescription">16        <property name="securityDescriptor">17            <!-- specifikace security modulu nutného pro nastavení zabezpečení -->18            <mtd:securityDescriptor>19                <mtd:securityModule moduleId="#{ getModuleName() }" moduleDescription="Export."/>20            </mtd:securityDescriptor>21        </property>22        <property name="treeDescriptor">23            <!-- specifikace stromu - specifický treeNode zařazený v dané sekci -->24            <mtd:treeDescriptor>25                <mtd:treeSection id="extensions" localize="true">26                    <mtd:treeNode>27                        <mtd:identifier id="#{ getModuleName() + '-export' }" title="export.title" moduleClass="#{ getClass().getName() }" moduleName="#{ getModuleName() }" localize="true"/>28                        <mtd:state expandability="closed"/>29                        <mtd:onClickAction type="openWindow" url="#{ '/' + getModuleName() + '/reports' }"/>30                        <mtd:security rights="W" moduleId="#{ getModuleName() }"/>31                        <mtd:attributes icon="edee-icon-page" dropTarget="myTreeDnD"/>32                        <!-- kontextové menu uzlu -->33                        <mtd:contextMenu>34                            <mtd:contextMenuItem>35                                <mtd:identifier id="create" title="report.create" moduleClass="#{ getClass().getName() }" moduleName="#{ getModuleName() }" localize="true"/>36                                <mtd:onClickAction type="openWindow" url="#{ '/' + getModuleName() + '/create' }"/>37                                <mtd:security rights="A" moduleId="#{ getModuleName() }">38                                    <mtd:securityIdentifier identifierId="report" identifierName="report"/>39                                </mtd:security>40                                <mtd:attributes icon="icon-edee-new icon-orange"/>41                            </mtd:contextMenuItem>42                        </mtd:contextMenu>43                    </mtd:treeNode>44                </mtd:treeSection>45            </mtd:treeDescriptor>46        </property>47    </bean>48</beans>

Strom modulu lze definovat i z java kódu, případně obohatit statický strom daty z databáze.

Příklad z com.fg.edee.security.cps.descriptor.SecurityAdminModuleDescriptor.buildTreeNodes

java
1 ModuleTreeNode treeNode = new RamjetTreeNodeBuilderChain(parent, rjManager, moduleRootNode, cpsModule)2    .start()3        .identifier(entityId, entity.getName(), false)4        .state(ModuleTreeNodeState.T.closed, false, true, false)5        .onClickAction(OnClickAction.T.openWindow, nodeAbsId, null, paramName, entityId)6        .security("W")7            .securityIdentifier(paramName, null, entityId)8        .attributes(ImmutableMap.<String, Object>of(9                "icon", nodeIcon,10                "dropTarget", "myTreeDnD"11        ))12        .startContextMenu()13            .nextContextMenuItem()14                .identifier(ITEM_IDENTIFIER_ID, itemTitle, true)15                .state(ModuleTreeNodeState.T.closed, false, true, false)16                .onClickAction(OnClickAction.T.ajaxCall, itemAbsId, ITEM_ACTION_EVENT_NAME, paramName, entityId)17                .security("D")18                    .securityIdentifier(paramName, null, entityId)19                .attributes(ImmutableMap.<String, Object>of("icon", REMOVE_ICON))20        .stopContextMenu()21    .end();

Zkopírovat odkaz na sekciExport ramjet stránky do existujícího stromu

Od verze 6 je možné pouze definicí vhodného metadata na stránce v administraci zajistit, že daná stránka se zařadí do stromu modulu do existující sekce.

Definice je prováděna na základě metadata moduleTree. Stránka s tímto nastavením je vložena do daného uzlu, pokud existuje.

Možné parametry:

  • targetNodeId - id cílového uzlu v existujícím stromu
  • title - titulek, resp. lokalizační klíč
  • module a moduleClass - název a třída modulu slouží k načtení správného lokalizačního balíku - klíč z title je očekáván v kontextu daného modulu
  • priority - číslo určující pořadí v dané části uzlu, vyšší priorita je nahoře
  • icon - ikona uzlu
  • security - uzel přebírá security z nadřazeného tagu, je podporována ale pouze nastavení dle acl a module
  • mode - od verze 6.1.1 lze uzel ve stromu zcela nahradit (hodnota replace - výchozí je append)

Příklad administrační stránky z mail modulu

xml
1 <?xml version="1.0" encoding="utf-8"?>2<gui>3    <guidelinePage id="serverStatus">4        <metadata>5            <security>6                <acl>O</acl>7                <module>mail</module>8            </security>9            <moduleTree>10                <targetNodeId>edee-diagnostics</targetNodeId>11                <title>edee.mail.title</title>12                <module>mail</module>13                <moduleClass>com.fg.mail.cps.MailModule</moduleClass>14                <priority>82</priority>15                <iconClass>icon-edee-email icon-blue</iconClass>16            </moduleTree>17        </metadata>18        <structure>19            ...20        </structure>21    </guidelinePage>22</gui>

Bez použití security descriptoru může vyvstat nutnost pro ruční registraci security modulu, pro získání možnosti omezení práv pomocí autorizace.

java
1 // na úrovni modulu2@Override3protected void setupModuleAfterSpringInitialization(Map config) throws CpsException {4    super.setupModuleAfterSpringInitialization(config);5    getModuleSupport().getSecuritySupport().registerSecurityModule(6            new SecurityModule("bulkoperations", "bulkoperations"));7}

Zkopírovat odkaz na sekciPriority ve stromu

Na úrovni uzlů lze definovat priority. To je vhodné především pro dodatečné modifikace.

Priorita je definována jako celé číslo (integer), nejvyšší hodnota je jako první. Pro položky bez specifikace, jsou priority doplněny automaticky v sekvenci od 5 000 (resp. 10 000 na hlavní úrovni) po stovkách.

Zkopírovat odkaz na sekciMožné problémy z praxe:

  • pokud budete dynamicky přidávat druhou úroveň (větve) stromu, pak v první úrovni nesmí být v tagu security zanořen tag securityIdentifier, jinak nebudete moci v dynamicky definovaných položkách nastavovat vlastní ID securityIdentifieru a následně na tyto položky nastavovat práva. Když v tomto případě vlastní ID nastavíte, pak dostanete při startu aplikace chybu. To je záludné, protože u čistě XML konfigurace je toto možné.
  • při vytváření další úrovně je pak nutné vkládat do securityIdentifieru navíc položku itemId
  • pozor při testování nastavení oprávnění na konkrétní uzel - i když jste nastavili uživateli oprávnění na nějakou položku stromu, tak pak jako cílový uživatel tuto položku nevidíte, pokud není nastaveni VIEW oprávnění na celý modul ale s omezenou dědičností.
  • pokud budete chtít provádět kontrolu oprávnění i v aplikaci (např. pro načtení dat do filtru), pak se podívejte na příklad uvedený níže. Proměnná securityId pak odpovídá hodnotě, kterou jste vložili jako ID do securityIdentifieru.
java
1 private static final String SEC_MODULE = "mycustommodule";2
3@Autowired4SecurityService securityService;5
6public boolean isAuthorized(MyEntity entity, SecurityRight right) {7    final SecurityModule securityModule = securityService.resolveModule(SEC_MODULE);8    final User user = LoggedUserHolder.getLoggedInUser();9    return securityService.isAuthorized(10            right,11            securityModule,12            Objects.requireNonNull(entity.getPrimaryKey()).toString(),13            null,14            entity.getLang(),15            user16    );17}

Zkopírovat odkaz na sekciPoužití stromu

Pro zobrazení stromu v administraci slouží komponenta tree případně selectableTree pomocí dataprovideru ModuleTreeDataProvider, který využívá podobu stromu sestavenou v ModuleDescriptorResolver rozšířenou dále o AdditionalModuleTreeNodeManager.