• 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/UserPage.java
1
package com.knowledgepixels.nanodash.page;
2

3
import com.knowledgepixels.nanodash.NanodashSession;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.View;
6
import com.knowledgepixels.nanodash.ViewDisplay;
7
import com.knowledgepixels.nanodash.component.*;
8
import com.knowledgepixels.nanodash.domain.IndividualAgent;
9
import com.knowledgepixels.nanodash.domain.User;
10
import org.apache.wicket.Component;
11
import org.apache.wicket.ajax.AjaxRequestTarget;
12
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
13
import org.apache.wicket.markup.html.WebMarkupContainer;
14
import org.apache.wicket.markup.html.basic.Label;
15
import org.apache.wicket.markup.html.image.ExternalImage;
16
import org.apache.wicket.markup.html.image.Image;
17
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
18
import org.apache.wicket.markup.html.panel.EmptyPanel;
19
import org.apache.wicket.model.Model;
20
import org.apache.wicket.request.flow.RedirectToUrlException;
21
import org.apache.wicket.request.mapper.parameter.PageParameters;
22
import org.apache.wicket.request.resource.ContextRelativeResourceReference;
23
import org.eclipse.rdf4j.model.IRI;
24
import org.slf4j.Logger;
25
import org.slf4j.LoggerFactory;
26

27
import java.util.List;
28
import java.util.Optional;
29

30
/**
31
 * Page that shows a user profile, including their nanopubs and stats.
32
 */
