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

knowledgepixels / nanodash / 27622721129

16 Jun 2026 01:55PM UTC coverage: 26.963% (+6.3%) from 20.697%
27622721129

Pull #483

github

web-flow
Merge 73a4d0fe1 into 663f14f46
Pull Request #483: Space/resource About pages, ref-aware spaces, and magic query params

1542 of 6717 branches covered (22.96%)

Branch coverage included in aggregate %.

3407 of 11638 relevant lines covered (29.27%)

4.31 hits per line

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

0.0
src/main/java/com/knowledgepixels/nanodash/page/ResourcePartPage.java
1
package com.knowledgepixels.nanodash.page;
2

3
import com.knowledgepixels.nanodash.ApiCache;
4
import com.knowledgepixels.nanodash.NanodashPageRef;
5
import com.knowledgepixels.nanodash.QueryApiAccess;
6
import com.knowledgepixels.nanodash.SpaceMemberRole;
7
import com.knowledgepixels.nanodash.Utils;
8
import com.knowledgepixels.nanodash.component.*;
9
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
10
import com.knowledgepixels.nanodash.domain.IndividualAgent;
11
import com.knowledgepixels.nanodash.domain.MaintainedResource;
12
import com.knowledgepixels.nanodash.domain.Space;
13
import com.knowledgepixels.nanodash.domain.User;
14
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
15
import com.knowledgepixels.nanodash.repository.SpaceRepository;
16
import org.apache.wicket.Component;
17
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
18
import org.apache.wicket.markup.html.WebMarkupContainer;
19
import org.apache.wicket.markup.html.basic.Label;
20
import org.apache.wicket.markup.html.panel.EmptyPanel;
21
import org.apache.wicket.model.Model;
22
import org.apache.wicket.request.mapper.parameter.PageParameters;
23
import org.eclipse.rdf4j.model.IRI;
24
import org.eclipse.rdf4j.model.Statement;
25
import org.eclipse.rdf4j.model.util.Values;
26
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
27
import org.eclipse.rdf4j.model.vocabulary.RDF;
28
import org.eclipse.rdf4j.model.vocabulary.RDFS;
29
import org.eclipse.rdf4j.model.vocabulary.SKOS;
30
import org.nanopub.Nanopub;
31
import org.nanopub.extra.services.ApiResponse;
32
import org.nanopub.extra.services.QueryRef;
33

34
import java.util.ArrayList;
35
import java.util.HashSet;
36
import java.util.List;
37
import java.util.Set;
38

39
/**
40
 * This class represents a page for a resource part in the context of a maintained resource, space, or user.
41
 */
