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

knowledgepixels / nanodash / 30449359054

29 Jul 2026 11:53AM UTC coverage: 34.201% (-0.2%) from 34.372%
30449359054

push

github

web-flow
Merge pull request #573 from knowledgepixels/572-header-views

feat(views): add header views (gen:HeaderView)

2460 of 7964 branches covered (30.89%)

Branch coverage included in aggregate %.

4802 of 13269 relevant lines covered (36.19%)

5.59 hits per line

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

0.0
src/main/java/com/knowledgepixels/nanodash/component/menu/ViewDisplayMenu.java
1
package com.knowledgepixels.nanodash.component.menu;
2

3
import com.knowledgepixels.nanodash.ApiCache;
4
import com.knowledgepixels.nanodash.MagicQueryParams;
5
import com.knowledgepixels.nanodash.NanodashSession;
6
import com.knowledgepixels.nanodash.NanopubElement;
7
import com.knowledgepixels.nanodash.NavigationContext;
8
import com.knowledgepixels.nanodash.QueryResult;
9
import com.knowledgepixels.nanodash.Utils;
10
import com.knowledgepixels.nanodash.ViewDisplay;
11
import com.knowledgepixels.nanodash.component.GuidedChoiceItem;
12
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
13
import com.knowledgepixels.nanodash.domain.IndividualAgent;
14
import com.knowledgepixels.nanodash.domain.Space;
15
import com.knowledgepixels.nanodash.page.ExplorePage;
16
import com.knowledgepixels.nanodash.page.PublishPage;
17
import com.knowledgepixels.nanodash.page.QueryPage;
18
import com.knowledgepixels.nanodash.page.ViewResultsPage;
19
import com.knowledgepixels.nanodash.template.TemplateData;
20
import org.apache.wicket.markup.html.WebMarkupContainer;
21
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
22
import org.apache.wicket.markup.html.link.ExternalLink;
23
import org.apache.wicket.markup.html.link.Link;
24
import org.apache.wicket.markup.repeater.Item;
25
import org.apache.wicket.markup.repeater.data.DataView;
26
import org.apache.wicket.markup.repeater.data.ListDataProvider;
27
import org.apache.wicket.model.Model;
28
import org.apache.wicket.request.mapper.parameter.PageParameters;
29
import org.eclipse.rdf4j.model.IRI;
30
import org.nanopub.extra.services.QueryRef;
31

32
import java.util.List;
33

34
/**
35
 * A dropdown menu panel for view displays, replacing the "^" source link.
36
 * Provides options to show the query, adjust the view display, and see its declaration.
37
 */
