From: Mathieu Baudier Date: Sun, 14 Feb 2021 16:33:15 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/master' into v2.x X-Git-Tag: argeo-suite-2.1.21~1 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=91a19e7f23eac8f073b833e2886bcb7771ff7469;hp=eefe3f8e11391aa2a39b1abf20caa92db64ffdf9 Merge remote-tracking branch 'origin/master' into v2.x Conflicts: pom.xml --- diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java index 1d9b6bc..36ae274 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java @@ -27,7 +27,7 @@ public abstract class AbstractTermsPart extends StyledControl implements Editabl protected final TermsManager termsManager; protected final Typology typology; - protected final boolean editable; + private final boolean editable; private CmsIcon deleteIcon; private CmsIcon addIcon; @@ -37,7 +37,7 @@ public abstract class AbstractTermsPart extends StyledControl implements Editabl private Composite highlight; protected final CmsTheme theme; - + public AbstractTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) { super(parent, style, item); if (item == null) @@ -49,6 +49,10 @@ public abstract class AbstractTermsPart extends StyledControl implements Editabl highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); } + public boolean isEditable() { + return editable; + } + protected void createHighlight(Composite block) { highlight = new Composite(block, SWT.NONE); highlight.setBackground(highlightColor); diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java index 9c3618d..af8df82 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java @@ -55,7 +55,8 @@ public class MultiTermsPart extends AbstractTermsPart { String display = getTermLabel(value); lbl.setText(display); CmsUiUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style); - if (editable) + processTermListLabel(value, lbl); + if (isEditable()) lbl.addMouseListener((MouseDoubleClick) (e) -> { startEditing(); }); @@ -79,7 +80,7 @@ public class MultiTermsPart extends AbstractTermsPart { } } else {// empty - if (editable && !isEditing()) { + if (isEditable() && !isEditing()) { ToolBar toolBar = new ToolBar(placeholder, SWT.HORIZONTAL); ToolItem addItem = new ToolItem(toolBar, SWT.FLAT); styleAdd(addItem); diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java index 0dcf13d..0b5948a 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java @@ -101,17 +101,21 @@ public class SingleTermPart extends AbstractTermsPart { String display = getTermLabel(currentValue); lbl.setText(display); CmsUiUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style); - - lbl.addMouseListener((MouseDoubleClick) (e) -> { - startEditing(); - }); + processTermListLabel(currentValue, lbl); + if (isEditable()) { + lbl.addMouseListener((MouseDoubleClick) (e) -> { + startEditing(); + }); + } } else { - ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL); - ToolItem addItem = new ToolItem(toolBar, SWT.FLAT); - styleAdd(addItem); - addItem.addSelectionListener((Selected) (e) -> { - startEditing(); - }); + if (isEditable()) { + ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL); + ToolItem addItem = new ToolItem(toolBar, SWT.FLAT); + styleAdd(addItem); + addItem.addSelectionListener((Selected) (e) -> { + startEditing(); + }); + } } return block; } diff --git a/core/org.argeo.suite.ui/OSGI-INF/footer.xml b/core/org.argeo.suite.ui/OSGI-INF/footer.xml new file mode 100644 index 0000000..3499b4f --- /dev/null +++ b/core/org.argeo.suite.ui/OSGI-INF/footer.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/core/org.argeo.suite.ui/OSGI-INF/header.xml b/core/org.argeo.suite.ui/OSGI-INF/header.xml index a8fc66d..526d9f9 100644 --- a/core/org.argeo.suite.ui/OSGI-INF/header.xml +++ b/core/org.argeo.suite.ui/OSGI-INF/header.xml @@ -1,9 +1,8 @@ - + - diff --git a/core/org.argeo.suite.ui/bnd.bnd b/core/org.argeo.suite.ui/bnd.bnd index a94d10a..abd4ae2 100644 --- a/core/org.argeo.suite.ui/bnd.bnd +++ b/core/org.argeo.suite.ui/bnd.bnd @@ -1,6 +1,7 @@ Service-Component:\ OSGI-INF/cmsApp.xml,\ OSGI-INF/header.xml,\ +OSGI-INF/footer.xml,\ OSGI-INF/leadPane.xml,\ OSGI-INF/loginScreen.xml,\ OSGI-INF/recentItems.xml,\ diff --git a/core/org.argeo.suite.ui/config/footer.properties b/core/org.argeo.suite.ui/config/footer.properties new file mode 100644 index 0000000..12aca56 --- /dev/null +++ b/core/org.argeo.suite.ui/config/footer.properties @@ -0,0 +1 @@ +service.pid=argeo.suite.ui.footer diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java index d017ee7..68081b4 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java @@ -105,9 +105,9 @@ public class DefaultEditionLayer implements SuiteLayer { } public void destroy(BundleContext bundleContext, Map properties) { - + } - + public void setEntryArea(CmsUiProvider entryArea) { this.entryArea = entryArea; } @@ -159,7 +159,11 @@ public class DefaultEditionLayer implements SuiteLayer { } if (startMaximized) setMaximizedControl(editorArea); - editorArea.setLayout(new GridLayout()); + GridLayout editorAreaLayout = new GridLayout(); + editorAreaLayout.verticalSpacing = 0; + editorAreaLayout.marginBottom = 0; + editorAreaLayout.marginHeight = 0; + editorArea.setLayout(editorAreaLayout); if (DefaultEditionLayer.this.workArea == null) { tabbedArea = createTabbedArea(editorArea, theme); diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultFooter.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultFooter.java new file mode 100644 index 0000000..7821e22 --- /dev/null +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultFooter.java @@ -0,0 +1,38 @@ +package org.argeo.suite.ui; + +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.osgi.framework.BundleContext; + +/** Footer of a standard Argeo Suite application. */ +public class DefaultFooter implements CmsUiProvider { + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + parent.setLayout(CmsUiUtils.noSpaceGridLayout()); + Composite content = new Composite(parent, SWT.NONE); + Control contentControl = createContent(content, context); + + // TODO support and guarantee + + return contentControl; + } + + protected Control createContent(Composite parent, Node context) throws RepositoryException { + return parent; + } + + public void init(BundleContext bundleContext, Map properties) { + } + + public void destroy(BundleContext bundleContext, Map properties) { + + } +} diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultHeader.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultHeader.java index 84a5ebd..91154c3 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultHeader.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultHeader.java @@ -1,8 +1,6 @@ package org.argeo.suite.ui; -import java.util.Dictionary; import java.util.Map; -import java.util.TreeMap; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -13,7 +11,6 @@ import org.argeo.cms.ui.CmsTheme; import org.argeo.cms.ui.CmsUiProvider; import org.argeo.cms.ui.CmsView; import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.util.LangUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; @@ -25,14 +22,10 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.osgi.framework.BundleContext; import org.osgi.framework.wiring.BundleWiring; -import org.osgi.service.cm.ConfigurationException; -import org.osgi.service.cm.ManagedService; -/** HEader of a standard Argeo Suite application. */ -public class DefaultHeader implements CmsUiProvider, ManagedService { +/** Header of a standard Argeo Suite application. */ +public class DefaultHeader implements CmsUiProvider { public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title"; - private Map properties; - private Localized title = null; @Override @@ -92,7 +85,6 @@ public class DefaultHeader implements CmsUiProvider, ManagedService { } public void init(BundleContext bundleContext, Map properties) { - this.properties = new TreeMap<>(properties); String titleStr = (String) properties.get(TITLE_PROPERTY); if (titleStr != null) { if (titleStr.startsWith("%")) { @@ -120,12 +112,6 @@ public class DefaultHeader implements CmsUiProvider, ManagedService { } - @Override - public void updated(Dictionary properties) throws ConfigurationException { - if (properties != null) - this.properties.putAll(LangUtils.dictToStringMap(properties)); - } - public Localized getTitle() { return title; } diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java index fcf7e1f..63a09f1 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java @@ -60,6 +60,7 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { private String pidPrefix; private String headerPid; + private String footerPid; private String leadPanePid; private String loginScreenPid; @@ -99,6 +100,7 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { throw new IllegalArgumentException("PID prefix must be set."); headerPid = pidPrefix + "header"; + footerPid = pidPrefix + "footer"; leadPanePid = pidPrefix + "leadPane"; loginScreenPid = pidPrefix + "loginScreen"; } @@ -151,6 +153,7 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { SuiteUi ui = (SuiteUi) parent; CmsView cmsView = CmsView.getCmsView(parent); CmsUiProvider headerUiProvider = findUiProvider(headerPid); + CmsUiProvider footerUiProvider = findUiProvider(footerPid); Localized appTitle = null; if (headerUiProvider instanceof DefaultHeader) { appTitle = ((DefaultHeader) headerUiProvider).getTitle(); @@ -159,9 +162,12 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login ui.logout(); - refreshPart(headerUiProvider, ui.getHeader(), context); + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); ui.refreshBelowHeader(false); refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context); + if (footerUiProvider != null) + refreshPart(footerUiProvider, ui.getFooter(), context); ui.layout(true, true); setState(ui, LOGIN); } else { @@ -169,7 +175,8 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { state = null; CmsSession cmsSession = cmsView.getCmsSession(); if (ui.getUserDir() == null) { - if (cmsView.isAnonymous()) { + // FIXME NPE on CMSSession when logging in from anonymous + if (cmsSession==null || cmsView.isAnonymous()) { assert publicBasePath != null; ui.initSessions(getRepository(), publicBasePath); } else { @@ -188,13 +195,16 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { if (context == null) context = ui.getUserDir(); - refreshPart(headerUiProvider, ui.getHeader(), context); + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); ui.refreshBelowHeader(true); for (String key : layersByPid.keySet()) { SuiteLayer layer = layersByPid.get(key).get(); ui.addLayer(key, layer); } refreshPart(findUiProvider(leadPanePid), ui.getLeadPane(), context); + if (footerUiProvider != null) + refreshPart(footerUiProvider, ui.getFooter(), context); ui.layout(true, true); setState(parent, state != null ? state : defaultLayerPid); } @@ -219,13 +229,13 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { private CmsUiProvider findUiProvider(String pid) { if (!uiProvidersByPid.containsKey(pid)) - throw new IllegalArgumentException("No UI provider registered as " + pid); + return null; return uiProvidersByPid.get(pid).get(); } private SuiteLayer findLayer(String pid) { if (!layersByPid.containsKey(pid)) - throw new IllegalArgumentException("No UI provider registered as " + pid); + return null; return layersByPid.get(pid).get(); } diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteStyle.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteStyle.java index 5183fa4..0156801 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteStyle.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteStyle.java @@ -4,15 +4,17 @@ import org.argeo.cms.ui.util.CmsStyle; /** Styles used by Argeo Suite work UI. */ public enum SuiteStyle implements CmsStyle { - // Header + // header header, headerTitle, headerMenu, headerMenuItem, - // Recent items + // footer + footer, + // recent items recentItems, - // Lead pane + // lead pane leadPane, leadPaneItem, leadPaneSectionTitle, leadPaneSubSectionTitle, - // Group composite + // group composite titleContainer, titleLabel, subTitleLabel, formLine, formColumn, navigationBar, navigationTitle, navigationButton, - // Forms elements + // forms elements simpleLabel, simpleText, simpleInput, // table titleCell, @@ -20,7 +22,7 @@ public enum SuiteStyle implements CmsStyle { workArea, // tabbed area mainTabBody, mainTabSelected, mainTab, - // Buttons + // buttons inlineButton; @Override diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java index 3c4474f..816c658 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java @@ -26,6 +26,7 @@ class SuiteUi extends Composite { private Localized title; private Composite header; + private Composite footer; private Composite belowHeader; private Composite leadPane; private Composite dynamicArea; @@ -52,6 +53,11 @@ class SuiteUi extends Composite { belowHeader = new Composite(this, SWT.NONE); belowHeader.setLayoutData(CmsUiUtils.fillAll()); + + footer = new Composite(this, SWT.NONE); + footer.setLayout(CmsUiUtils.noSpaceGridLayout()); + CmsUiUtils.style(footer, SuiteStyle.footer); + footer.setLayoutData(CmsUiUtils.fillWidth()); } public void refreshBelowHeader(boolean initApp) { @@ -181,6 +187,10 @@ class SuiteUi extends Composite { return header; } + Composite getFooter() { + return footer; + } + Composite getLeadPane() { return leadPane; } diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TabbedArea.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TabbedArea.java index e1cf154..8ce5fed 100644 --- a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TabbedArea.java +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TabbedArea.java @@ -91,7 +91,9 @@ public class TabbedArea extends Composite { title.setLayoutData(CmsUiUtils.fillWidth()); title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode()))); Node node = section.getNode(); - title.setText(Jcr.getTitle(node)); + String titleStr = Jcr.getTitle(node); + // TODO internationalize + title.setText(titleStr); if (!singleTab) { ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE); ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT); diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java index 814cfb3..044b675 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java @@ -75,6 +75,7 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke private boolean showMainTitle = true; private Integer maxMediaWidth = null; + private String defaultSectionStyle; protected AbstractDbkViewer(Section parent, int style, CmsEditable cmsEditable) { super(parent, style, cmsEditable); @@ -106,6 +107,11 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke CmsUiUtils.clear(section); Node node = section.getNode(); TextSection textSection = (TextSection) section; + String style = node.hasProperty(DbkAttr.role.name()) ? node.getProperty(DbkAttr.role.name()).getString() + : getDefaultSectionStyle(); + if (style != null) + CmsUiUtils.style(textSection, style); + if (node.hasNode(DbkType.title.get())) { boolean showTitle = getMainSection() == section ? showMainTitle : true; if (showTitle) { @@ -143,7 +149,7 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke for (NodeIterator ni = section.getNode().getNodes(DbkType.section.get()); ni.hasNext();) { Node child = ni.nextNode(); if (isDbk(child, DbkType.section)) { - TextSection newSection = new TextSection(section, SWT.NONE, child); + TextSection newSection = newTextSection(section, child); newSection.setLayoutData(CmsUiUtils.fillWidth()); refresh(newSection); } @@ -155,6 +161,11 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke // section.layout(true, true); } + /** To be overridden in order to provide additional SectionPart types */ + protected TextSection newTextSection(Section section, Node node) { + return new TextSection(section, SWT.NONE, node); + } + /** To be overridden in order to provide additional SectionPart types */ protected SectionPart newSectionPart(TextSection textSection, Node node) { return null; @@ -946,6 +957,14 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke this.showMainTitle = showMainTitle; } + public String getDefaultSectionStyle() { + return defaultSectionStyle; + } + + public void setDefaultSectionStyle(String defaultSectionStyle) { + this.defaultSectionStyle = defaultSectionStyle; + } + // FILE UPLOAD LISTENER private class FUL implements FileUploadListener { public void uploadProgress(FileUploadEvent event) { diff --git a/sdk/init/node/ou=roles,ou=node.ldif b/sdk/init/node/ou=roles,ou=node.ldif index d608c2b..3c70185 100644 --- a/sdk/init/node/ou=roles,ou=node.ldif +++ b/sdk/init/node/ou=roles,ou=node.ldif @@ -4,68 +4,20 @@ objectClass: top cn: admin member: uid=root,ou=People,dc=example,dc=com -dn: cn=org.argeo.activities.editor,ou=roles,ou=node +dn: cn=org.argeo.suite.coworker,ou=roles,ou=node objectClass: groupOfNames objectClass: top -cn: org.argeo.activities.editor -member: cn=org.argeo.office.manager,ou=roles,ou=node - -dn: cn=org.argeo.activities.reader,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.activities.reader -member: cn=org.argeo.office.coworker,ou=roles,ou=node - -dn: cn=org.argeo.connect.resources.editor,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.connect.resources.editor -member: cn=org.argeo.office.manager,ou=roles,ou=node - -dn: cn=org.argeo.connect.resources.reader,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.connect.resources.reader -member: cn=org.argeo.office.coworker,ou=roles,ou=node - -dn: cn=org.argeo.office.coworker,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.office.coworker -member: cn=org.argeo.office.manager,ou=roles,ou=node +cn: org.argeo.suite.coworker +member: cn=org.argeo.suite.manager,ou=roles,ou=node member: uid=coworker,ou=People,dc=example,dc=com -dn: cn=org.argeo.office.manager,ou=roles,ou=node +dn: cn=org.argeo.suite.manager,ou=roles,ou=node objectClass: groupOfNames objectClass: top -cn: org.argeo.office.manager +cn: org.argeo.suite.manager member: uid=manager,ou=People,dc=example,dc=com member: uid=root,ou=People,dc=example,dc=com -dn: cn=org.argeo.people.editor,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.people.editor -member: cn=org.argeo.office.manager,ou=roles,ou=node - -dn: cn=org.argeo.people.reader,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.people.reader -member: cn=org.argeo.office.coworker,ou=roles,ou=node - -dn: cn=org.argeo.tracker.editor,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.tracker.editor -member: cn=org.argeo.office.manager,ou=roles,ou=node - -dn: cn=org.argeo.tracker.reader,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.tracker.reader -member: cn=org.argeo.office.coworker,ou=roles,ou=node - dn: cn=userAdmin,ou=roles,ou=node objectClass: groupOfNames objectClass: top