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

knowledgepixels / nanodash / 28899187612

07 Jul 2026 09:14PM UTC coverage: 28.105% (-0.4%) from 28.465%
28899187612

Pull #543

github

web-flow
Merge 39c9ede0f into f1a7efda7
Pull Request #543: feat: query-form views (gen:QueryFormView) with view-results page

1813 of 7309 branches covered (24.81%)

Branch coverage included in aggregate %.

3717 of 12367 relevant lines covered (30.06%)

4.46 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/QueryResultItemList.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.*;
4
import com.knowledgepixels.nanodash.component.menu.EntryActionMenu;
5
import com.knowledgepixels.nanodash.domain.IndividualAgent;
6
import com.knowledgepixels.nanodash.domain.User;
7
import com.knowledgepixels.nanodash.page.PublishPage;
8
import com.knowledgepixels.nanodash.page.UserPage;
9
import org.apache.wicket.Component;
10
import org.apache.wicket.ajax.AjaxRequestTarget;
11
import org.apache.wicket.markup.html.link.AbstractLink;
12
import org.apache.wicket.ajax.markup.html.navigation.paging.AjaxPagingNavigator;
13
import org.apache.wicket.extensions.markup.html.repeater.data.table.NavigatorLabel;
14
import org.apache.wicket.markup.html.WebMarkupContainer;
15
import org.apache.wicket.markup.html.basic.Label;
16
import org.apache.wicket.markup.html.form.TextField;
17
import org.apache.wicket.markup.repeater.Item;
18
import org.apache.wicket.markup.repeater.data.DataView;
19
import org.apache.wicket.model.Model;
20
import org.apache.wicket.request.cycle.RequestCycle;
21
import org.apache.wicket.request.resource.ContextRelativeResourceReference;
22
import org.apache.wicket.util.string.Strings;
23
import org.eclipse.rdf4j.model.IRI;
24
import org.nanopub.extra.services.ApiResponse;
25
import org.nanopub.extra.services.ApiResponseEntry;
26
import org.nanopub.extra.services.QueryRef;
27

28
import java.util.List;
29

30
/**
31
 * Component for displaying query results as a vertical item list.
32
 * Each result row is rendered as a single linked item, with icon handling
33
 * for user_iri and template_iri columns.
34
 */