42
public class ResourcePartPage extends NanodashPage {
43

44
    /**
45
     * The mount path for this page.
46
     */
47
    public static final String MOUNT_PATH = "/part";
48

49
    /**
50
     * {@inheritDoc}
51
     */
52
    @Override
53
    public String getMountPath() {
54
        return MOUNT_PATH;
×
55
    }
56

57
    /**
58
     * Resource with profile (Space or MaintainedResource) object with the data shown on this page.
59
     */
60
    private AbstractResourceWithProfile resourceWithProfile;
61

62
    public ResourcePartPage(final PageParameters parameters) {
63
        super(parameters);
×
64

65
        final String id = parameters.get("id").toString();
×
66
        final String contextId = parameters.get("context").toString();
×
67
        final String nanopubId;
68
        String label = parameters.get("label").isEmpty() ? id.replaceFirst("^.*[#/]([^#/]+)$", "$1") : parameters.get("label").toString();
×
69
        String description = null;
×
70
        Set<IRI> classes = new HashSet<>();
×
71

72
        resourceWithProfile = MaintainedResourceRepository.get().findById(contextId);
×
73
        if (resourceWithProfile == null) {
×
74
            if (SpaceRepository.get().findById(contextId) != null) {
×
75
                resourceWithProfile = SpaceRepository.get().findById(contextId);
×
76
            } else if (IndividualAgent.isUser(contextId)) {
×
77
                resourceWithProfile = IndividualAgent.get(contextId);
×
78
            } else {
79
                throw new IllegalArgumentException("Not a resource, space, or user: " + contextId);
×
80
            }
81
        }
82

83
        QueryRef getDefQuery = new QueryRef(QueryApiAccess.GET_TERM_DEFINITIONS, "term", id);
×
84
        if (resourceWithProfile.getSpace() != null) {
×
85
            for (IRI userIri : resourceWithProfile.getSpace().getUsers()) {
×
86
                for (String pubkey : User.getUserData().getPubkeyHashes(userIri, true)) {
×
87
                    getDefQuery.getParams().put("pubkey", pubkey);
×
88
                }
×
89
            }
×
90
        } else {
91
            for (String pubkey : User.getUserData().getPubkeyHashes(Utils.vf.createIRI(contextId), true)) {
×
92
                getDefQuery.getParams().put("pubkey", pubkey);
×
93
            }
×
94
        }
95

96
        ApiResponse getDefResp = ApiCache.retrieveResponseSync(getDefQuery, false);
×
97
        if (getDefResp != null && !getDefResp.getData().isEmpty()) {
×
98
            nanopubId = getDefResp.getData().iterator().next().get("np");
×
99

100
            Nanopub nanopub = Utils.getAsNanopub(nanopubId);
×
101
            for (Statement st : nanopub.getAssertion()) {
×
102
                if (!st.getSubject().stringValue().equals(id)) {
×
103
                    continue;
×
104
                }
105
                if (st.getPredicate().equals(RDFS.LABEL)) {
×
106
                    label = st.getObject().stringValue();
×
107
                }
108
                if (st.getPredicate().equals(SKOS.DEFINITION) || st.getPredicate().equals(DCTERMS.DESCRIPTION) || st.getPredicate().equals(RDFS.COMMENT)) {
×
109
                    description = st.getObject().stringValue();
×
110
                }
111
                if (st.getPredicate().equals(RDF.TYPE) && st.getObject() instanceof IRI objIri) {
×
112
                    classes.add(objIri);
×
113
                }
114
            }
×
115
        } else {
×
116
            nanopubId = null;
×
117
        }
118
//        if (getDefResp == null || getDefResp.getData().isEmpty()) {
119
//            throw new RestartResponseException(ExplorePage.class, parameters);
120
//        }
121

122
        if (description != null) {
×
123
            add(new Label("description", description));
×
124
        } else {
125
            add(new Label("description").setVisible(false));
×
126
        }
127

128
        List<NanodashPageRef> breadCrumb;
129
        if (resourceWithProfile.getSpace() != null) {
×
130
            List<AbstractResourceWithProfile> superSpaces = resourceWithProfile.getSpace().getAllSuperSpacesUntilRoot();
×
131
            if (resourceWithProfile instanceof MaintainedResource) {
×
132
                superSpaces.add(resourceWithProfile.getSpace());
×
133
            }
134
            superSpaces.add(resourceWithProfile);
×
135
            breadCrumb = new ArrayList<>(superSpaces.stream().map(ss -> new NanodashPageRef(SpacePage.class, new PageParameters().add("id", ss.getId()), ss.getLabel())).toList());
×
136
        } else {
×
137
            breadCrumb = new ArrayList<>();
×
138
            breadCrumb.add(new NanodashPageRef(UserPage.class, new PageParameters().add("id", contextId), resourceWithProfile.getLabel()));
×
139
        }
140
        breadCrumb.add(new NanodashPageRef(ResourcePartPage.class, new PageParameters().add("id", id).add("context", contextId).add("label", label), label));
×
141
        NanodashPageRef[] breadCrumbArray = breadCrumb.toArray(new NanodashPageRef[0]);
×
142
        ResourceTabs.Tab activeTab = ResourceTabs.activeFromParam(parameters);
×
143
        add(new TitleBar("titlebar", this, null,
×
144
                breadCrumbArray
145
        ).setTabs(new ResourceTabs("tabs", "part", id, contextId, activeTab)));
×
146

147
        add(new Label("pagetitle", label + " (resource part) | nanodash"));
×
148
        add(new Label("name", label));
×
149
        add(new Label("titlesuffix", ResourceTabs.titleSuffix(activeTab)));
×
150
        add(new ExternalLinkWithActionsPanel("id", Model.of(id), Model.of(label), nanopubId == null ? Values.iri(id) : Values.iri(nanopubId)));
×
151

152
        boolean showButton = false;
×
153
        if (resourceWithProfile instanceof IndividualAgent ia) {
×
154
            showButton = ia.isCurrentUser();
×
155
        } else if (resourceWithProfile.getSpace() != null) {
×
156
            showButton = SpaceMemberRole.isCurrentUserAdmin(resourceWithProfile.getSpace());
×
157
        } else if (resourceWithProfile instanceof Space s) {
×
158
            showButton = SpaceMemberRole.isCurrentUserAdmin(s);
×
159
        }
160
        add(new AddViewDisplayButton("addviewdisplay",
×
161
                "https://w3id.org/np/RAZg-r7oQjVZ3Ewy7pUzd9eINl6fCa3HGclTsDeRag5to",
162
                "latest",
163
                resourceWithProfile.getId(),
×
164
                resourceWithProfile.getId(),
×
165
                new PageParameters()
166
                        .set("part", id)
×
167
                        .set("refresh-upon-publish", resourceWithProfile.getId())
×
168
        ).setVisible(showButton));
×
169

170
        final String nanopubRef = nanopubId == null ? "x:" : nanopubId;
×
171
        WebMarkupContainer contentContainer = new WebMarkupContainer("contentContainer");
×
172
        add(contentContainer);
×
173
        if (activeTab == ResourceTabs.Tab.ABOUT) {
×
174
            contentContainer.setVisible(false);
×
175
            // The panel constructor resolves view nanopubs over the network when they
176
            // aren't freshly cached, which would block the initial page render; the
177
            // view-id list must mirror the panel's View.get calls.
178
            add(LazyContentPanel.of("otherTab", markupId -> new AboutPartPanel(markupId, resourceWithProfile, id, classes),
×
179
                    AboutPartPanel.PART_INFO_VIEW, AboutSpacePanel.PRESET_ASSIGNMENTS_VIEW, AboutPartPanel.PART_VIEW_DISPLAYS_VIEW));
180
        } else if (activeTab == ResourceTabs.Tab.EXPLORE) {
×
181
            contentContainer.setVisible(false);
×
182
            // The panel constructor resolves a view nanopub over the network when
183
            // it isn't freshly cached, which would block the initial page render.
184
            add(LazyContentPanel.of("otherTab", markupId -> new ExplorePanel(markupId, id),
×
185
                    ReferencesPage.REFERENCES_VIEW));
186
        } else if (activeTab == ResourceTabs.Tab.RAW) {
×
187
            contentContainer.setVisible(false);
×
188
            add(new DownloadRdfLinks("otherTab", "part", id, resourceWithProfile.getId()));
×
189
        } else {
190
            add(new EmptyPanel("otherTab").setVisible(false));
×
191
            if (resourceWithProfile.isDataInitialized()) {
×
192
                contentContainer.add(new ViewList("views", resourceWithProfile, id, nanopubRef, classes));
×
193
            } else {
194
                contentContainer.add(new AjaxLazyLoadPanel<Component>("views") {
×
195

196
                    @Override
197
                    public Component getLazyLoadComponent(String markupId) {
198
                        return new ViewList(markupId, resourceWithProfile, id, nanopubRef, classes);
×
199
                    }
200

201
                    @Override
202
                    protected boolean isContentReady() {
203
                        return resourceWithProfile.isDataInitialized();
×
204
                    }
205

206
                    @Override
207
                    public Component getLoadingComponent(String id) {
208
                        return new Label(id, "<div class=\"row-section\"><div class=\"col-12\">" + ResultComponent.getWaitIconHtml() + "</div></div>").setEscapeModelStrings(false);
×
209
                    }
210

211
                });
212
            }
213
        }
214
    }
×
215

216
    /**
217
     * Checks if auto-refresh is enabled for this page.
218
     *
219
     * @return true if auto-refresh is enabled, false otherwise
220
     */
221
    protected boolean hasAutoRefreshEnabled() {
222
        return true;
×
223
    }
224

225
}
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