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

knowledgepixels / nanodash / 26455055595

26 May 2026 02:38PM UTC coverage: 20.427% (-0.3%) from 20.748%
26455055595

Pull #468

github

web-flow
Merge 0354b3eb9 into 65b0c8452
Pull Request #468: Source space data from nanopub-query spaces repo

1005 of 6260 branches covered (16.05%)

Branch coverage included in aggregate %.

2600 of 11388 relevant lines covered (22.83%)

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

3
import com.knowledgepixels.nanodash.NanodashPageRef;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.component.*;
6
import com.knowledgepixels.nanodash.component.menu.SpaceExploreMenu;
7
import com.knowledgepixels.nanodash.connector.ConnectorConfig;
8
import com.knowledgepixels.nanodash.connector.GenOverviewPage;
9
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
10
import com.knowledgepixels.nanodash.SpaceMemberRole;
11
import com.knowledgepixels.nanodash.domain.MaintainedResource;
12
import com.knowledgepixels.nanodash.domain.Space;
13
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
14
import com.knowledgepixels.nanodash.repository.SpaceRepository;
15
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
16
import org.apache.wicket.Component;
17
import org.apache.wicket.RestartResponseException;
18
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
19
import org.apache.wicket.markup.html.basic.Label;
20
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
21
import org.apache.wicket.model.IModel;
22
import org.apache.wicket.model.LoadableDetachableModel;
23
import org.apache.wicket.model.Model;
24
import org.apache.wicket.request.mapper.parameter.PageParameters;
25
import org.nanopub.Nanopub;
26

27
import java.time.ZoneId;
28
import java.time.ZonedDateTime;
29
import java.time.format.DateTimeFormatter;
30
import java.util.List;
31

32
/**
33
 * The SpacePage class represents a space page in the Nanodash application.
34
 */
