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

knowledgepixels / nanodash / 28231274435

26 Jun 2026 10:04AM UTC coverage: 27.677% (-0.1%) from 27.794%
28231274435

push

github

web-flow
Merge pull request #514 from knowledgepixels/feat/unconfigured-page-notice

Show general-info fallback on unconfigured space/resource pages

1676 of 6957 branches covered (24.09%)

Branch coverage included in aggregate %.

3561 of 11965 relevant lines covered (29.76%)

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

3
import com.knowledgepixels.nanodash.NanodashPageRef;
4
import com.knowledgepixels.nanodash.SpaceMemberRole;
5
import com.knowledgepixels.nanodash.View;
6
import com.knowledgepixels.nanodash.ViewDisplay;
7
import com.knowledgepixels.nanodash.component.*;
8
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
9
import com.knowledgepixels.nanodash.domain.MaintainedResource;
10
import com.knowledgepixels.nanodash.domain.Space;
11
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
12
import org.apache.wicket.Component;
13
import org.apache.wicket.ajax.AjaxRequestTarget;
14
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
15
import org.apache.wicket.markup.html.WebMarkupContainer;
16
import org.apache.wicket.markup.html.basic.Label;
17
import org.apache.wicket.markup.html.panel.EmptyPanel;
18
import org.apache.wicket.model.IModel;
19
import org.apache.wicket.model.LoadableDetachableModel;
20
import org.apache.wicket.model.Model;
21
import org.apache.wicket.request.mapper.parameter.PageParameters;
22
import org.eclipse.rdf4j.model.util.Values;
23

24
import java.util.List;
25
import java.util.Optional;
26

27
/**
28
 * This class represents a page for a maintained resource.
29
 */