38
public class ViewDisplayMenu extends BaseDisplayMenu {
39

40
    /**
41
     * Constructs a ViewDisplayMenu.
42
     *
43
     * @param id           the Wicket component ID
44
     * @param viewDisplay  the view display this menu acts on (must have a non-null nanopub)
45
     * @param queryRef     the query reference used by this view display, or null for a
46
     *                     query-less view (a header view), which hides the
47
     *                     query-dependent entries (show query, full screen, refresh)
48
     * @param pageResource the page-level resource used to determine whether "adjust" is visible
49
     * @param viewActions  the view-level actions to show as top entries (may be empty)
50
     */
51
    public ViewDisplayMenu(String id, ViewDisplay viewDisplay, QueryRef queryRef, AbstractResourceWithProfile pageResource, List<QueryResult.MenuAction> viewActions) {
52
        super(id);
×
53

54
        // View-level actions become the top entries of the menu, followed by a
55
        // separator and then the standard view-display options below.
56
        DataView<QueryResult.MenuAction> viewActionView = new DataView<>("viewActions", new ListDataProvider<>(viewActions)) {
×
57
            @Override
58
            protected void populateItem(Item<QueryResult.MenuAction> item) {
59
                QueryResult.MenuAction action = item.getModelObject();
×
60
                BookmarkablePageLink<Void> link = new BookmarkablePageLink<>("viewAction", action.pageClass(), action.params());
×
61
                // A label that starts with a leading symbol/emoji renders that as the entry icon.
62
                String iconBody = Utils.menuEntryIconBodyHtml(action.label());
×
63
                if (iconBody != null) {
×
64
                    link.setBody(Model.of(iconBody)).setEscapeModelStrings(false);
×
65
                } else {
66
                    link.setBody(Model.of(action.label()));
×
67
                }
68
                item.add(link);
×
69
            }
×
70
        };
71
        addEntry("viewActions", viewActionView);
×
72
        WebMarkupContainer separator = new WebMarkupContainer("separator");
×
73
        separator.setVisible(!viewActions.isEmpty());
×
74
        addEntry("separator", separator);
×
75

76
        if (queryRef != null) {
×
77
            PageParameters showQueryParams = new PageParameters().set("id", queryRef.getQueryId());
×
78
            for (var entry : queryRef.getParams().entries()) {
×
79
                showQueryParams.add("queryparam_" + entry.getKey(), entry.getValue());
×
80
            }
×
81
            addEntry("showQuery", new BookmarkablePageLink<Void>("showQuery", QueryPage.class, showQueryParams)
×
82
                    .add(NavigationContext.pageContextFallback()));
×
83
        } else {
×
84
            addEntry("showQuery", new WebMarkupContainer("showQuery").setVisible(false));
×
85
        }
86

87
        addEntry("showView", new BookmarkablePageLink<Void>("showView", ExplorePage.class,
×
88
                new PageParameters().set("id", viewDisplay.getView().getNanopub().getUri()))
×
89
                .add(NavigationContext.pageContextFallback()));
×
90

91
        // Full-screen version of this view on the standalone view-results page, with the
92
        // current query parameters carried along (magic ones excluded — the results page
93
        // re-binds them from the session, so carrying them would duplicate the bindings).
94
        if (queryRef != null) {
×
95
            PageParameters fullScreenParams = new PageParameters().set("view", viewDisplay.getView().getId());
×
96
            for (var entry : queryRef.getParams().entries()) {
×
97
                if (MagicQueryParams.isMagic(entry.getKey())) continue;
×
98
                fullScreenParams.add("queryparam_" + entry.getKey(), entry.getValue());
×
99
            }
×
100
            BookmarkablePageLink<Void> fullScreenLink = new BookmarkablePageLink<>("fullScreen", ViewResultsPage.class, fullScreenParams) {
×
101
                @Override
102
                protected void onConfigure() {
103
                    super.onConfigure();
×
104
                    // Self-referential on the full-screen page itself.
105
                    setVisible(!(getPage() instanceof ViewResultsPage));
×
106
                }
×
107
            };
108
            fullScreenLink.add(NavigationContext.pageContextFallback());
×
109
            addEntry("fullScreen", fullScreenLink);
×
110
        } else {
×
111
            addEntry("fullScreen", new WebMarkupContainer("fullScreen").setVisible(false));
×
112
        }
113

114
        IRI nanopubId = viewDisplay.getNanopubId();
×
115

116
        // Determine whether "adjust" should be visible for this user on this page
117
        boolean showAdjust = false;
×
118
        NanodashSession session = NanodashSession.get();
×
119
        if (pageResource instanceof IndividualAgent ia) {
×
120
            showAdjust = ia.isCurrentUser();
×
121
        } else if (pageResource instanceof Space s) {
×
122
            String pubkeyhash = session.getPubkeyhash();
×
123
            showAdjust = pubkeyhash != null && s.isAdminPubkey(pubkeyhash);
×
124
        } else if (pageResource != null) {
×
125
            Space space = pageResource.getSpace();
×
126
            if (space != null) {
×
127
                String pubkeyhash = session.getPubkeyhash();
×
128
                showAdjust = pubkeyhash != null && space.isAdminPubkey(pubkeyhash);
×
129
            }
130
        }
131

132
        // Determine supersede vs derive based on whether this user's pubkey matches the nanopub's.
133
        // These are only needed when showAdjust is true (i.e. pageResource is non-null).
134
        String adjustUrl = "";
×
135
        String pageResourceId = pageResource != null ? pageResource.getId() : "";
×
136
        if (showAdjust) {
×
137
            String nanopubPubkey = NanopubElement.get(viewDisplay.getNanopub()).getPubkey();
×
138
            String sessionPubkey = session.getPubkeyString();
×
139
            String adjustParam = (nanopubPubkey != null && nanopubPubkey.equals(sessionPubkey))
×
140
                    ? "supersede" : "derive";
×
141
            IRI templateId = TemplateData.get().getTemplateId(viewDisplay.getNanopub());
×
142
            String templateUri = templateId != null ? templateId.stringValue()
×
143
                    : "http://purl.org/np/RACyK2NjqFgezYLiE8FQu7JI0xY1M1aNQbykeCW8oqXkA";
×
144
            adjustUrl = PublishPage.MOUNT_PATH + "?template=" + Utils.urlEncode(templateUri)
×
145
                    + "&" + adjustParam + "=" + Utils.urlEncode(nanopubId.stringValue())
×
146
                    + "&template-version=latest"
147
                    + "&context=" + Utils.urlEncode(pageResourceId);
×
148
        }
149
        // "edit"/"deactivate view display" only make sense for an actual view-display
150
        // assignment (one with a resolved view IRI). Built-in views rendered directly —
151
        // e.g. a space's About-tab meta-views (roles/members/presets/view-displays) — have
152
        // no view-display nanopub, so these options are hidden for them.
153
        boolean isViewDisplay = viewDisplay.getViewIri() != null;
×
154
        // Label (with its leading icon) comes from the markup body, so no label arg here.
155
        ExternalLink adjustLink = new ExternalLink("adjust", adjustUrl);
×
156
        adjustLink.setVisible(showAdjust && isViewDisplay);
×
157
        addEntry("adjust", adjustLink);
×
158

159
        BookmarkablePageLink<Void> deactivateLink = new BookmarkablePageLink<>("deactivate", PublishPage.class,
×
160
                new PageParameters()
161
                        .set("template", "https://w3id.org/np/RAZ47_4JquvEXk30HYnVeSgFRcQqHtpdibcfBOeqHI2j4")
×
162
                        .set("template-version", "latest")
×
163
                        .set("param_resource", pageResourceId)
×
164
                        .set("param_view", viewDisplay.getViewIri() != null ? viewDisplay.getViewIri().stringValue() : viewDisplay.getView().getId())
×
165
                        .set("context", pageResourceId)
×
166
                        .set("refresh-upon-publish", pageResourceId));
×
167
        deactivateLink.setVisible(showAdjust && isViewDisplay);
×
168
        addEntry("deactivate", deactivateLink);
×
169

170
        boolean showAddToOwn = session.getUserIri() != null
×
171
                && viewDisplay.getViewIri() != null
×
172
                && pageResource instanceof IndividualAgent ia && !ia.isCurrentUser();
×
173
        String addToOwnUrl = "";
×
174
        if (showAddToOwn) {
×
175
            String userIri = session.getUserIri().stringValue();
×
176
            String viewIri = viewDisplay.getViewIri().stringValue();
×
177
            if (viewDisplay.getView() != null && viewDisplay.getView().getLabel() != null) {
×
178
                GuidedChoiceItem.setLabel(viewIri, viewDisplay.getView().getLabel());
×
179
            }
180
            addToOwnUrl = PublishPage.MOUNT_PATH + "?template=" + Utils.urlEncode("https://w3id.org/np/RAQhTCHtfzGCj1YiE1LualWcZjg3thlRiquFWUE14UF-g")
×
181
                    + "&template-version=latest"
182
                    + "&param_resource=" + Utils.urlEncode(userIri)
×
183
                    + "&param_view=" + Utils.urlEncode(viewIri)
×
184
                    + "&context=" + Utils.urlEncode(userIri)
×
185
                    + "&refresh-upon-publish=" + Utils.urlEncode(userIri)
×
186
                    + "&param_appliesToResource=" + Utils.urlEncode(userIri);
×
187
        }
188
        // Label (with its leading icon) comes from the markup body, so no label arg here.
189
        ExternalLink addToOwnLink = new ExternalLink("addToOwn", addToOwnUrl);
×
190
        addToOwnLink.setVisible(showAddToOwn);
×
191
        addEntry("addToOwn", addToOwnLink);
×
192

193
        Link<Void> refreshLink = new Link<>("refreshNow") {
×
194
            @Override
195
            public void onClick() {
196
                ApiCache.clearCache(queryRef, 0);
×
197
                setResponsePage(getPage().getClass(), getPage().getPageParameters());
×
198
            }
×
199
        };
200
        refreshLink.setVisible(session.getUserIri() != null && queryRef != null);
×
201
        addEntry("refreshNow", refreshLink);
×
202

203
        BookmarkablePageLink<Void> viewDeclarationLink = new BookmarkablePageLink<>("viewDeclaration", ExplorePage.class,
×
204
                new PageParameters().set("id", nanopubId));
×
205
        viewDeclarationLink.add(NavigationContext.pageContextFallback());
×
206
        viewDeclarationLink.setVisible(viewDisplay.getId() != null);
×
207
        addEntry("viewDeclaration", viewDeclarationLink);
×
208
    }
×
209

210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc