• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 27673718081

17 Jun 2026 07:43AM UTC coverage: 26.735% (-0.2%) from 26.963%
27673718081

Pull #484

github

web-flow
Merge c629e5b2b into 0f6281554
Pull Request #484: Space-ref disambiguation: conflict notice, claimants overview, ref-pinned pages

1545 of 6799 branches covered (22.72%)

Branch coverage included in aggregate %.

3414 of 11750 relevant lines covered (29.06%)

4.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
src/main/java/com/knowledgepixels/nanodash/component/AboutSpacePanel.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.google.common.collect.ArrayListMultimap;
4
import com.google.common.collect.Multimap;
5
import com.knowledgepixels.nanodash.QueryApiAccess;
6
import com.knowledgepixels.nanodash.View;
7
import com.knowledgepixels.nanodash.ViewDisplay;
8
import com.knowledgepixels.nanodash.domain.Space;
9
import org.apache.wicket.markup.html.panel.Panel;
10
import org.nanopub.extra.services.QueryRef;
11

12
/**
13
 * The "About" tab body for a space: its structure (assigned presets, roles, and
14
 * configured view displays; issue #302), its users (members and observers), and
15
 * its sub-units (sub-spaces and maintained resources). Rendered as views
16
 * (query result tables) rather than the live view content.
17
 */