30
public class MaintainedResourcePage extends NanodashPage {
31

32
    /**
33
     * The mount path for this page.
34
     */
35
    public static final String MOUNT_PATH = "/resource";
36

37
    /**
38
     * {@inheritDoc}
39
     */
40
    @Override
41
    public String getMountPath() {
42
        return MOUNT_PATH;
×
43
    }
44

45
    /**
46
     * Id of the maintained resource shown on this page. Only the id is held in
47
     * the page state; the {@link MaintainedResource} itself is re-fetched from
48
     * the repository on every render via {@link #resourceModel}, so the page
49
     * tree never carries a serialized snapshot of singleton data.
50
     */
51
    private final String resourceId;
52

53
    /**
54
     * LDM that resolves {@link #resourceId} to the live {@link MaintainedResource} singleton.
55
     */
56
    private final IModel<MaintainedResource> resourceModel;
57

58
    public MaintainedResourcePage(final PageParameters parameters) {
59
        super(parameters);
×
60

61
        MaintainedResource resource = MaintainedResourceRepository.get().findById(parameters.get("id").toString());
×
62
        resourceId = resource.getId();
×
63
        resourceModel = new LoadableDetachableModel<MaintainedResource>() {
×
64
            @Override
65
            protected MaintainedResource load() {
66
                return MaintainedResourceRepository.get().findById(resourceId);
×
67
            }
68
        };
69
        resource.triggerDataUpdate();
×
70

71
        ResourceTabs.Tab activeTab = ResourceTabs.activeFromParam(parameters);
×
72

73
        List<AbstractResourceWithProfile> superSpaces = resource.getAllSuperSpacesUntilRoot();
×
74
        superSpaces.add(resource.getSpace());
×
75
        superSpaces.add(resource);
×
76
        add(new TitleBar("titlebar", this, null,
×
77
                superSpaces.stream().map(ss -> new NanodashPageRef(SpacePage.class, new PageParameters().add("id", ss.getId()), ss.getLabel())).toArray(NanodashPageRef[]::new)
×
78
        ).setTabs(new ResourceTabs("tabs", "resource", resource.getId(), activeTab)));
×
79

80
        add(new Label("pagetitle", resource.getLabel() + " (resource) | nanodash"));
×
81
        add(new Label("resourcename", resource.getLabel()));
×
82
        add(new Label("titlesuffix", ResourceTabs.titleSuffix(activeTab)));
×
83
        add(new ExternalLinkWithActionsPanel("id", Model.of(resource.getId()), Model.of(resource.getLabel()), Values.iri(resource.getNanopubId())));
×
84

85
        WebMarkupContainer contentContainer = new WebMarkupContainer("contentContainer");
×
86
        add(contentContainer);
×
87
        if (activeTab == ResourceTabs.Tab.CONTENT) {
×
88
            add(new EmptyPanel("otherTab").setVisible(false));
×
89
            if (resource.isDataInitialized()) {
×
90
                boolean empty = resource.getTopLevelViewDisplays().isEmpty();
×
91
                if (empty) {
×
92
                    contentContainer.add(new WebMarkupContainer("views").setVisible(false));
×
93
                } else {
94
                    contentContainer.add(new ViewList("views", resource));
×
95
                }
96
                addUnconfiguredFallback(contentContainer, resource, empty);
×
97
            } else {
×
98
                // Data not yet loaded: render the views lazily, then reveal the unconfigured
99
                // notice + general-info fallback once we know whether any views exist.
100
                final WebMarkupContainer unconfiguredNotice = new WebMarkupContainer("unconfigured-notice");
×
101
                unconfiguredNotice.setVisible(false);
×
102
                unconfiguredNotice.setOutputMarkupPlaceholderTag(true);
×
103
                contentContainer.add(unconfiguredNotice);
×
104

105
                final ViewList generalInfoView = new ViewList("generalinfoview", resource, List.of(generalInfoViewDisplay()));
×
106
                generalInfoView.setVisible(false);
×
107
                generalInfoView.setOutputMarkupPlaceholderTag(true);
×
108
                contentContainer.add(generalInfoView);
×
109

110
                contentContainer.add(new AjaxLazyLoadPanel<Component>("views") {
×
111

112
                    @Override
113
                    public Component getLazyLoadComponent(String markupId) {
114
                        return new ViewList(markupId, resourceModel.getObject());
×
115
                    }
116

117
                    @Override
118
                    protected boolean isContentReady() {
119
                        return resourceModel.getObject().isDataInitialized();
×
120
                    }
121

122
                    @Override
123
                    public Component getLoadingComponent(String id) {
124
                        return new Label(id, "<div class=\"row-section\"><div class=\"col-12\">" + ResultComponent.getWaitIconHtml() + "</div></div>").setEscapeModelStrings(false);
×
125
                    }
126

127
                    @Override
128
                    protected void onContentLoaded(Component content, Optional<AjaxRequestTarget> target) {
129
                        super.onContentLoaded(content, target);
×
130
                        target.ifPresent(t -> {
×
131
                            boolean isEmpty = resourceModel.getObject().getTopLevelViewDisplays().isEmpty();
×
132
                            if (isEmpty) {
×
133
                                t.appendJavaScript("document.getElementById('" + getMarkupId() + "').remove();");
×
134
                            }
135
                            unconfiguredNotice.setVisible(isEmpty);
×
136
                            t.add(unconfiguredNotice);
×
137
                            generalInfoView.setVisible(isEmpty);
×
138
                            t.add(generalInfoView);
×
139
                        });
×
140
                    }
×
141

142
                });
143
            }
×
144
        } else {
145
            contentContainer.setVisible(false);
×
146
            if (activeTab == ResourceTabs.Tab.ABOUT) {
×
147
                // The panel constructor resolves view nanopubs over the network when
148
                // they aren't freshly cached, which would block the initial page
149
                // render; the view-id list must mirror the panel's View.get calls.
150
                add(LazyContentPanel.of("otherTab", markupId -> new AboutResourcePanel(markupId, resourceModel.getObject()),
×
151
                        AboutResourcePanel.MAINTAINED_RESOURCE_INFO_VIEW, AboutSpacePanel.PRESET_ASSIGNMENTS_VIEW, AboutSpacePanel.VIEW_DISPLAYS_VIEW));
152
            } else if (activeTab == ResourceTabs.Tab.EXPLORE) {
×
153
                add(LazyContentPanel.of("otherTab", markupId -> new ExplorePanel(markupId, resourceId),
×
154
                        ReferencesPage.REFERENCES_VIEW));
155
            } else {
156
                add(new DownloadRdfLinks("otherTab", "resource", resource.getId()));
×
157
            }
158
        }
159
    }
×
160

161
    /**
162
     * View shown as a general-information fallback on pages that have no view displays
163
     * configured yet.
164
     */
165
    private static final String GENERAL_INFO_VIEW = "https://w3id.org/np/RA9FKwtTWqknnDrFz1vMNeUdWpYYVQp7sznigpaXlBrU8/resource-info-view";
166

167
    private static ViewDisplay generalInfoViewDisplay() {
168
        return new ViewDisplay(View.get(GENERAL_INFO_VIEW));
×
169
    }
170

171
    /**
172
     * Adds the "page not configured yet" notice and the general-information fallback view,
173
     * both visible only when the resource has no view displays.
174
     */
175
    private void addUnconfiguredFallback(WebMarkupContainer contentContainer, AbstractResourceWithProfile resource, boolean empty) {
176
        contentContainer.add(new WebMarkupContainer("unconfigured-notice").setVisible(empty));
×
177
        if (empty) {
×
178
            contentContainer.add(new ViewList("generalinfoview", resource, List.of(generalInfoViewDisplay())));
×
179
        } else {
180
            contentContainer.add(new EmptyPanel("generalinfoview").setVisible(false));
×
181
        }
182
    }
×
183

184
    /**
185
     * Checks if auto-refresh is enabled for this page.
186
     *
187
     * @return true if auto-refresh is enabled, false otherwise
188
     */
189
    protected boolean hasAutoRefreshEnabled() {
190
        return true;
×
191
    }
192

193
    /**
194
     * {@inheritDoc}
195
     */
196
    @Override
197
    protected void onDetach() {
198
        resourceModel.detach();
×
199
        super.onDetach();
×
200
    }
×
201

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