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

knowledgepixels / nanodash / 28794658420

06 Jul 2026 01:21PM UTC coverage: 28.458% (-0.05%) from 28.504%
28794658420

Pull #537

github

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

1811 of 7203 branches covered (25.14%)

Branch coverage included in aggregate %.

3713 of 12208 relevant lines covered (30.41%)

4.52 hits per line

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

12.07
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.ComponentTag;
17
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
18
import org.apache.wicket.request.mapper.parameter.PageParameters;
19
import org.nanopub.Nanopub;
20

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

29
    private NavigationContext() {
30
    }
31

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

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

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

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

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

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

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

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

141
    /**
142
     * A behavior that appends the page's navigation context to a link whose target is a
143
     * URL string (e.g. from {@link com.knowledgepixels.nanodash.component.NanodashLink#getPageUrl(String)})
144
     * rather than page parameters, where {@link #pageContextFallback()} cannot apply.
145
     * Only internal page URLs (starting with "/") that don't carry a context yet are touched.
146
     *
147
     * @return the context-fallback behavior
148
     */
149
    public static Behavior hrefContextFallback() {
150
        return new Behavior() {
21✔
151
            @Override
152
            public void onComponentTag(Component component, ComponentTag tag) {
153
                if (!(component.getPage() instanceof NanodashPage page)) return;
×
154
                String contextId = page.getContextId();
×
155
                if (contextId == null) return;
×
156
                String href = tag.getAttribute("href");
×
157
                if (href == null || !href.startsWith("/") || href.contains(CONTEXT_PARAM + "=")) return;
×
158
                tag.put("href", href + (href.contains("?") ? "&" : "?") + CONTEXT_PARAM + "=" + Utils.urlEncode(contextId));
×
159
            }
×
160
        };
161
    }
162

163
    /**
164
     * Forwards to the page of the context resource (or its part) after a successful
165
     * publication, with the just-published nanopub shown only in the title bar message;
166
     * forwards to the home page if no (resolvable) context is set. Always throws.
167
     *
168
     * @param signedNp   the just-published nanopub
169
     * @param pageParams the parameters of the publish form's page
170
     */
171
    public static void redirectAfterPublish(Nanopub signedNp, PageParameters pageParams) {
172
        String npUri = signedNp.getUri().stringValue();
×
173
        String contextId = getContextId(pageParams);
×
174
        if (contextId != null) {
×
175
            PageParameters redirectParams = new PageParameters().set("just-published", npUri);
×
176
            String partId = pageParams.get("part").toString("");
×
177
            if (!partId.isEmpty()) {
×
178
                // User was on a part page (e.g. paper collection); redirect back to the part page
179
                redirectParams.set("id", partId).set(CONTEXT_PARAM, contextId);
×
180
                throw new RestartResponseException(ResourcePartPage.class, redirectParams);
×
181
            }
182
            if (isHomeResource(contextId)) {
×
183
                throw new RestartResponseException(HomePage.class, redirectParams);
×
184
            }
185
            redirectParams.set("id", contextId);
×
186
            // Return to the tab the action asked for (e.g. "about" for a
187
            // space's About-tab role actions); default is the Content tab.
188
            String postpubTab = pageParams.get("postpub-tab").toString("");
×
189
            if (!postpubTab.isEmpty()) redirectParams.set("tab", postpubTab);
×
190
            AbstractResourceWithProfile resource = resolve(contextId);
×
191
            if (resource != null) {
×
192
                throw new RestartResponseException(getPageClass(resource), redirectParams);
×
193
            }
194
        }
195
        throw new RestartResponseException(HomePage.class, new PageParameters().set("just-published", npUri));
×
196
    }
197

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