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

knowledgepixels / nanodash / 28793681458

06 Jul 2026 01:05PM UTC coverage: 28.475% (-0.03%) from 28.504%
28793681458

Pull #537

github

web-flow
Merge 58b5e64fc into 5ef3629cc
Pull Request #537: fix: treat home resource context as plain Home in navigation

1811 of 7191 branches covered (25.18%)

Branch coverage included in aggregate %.

3710 of 12198 relevant lines covered (30.41%)

4.52 hits per line

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

13.54
src/main/java/com/knowledgepixels/nanodash/NavigationContext.java
1
package com.knowledgepixels.nanodash;
2

3
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
4
import com.knowledgepixels.nanodash.domain.IndividualAgent;
5
import com.knowledgepixels.nanodash.domain.MaintainedResource;
6
import com.knowledgepixels.nanodash.domain.Space;
7
import com.knowledgepixels.nanodash.page.HomePage;
8
import com.knowledgepixels.nanodash.page.MaintainedResourcePage;
9
import com.knowledgepixels.nanodash.page.NanodashPage;
10
import com.knowledgepixels.nanodash.page.ResourcePartPage;
11
import com.knowledgepixels.nanodash.page.SpacePage;
12
import com.knowledgepixels.nanodash.page.UserPage;
13
import org.apache.wicket.Component;
14
import org.apache.wicket.RestartResponseException;
15
import org.apache.wicket.behavior.Behavior;
16
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
17
import org.apache.wicket.request.mapper.parameter.PageParameters;
18
import org.nanopub.Nanopub;
19

20
/**
21
 * The navigation context: the space, user, or maintained resource a page was reached
22
 * under, carried across pages as the {@code context} URL parameter. It determines where
23
 * the user is forwarded to after publishing a nanopub and where the title bar's
24
 * back-link points on pages that are not themselves a context resource's page.
25
 */
26
public class NavigationContext {
27

28
    private NavigationContext() {
29
    }
30

31
    /**
32
     * Name of the page parameter holding the navigation context resource id.
33
     */
34
    public static final String CONTEXT_PARAM = "context";
35

36
    /**
37
     * Reads the navigation context id from the given page parameters.
38
     *
39
     * @param params the page parameters
40
     * @return the context resource id, or null if not set
41
     */
42
    public static String getContextId(PageParameters params) {
43
        if (params == null) return null;
6!
44
        String contextId = params.get(CONTEXT_PARAM).toString("");
18✔
45
        return contextId.isEmpty() ? null : contextId;
18!
46
    }
47

48
    /**
49
     * Resolves a context id to its space, maintained resource, or user. Agents are
50
     * created lazily, so a plain {@link AbstractResourceWithProfile#get(String)} lookup
51
     * alone would miss users not touched yet this session; hence the explicit user check.
52
     *
53
     * @param contextId the context resource id
54
     * @return the resolved resource, or null if the id is not a known space, maintained resource, or user
55
     */
56
    public static AbstractResourceWithProfile resolve(String contextId) {
57
        if (contextId == null || contextId.isEmpty()) return null;
×
58
        AbstractResourceWithProfile resource = AbstractResourceWithProfile.get(contextId);
×
59
        if (resource == null && IndividualAgent.isUser(contextId)) {
×
60
            resource = IndividualAgent.get(contextId);
×
61
        }
62
        if (resource instanceof IndividualAgent && !IndividualAgent.isUser(contextId)) return null;
×
63
        return resource;
×
64
    }
65

66
    /**
67
     * The page class showing the given resource.
68
     *
69
     * @param resource the context resource
70
     * @return the page class, or null if the resource is null
71
     */
72
    public static Class<? extends NanodashPage> getPageClass(AbstractResourceWithProfile resource) {
73
        if (resource instanceof Space) return SpacePage.class;
×
74
        if (resource instanceof MaintainedResource) return MaintainedResourcePage.class;
×
75
        if (resource instanceof IndividualAgent) return UserPage.class;
×
76
        return null;
×
77
    }
78

79
    /**
80
     * Whether the given context id is the configured home resource, whose page is the
81
     * home page itself rather than its maintained-resource page.
82
     *
83
     * @param contextId the context resource id
84
     * @return true if it is the home resource
85
     */
86
    public static boolean isHomeResource(String contextId) {
87
        return contextId != null && contextId.equals(NanodashPreferences.get().getHomeResource());
×
88
    }
89

90
    /**
91
     * A page reference (link target + label) for the given context id.
92
     *
93
     * @param contextId the context resource id
94
     * @return the page reference, or null if the id cannot be resolved
95
     */
96
    public static NanodashPageRef getPageRef(String contextId) {
97
        if (isHomeResource(contextId)) {
×
98
            return new NanodashPageRef(HomePage.class, "Home");
×
99
        }
100
        AbstractResourceWithProfile resource = resolve(contextId);
×
101
        if (resource == null) return null;
×
102
        return new NanodashPageRef(getPageClass(resource), new PageParameters().set("id", contextId), resource.getLabel());
×
103
    }
104

105
    /**
106
     * Sets the given context id on the parameters, unless it is null or a context is
107
     * already set (call sites that know the context resource remain authoritative).
108
     *
109
     * @param params    the page parameters to extend
110
     * @param contextId the context resource id, or null for a no-op
111
     * @return the same page parameters, for chaining
112
     */
113
    public static PageParameters withContext(PageParameters params, String contextId) {
114
        if (contextId != null && !contextId.isEmpty() && params.get(CONTEXT_PARAM).isEmpty()) {
6!
115
            params.set(CONTEXT_PARAM, contextId);
×
116
        }
117
        return params;
6✔
118
    }
119

120
    /**
121
     * A behavior that fills in the page's navigation context on a
122
     * {@link BookmarkablePageLink} that doesn't carry one yet. Useful where the context
123
     * id isn't at hand when the link is built (e.g. nanopub cards); runs at configure
124
     * time, when the component is attached to its page.
125
     *
126
     * @return the context-fallback behavior
127
     */
128
    public static Behavior pageContextFallback() {
129
        return new Behavior() {
21✔
130
            @Override
131
            public void onConfigure(Component component) {
132
                if (component instanceof BookmarkablePageLink<?> link && component.getPage() instanceof NanodashPage page
36!
133
                        && link.getPageParameters() != null) {
×
134
                    withContext(link.getPageParameters(), page.getContextId());
×
135
                }
136
            }
3✔
137
        };
138
    }
139

140
    /**
141
     * Forwards to the page of the context resource (or its part) after a successful
142
     * publication, with the just-published nanopub shown only in the title bar message;
143
     * forwards to the home page if no (resolvable) context is set. Always throws.
144
     *
145
     * @param signedNp   the just-published nanopub
146
     * @param pageParams the parameters of the publish form's page
147
     */
148
    public static void redirectAfterPublish(Nanopub signedNp, PageParameters pageParams) {
149
        String npUri = signedNp.getUri().stringValue();
×
150
        String contextId = getContextId(pageParams);
×
151
        if (contextId != null) {
×
152
            PageParameters redirectParams = new PageParameters().set("just-published", npUri);
×
153
            String partId = pageParams.get("part").toString("");
×
154
            if (!partId.isEmpty()) {
×
155
                // User was on a part page (e.g. paper collection); redirect back to the part page
156
                redirectParams.set("id", partId).set(CONTEXT_PARAM, contextId);
×
157
                throw new RestartResponseException(ResourcePartPage.class, redirectParams);
×
158
            }
159
            if (isHomeResource(contextId)) {
×
160
                throw new RestartResponseException(HomePage.class, redirectParams);
×
161
            }
162
            redirectParams.set("id", contextId);
×
163
            // Return to the tab the action asked for (e.g. "about" for a
164
            // space's About-tab role actions); default is the Content tab.
165
            String postpubTab = pageParams.get("postpub-tab").toString("");
×
166
            if (!postpubTab.isEmpty()) redirectParams.set("tab", postpubTab);
×
167
            AbstractResourceWithProfile resource = resolve(contextId);
×
168
            if (resource != null) {
×
169
                throw new RestartResponseException(getPageClass(resource), redirectParams);
×
170
            }
171
        }
172
        throw new RestartResponseException(HomePage.class, new PageParameters().set("just-published", npUri));
×
173
    }
174

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