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

knowledgepixels / nanodash / 23302971328

19 Mar 2026 03:35PM UTC coverage: 16.327% (+0.08%) from 16.251%
23302971328

push

github

tkuhn
feat: add emojis to space type section titles

Add emoji icons for all 14 space types (Alliance, Consortium, etc.)
shown in section headings on the spaces list page and subspace sections
on individual space pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

728 of 5521 branches covered (13.19%)

Branch coverage included in aggregate %.

1865 of 10361 relevant lines covered (18.0%)

2.47 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.QueryApiAccess;
5
import com.knowledgepixels.nanodash.Utils;
6
import com.knowledgepixels.nanodash.component.*;
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.Model;
22
import org.apache.wicket.request.mapper.parameter.PageParameters;
23
import org.nanopub.Nanopub;
24
import org.nanopub.extra.services.QueryRef;
25

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

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

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

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

49
    /**
50
     * Space object with the data shown on this page.
51
     */
52
    private final Space space;
53
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
×
54
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
×
55

56
    /**
57
     * Constructor for the SpacePage.
58
     *
59
     * @param parameters the page parameters
60
     */
61
    public SpacePage(final PageParameters parameters) {
62
        super(parameters);
×
63

64
        space = resolveSpace(parameters);
×
65
        space.triggerDataUpdate();
×
66

67
        Nanopub np = space.getNanopub();
×
68

69
        List<AbstractResourceWithProfile> superSpaces = space.getAllSuperSpacesUntilRoot();
×
70
        if (superSpaces.isEmpty()) {
×
71
            add(new TitleBar("titlebar", this, null,
×
72
                    new NanodashPageRef(SpacePage.class, new PageParameters().add("id", space.getId()), space.getLabel())
×
73
            ));
74
        } else {
75
            superSpaces.add(space);
×
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
            ));
79
        }
80

81
        add(new JustPublishedMessagePanel("justPublishedMessage", parameters));
×
82

83
        add(new Label("pagetitle", space.getLabel() + " (space) | nanodash"));
×
84
        add(new Label("spacename", space.getLabel()));
×
85
        add(new Label("spacetype", space.getTypeLabel()));
×
86
        add(new ExternalLinkWithActionsPanel("id", Model.of(space.getId()), Model.of(space.getLabel()), np.getUri()));
×
87

88
        boolean isAdmin = SpaceMemberRole.isCurrentUserAdmin(space);
×
89
        add(new AddViewDisplayButton("addviewdisplay",
×
90
                "https://w3id.org/np/RAwPPxDxkXwgWwYhmvzi6SUs8djPZS4IgWJdp2G0blqoQ",
91
                "latest",
92
                space.getId(),
×
93
                space.getId(),
×
94
                new PageParameters()
95
                        .set("param_appliesToResource", space.getId())
×
96
                        .set("refresh-upon-publish", space.getId())
×
97
        ).setVisible(isAdmin));
×
98

99
        add(new ItemListPanel<String>(
×
100
                "altids",
101
                "Alternative IDs:",
102
                space.getAltIDs(),
×
103
                i -> new ItemListElement("item", ExplorePage.class, new PageParameters().set("id", i), i)
×
104
        ));
105

106
        if (space.getStartDate() != null) {
×
107
            ZoneId startZone = space.getStartDate().getTimeZone().toZoneId();
×
108
            ZonedDateTime startDt = ZonedDateTime.ofInstant(space.getStartDate().toInstant(), startZone);
×
109
            String dateString = DATE_FORMATTER.format(startDt);
×
110
            if (space.getEndDate() != null) {
×
111
                ZoneId endZone = space.getEndDate().getTimeZone().toZoneId();
×
112
                ZonedDateTime endDt = ZonedDateTime.ofInstant(space.getEndDate().toInstant(), endZone);
×
113
                String endDateStr = DATE_FORMATTER.format(endDt);
×
114
                if (dateString.equals(endDateStr)) {
×
115
                    String tzAbbr = startDt.getZone().getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.ENGLISH);
×
116
                    dateString += " " + TIME_FORMATTER.format(startDt) + " - " + TIME_FORMATTER.format(endDt) + " " + tzAbbr;
×
117
                } else {
×
118
                    dateString += " - " + endDateStr;
×
119
                }
120
            }
121
            add(new Label("date", dateString));
×
122
        } else {
×
123
            add(new Label("date").setVisible(false));
×
124
        }
125

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

128
        if (space.isDataInitialized()) {
×
129
            add(new PinGroupList("pinned-section", space));
×
130
        } else {
131
            add(new AjaxLazyLoadPanel<Component>("pinned-section") {
×
132

133
                @Override
134
                public Component getLazyLoadComponent(String markupId) {
135
                    return new PinGroupList(markupId, space);
×
136
                }
137

138
                @Override
139
                protected boolean isContentReady() {
140
                    return space.isDataInitialized();
×
141
                }
142

143
                @Override
144
                public Component getLoadingComponent(String id) {
145
                    return new Label(id).setVisible(false);
×
146
                }
147

148
            });
149
        }
150

151
        if (space.isDataInitialized()) {
×
152
            add(new ViewList("views", space));
×
153
        } else {
154
            add(new AjaxLazyLoadPanel<Component>("views") {
×
155

156
                @Override
157
                public Component getLazyLoadComponent(String markupId) {
158
                    return new ViewList(markupId, space);
×
159
                }
160

161
                @Override
162
                protected boolean isContentReady() {
163
                    return space.isDataInitialized();
×
164
                }
165

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

171
            });
172
        }
173

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

191
        if (space.isDataInitialized()) {
×
192
            add(new SpaceUserList("user-lists", space));
×
193
        } else {
194
            add(new AjaxLazyLoadPanel<Component>("user-lists") {
×
195

196
                @Override
197
                public Component getLazyLoadComponent(String markupId) {
198
                    return new SpaceUserList(markupId, space);
×
199
                }
200

201
                @Override
202
                protected boolean isContentReady() {
203
                    return space.isDataInitialized();
×
204
                }
205

206
            });
207
        }
208

209
        addSubspacePanel("Alliance", true);
×
210
        addSubspacePanel("Consortium", false);
×
211
        addSubspacePanel("Organization", true);
×
212
        addSubspacePanel("Taskforce", false);
×
213
        addSubspacePanel("Division", true);
×
214
        addSubspacePanel("Taskunit", false);
×
215
        addSubspacePanel("Group", true);
×
216
        addSubspacePanel("Project", false);
×
217
        addSubspacePanel("Program", true);
×
218
        addSubspacePanel("Initiative", false);
×
219
        addSubspacePanel("Outlet", true);
×
220
        addSubspacePanel("Campaign", false);
×
221
        addSubspacePanel("Community", true);
×
222
        addSubspacePanel("Event", false);
×
223

224
        add(new ItemListPanel<MaintainedResource>(
×
225
                "resources",
226
                "📦 Maintained Resources",
227
                new QueryRef(QueryApiAccess.GET_MAINTAINED_RESOURCES),
228
                (apiResponse) -> {
229
                    MaintainedResourceRepository.get().ensureLoaded();
×
230
                    return MaintainedResourceRepository.get().findResourcesBySpace(space);
×
231
                },
232
                (resource) -> new ItemListElement("item", MaintainedResourcePage.class, new PageParameters().set("id", resource.getId()), resource.getLabel())
×
233
        )
234
                .setResourceWithProfile(space)
×
235
                .setReadyFunction(space::isDataInitialized)
×
236
                .addMemberButton("+", PublishPage.class, new PageParameters()
×
237
                        .set("template", "https://w3id.org/np/RA25VaVFxSOgKEuZ70gFINn-N3QV4Pf62-IMK_SWkg-c8")
×
238
                        .set("param_space", space.getId())
×
239
                        .set("context", space.getId())
×
240
                        .set("refresh-upon-publish", "maintainedResources")
×
241
                        .set("template-version", "latest")
×
242
                ));
243

244
        String shortId = space.getId().replace("https://w3id.org/spaces/", "");
×
245
        ConnectorConfig cc = ConnectorConfig.get(shortId);
×
246
        if (cc != null) {
×
247
            add(new BookmarkablePageLink<Void>("content-button", GenOverviewPage.class, new PageParameters().set("journal", shortId)).setBody(Model.of("Nanopublication Submissions")));
×
248
        } else {
249
            add(new Label("content-button").setVisible(false));
×
250
        }
251
    }
×
252

253
    private void addSubspacePanel(String type, boolean openEnded) {
254
        String typePl = type + "s";
×
255
        typePl = typePl.replaceFirst("ys$", "ies");
×
256

257
        add(new ItemListPanel<>(
×
258
                        typePl.toLowerCase(),
×
259
                        Space.getTypeEmoji(type) + " " + typePl,
×
260
                        SpaceRepository.get().findSubspaces(space, KPXL_TERMS.NAMESPACE + type),
×
261
                        (space) -> new ItemListElement("item", SpacePage.class, new PageParameters().set("id", space), space.getLabel())
×
262
                )
263
                        .setResourceWithProfile(space)
×
264
                        .setReadyFunction(space::isDataInitialized)
×
265
                        .addMemberButton("+", PublishPage.class, new PageParameters()
×
266
                                .set("template", openEnded ? "https://w3id.org/np/RA7dQfmndqKmooQ4PlHyQsAql9i2tg_8GLHf_dqtxsGEQ" : "https://w3id.org/np/RAaE7NP9RNIx03AHZxanFMdtUuaTfe50ns5tHhpEVloQ4")
×
267
                                .set("param_type", KPXL_TERMS.NAMESPACE + type)
×
268
                                .set("param_space", space.getId().replaceFirst("https://w3id.org/spaces/", "") + "/<SET-SUFFIX>")
×
269
                                .set("refresh-upon-publish", "spaces")
×
270
                                .set("template-version", "latest"))
×
271
        );
272
    }
×
273

274
    /**
275
     * Checks if auto-refresh is enabled for this page.
276
     *
277
     * @return true if auto-refresh is enabled, false otherwise
278
     */
279
    protected boolean hasAutoRefreshEnabled() {
280
        return true;
×
281
    }
282

283
    /**
284
     * Resolves the {@link Space} from the repository, or redirects as needed.
285
     *
286
     * @param parameters page parameters containing the space {@code id}
287
     * @return the resolved {@link Space}; never {@code null}
288
     * @throws RestartResponseException if the id belongs to a {@link MaintainedResource}
289
     * @throws IllegalArgumentException if the id cannot be resolved to any known resource
290
     */
291
    private Space resolveSpace(PageParameters parameters) {
292
        String id = parameters.get("id").toString();
×
293
        Space resolved = SpaceRepository.get().findById(id);
×
294
        if (resolved == null) {
×
295
            if (MaintainedResourceRepository.get().findById(id) != null) {
×
296
                throw new RestartResponseException(MaintainedResourcePage.class, parameters);
×
297
            }
298
            throw new IllegalArgumentException("No space or resource found for id: " + id);
×
299
        }
300

301
        return resolved;
×
302
    }
303

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