35
public class SpacePage extends NanodashPage {
36

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

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

50
    /**
51
     * Id of the space shown on this page. Only the id is held in the page
52
     * state; the {@link Space} itself is re-fetched from the repository on
53
     * every render via {@link #spaceModel}, so the page tree never carries
54
     * a serialized snapshot of singleton data.
55
     */
56
    private final String spaceId;
57

58
    /**
59
     * LDM that resolves {@link #spaceId} to the live {@link Space} singleton.
60
     */
61
    private final IModel<Space> spaceModel;
62

63
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
×
64
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
×
65

66
    /**
67
     * Constructor for the SpacePage.
68
     *
69
     * @param parameters the page parameters
70
     */
71
    public SpacePage(final PageParameters parameters) {
72
        super(parameters);
×
73

74
        Space space = resolveSpace(parameters);
×
75
        spaceId = space.getId();
×
76
        spaceModel = new LoadableDetachableModel<Space>() {
×
77
            @Override
78
            protected Space load() {
79
                return SpaceRepository.get().findById(spaceId);
×
80
            }
81
        };
82
        space.triggerDataUpdate();
×
83

84
        Nanopub np = space.getNanopub();
×
85

86
        List<AbstractResourceWithProfile> superSpaces = space.getAllSuperSpacesUntilRoot();
×
87
        if (superSpaces.isEmpty()) {
×
88
            add(new TitleBar("titlebar", this, null,
×
89
                    new NanodashPageRef(SpacePage.class, new PageParameters().add("id", space.getId()), space.getLabel())
×
90
            ));
91
        } else {
92
            superSpaces.add(space);
×
93
            add(new TitleBar("titlebar", this, null,
×
94
                    superSpaces.stream().map(ss -> new NanodashPageRef(SpacePage.class, new PageParameters().add("id", ss.getId()), ss.getLabel())).toArray(NanodashPageRef[]::new)
×
95
            ));
96
        }
97

98
        add(new JustPublishedMessagePanel("justPublishedMessage", parameters));
×
99

100
        add(new Label("pagetitle", space.getLabel() + " (space) | nanodash"));
×
101
        add(new Label("spacename", space.getLabel()));
×
102
        add(new Label("spacetype", space.getTypeLabel()));
×
103
        add(new ExternalLinkWithActionsPanel("id", Model.of(space.getId()), Model.of(space.getLabel()),
×
104
                new SpaceExploreMenu("np", space.getId(), space.getLabel(), np.getUri(), space)));
×
105

106
        boolean isAdmin = SpaceMemberRole.isCurrentUserAdmin(space);
×
107
        add(new AddViewDisplayButton("addviewdisplay",
×
108
                "https://w3id.org/np/RAwPPxDxkXwgWwYhmvzi6SUs8djPZS4IgWJdp2G0blqoQ",
109
                "latest",
110
                space.getId(),
×
111
                space.getId(),
×
112
                new PageParameters()
113
                        .set("param_appliesToResource", space.getId())
×
114
                        .set("refresh-upon-publish", space.getId())
×
115
        ).setVisible(isAdmin));
×
116
        add(new DownloadRdfLinks("download-rdf", "space", space.getId()));
×
117

118
        add(new ItemListPanel<String>(
×
119
                "altids",
120
                "Alternative IDs:",
121
                space.getAltIDs(),
×
122
                i -> new ExternalLinkWithActionsPanel("item", Model.of(i), Model.of(i))
×
123
        ));
124

125
        if (space.getStartDate() != null) {
×
126
            ZoneId startZone = space.getStartDate().getTimeZone().toZoneId();
×
127
            ZonedDateTime startDt = ZonedDateTime.ofInstant(space.getStartDate().toInstant(), startZone);
×
128
            String dateString = DATE_FORMATTER.format(startDt);
×
129
            if (space.getEndDate() != null) {
×
130
                ZoneId endZone = space.getEndDate().getTimeZone().toZoneId();
×
131
                ZonedDateTime endDt = ZonedDateTime.ofInstant(space.getEndDate().toInstant(), endZone);
×
132
                String endDateStr = DATE_FORMATTER.format(endDt);
×
133
                if (dateString.equals(endDateStr)) {
×
134
                    String tzAbbr = startDt.getZone().getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.ENGLISH);
×
135
                    dateString += " " + TIME_FORMATTER.format(startDt) + " - " + TIME_FORMATTER.format(endDt) + " " + tzAbbr;
×
136
                } else {
×
137
                    dateString += " - " + endDateStr;
×
138
                }
139
            }
140
            add(new Label("date", dateString));
×
141
        } else {
×
142
            add(new Label("date").setVisible(false));
×
143
        }
144

145
        add(new Label("description", "<span>" + Utils.sanitizeHtml(space.getDescription()) + "</span>").setEscapeModelStrings(false));
×
146

147
        if (space.isDataInitialized()) {
×
148
            add(new ViewList("views", space));
×
149
        } else {
150
            add(new AjaxLazyLoadPanel<Component>("views") {
×
151

152
                @Override
153
                public Component getLazyLoadComponent(String markupId) {
154
                    return new ViewList(markupId, spaceModel.getObject());
×
155
                }
156

157
                @Override
158
                protected boolean isContentReady() {
159
                    return spaceModel.getObject().isDataInitialized();
×
160
                }
161

162
                @Override
163
                public Component getLoadingComponent(String id) {
164
                    return new Label(id, "<div class=\"row-section\"><div class=\"col-12\">" + ResultComponent.getWaitIconHtml() + "</div></div>").setEscapeModelStrings(false);
×
165
                }
166

167
            });
168
        }
169

170
        add(new ItemListPanel<>(
×
171
                        "roles",
172
                        "Roles:",
173
                        () -> spaceModel.getObject().isDataInitialized(),
×
174
                        () -> spaceModel.getObject().getRoles(),
×
175
                        r -> new ItemListElement("item", ExplorePage.class, new PageParameters().set("id", r.getRole().getId()), r.getRole().getName(), null, Utils.getAsNanopub(r.getNanopubUri()))
×
176
                )
177
                        .makeInline()
×
178
                        .setResourceWithProfile(space)
×
179
                        .addAdminButton("+", PublishPage.class, new PageParameters()
×
180
                                .set("template", "https://w3id.org/np/RARBzGkEqiQzeiHk0EXFcv9Ol1d-17iOh9MoFJzgfVQDc")
×
181
                                .set("param_space", space.getId())
×
182
                                .set("refresh-upon-publish", space.getId())
×
183
                                .set("template-version", "latest")
×
184
                        )
185
        );
186

187
        if (space.isDataInitialized()) {
×
188
            add(new SpaceUserList("user-lists", space));
×
189
        } else {
190
            add(new AjaxLazyLoadPanel<Component>("user-lists") {
×
191

192
                @Override
193
                public Component getLazyLoadComponent(String markupId) {
194
                    return new SpaceUserList(markupId, spaceModel.getObject());
×
195
                }
196

197
                @Override
198
                protected boolean isContentReady() {
199
                    return spaceModel.getObject().isDataInitialized();
×
200
                }
201

202
            });
203
        }
204

205
        addSubspacePanel("Alliance");
×
206
        addSubspacePanel("Consortium");
×
207
        addSubspacePanel("Organization");
×
208
        addSubspacePanel("Taskforce");
×
209
        addSubspacePanel("Division");
×
210
        addSubspacePanel("Taskunit");
×
211
        addSubspacePanel("Group");
×
212
        addSubspacePanel("Project");
×
213
        addSubspacePanel("Program");
×
214
        addSubspacePanel("Initiative");
×
215
        addSubspacePanel("Outlet");
×
216
        addSubspacePanel("Campaign");
×
217
        addSubspacePanel("Community");
×
218
        addSubspacePanel("Event");
×
219

220
        add(new ItemListPanel<MaintainedResource>(
×
221
                "resources",
222
                "📦 Maintained Resources",
223
                () -> {
224
                    MaintainedResourceRepository.get().ensureLoaded();
×
225
                    return true;
×
226
                },
227
                () -> MaintainedResourceRepository.get().findResourcesBySpace(spaceModel.getObject()),
×
228
                (resource) -> new ItemListElement("item", MaintainedResourcePage.class, new PageParameters().set("id", resource.getId()), resource.getLabel())
×
229
        ));
230

231
        String shortId = space.getId().replace("https://w3id.org/spaces/", "");
×
232
        ConnectorConfig cc = ConnectorConfig.get(shortId);
×
233
        if (cc != null) {
×
234
            add(new BookmarkablePageLink<Void>("content-button", GenOverviewPage.class, new PageParameters().set("journal", shortId)).setBody(Model.of("Nanopublication Submissions")));
×
235
        } else {
236
            add(new Label("content-button").setVisible(false));
×
237
        }
238
    }
×
239

240
    private void addSubspacePanel(String type) {
241
        String typePl = type + "s";
×
242
        typePl = typePl.replaceFirst("ys$", "ies");
×
243

244
        add(new ItemListPanel<>(
×
245
                        typePl.toLowerCase(),
×
246
                        Space.getTypeEmoji(type) + " " + typePl,
×
247
                        SpaceRepository.get().findSubspaces(spaceModel.getObject(), KPXL_TERMS.NAMESPACE + type),
×
248
                        (subspace) -> new ItemListElement("item", SpacePage.class, new PageParameters().set("id", subspace), subspace.getLabel())
×
249
                )
250
        );
251
    }
×
252

253
    /**
254
     * Checks if auto-refresh is enabled for this page.
255
     *
256
     * @return true if auto-refresh is enabled, false otherwise
257
     */
258
    protected boolean hasAutoRefreshEnabled() {
259
        return true;
×
260
    }
261

262
    /**
263
     * {@inheritDoc}
264
     */
265
    @Override
266
    protected void onDetach() {
267
        spaceModel.detach();
×
268
        super.onDetach();
×
269
    }
×
270

271
    /**
272
     * Resolves the {@link Space} from the repository, or redirects as needed.
273
     *
274
     * @param parameters page parameters containing the space {@code id}
275
     * @return the resolved {@link Space}; never {@code null}
276
     * @throws RestartResponseException if the id belongs to a {@link MaintainedResource} or to a part within one
277
     * @throws IllegalArgumentException if the id cannot be resolved to any known resource
278
     */
279
    private Space resolveSpace(PageParameters parameters) {
280
        String id = parameters.get("id").toString();
×
281
        Space resolved = SpaceRepository.get().findById(id);
×
282
        if (resolved == null) {
×
283
            if (MaintainedResourceRepository.get().findById(id) != null) {
×
284
                throw new RestartResponseException(MaintainedResourcePage.class, parameters);
×
285
            }
286
            MaintainedResource containingResource = MaintainedResourceRepository.get().findByNamespace(MaintainedResource.getNamespace(id));
×
287
            if (containingResource != null) {
×
288
                PageParameters partParameters = new PageParameters(parameters);
×
289
                partParameters.set("context", containingResource.getId());
×
290
                throw new RestartResponseException(ResourcePartPage.class, partParameters);
×
291
            }
292
            throw new IllegalArgumentException("No space or resource found for id: " + id);
×
293
        }
294

295
        return resolved;
×
296
    }
297

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