33
public class UserPage extends NanodashPage {
34

35
    private static final Logger logger = LoggerFactory.getLogger(UserPage.class);
×
36

37
    /**
38
     * The mount path for this page.
39
     */
40
    public static final String MOUNT_PATH = "/user";
41

42
    /**
43
     * {@inheritDoc}
44
     */
45
    @Override
46
    public String getMountPath() {
47
        return MOUNT_PATH;
×
48
    }
49

50
    @Override
51
    public String getContextId() {
52
        return userIri.stringValue();
×
53
    }
54

55
    @Override
56
    public boolean isContextPage() {
57
        return true;
×
58
    }
59

60
    private IRI userIri;
61
    private String pubkeyHashes = "";
×
62

63
    /**
64
     * Constructor for UserPage.
65
     *
66
     * @param parameters Page parameters, must include "id" with the user IRI.
67
     */
68
    public UserPage(final PageParameters parameters) {
69
        super(parameters);
×
70
        setOutputMarkupId(true);
×
71

72
        if (parameters.get("id") == null) throw new RedirectToUrlException(ProfilePage.MOUNT_PATH);
×
73
        final String userIriString = parameters.get("id").toString();
×
74
        userIri = Utils.vf.createIRI(userIriString);
×
75

76
        for (String pk : User.getPubkeyhashes(userIri, null)) {
×
77
            pubkeyHashes += " " + pk;
×
78
        }
×
79
        if (!pubkeyHashes.isEmpty()) pubkeyHashes = pubkeyHashes.substring(1);
×
80

81
        ResourceTabs.Tab activeTab = ResourceTabs.activeFromParam(parameters);
×
82
        String pageType = "users";
×
83
        add(new TitleBar("titlebar", this, pageType)
×
84
                .setTabs(new ResourceTabs("tabs", "user", userIriString, activeTab)));
×
85

86
        IRI profilePictureIri = User.getProfilePicture(userIri);
×
87
        if (profilePictureIri != null) {
×
88
            ExternalImage userIcon = new ExternalImage("userIcon", profilePictureIri);
×
89
            add(userIcon);
×
90
        } else {
×
91
            boolean isSoftware = IndividualAgent.isSoftware(userIri);
×
92
            Image userIcon;
93
            if (isSoftware) {
×
94
                userIcon = new Image("userIcon", new ContextRelativeResourceReference("images/bot-icon.svg", false));
×
95
            } else {
96
                userIcon = new Image("userIcon", new ContextRelativeResourceReference("images/user-icon.svg", false));
×
97

98
            }
99
            add(userIcon);
×
100
        }
101

102
        final String displayName = User.getShortDisplayName(userIri);
×
103
        add(new Label("pagetitle", displayName + " (user) | nanodash"));
×
104
        add(new Label("username", displayName));
×
105
        add(new Label("titlesuffix", ResourceTabs.titleSuffix(activeTab)));
×
106

107
        add(new ExternalLinkWithActionsPanel("fullid", Model.of(userIriString), Model.of(displayName)));
×
108

109
        // The owner's account + signing-key controls share the title/ORCID header
110
        // stripe (own About page only).
111
        boolean ownPage = NanodashSession.get().getUserIri() != null
×
112
                && NanodashSession.get().getUserIri().stringValue().equals(userIriString);
×
113
        if (ownPage && activeTab == ResourceTabs.Tab.ABOUT) {
×
114
            add(new ProfileAccountPanel("accountpart", userIriString));
×
115
        } else {
116
            add(new EmptyPanel("accountpart").setVisible(false));
×
117
        }
118

119
//                final Map<String,String> statsParams = new HashMap<>();
120
//                final String statsQueryName;
121
//                if (pubkeyHashes.isEmpty()) {
122
//                        statsQueryName = "get-user-stats-from-userid";
123
//                        statsParams.put("userid", userIriString);
124
//                } else {
125
//                        statsQueryName = "get-user-stats-from-pubkeys";
126
//                        statsParams.put("userid", userIriString);
127
//                        statsParams.put("pubkeyhashes", pubkeyHashes);
128
//                } 
129
//                Map<String,String> statsMap = ApiCache.retrieveMap(statsQueryName, statsParams);
130
//                if (statsMap != null) {
131
//                        add(new StatsPanel("stats", userIriString, pubkeyHashes, statsMap));
132
//                } else {
133
//                        add(new AjaxLazyLoadPanel<Component>("stats") {
134
//        
135
//                                @Override
136
//                                public Component getLazyLoadComponent(String markupId) {
137
//                                        Map<String,String> m = null;
138
//                                        while (true) {
139
//                                                try {
140
//                                                        Thread.sleep(500);
141
//                                                } catch (InterruptedException ex) {
142
//                                                        logger.error();
143
//                                                }
144
//                                                if (!ApiCache.isRunning(statsQueryName, statsParams)) {
145
//                                                        m = ApiCache.retrieveMap(statsQueryName, statsParams);
146
//                                                        if (m != null) break;
147
//                                                }
148
//                                        }
149
//                                        return new StatsPanel(markupId, userIriString, pubkeyHashes, m);
150
//                                }
151
//
152
//                                @Override
153
//                                protected void onContentLoaded(Component content, Optional<AjaxRequestTarget> target) {
154
//                                        super.onContentLoaded(content, target);
155
//                                        if (target.get() != null) target.get().appendJavaScript("updateElements();");
156
//                                }
157
//        
158
//                        });
159
//                }
160

161
        WebMarkupContainer contentContainer = new WebMarkupContainer("contentContainer");
×
162
        add(contentContainer);
×
163
        if (activeTab != ResourceTabs.Tab.CONTENT) {
×
164
            contentContainer.setVisible(false);
×
165
            if (activeTab == ResourceTabs.Tab.ABOUT) {
×
166
                // The panel constructor resolves view nanopubs over the network when
167
                // they aren't freshly cached, which would block the initial page
168
                // render; the view-id list must mirror the panel's View.get calls.
169
                add(LazyContentPanel.of("otherTab", markupId -> new AboutUserPanel(markupId, userIriString),
×
170
                        AboutUserPanel.INTRODUCTIONS_VIEW, AboutUserPanel.PROFILE_VIEW,
171
                        AboutSpacePanel.PRESET_ASSIGNMENTS_VIEW, AboutSpacePanel.VIEW_DISPLAYS_VIEW));
172
            } else if (activeTab == ResourceTabs.Tab.EXPLORE) {
×
173
                add(LazyContentPanel.of("otherTab", markupId -> new ExplorePanel(markupId, userIriString),
×
174
                        ReferencesPage.REFERENCES_VIEW));
175
            } else {
176
                add(new DownloadRdfLinks("otherTab", "user", userIriString));
×
177
            }
178
        } else {
179
            add(new EmptyPanel("otherTab").setVisible(false));
×
180
            IndividualAgent individualAgent = IndividualAgent.get(userIriString);
×
181
            if (individualAgent.isDataInitialized()) {
×
182
                boolean empty = individualAgent.getTopLevelViewDisplays().isEmpty();
×
183
                if (empty) {
×
184
                    contentContainer.add(new WebMarkupContainer("views").setVisible(false));
×
185
                } else {
186
                    contentContainer.add(new ViewList("views", individualAgent));
×
187
                }
188
                contentContainer.add(new WebMarkupContainer("unconfigured-notice").setVisible(empty));
×
189
                if (empty) {
×
190
                    ViewDisplay defaultViewDisplay = new ViewDisplay(View.get("https://w3id.org/np/RAwktOZ3vwTZJcGRbueLpxIFSiOj7XmMG2-8rzPuDEpPc/latest-nanopubs-by-user"));
×
191
                    contentContainer.add(new ViewList("latestnanopubsview", individualAgent, List.of(defaultViewDisplay)));
×
192
                } else {
×
193
                    contentContainer.add(new EmptyPanel("latestnanopubsview").setVisible(false));
×
194
                }
195
            } else {
×
196
                final WebMarkupContainer unconfiguredNotice = new WebMarkupContainer("unconfigured-notice");
×
197
                unconfiguredNotice.setVisible(false);
×
198
                unconfiguredNotice.setOutputMarkupPlaceholderTag(true);
×
199
                contentContainer.add(unconfiguredNotice);
×
200

201
                ViewDisplay defaultViewDisplay = new ViewDisplay(View.get("https://w3id.org/np/RAwktOZ3vwTZJcGRbueLpxIFSiOj7XmMG2-8rzPuDEpPc/latest-nanopubs-by-user"));
×
202
                final ViewList latestNanopubsView = new ViewList("latestnanopubsview", individualAgent, List.of(defaultViewDisplay));
×
203
                latestNanopubsView.setVisible(false);
×
204
                latestNanopubsView.setOutputMarkupPlaceholderTag(true);
×
205
                contentContainer.add(latestNanopubsView);
×
206

207
                contentContainer.add(new AjaxLazyLoadPanel<Component>("views") {
×
208

209
                    @Override
210
                    public Component getLazyLoadComponent(String markupId) {
211
                        return new ViewList(markupId, individualAgent);
×
212
                    }
213

214
                    @Override
215
                    protected boolean isContentReady() {
216
                        return individualAgent.isDataInitialized();
×
217
                    }
218

219
                    @Override
220
                    public Component getLoadingComponent(String id) {
221
                        return new Label(id, "<div class=\"row-section\"><div class=\"col-12\">" + ResultComponent.getWaitIconHtml() + "</div></div>").setEscapeModelStrings(false);
×
222
                    }
223

224
                    @Override
225
                    protected void onContentLoaded(Component content, Optional<AjaxRequestTarget> target) {
226
                        super.onContentLoaded(content, target);
×
227
                        target.ifPresent(t -> {
×
228
                            boolean isEmpty = individualAgent.getTopLevelViewDisplays().isEmpty();
×
229
                            if (isEmpty) {
×
230
                                t.appendJavaScript("document.getElementById('" + getMarkupId() + "').remove();");
×
231
                            }
232
                            unconfiguredNotice.setVisible(isEmpty);
×
233
                            t.add(unconfiguredNotice);
×
234
                            latestNanopubsView.setVisible(isEmpty);
×
235
                            t.add(latestNanopubsView);
×
236
                        });
×
237
                    }
×
238

239
                });
240
            }
241
        }
242
    }
×
243

244
    /**
245
     * <p>hasAutoRefreshEnabled.</p>
246
     *
247
     * @return a boolean
248
     */
249
    protected boolean hasAutoRefreshEnabled() {
250
        return true;
×
251
    }
252

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