18
public class AboutSpacePanel extends Panel {
19

20
    /**
21
     * The "ℹ️ Info" view: key-value facts about the space (type, alternative IDs,
22
     * dates, latest and root definition). Also shown on the Content tab; surfaced
23
     * here at the top of the About tab. Its query needs both the space IRI
24
     * ({@code space}) and the space's nanopub ({@code spaceNp}) so it can scope to
25
     * a single space-ref.
26
     */
27
    public static final String SPACE_INFO_VIEW = "https://w3id.org/np/RAIh3Cq4K99abRiL2xZphMYTjByvZYATK-d--dI3DD05g/space-info-view-kind";
28

29
    /**
30
     * View that lists all assigned view displays of a resource (built on the
31
     * get-view-displays query Nanodash uses internally). Shown on About tabs
32
     * instead of rendering the assigned views themselves.
33
     */
34
    public static final String VIEW_DISPLAYS_VIEW = "https://w3id.org/np/RA2Wxm80NAzOrXCjIZK9oVJ2vzbycuQtT3wLSGTX5vDVw/view-displays-view";
35

36
    /**
37
     * View listing the presets assigned to a resource (issue #302).
38
     */
39
    public static final String PRESET_ASSIGNMENTS_VIEW = "https://w3id.org/np/RA1kCQYXscKPY_qDvQ-WAKhoVomrTQLSt91JSD4Y03CKI/preset-assignments-view";
40

41
    /**
42
     * View listing a space's assigned roles as a table (role, schema:name, and a
43
     * count of how many of the space's users hold each role), built on the
44
     * list-space-roles query. The built-in Admin role is always the first row.
45
     */
46
    public static final String SPACE_ROLES_VIEW = "https://w3id.org/np/RActRjB7sOPegsSWdlJPNXvEfqEuomlO9HvjmBuXMqfQw/space-roles-view";
47

48
    /**
49
     * View listing a space's members (admins, maintainers, members) with their
50
     * highest role tier, built on the list-space-members query. Observer-tier
51
     * members are excluded.
52
     */
53
    public static final String MEMBERS_VIEW = "https://w3id.org/np/RAFmrcEqniX7mqrZQ8c4OCqjU-3wwKjLhE2glXAZKFNT0/space-members-view";
54

55
    /**
56
     * View listing a space's non-approved role claims (agents holding an
57
     * admin/maintainer/member-tier role instantiation that is not in the
58
     * validated state — a self-assigned or otherwise ungranted claim awaiting
59
     * approval), built on the list-space-non-approved query. Carries a per-row
60
     * "approve" action (visible to members and above) that re-asserts the same
61
     * role triple, signed by the approver.
62
     */
63
    public static final String NON_APPROVED_VIEW = "https://w3id.org/np/RAk5nU4XXK1-CzrE2mcSLcRmJXnANVWgkQ2dNUQzDVR64/pending-members-view";
64

65
    /**
66
     * View listing a space's observers (members whose highest tier is observer,
67
     * i.e. holding no admin/maintainer/member role), built on the
68
     * list-space-observers query.
69
     */
70
    public static final String OBSERVERS_VIEW = "https://w3id.org/np/RA7uYe4WmsJCk3NaMTE8p0YofcMcRfjLyM8PGLaADkH18/space-observers-view";
71

72
    /**
73
     * View listing a space's direct sub-spaces with their types, built on the
74
     * list-sub-spaces query.
75
     */
76
    public static final String SUB_SPACES_VIEW = "https://w3id.org/np/RAoO4uYnSJXCHr0F5uVC5sYpkD4HOh-lsHQj4k3epqeH0/sub-spaces-view";
77

78
    /**
79
     * View listing the resources maintained by a space, built on the
80
     * list-maintained-resources query.
81
     */
82
    public static final String MAINTAINED_RESOURCES_VIEW = "https://w3id.org/np/RA4mk84QDZ4njO5N1sryJ5_wbyG7bAisL4BIAsNoISt-Y/maintained-resources-view";
83

84
    /**
85
     * @param id    the Wicket markup id
86
     * @param space the space whose About listings to render
87
     */
88
    public AboutSpacePanel(String id, Space space) {
89
        this(id, space, null);
×
90
    }
×
91

92
    /**
93
     * @param id            the Wicket markup id
94
     * @param space         the space whose About listings to render
95
     * @param effectiveRoot the root nanopub of the specific ref to scope the ref-aware views
96
     *                      (Info, Observers) to, or null to use the representative ref. See
97
     *                      docs/space-ref-identity.md.
98
     */
99
    public AboutSpacePanel(String id, Space space, String effectiveRoot) {
100
        super(id);
×
101

102
        // The ref (root definition) every ref-scoped listing on this page is keyed to: the
103
        // pinned claimant when viewing one (?root=), otherwise this space's representative ref.
104
        // Falls back to null when no ref root is known (pre-v3 data), in which case each table
105
        // below uses its IRI-keyed query. See docs/space-ref-identity.md.
106
        final String refRoot = effectiveRoot != null ? effectiveRoot : space.getRefRootId();
×
107

108
        // "Structure" section: key-value info, presets, assigned roles, view displays.
109

110
        // The info view leads the section (to the left of the presets). Its query is
111
        // scoped to a single space-ref, so it needs both the space IRI and the ref's
112
        // nanopub — bind them as separate params. When viewing a specific claimant the
113
        // effectiveRoot pins it to that ref's root definition.
114
        View infoView = View.get(SPACE_INFO_VIEW);
×
115
        Multimap<String, String> infoParams = ArrayListMultimap.create();
×
116
        infoParams.put("space", space.getId());
×
117
        infoParams.put("spaceNp", effectiveRoot != null ? effectiveRoot : space.getNanopubId());
×
118
        add(QueryResultTableBuilder.create("info", new QueryRef(infoView.getQuery().getQueryId(), infoParams), new ViewDisplay(infoView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).build());
×
119

120
        View presetsView = View.get(PRESET_ASSIGNMENTS_VIEW);
×
121
        add(QueryResultTableBuilder.create("presets", new QueryRef(presetsView.getQuery().getQueryId(), "resource", space.getId()), new ViewDisplay(presetsView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).build());
×
122

123
        View rolesView = View.get(SPACE_ROLES_VIEW);
×
124
        // Drive the roles table from the ref-scoped query (scoped by npa:forSpaceRef) so a
125
        // ?root=-pinned page shows only that ref's roles, falling back to the view's IRI-keyed
126
        // query when the ref root is unknown. The view nanopub is left untouched. Pass the space
127
        // as resource/context so the roles view's per-entry action button (publish a role
128
        // assignment) renders with param_space prefilled, mirroring the "+" button on the content
129
        // tab's role list. postPublishTab keeps the user on the About tab after publishing a
130
        // role/assignment, so they see the updated roles list (the presets/view-display views
131
        // intentionally fall through to the Content tab, where their effect shows).
132
        QueryRef rolesQuery = (refRoot != null && !refRoot.isEmpty())
×
133
                ? new QueryRef(QueryApiAccess.LIST_SPACE_ROLES_REF, "root_np", refRoot)
×
134
                : new QueryRef(rolesView.getQuery().getQueryId(), "space", space.getId());
×
135
        add(QueryResultTableBuilder.create("roles", rolesQuery, new ViewDisplay(rolesView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).postPublishTab("about").build());
×
136

137
        View vdView = View.get(VIEW_DISPLAYS_VIEW);
×
138
        add(QueryResultTableBuilder.create("viewdisplays", new QueryRef(vdView.getQuery().getQueryId(), "resource", space.getId()), new ViewDisplay(vdView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).build());
×
139

140
        // "Users" section: admins/maintainers/members, then observers.
141

142
        // Drive the members table from the ref-scoped query (scoped by npa:forSpaceRef) so a
143
        // ?root=-pinned page shows only that ref's members, falling back to the view's IRI-keyed
144
        // query when the ref root is unknown. The view nanopub is left untouched.
145
        View membersView = View.get(MEMBERS_VIEW);
×
146
        QueryRef membersQuery = (refRoot != null && !refRoot.isEmpty())
×
147
                ? new QueryRef(QueryApiAccess.LIST_SPACE_MEMBERS_REF, "root_np", refRoot)
×
148
                : new QueryRef(membersView.getQuery().getQueryId(), "space", space.getId());
×
149
        add(QueryResultTableBuilder.create("members", membersQuery, new ViewDisplay(membersView)).build());
×
150

151
        // Non-approved (pending) higher-tier role claims, between members and observers.
152
        // Ref-scoped (root_np), like the observers table below; there is no IRI-keyed
153
        // fallback query, so when the ref root is unknown (pre-v3 data) the table is driven
154
        // by the param-less query, which yields no rows. The space is passed as
155
        // resource/context so the per-row "approve" action pre-fills param_space; the agent
156
        // and role template come from the row via the view's query mappings. postPublishTab
157
        // returns the approver to the About tab, where the approved member now shows.
158
        View nonApprovedView = View.get(NON_APPROVED_VIEW);
×
159
        QueryRef nonApprovedQuery = (refRoot != null && !refRoot.isEmpty())
×
160
                ? new QueryRef(QueryApiAccess.LIST_SPACE_NON_APPROVED_REF, "root_np", refRoot)
×
161
                : new QueryRef(QueryApiAccess.LIST_SPACE_NON_APPROVED_REF);
×
162
        add(QueryResultTableBuilder.create("pendingmembers", nonApprovedQuery, new ViewDisplay(nonApprovedView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).postPublishTab("about").build());
×
163

164
        View observersView = View.get(OBSERVERS_VIEW);
×
165
        // Drive the Observers table from the ref-scoped query that also includes un-introduced
166
        // self-declared observers (flagged via the headerless ⚠️ column), instead of the view's
167
        // own validated-only query. The view nanopub is left untouched. Falls back to the view's
168
        // IRI-keyed query when the ref root is unknown (pre-v3 data). See docs/space-ref-identity.md.
169
        QueryRef observersQuery = (refRoot != null && !refRoot.isEmpty())
×
170
                ? new QueryRef(QueryApiAccess.LIST_SPACE_OBSERVERS_REF, "root_np", refRoot)
×
171
                : new QueryRef(observersView.getQuery().getQueryId(), "space", space.getId());
×
172
        add(QueryResultTableBuilder.create("observers", observersQuery, new ViewDisplay(observersView)).build());
×
173

174
        // "Sub-units" section: sub-spaces and maintained resources, side by side
175
        // (both views declare 6/12 width). resourceWithProfile/id/contextId let the
176
        // views' "add" actions pre-fill the new sub-space's IRI under this space's
177
        // namespace and this space as the maintainer, respectively; postPublishTab
178
        // returns the user here, where the new entry shows up.
179

180
        // Both sub-unit tables are ref-scoped via the ref-level npa:hasSubSpace /
181
        // npa:hasMaintainedResource edges (subject = the ref), falling back to their IRI-keyed
182
        // queries when the ref root is unknown. The view nanopubs are left untouched.
183
        View subSpacesView = View.get(SUB_SPACES_VIEW);
×
184
        QueryRef subSpacesQuery = (refRoot != null && !refRoot.isEmpty())
×
185
                ? new QueryRef(QueryApiAccess.LIST_SUB_SPACES_REF, "root_np", refRoot)
×
186
                : new QueryRef(subSpacesView.getQuery().getQueryId(), "space", space.getId());
×
187
        add(QueryResultListBuilder.create("subspaces", subSpacesQuery, new ViewDisplay(subSpacesView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).postPublishTab("about").build());
×
188

189
        View maintainedResourcesView = View.get(MAINTAINED_RESOURCES_VIEW);
×
190
        QueryRef maintainedResourcesQuery = (refRoot != null && !refRoot.isEmpty())
×
191
                ? new QueryRef(QueryApiAccess.LIST_MAINTAINED_RESOURCES_REF, "root_np", refRoot)
×
192
                : new QueryRef(maintainedResourcesView.getQuery().getQueryId(), "space", space.getId());
×
193
        add(QueryResultListBuilder.create("maintainedresources", maintainedResourcesQuery, new ViewDisplay(maintainedResourcesView)).resourceWithProfile(space).id(space.getId()).contextId(space.getId()).postPublishTab("about").build());
×
194
    }
×
195

196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc