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

knowledgepixels / nanodash / 29307332916

14 Jul 2026 04:59AM UTC coverage: 29.177% (+0.7%) from 28.504%
29307332916

push

github

web-flow
Merge pull request #554 from knowledgepixels/feat/optional-statements-in-groups

feat: support optional statements inside statement groups

1959 of 7463 branches covered (26.25%)

Branch coverage included in aggregate %.

3865 of 12498 relevant lines covered (30.92%)

4.63 hits per line

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

0.53
src/main/java/com/knowledgepixels/nanodash/component/PublishForm.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.*;
4
import com.knowledgepixels.nanodash.domain.AbstractResourceWithProfile;
5
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
6
import com.knowledgepixels.nanodash.domain.User;
7
import com.knowledgepixels.nanodash.component.menu.EntryActionMenu;
8
import com.knowledgepixels.nanodash.page.*;
9
import com.knowledgepixels.nanodash.template.*;
10
import org.apache.commons.lang3.Strings;
11
import org.apache.wicket.Component;
12
import org.apache.wicket.RestartResponseException;
13
import org.apache.wicket.ajax.AjaxEventBehavior;
14
import org.apache.wicket.ajax.AjaxRequestTarget;
15
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
16
import org.apache.wicket.feedback.FeedbackMessage;
17
import org.apache.wicket.markup.html.WebMarkupContainer;
18
import org.apache.wicket.markup.html.WebPage;
19
import org.apache.wicket.markup.html.basic.Label;
20
import org.apache.wicket.markup.html.form.Button;
21
import org.apache.wicket.markup.html.form.CheckBox;
22
import org.apache.wicket.markup.html.form.Form;
23
import org.apache.wicket.markup.html.form.FormComponent;
24
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
25
import org.apache.wicket.markup.html.list.ListItem;
26
import org.apache.wicket.markup.html.list.ListView;
27
import org.apache.wicket.markup.html.panel.FeedbackPanel;
28
import org.apache.wicket.markup.html.panel.Panel;
29
import org.apache.wicket.markup.repeater.Item;
30
import org.apache.wicket.markup.repeater.data.DataView;
31
import org.apache.wicket.markup.repeater.data.ListDataProvider;
32
import org.apache.wicket.model.IModel;
33
import org.apache.wicket.model.Model;
34
import org.apache.wicket.request.mapper.parameter.PageParameters;
35
import org.eclipse.rdf4j.model.IRI;
36
import org.eclipse.rdf4j.model.Literal;
37
import org.eclipse.rdf4j.model.Statement;
38
import org.eclipse.rdf4j.model.ValueFactory;
39
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
40
import org.eclipse.rdf4j.model.vocabulary.FOAF;
41
import org.eclipse.rdf4j.model.vocabulary.RDF;
42
import org.eclipse.rdf4j.model.vocabulary.RDFS;
43
import org.nanopub.MalformedNanopubException;
44
import org.nanopub.Nanopub;
45
import org.nanopub.NanopubAlreadyFinalizedException;
46
import org.nanopub.NanopubCreator;
47
import org.nanopub.extra.security.SignNanopub;
48
import org.nanopub.extra.security.SignatureAlgorithm;
49
import org.nanopub.extra.security.TransformContext;
50
import org.nanopub.extra.server.PublishNanopub;
51
import org.nanopub.extra.services.ApiResponse;
52
import org.nanopub.extra.services.ApiResponseEntry;
53
import org.nanopub.extra.services.QueryRef;
54
import org.nanopub.vocabulary.NPX;
55
import org.nanopub.vocabulary.NTEMPLATE;
56
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
58
import org.wicketstuff.select2.ChoiceProvider;
59
import org.wicketstuff.select2.Response;
60
import org.wicketstuff.select2.Select2Choice;
61

62
import java.lang.reflect.InvocationTargetException;
63
import java.util.*;
64

65
/**
66
 * Form for publishing a nanopublication.
67
 */
68
public class PublishForm extends Panel {
69

70
    private static final Logger logger = LoggerFactory.getLogger(PublishForm.class);
×
71

72
    private static final ValueFactory vf = SimpleValueFactory.getInstance();
×
73

74
    public static final String CREATOR_PUB_INFO_TEMPLATE = "https://w3id.org/np/RAukAcWHRDlkqxk7H2XNSegc1WnHI569INvNr-xdptDGI";
75
    public static final String LICENSE_PUB_INFO_TEMPLATE = "https://w3id.org/np/RACJ58Gvyn91LqCKIO9zu1eijDQIeEff28iyDrJgjSJF8";
76
    public static final String DEFAULT_PROV_TEMPLATE = "https://w3id.org/np/RA7lSq6MuK_TIC6JMSHvLtee3lpLoZDOqLJCLXevnrPoU";
77
    private static final String supersedesPubInfoTemplateId = "https://w3id.org/np/RAoTD7udB2KtUuOuAe74tJi1t3VzK0DyWS7rYVAq1GRvw";
78
    private static final String derivesFromPubInfoTemplateId = "https://w3id.org/np/RARW4MsFkHuwjycNElvEVtuMjpf4yWDL10-0C5l2MqqRQ";
79

80
    private static final String[] fixedPubInfoTemplates = new String[]{CREATOR_PUB_INFO_TEMPLATE, LICENSE_PUB_INFO_TEMPLATE};
×
81

82
    /**
83
     * Fill modes for the nanopublication to be created.
84
     */
85
    public enum FillMode {
9✔
86
        /**
87
         * Use fill mode
88
         */
89
        USE,
18✔
90
        /**
91
         * Supersede fill mode
92
         */
93
        SUPERSEDE,
18✔
94
        /**
95
         * Derive fill mode
96
         */
97
        DERIVE,
18✔
98
        /**
99
         * Override fill mode: like {@link #DERIVE} in that it records a
100
         * {@code prov:wasDerivedFrom} link, but (like {@link #SUPERSEDE}) it keeps the
101
         * source nanopub's introduced resource IRIs and its root definition nanopub.
102
         */
103
        OVERRIDE
18✔
104
    }
105

106
    protected Form<?> form;
107
    protected FeedbackPanel feedbackPanel;
108
    private final TemplateContext assertionContext;
109
    private TemplateContext provenanceContext;
110
    private final List<TemplateContext> pubInfoContexts = new ArrayList<>();
×
111
    private final Map<String, TemplateContext> pubInfoContextMap = new HashMap<>();
×
112
    private final List<TemplateContext> requiredPubInfoContexts = new ArrayList<>();
×
113
    private String targetNamespace;
114
    private final Class<? extends WebPage> confirmPageClass;
115

116
    /**
117
     * Constructor for the PublishForm.
118
     *
119
     * @param id               the Wicket component ID
120
     * @param pageParams       the parameters for the page, which may include information on how to fill the form
121
     * @param publishPageClass the class of the page to redirect to after successful publication
122
     * @param confirmPageClass the class of the confirmation page to show after publication
123
     */
124
    public PublishForm(String id, final PageParameters pageParams, Class<? extends WebPage> publishPageClass, Class<? extends WebPage> confirmPageClass) {
125
        super(id);
×
126
        setOutputMarkupId(true);
×
127
        this.confirmPageClass = confirmPageClass;
×
128

129
        WebMarkupContainer linkMessageItem = new WebMarkupContainer("link-message-item");
×
130
        if (pageParams.get("link-message").isNull()) {
×
131
            linkMessageItem.add(new Label("link-message", ""));
×
132
            linkMessageItem.setVisible(false);
×
133
        } else {
134
            linkMessageItem.add(new Label("link-message", Utils.sanitizeHtml(pageParams.get("link-message").toString())).setEscapeModelStrings(false));
×
135
        }
136
        add(linkMessageItem);
×
137

138
        final Nanopub fillNp;
139
        final FillMode fillMode;
140
        boolean fillOnlyAssertion = false;
×
141
        if (!pageParams.get("use").isNull()) {
×
142
            fillNp = Utils.getNanopub(pageParams.get("use").toString());
×
143
            fillMode = FillMode.USE;
×
144
        } else if (!pageParams.get("use-a").isNull()) {
×
145
            fillNp = Utils.getNanopub(pageParams.get("use-a").toString());
×
146
            fillMode = FillMode.USE;
×
147
            fillOnlyAssertion = true;
×
148
        } else if (!pageParams.get("supersede").isNull()) {
×
149
            fillNp = Utils.getNanopub(pageParams.get("supersede").toString());
×
150
            fillMode = FillMode.SUPERSEDE;
×
151
            targetNamespace = pageParams.get("supersede").toString().replaceFirst("RA[A-Za-z0-9-_]{43}$", "");
×
152
        } else if (!pageParams.get("supersede-a").isNull()) {
×
153
            fillNp = Utils.getNanopub(pageParams.get("supersede-a").toString());
×
154
            fillMode = FillMode.SUPERSEDE;
×
155
            fillOnlyAssertion = true;
×
156
        } else if (!pageParams.get("derive").isNull()) {
×
157
            fillNp = Utils.getNanopub(pageParams.get("derive").toString());
×
158
            fillMode = FillMode.DERIVE;
×
159
        } else if (!pageParams.get("derive-a").isNull()) {
×
160
            fillNp = Utils.getNanopub(pageParams.get("derive-a").toString());
×
161
            fillMode = FillMode.DERIVE;
×
162
            fillOnlyAssertion = true;
×
163
        } else if (!pageParams.get("override").isNull()) {
×
164
            fillNp = Utils.getNanopub(pageParams.get("override").toString());
×
165
            fillMode = FillMode.OVERRIDE;
×
166
        } else if (!pageParams.get("override-a").isNull()) {
×
167
            fillNp = Utils.getNanopub(pageParams.get("override-a").toString());
×
168
            fillMode = FillMode.OVERRIDE;
×
169
            fillOnlyAssertion = true;
×
170
        } else if (!pageParams.get("fill-all").isNull()) {
×
171
            // TODO: This is deprecated and should be removed at some point
172
            fillNp = Utils.getNanopub(pageParams.get("fill-all").toString());
×
173
            fillMode = FillMode.USE;
×
174
        } else if (!pageParams.get("fill").isNull()) {
×
175
            // TODO: This is deprecated and should be removed at some point
176
            fillNp = Utils.getNanopub(pageParams.get("fill").toString());
×
177
            fillMode = FillMode.SUPERSEDE;
×
178
        } else {
179
            fillNp = null;
×
180
            fillMode = null;
×
181
        }
182

183
        final TemplateData td = TemplateData.get();
×
184

185
        // TODO Properly integrate this namespace feature:
186
        String templateId = pageParams.get("template").toString();
×
187
        Template template = td.getTemplate(templateId);
×
188
        if (template != null && template.getTargetNamespace() != null) {
×
189
            targetNamespace = template.getTargetNamespace();
×
190
        }
191
        if (!pageParams.get("target-namespace").isNull()) {
×
192
            targetNamespace = pageParams.get("target-namespace").toString();
×
193
        }
194
        if (targetNamespace == null) {
×
195
            targetNamespace = Template.DEFAULT_TARGET_NAMESPACE;
×
196
        }
197
        String targetNamespaceLabel = targetNamespace + "...";
×
198
        targetNamespace = targetNamespace + "~~~ARTIFACTCODE~~~/";
×
199

200
        assertionContext = new TemplateContext(ContextType.ASSERTION, templateId, "statement", targetNamespace);
×
201
        assertionContext.setFillMode(fillMode);
×
202
        final String prTemplateId;
203
        if (pageParams.get("prtemplate").toString() != null) {
×
204
            prTemplateId = pageParams.get("prtemplate").toString();
×
205
        } else {
206
            if (fillNp != null && !fillOnlyAssertion) {
×
207
                if (td.getProvenanceTemplateId(fillNp) != null) {
×
208
                    prTemplateId = td.getProvenanceTemplateId(fillNp).stringValue();
×
209
                } else {
210
                    prTemplateId = "http://purl.org/np/RAcm8OurwUk15WOgBM9wySo-T3a5h6as4K8YR5MBrrxUc";
×
211
                }
212
            } else if (assertionContext.getTemplate().getDefaultProvenance() != null) {
×
213
                prTemplateId = assertionContext.getTemplate().getDefaultProvenance().stringValue();
×
214
            } else {
215
                prTemplateId = DEFAULT_PROV_TEMPLATE;
×
216
            }
217
        }
218
        provenanceContext = new TemplateContext(ContextType.PROVENANCE, prTemplateId, "pr-statement", targetNamespace);
×
219
        for (String t : fixedPubInfoTemplates) {
×
220
            // TODO consistently check for latest versions of templates here and below:
221
            TemplateContext c = new TemplateContext(ContextType.PUBINFO, t, "pi-statement", targetNamespace);
×
222
            pubInfoContexts.add(c);
×
223
            pubInfoContextMap.put(c.getTemplate().getId(), c);
×
224
            requiredPubInfoContexts.add(c);
×
225
            if (t.equals(LICENSE_PUB_INFO_TEMPLATE) && fillMode != FillMode.SUPERSEDE && fillMode != FillMode.DERIVE && fillMode != FillMode.OVERRIDE) {
×
226
                IRI defaultLicense = User.getDefaultLicense(NanodashSession.get().getUserIri());
×
227
                if (defaultLicense != null) {
×
228
                    c.setParam("license", defaultLicense.stringValue());
×
229
                }
230
            }
231
        }
232
        if (fillMode == FillMode.SUPERSEDE) {
×
233
            TemplateContext c = new TemplateContext(ContextType.PUBINFO, supersedesPubInfoTemplateId, "pi-statement", targetNamespace);
×
234
            pubInfoContexts.add(c);
×
235
            pubInfoContextMap.put(supersedesPubInfoTemplateId, c);
×
236
            //requiredPubInfoContexts.add(c);
237
            c.setParam("np", fillNp.getUri().stringValue());
×
238
        } else if (fillMode == FillMode.DERIVE || fillMode == FillMode.OVERRIDE) {
×
239
            TemplateContext c = new TemplateContext(ContextType.PUBINFO, derivesFromPubInfoTemplateId, "pi-statement", targetNamespace);
×
240
            pubInfoContexts.add(c);
×
241
            pubInfoContextMap.put(derivesFromPubInfoTemplateId, c);
×
242
            c.setParam("np", fillNp.getUri().stringValue());
×
243
        }
244
        for (IRI r : assertionContext.getTemplate().getRequiredPubInfoElements()) {
×
245
            String latestId = td.getLatestTemplateId(r.stringValue());
×
246
            if (pubInfoContextMap.containsKey(r.stringValue()) || pubInfoContextMap.containsKey(latestId)) continue;
×
247
            TemplateContext c = new TemplateContext(ContextType.PUBINFO, r.stringValue(), "pi-statement", targetNamespace);
×
248
            pubInfoContexts.add(c);
×
249
            pubInfoContextMap.put(c.getTemplateId(), c);
×
250
            requiredPubInfoContexts.add(c);
×
251
        }
×
252
        Map<Integer, TemplateContext> piParamIdMap = new HashMap<>();
×
253
        for (String k : pageParams.getNamedKeys()) {
×
254
            if (!k.matches("pitemplate[1-9][0-9]*")) continue;
×
255
            Integer i = Integer.parseInt(k.replaceFirst("^pitemplate([1-9][0-9]*)$", "$1"));
×
256
            String tid = pageParams.get(k).toString();
×
257
            // TODO Allow for automatically using latest template version:
258
            //String piTemplateIdLatest = QueryApiAccess.getLatestVersionId(tid);
259
            TemplateContext c = createPubInfoContext(tid);
×
260
            if (piParamIdMap.containsKey(i)) {
×
261
                // TODO: handle this error better
262
                logger.error("Publication Info template param identifier defined multiple times: {}", i);
×
263
            }
264
            piParamIdMap.put(i, c);
×
265
        }
×
266
        if (fillNp != null && !fillOnlyAssertion) {
×
267
            for (IRI piTemplateId : td.getPubinfoTemplateIds(fillNp)) {
×
268
                String piTemplateIdLatest = td.getLatestTemplateId(piTemplateId.stringValue());
×
269
                if (piTemplateIdLatest.equals(supersedesPubInfoTemplateId)) continue;
×
270
                if (!pubInfoContextMap.containsKey(piTemplateIdLatest)) {
×
271
                    // TODO Allow for automatically using latest template version
272
                    createPubInfoContext(piTemplateId.stringValue());
×
273
                }
274
            }
×
275
        }
276
        if (!pageParams.get("values-from-query").isEmpty() && !pageParams.get("values-from-query-mapping").isEmpty()) {
×
277
            String querySpec = pageParams.get("values-from-query").toString();
×
278

279
            String mapping = pageParams.get("values-from-query-mapping").toString();
×
280
            String mapsFrom, mapsTo;
281
            if (mapping.contains(":")) {
×
282
                mapsFrom = mapping.split(":")[0];
×
283
                mapsTo = mapping.split(":")[1];
×
284
            } else {
285
                mapsFrom = mapping;
×
286
                mapsTo = mapping;
×
287
            }
288
            ApiResponse resp = ApiCache.retrieveResponseSync(QueryRef.parseString(querySpec), false);
×
289
            int i = 0;
×
290
            for (ApiResponseEntry e : resp.getData()) {
×
291
                String mapsToSuffix = "";
×
292
                if (i > 0) mapsToSuffix = "__" + i;
×
293
                assertionContext.setParam(mapsTo + mapsToSuffix, e.get(mapsFrom));
×
294
                i++;
×
295
            }
×
296
        }
297
        for (String k : pageParams.getNamedKeys()) {
×
298
            if (k.startsWith("param_")) assertionContext.setParam(k.substring(6), pageParams.get(k).toString());
×
299
            if (k.startsWith("prparam_")) provenanceContext.setParam(k.substring(8), pageParams.get(k).toString());
×
300
            if (k.matches("piparam[1-9][0-9]*_.*")) {
×
301
                Integer i = Integer.parseInt(k.replaceFirst("^piparam([1-9][0-9]*)_.*$", "$1"));
×
302
                if (!piParamIdMap.containsKey(i)) {
×
303
                    logger.error("Parameter {} of the publication info template not found", i);
×
304
                    continue;
×
305
                }
306
                String n = k.replaceFirst("^piparam[1-9][0-9]*_(.*)$", "$1");
×
307
                logger.info(n);
×
308
                piParamIdMap.get(i).setParam(n, pageParams.get(k).toString());
×
309
            }
310
        }
×
311

312
        final Nanopub improveNp;
313
        if (!pageParams.get("improve").isNull()) {
×
314
            improveNp = Utils.getNanopub(pageParams.get("improve").toString());
×
315
        } else {
316
            improveNp = null;
×
317
        }
318

319
        // Propagate fill source (supersede/derive/improve) so contexts can resolve
320
        // the `local:nanopub`/`local:assertion` sentinels to the fill nanopub's URIs.
321
        Nanopub fillSource = fillNp != null ? fillNp : improveNp;
×
322
        if (fillSource != null) {
×
323
            assertionContext.setFillSource(fillSource);
×
324
            provenanceContext.setFillSource(fillSource);
×
325
            for (TemplateContext c : pubInfoContexts) {
×
326
                c.setFillSource(fillSource);
×
327
            }
×
328
        }
329

330
        // Init statements only now, in order to pick up parameter values:
331
        assertionContext.initStatements();
×
332
        provenanceContext.initStatements();
×
333
        for (TemplateContext c : pubInfoContexts) {
×
334
            c.initStatements();
×
335
        }
×
336

337
        String latestAssertionId = td.getLatestTemplateId(assertionContext.getTemplateId());
×
338
        if (!assertionContext.getTemplateId().equals(latestAssertionId)) {
×
339
            add(new Label("newversion", "There is a new version of this assertion template:"));
×
340
            PageParameters params = new PageParameters(pageParams);
×
341
            params.set("template", latestAssertionId);
×
342
            add(new BookmarkablePageLink<Void>("newversionlink", publishPageClass, params));
×
343
            if ("latest".equals(pageParams.get("template-version").toString())) {
×
344
                throw new RestartResponseException(publishPageClass, params);
×
345
            }
346
        } else {
×
347
            add(new Label("newversion", "").setVisible(false));
×
348
            add(new Label("newversionlink", "").setVisible(false));
×
349
        }
350

351
        final List<Statement> unusedStatementList = new ArrayList<>();
×
352
        final List<Statement> unusedPrStatementList = new ArrayList<>();
×
353
        final List<Statement> unusedPiStatementList = new ArrayList<>();
×
354
        if (fillNp != null) {
×
355
            ValueFiller filler = new ValueFiller(fillNp, ContextType.ASSERTION, true, fillMode);
×
356
            filler.fill(assertionContext);
×
357
            unusedStatementList.addAll(filler.getUnusedStatements());
×
358

359
            if (!fillOnlyAssertion) {
×
360
                ValueFiller prFiller = new ValueFiller(fillNp, ContextType.PROVENANCE, true);
×
361
                prFiller.fill(provenanceContext);
×
362
                unusedPrStatementList.addAll(prFiller.getUnusedStatements());
×
363

364
                ValueFiller piFiller = new ValueFiller(fillNp, ContextType.PUBINFO, true);
×
365
                if (!assertionContext.getTemplate().getTargetNanopubTypes().isEmpty()) {
×
366
                    for (Statement st : new ArrayList<>(piFiller.getUnusedStatements())) {
×
367
                        if (st.getSubject().stringValue().equals(LocalUri.of("nanopub").stringValue()) && st.getPredicate().equals(NPX.HAS_NANOPUB_TYPE)) {
×
368
                            if (assertionContext.getTemplate().getTargetNanopubTypes().contains(st.getObject())) {
×
369
                                piFiller.removeUnusedStatement(st);
×
370
                            }
371
                        }
372
                    }
×
373
                }
374
                for (TemplateContext c : pubInfoContexts) {
×
375
                    piFiller.fill(c);
×
376
                }
×
377
                piFiller.removeUnusedStatements(NanodashSession.get().getUserIri(), FOAF.NAME, null);
×
378
                if (piFiller.hasUnusedStatements()) {
×
379
                    final String handcodedStatementsTemplateId = "https://w3id.org/np/RAMEgudZsQ1bh1fZhfYnkthqH6YSXpghSE_DEN1I-6eAI";
×
380
                    if (!pubInfoContextMap.containsKey(handcodedStatementsTemplateId)) {
×
381
                        TemplateContext c = createPubInfoContext(handcodedStatementsTemplateId);
×
382
                        c.setFillSource(fillNp);
×
383
                        c.initStatements();
×
384
                        piFiller.fill(c);
×
385
                    }
386
                }
387
                unusedPiStatementList.addAll(piFiller.getUnusedStatements());
×
388
                // TODO: Also use pubinfo templates stated in nanopub to be filled in?
389
//                                Set<IRI> pubinfoTemplateIds = Template.getPubinfoTemplateIds(fillNp);
390
//                                if (!pubinfoTemplateIds.isEmpty()) {
391
//                                        ValueFiller piFiller = new ValueFiller(fillNp, ContextType.PUBINFO);
392
//                                        for (IRI pubinfoTemplateId : pubinfoTemplateIds) {
393
//                                                // TODO: Make smart choice on the ordering in trying to fill in all pubinfo elements
394
//                                                piFiller.fill(pubInfoContextMap.get(pubinfoTemplateId.stringValue()));
395
//                                        }
396
//                                        warningMessage += (piFiller.getWarningMessage() == null ? "" : "Publication info: " + piFiller.getWarningMessage() + " ");
397
//                                }
398
            }
399
        } else if (improveNp != null) {
×
400
            ValueFiller filler = new ValueFiller(improveNp, ContextType.ASSERTION, true);
×
401
            filler.fill(assertionContext);
×
402
            unusedStatementList.addAll(filler.getUnusedStatements());
×
403
        }
404
        if (!unusedStatementList.isEmpty()) {
×
405
            add(new Label("warnings", "Some content from the existing nanopublication could not be filled in:"));
×
406
        } else {
407
            add(new Label("warnings", "").setVisible(false));
×
408
        }
409
        add(new DataView<Statement>("unused-statements", new ListDataProvider<Statement>(unusedStatementList)) {
×
410

411
            @Override
412
            protected void populateItem(Item<Statement> item) {
413
                item.add(new TripleItem("unused-statement", item.getModelObject(), (fillNp != null ? fillNp : improveNp), null));
×
414
            }
×
415

416
        });
417
        add(new DataView<Statement>("unused-prstatements", new ListDataProvider<Statement>(unusedPrStatementList)) {
×
418

419
            @Override
420
            protected void populateItem(Item<Statement> item) {
421
                item.add(new TripleItem("unused-prstatement", item.getModelObject(), (fillNp != null ? fillNp : improveNp), null));
×
422
            }
×
423

424
        });
425
        add(new DataView<Statement>("unused-pistatements", new ListDataProvider<Statement>(unusedPiStatementList)) {
×
426

427
            @Override
428
            protected void populateItem(Item<Statement> item) {
429
                item.add(new TripleItem("unused-pistatement", item.getModelObject(), (fillNp != null ? fillNp : improveNp), null));
×
430
            }
×
431

432
        });
433

434
        // Finalize statements, which picks up parameter values in repetitions:
435
        assertionContext.finalizeStatements();
×
436
        provenanceContext.finalizeStatements();
×
437
        for (TemplateContext c : pubInfoContexts) {
×
438
            c.finalizeStatements();
×
439
        }
×
440

441
        final CheckBox consentCheck = new CheckBox("consentcheck", new Model<>(false));
×
442
        consentCheck.add(new InvalidityHighlighting());
×
443

444
        form = new Form<Void>("form") {
×
445

446
            @Override
447
            protected void onConfigure() {
448
                super.onConfigure();
×
449
                form.getFeedbackMessages().clear();
×
450
//                                formComponents.clear();
451
            }
×
452

453
            protected void onSubmit() {
454
                if (!Boolean.TRUE.equals(consentCheck.getModelObject())) {
×
455
                    feedbackPanel.error("You need to check the checkbox that you understand the consequences.");
×
456
                    return;
×
457
                }
458
                logger.info("Publish form submitted");
×
459
                Nanopub signedNp;
460
                try {
461
                    Nanopub np = createNanopub();
×
462
                    logger.info("Nanopublication created: {}", np.getUri());
×
463
                    TransformContext tc = new TransformContext(SignatureAlgorithm.RSA, NanodashSession.get().getKeyPair(), NanodashSession.get().getUserIri(), false, false, false);
×
464
                    signedNp = SignNanopub.signAndTransform(np, tc);
×
465
                    logger.info("Nanopublication signed: {}", signedNp.getUri());
×
466
                    String npUrl = PublishNanopub.publish(signedNp);
×
467
                    logger.info("Nanopublication published: {}", npUrl);
×
468
                    Utils.cacheNanopub(signedNp);
×
469
                } catch (Exception ex) {
×
470
                    signedNp = null;
×
471
                    logger.error("Nanopublication publishing failed: {}", ex.getMessage());
×
472
                    String message = ex.getClass().getName();
×
473
                    if (ex.getMessage() != null) {
×
474
                        message = ex.getMessage();
×
475
                    }
476
                    feedbackPanel.error(message);
×
477
                }
×
478
                if (signedNp != null) {
×
479
                    String toRefresh = pageParams.get("refresh-upon-publish").toString("");
×
480
                    if (!toRefresh.isEmpty()) {
×
481
                        WicketApplication.get().notifyNanopubPublished(signedNp, toRefresh, 5 * 1000);
×
482
                    }
483
                    String contextId = pageParams.get("context").toString("");
×
484
                    // Broaden the refresh: also force-refresh the context resource's own
485
                    // data (e.g. a space's roles/members) so the page we redirect to —
486
                    // typically its Content tab — reflects the just-published change, not
487
                    // only the specific view query that was acted on.
488
                    if (!contextId.isEmpty() && !contextId.equals(toRefresh)
×
489
                            && AbstractResourceWithProfile.isResourceWithProfile(contextId)) {
×
490
                        WicketApplication.get().notifyNanopubPublished(signedNp, contextId, 5 * 1000);
×
491
                    }
492
                    if (pageParams.get("postpub-redirect-url").isEmpty() && confirmPageClass == null) {
×
493
                        // Forward to the context resource's page, or home if no context; always throws.
494
                        NavigationContext.redirectAfterPublish(signedNp, pageParams);
×
495
                    }
496
                    // Connector flow: show the connector's own confirmation page.
497
                    NanodashPage confirmPage = getConfirmPage(signedNp, pageParams);
×
498
                    if (confirmPage != null) {
×
499
                        throw new RestartResponseException(confirmPage);
×
500
                    }
501
                    NavigationContext.redirectAfterPublish(signedNp, pageParams);
×
502
                }
503
            }
×
504

505
            @Override
506
            protected void onValidate() {
507
                super.onValidate();
×
508
                for (Component fc : assertionContext.getComponents()) {
×
509
                    processFeedback(fc);
×
510
                }
×
511
                for (Component fc : provenanceContext.getComponents()) {
×
512
                    processFeedback(fc);
×
513
                }
×
514
                for (TemplateContext c : pubInfoContexts) {
×
515
                    for (Component fc : c.getComponents()) {
×
516
                        processFeedback(fc);
×
517
                    }
×
518
                }
×
519
            }
×
520

521
            private void processFeedback(Component c) {
522
                if (c instanceof FormComponent) {
×
523
                    ((FormComponent<?>) c).processInput();
×
524
                }
525
                for (FeedbackMessage fm : c.getFeedbackMessages()) {
×
526
                    form.getFeedbackMessages().add(fm);
×
527
                }
×
528
            }
×
529

530
        };
531
        form.setOutputMarkupId(true);
×
532

533
        //form.add(new Label("nanopub-namespace", targetNamespaceLabel));
534

535
        form.add(newSourceMenu("templatelink", assertionContext.getTemplate().getId()));
×
536
        form.add(new Label("templatename", assertionContext.getTemplate().getLabel()));
×
537
        String description = assertionContext.getTemplate().getLabel();
×
538
        form.add(new Label("templatedesc", assertionContext.getTemplate().getDescription()).setEscapeModelStrings(false));
×
539

540
        form.add(new ListView<StatementItem>("statements", assertionContext.getStatementItems()) {
×
541

542
            protected void populateItem(ListItem<StatementItem> item) {
543
                item.add(item.getModelObject());
×
544
            }
×
545

546
        });
547

548
        final Map<String, Boolean> handledProvTemplates = new HashMap<>();
×
549
        final String defaultProvTemplateId;
550
        if (assertionContext.getTemplate().getDefaultProvenance() != null) {
×
551
            defaultProvTemplateId = assertionContext.getTemplate().getDefaultProvenance().stringValue();
×
552
            handledProvTemplates.put(defaultProvTemplateId, true);
×
553
        } else {
554
            defaultProvTemplateId = null;
×
555
        }
556
        final List<String> recommendedProvTemplateOptionIds = new ArrayList<>();
×
557
        final List<String> provTemplateOptionIds = new ArrayList<>();
×
558
        if (pageParams.get("prtemplate-options").isNull()) {
×
559
            // TODO Make this dynamic and consider updated templates:
560
            recommendedProvTemplateOptionIds.add(DEFAULT_PROV_TEMPLATE);
×
561
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RAcTpoh5Ra0ssqmcpOgWdaZ_YiPE6demO6cpw-2RvSNs8");
×
562
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RA4LGtuOqTIMqVAkjnfBXk1YDcAPNadP5CGiaJiBkdHCQ");
×
563
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RAl_-VTw9Re_uRF8r8y0rjlfnu7FlhTa8xg_8xkcweqiE");
×
564
            recommendedProvTemplateOptionIds.add("https://w3id.org/np/RASORV2mMEVpS4lWh2bwUTEcV-RWjbD9RPbN7J0PIeYAU");
×
565
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RAjkBbM5yQm7hKH1l_Jk3HAUqWi3Bd57TPmAOZCsZmi_M");
×
566
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RAGXx_k9eQMnXaCbsXMsJbGClwZtQEGNg0GVJu6amdAVw");
×
567
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RA1fnITI3Pu1UQ0CHghNpys3JwQrM32LBnjmDLoayp9-4");
×
568
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RAJgbsGeGdTG-zq_gU0TLw4s3raMgoRk-mPlc2DSLXvE0");
×
569
            recommendedProvTemplateOptionIds.add("http://purl.org/np/RA6SXfhUY-xeblZU8HhPddw6tsu-C5NXevG6C_zv4bMxU");
×
570
            for (String s : recommendedProvTemplateOptionIds) {
×
571
                handledProvTemplates.put(s, true);
×
572
            }
×
573

574
            for (ApiResponseEntry t : td.getProvenanceTemplates()) {
×
575
                String tid = t.get("np");
×
576
                if (handledProvTemplates.containsKey(tid)) continue;
×
577
                provTemplateOptionIds.add(tid);
×
578
                handledProvTemplates.put(tid, true);
×
579
            }
×
580
        } else {
581
            for (String s : pageParams.get("prtemplate-options").toString().split(" ")) {
×
582
                if (handledProvTemplates.containsKey(s)) continue;
×
583
                recommendedProvTemplateOptionIds.add(s);
×
584
                handledProvTemplates.put(s, true);
×
585
            }
586
        }
587

588
        ChoiceProvider<String> prTemplateChoiceProvider = new ChoiceProvider<String>() {
×
589

590
            @Override
591
            public String getDisplayValue(String object) {
592
                if (object == null || object.isEmpty()) return "";
×
593
                Template t = td.getTemplate(object);
×
594
                if (t != null) return t.getLabel();
×
595
                return object;
×
596
            }
597

598
            @Override
599
            public String getIdValue(String object) {
600
                return object;
×
601
            }
602

603
            // Getting strange errors with Tomcat if this method is not overridden:
604
            @Override
605
            public void detach() {
606
            }
×
607

608
            @Override
609
            public void query(String term, int page, Response<String> response) {
610
                if (term == null) term = "";
×
611
                term = term.toLowerCase();
×
612
                if (pageParams.get("prtemplate").toString() != null) {
×
613
                    // Using this work-around with "——" because 'optgroup' is not available through Wicket's Select2 classes
614
                    response.add("—— default for this link ——");
×
615
                    response.add(prTemplateId);
×
616
                }
617
                if (defaultProvTemplateId != null) {
×
618
                    response.add("—— default for this template ——");
×
619
                    response.add(defaultProvTemplateId);
×
620
                }
621
                if (!recommendedProvTemplateOptionIds.isEmpty()) {
×
622
                    if (pageParams.get("prtemplate-options").isNull()) {
×
623
                        response.add("—— recommended ——");
×
624
                    }
625
                    for (String s : recommendedProvTemplateOptionIds) {
×
626
                        if (s.toLowerCase().contains(term) || getDisplayValue(s).toLowerCase().contains(term)) {
×
627
                            response.add(s);
×
628
                        }
629
                    }
×
630
                }
631
                if (!provTemplateOptionIds.isEmpty()) {
×
632
                    response.add("—— others ——");
×
633
                    for (String s : provTemplateOptionIds) {
×
634
                        if (s.toLowerCase().contains(term) || getDisplayValue(s).toLowerCase().contains(term)) {
×
635
                            response.add(s);
×
636
                        }
637
                    }
×
638
                }
639
            }
×
640

641
            @Override
642
            public Collection<String> toChoices(Collection<String> ids) {
643
                return ids;
×
644
            }
645

646
        };
647
        final IModel<String> prTemplateModel = Model.of(provenanceContext.getTemplate().getId());
×
648
        Select2Choice<String> prTemplateChoice = new Select2Choice<String>("prtemplate", prTemplateModel, prTemplateChoiceProvider);
×
649
        prTemplateChoice.setRequired(true);
×
650
        prTemplateChoice.getSettings().setCloseOnSelect(true);
×
651
        prTemplateChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
×
652

653
            @Override
654
            protected void onUpdate(AjaxRequestTarget target) {
655
                String o = prTemplateModel.getObject();
×
656
                if (o.startsWith("——")) {
×
657
                    o = provenanceContext.getTemplate().getId();
×
658
                    prTemplateModel.setObject(o);
×
659
                }
660
                provenanceContext = new TemplateContext(ContextType.PROVENANCE, prTemplateModel.getObject(), "pr-statement", targetNamespace);
×
661
                provenanceContext.initStatements();
×
662
                refreshProvenance(target);
×
663
                provenanceContext.finalizeStatements();
×
664
            }
×
665

666
        });
667
        form.add(prTemplateChoice);
×
668
        refreshProvenance(null);
×
669

670
        final Map<String, Boolean> handledPiTemplates = new HashMap<>();
×
671
        final List<String> recommendedPiTemplateOptionIds = new ArrayList<>();
×
672
        final List<String> piTemplateOptionIds = new ArrayList<>();
×
673
        // TODO Make this dynamic and consider updated templates:
674
        recommendedPiTemplateOptionIds.add("http://purl.org/np/RAXflINqt3smqxV5Aq7E9lzje4uLdkKIOefa6Bp8oJ8CY");
×
675
        recommendedPiTemplateOptionIds.add("https://w3id.org/np/RARW4MsFkHuwjycNElvEVtuMjpf4yWDL10-0C5l2MqqRQ");
×
676
        recommendedPiTemplateOptionIds.add("https://w3id.org/np/RA16U9Wo30ObhrK1NzH7EsmVRiRtvEuEA_Dfc-u8WkUCA");
×
677
        recommendedPiTemplateOptionIds.add("http://purl.org/np/RAdyqI6k07V5nAS82C6hvIDtNWk179EIV4DV-sLbOFKg4");
×
678
        recommendedPiTemplateOptionIds.add("https://w3id.org/np/RAjvEpLZUE7rMoa8q6mWSsN6utJDp-5FmgO47YGsbgw3w");
×
679
        recommendedPiTemplateOptionIds.add("http://purl.org/np/RAxuGRKID6yNg63V5Mf0ot2NjncOnodh-mkN3qT_1txGI");
×
680
        for (TemplateContext c : pubInfoContexts) {
×
681
            // Key both ID forms: the "others" dedup below compares against listing
682
            // entries keyed by nanopub URI, which for a template with embedded
683
            // identity differs from its canonical ID.
684
            handledPiTemplates.put(c.getTemplate().getId(), true);
×
685
            handledPiTemplates.put(c.getTemplate().getNanopub().getUri().stringValue(), true);
×
686
        }
×
687
        for (String s : recommendedPiTemplateOptionIds) {
×
688
            handledPiTemplates.put(s, true);
×
689
        }
×
690

691
        for (ApiResponseEntry entry : td.getPubInfoTemplates()) {
×
692
            String tid = entry.get("np");
×
693
            if (handledPiTemplates.containsKey(tid)) continue;
×
694
            piTemplateOptionIds.add(tid);
×
695
            handledPiTemplates.put(tid, true);
×
696
        }
×
697

698
        ChoiceProvider<String> piTemplateChoiceProvider = new ChoiceProvider<String>() {
×
699

700
            @Override
701
            public String getDisplayValue(String object) {
702
                if (object == null || object.isEmpty()) return "";
×
703
                Template t = td.getTemplate(object);
×
704
                if (t != null) return t.getLabel();
×
705
                return object;
×
706
            }
707

708
            @Override
709
            public String getIdValue(String object) {
710
                return object;
×
711
            }
712

713
            // Getting strange errors with Tomcat if this method is not overridden:
714
            @Override
715
            public void detach() {
716
            }
×
717

718
            @Override
719
            public void query(String term, int page, Response<String> response) {
720
                if (term == null) term = "";
×
721
                term = term.toLowerCase();
×
722
                if (!recommendedPiTemplateOptionIds.isEmpty()) {
×
723
                    response.add("—— recommended ——");
×
724
                    for (String s : recommendedPiTemplateOptionIds) {
×
725
                        boolean isAlreadyUsed = false;
×
726
                        for (TemplateContext c : pubInfoContexts) {
×
727
                            // TODO: make this more efficient/nicer
728
                            if (c.getTemplate().hasId(s)) {
×
729
                                isAlreadyUsed = true;
×
730
                                break;
×
731
                            }
732
                        }
×
733
                        if (isAlreadyUsed) continue;
×
734
                        if (s.toLowerCase().contains(term) || getDisplayValue(s).toLowerCase().contains(term)) {
×
735
                            response.add(s);
×
736
                        }
737
                    }
×
738
                }
739
                if (!piTemplateOptionIds.isEmpty()) {
×
740
                    response.add("—— others ——");
×
741
                    for (String s : piTemplateOptionIds) {
×
742
                        boolean isAlreadyUsed = false;
×
743
                        for (TemplateContext c : pubInfoContexts) {
×
744
                            // TODO: make this more efficient/nicer
745
                            if (c.getTemplate().hasId(s)) {
×
746
                                isAlreadyUsed = true;
×
747
                                break;
×
748
                            }
749
                        }
×
750
                        if (isAlreadyUsed) continue;
×
751
                        if (s.toLowerCase().contains(term) || getDisplayValue(s).toLowerCase().contains(term)) {
×
752
                            response.add(s);
×
753
                        }
754
                    }
×
755
                }
756
            }
×
757

758
            @Override
759
            public Collection<String> toChoices(Collection<String> ids) {
760
                return ids;
×
761
            }
762

763
        };
764
        final IModel<String> newPiTemplateModel = Model.of();
×
765
        Select2Choice<String> piTemplateChoice = new Select2Choice<String>("pitemplate", newPiTemplateModel, piTemplateChoiceProvider);
×
766
        piTemplateChoice.getSettings().setCloseOnSelect(true);
×
767
        piTemplateChoice.getSettings().setPlaceholder("add element...");
×
768
        piTemplateChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
×
769

770
            @Override
771
            protected void onUpdate(AjaxRequestTarget target) {
772
                if (newPiTemplateModel.getObject().startsWith("——")) {
×
773
                    newPiTemplateModel.setObject(null);
×
774
                    refreshPubInfo(target);
×
775
                    return;
×
776
                }
777
                String id = newPiTemplateModel.getObject();
×
778
                TemplateContext c = new TemplateContext(ContextType.PUBINFO, id, "pi-statement", targetNamespace);
×
779
                c.initStatements();
×
780
                pubInfoContexts.add(c);
×
781
                newPiTemplateModel.setObject(null);
×
782
                refreshPubInfo(target);
×
783
                c.finalizeStatements();
×
784
            }
×
785

786
        });
787
        form.add(piTemplateChoice);
×
788
        refreshPubInfo(null);
×
789

790
        form.add(consentCheck);
×
791

792
        form.add(new Button("preview-button") {
×
793
            @Override
794
            public void onSubmit() {
795
                try {
796
                    Nanopub np = createNanopub();
×
797
                    TransformContext tc = new TransformContext(SignatureAlgorithm.RSA, NanodashSession.get().getKeyPair(), NanodashSession.get().getUserIri(), false, false, false);
×
798
                    Nanopub signedNp = SignNanopub.signAndTransform(np, tc);
×
799
                    String previewId = signedNp.getUri().stringValue();
×
800
                    NanodashSession.get().setPreviewNanopub(previewId,
×
801
                            new NanodashSession.PreviewNanopub(signedNp, pageParams, confirmPageClass, Boolean.TRUE.equals(consentCheck.getModelObject()), getPage().getPageReference()));
×
802
                    throw new RestartResponseException(PreviewPage.class, new PageParameters().set("id", previewId));
×
803
                } catch (RestartResponseException ex) {
×
804
                    throw ex;
×
805
                } catch (Exception ex) {
×
806
                    logger.error("Preview failed: {}", ex.getMessage());
×
807
                    String message = ex.getClass().getName();
×
808
                    if (ex.getMessage() != null) {
×
809
                        message = ex.getMessage();
×
810
                    }
811
                    feedbackPanel.error(message);
×
812
                }
813
            }
×
814
        });
815

816
        add(form);
×
817

818
        feedbackPanel = new FeedbackPanel("feedback");
×
819
        feedbackPanel.setOutputMarkupId(true);
×
820
        add(feedbackPanel);
×
821
    }
×
822

823
    static EntryActionMenu newSourceMenu(String id, String templateId) {
824
        BookmarkablePageLink<Void> sourceLink = new BookmarkablePageLink<>("link", ExplorePage.class, new PageParameters().set("id", templateId));
×
825
        sourceLink.add(NavigationContext.pageContextFallback());
×
826
        sourceLink.setBody(Model.of("<span class=\"actionmenu-icon\">↗︎</span>source")).setEscapeModelStrings(false);
×
827
        return new EntryActionMenu(id, List.of(sourceLink));
×
828
    }
829

830
    private void refreshProvenance(AjaxRequestTarget target) {
831
        if (target != null) {
×
832
            form.remove("prtemplatelink");
×
833
            form.remove("pr-statements");
×
834
            target.add(form);
×
835
            target.appendJavaScript("updateElements();");
×
836
        }
837
        form.add(newSourceMenu("prtemplatelink", provenanceContext.getTemplate().getId()));
×
838
        ListView<StatementItem> list = new ListView<StatementItem>("pr-statements", provenanceContext.getStatementItems()) {
×
839

840
            protected void populateItem(ListItem<StatementItem> item) {
841
                item.add(item.getModelObject());
×
842
            }
×
843

844
        };
845
        list.setOutputMarkupId(true);
×
846
        form.add(list);
×
847
    }
×
848

849
    private void refreshPubInfo(AjaxRequestTarget target) {
850
        ListView<TemplateContext> list = new ListView<TemplateContext>("pis", pubInfoContexts) {
×
851

852
            protected void populateItem(ListItem<TemplateContext> item) {
853
                final TemplateContext pic = item.getModelObject();
×
854
                item.add(new Label("pitemplatename", pic.getTemplate().getLabel()));
×
855
                item.add(newSourceMenu("pitemplatelink", pic.getTemplate().getId()));
×
856
                Label remove = new Label("piremove", "×");
×
857
                item.add(remove);
×
858
                remove.add(new AjaxEventBehavior("click") {
×
859

860
                    @Override
861
                    protected void onEvent(AjaxRequestTarget target) {
862
                        pubInfoContexts.remove(pic);
×
863
                        target.add(PublishForm.this);
×
864
                        target.appendJavaScript("updateElements();");
×
865
                    }
×
866

867
                });
868
                if (requiredPubInfoContexts.contains(pic)) remove.setVisible(false);
×
869
                item.add(new ListView<StatementItem>("pi-statements", pic.getStatementItems()) {
×
870

871
                    protected void populateItem(ListItem<StatementItem> item) {
872
                        item.add(item.getModelObject());
×
873
                    }
×
874

875
                });
876
            }
×
877

878
        };
879
        list.setOutputMarkupId(true);
×
880
        if (target == null) {
×
881
            form.add(list);
×
882
        } else {
883
            form.remove("pis");
×
884
            form.add(list);
×
885
            target.add(form);
×
886
            target.appendJavaScript("updateElements();");
×
887
        }
888
    }
×
889

890
    private TemplateContext createPubInfoContext(String piTemplateId) {
891
        TemplateContext c;
892
        if (pubInfoContextMap.containsKey(piTemplateId)) {
×
893
            c = pubInfoContextMap.get(piTemplateId);
×
894
        } else {
895
            c = new TemplateContext(ContextType.PUBINFO, piTemplateId, "pi-statement", targetNamespace);
×
896
            pubInfoContextMap.put(piTemplateId, c);
×
897
            pubInfoContexts.add(c);
×
898
        }
899
        return c;
×
900
    }
901

902
    private synchronized Nanopub createNanopub() throws MalformedNanopubException, NanopubAlreadyFinalizedException {
903
        assertionContext.getIntroducedIris().clear();
×
904
        assertionContext.getRolePropertyPins().clear();
×
905
        NanopubCreator npCreator = new NanopubCreator(targetNamespace);
×
906
        npCreator.setAssertionUri(vf.createIRI(targetNamespace + "assertion"));
×
907
        assertionContext.propagateStatements(npCreator);
×
908
        provenanceContext.propagateStatements(npCreator);
×
909
        for (TemplateContext c : pubInfoContexts) {
×
910
            c.propagateStatements(npCreator);
×
911
        }
×
912
        for (IRI introducedIri : assertionContext.getIntroducedIris()) {
×
913
            npCreator.addPubinfoStatement(NPX.INTRODUCES, introducedIri);
×
914
        }
×
915
        for (IRI embeddedIri : assertionContext.getEmbeddedIris()) {
×
916
            npCreator.addPubinfoStatement(NPX.EMBEDS, embeddedIri);
×
917
        }
×
918
        Map<IRI, IRI> rolePropertyPins = assertionContext.getRolePropertyPins();
×
919
        if (!rolePropertyPins.isEmpty()) {
×
920
            // A role predicate carrying a direction pin makes this a role-instantiation
921
            // nanopub; nanopub-query's pin-resolution branch is gated on this type (#525).
922
            npCreator.addPubinfoStatement(NPX.HAS_NANOPUB_TYPE, KPXL_TERMS.ROLE_INSTANTIATION);
×
923
            for (Map.Entry<IRI, IRI> pin : rolePropertyPins.entrySet()) {
×
924
                npCreator.addPubinfoStatement(pin.getKey(), RDF.TYPE, pin.getValue());
×
925
            }
×
926
        }
927
        npCreator.addNamespace("this", targetNamespace);
×
928
        npCreator.addNamespace("sub", targetNamespace + "/");
×
929
        npCreator.addTimestampNow();
×
930
        // The canonical template ID: the embedded template-node IRI for templates
931
        // with embedded identity, the nanopub URI for legacy ones.
932
        IRI templateUri = vf.createIRI(assertionContext.getTemplate().getId());
×
933
        npCreator.addPubinfoStatement(NTEMPLATE.WAS_CREATED_FROM_TEMPLATE, templateUri);
×
934
        IRI prTemplateUri = vf.createIRI(provenanceContext.getTemplate().getId());
×
935
        npCreator.addPubinfoStatement(NTEMPLATE.WAS_CREATED_FROM_PROVENANCE_TEMPLATE, prTemplateUri);
×
936
        for (TemplateContext c : pubInfoContexts) {
×
937
            IRI piTemplateUri = vf.createIRI(c.getTemplate().getId());
×
938
            npCreator.addPubinfoStatement(NTEMPLATE.WAS_CREATED_FROM_PUBINFO_TEMPLATE, piTemplateUri);
×
939
        }
×
940
        String nanopubLabel = getNanopubLabel(npCreator);
×
941
        if (nanopubLabel != null) {
×
942
            npCreator.addPubinfoStatement(RDFS.LABEL, vf.createLiteral(nanopubLabel));
×
943
        }
944
        for (IRI type : assertionContext.getTemplate().getTargetNanopubTypes()) {
×
945
            npCreator.addPubinfoStatement(NPX.HAS_NANOPUB_TYPE, type);
×
946
        }
×
947
        IRI userIri = NanodashSession.get().getUserIri();
×
948
        if (User.getName(userIri) != null) {
×
949
            npCreator.addPubinfoStatement(userIri, FOAF.NAME, vf.createLiteral(User.getName(userIri)));
×
950
            npCreator.addNamespace("foaf", FOAF.NAMESPACE);
×
951
        }
952
        String websiteUrl = NanodashPreferences.get().getWebsiteUrl();
×
953
        if (websiteUrl != null) {
×
954
            npCreator.addPubinfoStatement(NPX.WAS_CREATED_AT, vf.createIRI(websiteUrl));
×
955
        }
956
        return npCreator.finalizeNanopub();
×
957
    }
958

959
    private String getNanopubLabel(NanopubCreator npCreator) {
960
        if (assertionContext.getTemplate().getNanopubLabelPattern() == null) return null;
×
961

962
        Map<IRI, String> labelMap = new HashMap<>();
×
963
        for (Statement st : npCreator.getCurrentPubinfoStatements()) {
×
964
            if (st.getPredicate().equals(RDFS.LABEL) && st.getObject() instanceof Literal objL) {
×
965
                labelMap.put((IRI) st.getSubject(), objL.stringValue());
×
966
            }
967
        }
×
968

969
        String nanopubLabel = assertionContext.getTemplate().getNanopubLabelPattern();
×
970
        while (nanopubLabel.matches(".*\\$\\{[_a-zA-Z0-9-]+\\}.*")) {
×
971
            String placeholderPostfix = nanopubLabel.replaceFirst("^.*\\$\\{([_a-zA-Z0-9-]+)\\}.*$", "$1");
×
972
            IRI placeholderIriHash = vf.createIRI(assertionContext.getTemplateNanopubUri() + "#" + placeholderPostfix);
×
973
            IRI placeholderIriSlash = vf.createIRI(assertionContext.getTemplateNanopubUri() + "/" + placeholderPostfix);
×
974
            IRI placeholderIri;
975
            String placeholderValue = "";
×
976
            if (assertionContext.getComponentModels().get(placeholderIriSlash) != null) {
×
977
                placeholderIri = placeholderIriSlash;
×
978
            } else {
979
                placeholderIri = placeholderIriHash;
×
980
            }
981
            IModel<String> m = (IModel<String>) assertionContext.getComponentModels().get(placeholderIri);
×
982
            if (m != null) placeholderValue = m.orElse("").getObject();
×
983
            if (placeholderValue == null) placeholderValue = "";
×
984
            String placeholderLabel = placeholderValue;
×
985
            if (assertionContext.getTemplate().isUriPlaceholder(placeholderIri)) {
×
986
                try {
987
                    // TODO Fix this. It doesn't work for placeholders with auto-encode placeholders, etc.
988
                    //      Not sure we need labels for these, but this code should be improved anyway.
989
                    String prefix = assertionContext.getTemplate().getPrefix(placeholderIri);
×
990
                    if (prefix != null) placeholderValue = prefix + placeholderValue;
×
991
                    IRI placeholderValueIri = vf.createIRI(placeholderValue);
×
992
                    String l = assertionContext.getTemplate().getLabel(placeholderValueIri);
×
993
                    if (labelMap.containsKey(placeholderValueIri)) {
×
994
                        l = labelMap.get(placeholderValueIri);
×
995
                    }
996
                    if (l == null) l = GuidedChoiceItem.getLabel(placeholderValue);
×
997
                    if (assertionContext.getTemplate().isAgentPlaceholder(placeholderIri) && !placeholderValue.isEmpty()) {
×
998
                        l = User.getName(vf.createIRI(placeholderValue));
×
999
                    }
1000
                    if (l != null && !l.isEmpty()) {
×
1001
                        placeholderLabel = l.replaceFirst(" - .*$", "");
×
1002
                    } else {
1003
                        placeholderLabel = Utils.getShortNameFromURI(placeholderValueIri);
×
1004
                    }
1005
                } catch (Exception ex) {
×
1006
                    logger.error("Nanopub label placeholder IRI error: {}", ex.getMessage());
×
1007
                }
×
1008
            }
1009
            placeholderLabel = placeholderLabel.replaceAll("\\s+", " ");
×
1010
            if (placeholderLabel.length() > 100) placeholderLabel = placeholderLabel.substring(0, 97) + "...";
×
1011
            nanopubLabel = Strings.CS.replace(nanopubLabel, "${" + placeholderPostfix + "}", placeholderLabel);
×
1012
        }
×
1013
        return nanopubLabel;
×
1014
    }
1015

1016
    private NanodashPage getConfirmPage(Nanopub signedNp, PageParameters pageParams) {
1017
        if (confirmPageClass == null) return null;
×
1018
        try {
1019
            return (NanodashPage) confirmPageClass.getConstructor(Nanopub.class, PageParameters.class).newInstance(signedNp, pageParams);
×
1020
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
×
1021
                 InvocationTargetException | NoSuchMethodException | SecurityException ex) {
1022
            logger.error("Could not create instance of confirmation page: {}", ex.getMessage());
×
1023
        }
1024
        return null;
×
1025
    }
1026

1027
    /**
1028
     * Returns a hint whether the form is stateless or not.
1029
     *
1030
     * @return false if the form is stateful, true if it is stateless.
1031
     */
1032
    // This is supposed to solve the problem that sometimes (but only sometimes) form content is reset
1033
    // if the user browses other pages in parallel:
1034
    protected boolean getStatelessHint() {
1035
        return false;
×
1036
    }
1037

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