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

knowledgepixels / nanodash / 28789064412

06 Jul 2026 11:44AM UTC coverage: 28.51% (+0.008%) from 28.502%
28789064412

push

github

web-flow
Merge pull request #535 from knowledgepixels/feat/navigation-context

feat: persistent navigation context with back-link and post-publish forwarding

1811 of 7177 branches covered (25.23%)

Branch coverage included in aggregate %.

3710 of 12188 relevant lines covered (30.44%)

4.52 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.Utils;
7
import com.knowledgepixels.nanodash.component.*;
8
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
9
import com.knowledgepixels.nanodash.domain.IndividualAgent;
10
import com.knowledgepixels.nanodash.domain.MaintainedResource;
11
import com.knowledgepixels.nanodash.domain.User;
12
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
13
import com.knowledgepixels.nanodash.repository.SpaceRepository;
14
import org.apache.wicket.Component;
15
import org.apache.wicket.RestartResponseException;
16
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
17
import org.apache.wicket.markup.html.WebMarkupContainer;
18
import org.apache.wicket.markup.html.basic.Label;
19
import org.apache.wicket.markup.html.panel.EmptyPanel;
20
import org.apache.wicket.model.Model;
21
import org.apache.wicket.request.mapper.parameter.PageParameters;
22
import org.eclipse.rdf4j.model.IRI;
23
import org.eclipse.rdf4j.model.Statement;
24
import org.eclipse.rdf4j.model.util.Values;
25
import org.eclipse.rdf4j.model.vocabulary.RDF;
26
import org.eclipse.rdf4j.model.vocabulary.RDFS;
27
import org.nanopub.Nanopub;
28
import org.nanopub.extra.services.ApiResponse;
29
import org.nanopub.extra.services.QueryRef;
30

31
import java.util.ArrayList;
32
import java.util.HashSet;
33
import java.util.List;
34
import java.util.Set;
35

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

41
    /**
42
     * The mount path for this page.
43
     */
44
    public static final String MOUNT_PATH = "/part";
45

46
    /**
47
     * {@inheritDoc}
48
     */
49
    @Override
50
    public String getMountPath() {
51
        return MOUNT_PATH;
×
52
    }
53

54
    // getContextId() is inherited: the context param holds the maintaining resource.
55
    @Override
56
    public boolean isContextPage() {
57
        return true;
×
58
    }
59

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

65
    /**
66
     * If the {@code id} in the given parameters falls under a namespace declared by a
67
     * maintained resource, forward to this page with that resource set as the
68
     * {@code context}. Does nothing if no maintained resource declares the namespace.
69
     *
70
     * @param parameters page parameters containing the {@code id} to resolve
71
     * @throws RestartResponseException if a containing maintained resource is found
72
     */
73
    public static void forwardToContainingResource(PageParameters parameters) {
74
        String id = parameters.get("id").toString();
×
75
        MaintainedResource containingResource = MaintainedResourceRepository.get().findByNamespace(MaintainedResource.getNamespace(id));
×
76
        if (containingResource != null) {
×
77
            PageParameters partParameters = new PageParameters(parameters);
×
78
            partParameters.set("context", containingResource.getId());
×
79
            throw new RestartResponseException(ResourcePartPage.class, partParameters);
×
80
        }
81
    }
×
82

83
    public ResourcePartPage(final PageParameters parameters) {
84
        super(parameters);
×
85

86
        final String id = parameters.get("id").toString();
×
87
        final String contextId = parameters.get("context").toString();
×
88
        final String nanopubId;
89
        String label = parameters.get("label").isEmpty() ? id.replaceFirst("^.*[#/]([^#/]+)$", "$1") : parameters.get("label").toString();
×
90
        Set<IRI> classes = new HashSet<>();
×
91

92
        resourceWithProfile = MaintainedResourceRepository.get().findById(contextId);
×
93
        if (resourceWithProfile == null) {
×
94
            if (SpaceRepository.get().findById(contextId) != null) {
×
95
                resourceWithProfile = SpaceRepository.get().findById(contextId);
×
96
            } else if (IndividualAgent.isUser(contextId)) {
×
97
                resourceWithProfile = IndividualAgent.get(contextId);
×
98
            } else {
99
                throw new IllegalArgumentException("Not a resource, space, or user: " + contextId);
×
100
            }
101
        }
102

103
        QueryRef getDefQuery = new QueryRef(QueryApiAccess.GET_TERM_DEFINITIONS, "term", id);
×
104
        if (resourceWithProfile.getSpace() != null) {
×
105
            for (IRI userIri : resourceWithProfile.getSpace().getUsers()) {
×
106
                for (String pubkey : User.getUserData().getPubkeyHashes(userIri, true)) {
×
107
                    getDefQuery.getParams().put("pubkey", pubkey);
×
108
                }
×
109
            }
×
110
        } else {
111
            for (String pubkey : User.getUserData().getPubkeyHashes(Utils.vf.createIRI(contextId), true)) {
×
112
                getDefQuery.getParams().put("pubkey", pubkey);
×
113
            }
×
114
        }
115

116
        ApiResponse getDefResp = ApiCache.retrieveResponseSync(getDefQuery, false);
×
117
        if (getDefResp != null && !getDefResp.getData().isEmpty()) {
×
118
            nanopubId = getDefResp.getData().iterator().next().get("np");
×
119

120
            Nanopub nanopub = Utils.getAsNanopub(nanopubId);
×
121
            for (Statement st : nanopub.getAssertion()) {
×
122
                if (!st.getSubject().stringValue().equals(id)) {
×
123
                    continue;
×
124
                }
125
                if (st.getPredicate().equals(RDFS.LABEL)) {
×
126
                    label = st.getObject().stringValue();
×
127
                }
128
                if (st.getPredicate().equals(RDF.TYPE) && st.getObject() instanceof IRI objIri) {
×
129
                    classes.add(objIri);
×
130
                }
131
            }
×
132
        } else {
×
133
            nanopubId = null;
×
134
        }
135
//        if (getDefResp == null || getDefResp.getData().isEmpty()) {
136
//            throw new RestartResponseException(ExplorePage.class, parameters);
137
//        }
138

139
        List<NanodashPageRef> breadCrumb;
140
        if (resourceWithProfile.getSpace() != null) {
×
141
            List<AbstractResourceWithProfile> superSpaces = resourceWithProfile.getSpace().getAllSuperSpacesUntilRoot();
×
142
            if (resourceWithProfile instanceof MaintainedResource) {
×
143
                superSpaces.add(resourceWithProfile.getSpace());
×
144
            }
145
            superSpaces.add(resourceWithProfile);
×
146
            breadCrumb = new ArrayList<>(superSpaces.stream().map(ss -> new NanodashPageRef(SpacePage.class, new PageParameters().add("id", ss.getId()), ss.getLabel())).toList());
×
147
        } else {
×
148
            breadCrumb = new ArrayList<>();
×
149
            breadCrumb.add(new NanodashPageRef(UserPage.class, new PageParameters().add("id", contextId), resourceWithProfile.getLabel()));
×
150
        }
151
        breadCrumb.add(new NanodashPageRef(ResourcePartPage.class, new PageParameters().add("id", id).add("context", contextId).add("label", label), label));
×
152
        NanodashPageRef[] breadCrumbArray = breadCrumb.toArray(new NanodashPageRef[0]);
×
153
        ResourceTabs.Tab activeTab = ResourceTabs.activeFromParam(parameters);
×
154
        add(new TitleBar("titlebar", this, null,
×
155
                breadCrumbArray
156
        ).setTabs(new ResourceTabs("tabs", "part", id, contextId, activeTab)));
×
157

158
        add(new Label("pagetitle", label + " (resource part) | nanodash"));
×
159
        add(new Label("name", label));
×
160
        add(new Label("titlesuffix", ResourceTabs.titleSuffix(activeTab)));
×
161
        add(new ExternalLinkWithActionsPanel("id", Model.of(id), Model.of(label), nanopubId == null ? Values.iri(id) : Values.iri(nanopubId)));
×
162

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

189
                    @Override
190
                    public Component getLazyLoadComponent(String markupId) {
191
                        return new ViewList(markupId, resourceWithProfile, id, nanopubRef, classes);
×
192
                    }
193

194
                    @Override
195
                    protected boolean isContentReady() {
196
                        return resourceWithProfile.isDataInitialized();
×
197
                    }
198

199
                    @Override
200
                    public Component getLoadingComponent(String id) {
201
                        return new Label(id, "<div class=\"row-section\"><div class=\"col-12\">" + ResultComponent.getWaitIconHtml() + "</div></div>").setEscapeModelStrings(false);
×
202
                    }
203

204
                });
205
            }
206
        }
207
    }
×
208

209
    /**
210
     * Checks if auto-refresh is enabled for this page.
211
     *
212
     * @return true if auto-refresh is enabled, false otherwise
213
     */
214
    protected boolean hasAutoRefreshEnabled() {
215
        return true;
×
216
    }
217

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