35
public class QueryResultItemList extends QueryResult {
36

37
    private FilteredQueryResultDataProvider filteredDataProvider;
38
    private final Model<String> filterModel = Model.of("");
×
39
    private WebMarkupContainer itemsContainer;
40

41
    QueryResultItemList(String markupId, QueryRef queryRef, ApiResponse response, ViewDisplay viewDisplay) {
42
        super(markupId, queryRef, response, viewDisplay);
×
43

44
        String label = grlcQuery.getLabel();
×
45
        if (viewDisplay.getTitle() != null) {
×
46
            label = viewDisplay.getTitle();
×
47
        }
48
        add(new Label("label", label).setVisible(label != null && !label.isEmpty()));
×
49
        setOutputMarkupId(true);
×
50

51
        TextField<String> filterField = new TextField<>("filter", filterModel);
×
52
        filterField.setOutputMarkupId(true);
×
53
        filterField.add(new FilterUpdatingBehavior() {
×
54
            @Override
55
            protected void onUpdate(AjaxRequestTarget target) {
56
                if (filteredDataProvider != null && itemsContainer != null) {
×
57
                    filteredDataProvider.setFilterText(filterModel.getObject());
×
58
                    target.add(itemsContainer);
×
59
                }
60
            }
×
61
        });
62
        filterField.setVisible(!fitsOnFirstPage());
×
63
        add(filterField);
×
64

65
        populateComponent();
×
66
    }
×
67

68
    @Override
69
    protected void populateComponent() {
70
        QueryResultDataProvider dataProvider = new QueryResultDataProvider(response.getData());
×
71
        filteredDataProvider = new FilteredQueryResultDataProvider(dataProvider, response);
×
72

73
        DataView<ApiResponseEntry> dataView = new DataView<>("items", filteredDataProvider) {
×
74
            @Override
75
            protected void populateItem(Item<ApiResponseEntry> item) {
76
                ApiResponseEntry entry = item.getModelObject();
×
77
                item.add(buildItemComponent(entry));
×
78
                List<AbstractLink> actionLinks = ViewActionMappings.buildEntryActionLinks(viewDisplay.getView(), entry,
×
79
                        queryRef, resourceWithProfile != null ? resourceWithProfile : pageResource,
×
80
                        contextId, partId, refRoot, postPublishTab);
×
81
                if (actionLinks.isEmpty()) {
×
82
                    item.add(new Label("entryActions").setVisible(false));
×
83
                } else {
84
                    item.add(new EntryActionMenu("entryActions", actionLinks));
×
85
                }
86
            }
×
87
        };
88
        dataView.setItemsPerPage(viewDisplay.getPageSize());
×
89

90
        WebMarkupContainer navigation = new WebMarkupContainer("navigation");
×
91
        navigation.add(new NavigatorLabel("navigatorLabel", dataView));
×
92
        AjaxPagingNavigator pagingNavigator = new AjaxPagingNavigator("navigator", dataView);
×
93
        navigation.setVisible(dataView.getPageCount() > 1);
×
94
        navigation.add(pagingNavigator);
×
95

96
        Label noRecordsLabel = new Label("no-records", "(nothing found)") {
×
97
            @Override
98
            protected void onConfigure() {
99
                super.onConfigure();
×
100
                setVisible(filteredDataProvider.size() == 0);
×
101
            }
×
102
        };
103

104
        itemsContainer = new WebMarkupContainer("items-container");
×
105
        itemsContainer.setOutputMarkupId(true);
×
106
        itemsContainer.add(dataView);
×
107
        itemsContainer.add(noRecordsLabel);
×
108
        itemsContainer.add(navigation);
×
109
        add(itemsContainer);
×
110
    }
×
111

112
    /**
113
     * Builds the row's single linked item (markup id {@code "listItem"}) from the
114
     * first non-empty result column.
115
     */
116
    private Component buildItemComponent(ApiResponseEntry entry) {
117
        for (String key : response.getHeader()) {
×
118
            if (key.endsWith("_label") || key.endsWith("_label_multi")) continue;
×
119
            String value = entry.get(key);
×
120
            if (value == null || value.isBlank()) continue;
×
121
            String entryLabel = entry.get(key + "_label");
×
122

123
            if (key.endsWith("user_iri")) {
×
124
                IRI userIri = Utils.vf.createIRI(value);
×
125
                IRI profilePicIri = User.getProfilePicture(userIri);
×
126
                String imgSrc;
127
                String iconClass;
128
                if (profilePicIri != null) {
×
129
                    imgSrc = Strings.escapeMarkup(profilePicIri.stringValue()).toString();
×
130
                    iconClass = "user-icon";
×
131
                } else if (IndividualAgent.isSoftware(userIri)) {
×
132
                    imgSrc = RequestCycle.get().urlFor(new ContextRelativeResourceReference("images/bot-icon.svg", false), null).toString();
×
133
                    iconClass = "bot-icon";
×
134
                } else {
135
                    imgSrc = RequestCycle.get().urlFor(new ContextRelativeResourceReference("images/user-icon.svg", false), null).toString();
×
136
                    iconClass = "user-icon";
×
137
                }
138
                String displayLabel = (entryLabel != null && !entryLabel.isBlank()) ? entryLabel : User.getShortDisplayName(userIri);
×
139
                String userUrl = UserPage.MOUNT_PATH + "?id=" + Utils.urlEncode(value);
×
140
                String html = "<img class=\"" + iconClass + "\" src=\"" + imgSrc + "\" /> <a href=\"" + Strings.escapeMarkup(userUrl) + "\">" + Strings.escapeMarkup(displayLabel) + "</a>";
×
141
                return new Label("listItem", html).setEscapeModelStrings(false);
×
142
            } else if (key.endsWith("template_iri")) {
×
143
                String displayLabel = (entryLabel != null && !entryLabel.isBlank()) ? entryLabel : value;
×
144
                String templateUrl = PublishPage.MOUNT_PATH + "?template=" + Utils.urlEncode(value) + "&template-version=latest" + templateLinkContextParam();
×
145
                String html = "<span class=\"form-icon\"></span> <a href=\"" + Strings.escapeMarkup(templateUrl) + "\">" + Strings.escapeMarkup(displayLabel) + "</a>";
×
146
                return new Label("listItem", html).setEscapeModelStrings(false);
×
147
            } else if (value.matches("https?://.*")) {
×
148
                return new NanodashLink("listItem", value, null, null, entryLabel, contextId);
×
149
            } else if (entryLabel != null && !entryLabel.isBlank() && !entryLabel.equals(value)) {
×
150
                // Separate display label for a (non-IRI) literal value; the full
151
                // literal is shown on hover via the standard styled tooltip.
152
                String html = "<span class=\"tooltip\"><span class=\"tooltiptext tooltiptext-auto\">" + Strings.escapeMarkup(value) + "</span>" + Strings.escapeMarkup(entryLabel) + "</span>";
×
153
                return new Label("listItem", html).setEscapeModelStrings(false);
×
154
            } else if (Utils.isDateTimeLiteral(value)) {
×
155
                // Show a friendly relative time (client-side); raw ISO value stays as no-script fallback.
156
                return new Label("listItem", Utils.friendlyDateHtml(value, value)).setEscapeModelStrings(false);
×
157
            } else {
158
                return new Label("listItem", value);
×
159
            }
160
        }
161
        return new Label("listItem", "");
×
162
    }
163
}
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