Skip to content

Commit f911484

Browse files
committed
feat: send vcs via mail
1 parent d4638db commit f911484

File tree

3 files changed

+84
-82
lines changed

3 files changed

+84
-82
lines changed

amd/src/ui_driver.js

+78-78
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ define(["mod_eductx/main",
1313
"mod_eductx/contract_driver",
1414
"mod_eductx/get_pdf_content",
1515
"mod_eductx/connector"
16-
], function(main, Buffer, Jpack, Pdfmake, Ecies, w3d, cd, pdf, Connector) {
16+
], function (main, Buffer, Jpack, Pdfmake, Ecies, w3d, cd, pdf, Connector) {
1717
// Modules variables
1818
let web3;
1919

@@ -41,22 +41,22 @@ define(["mod_eductx/main",
4141
STUDENT: "student", AP: "ap", AP_NO_REC: "apnorec", NONE: "none", NOT_IN_SYNC: "unsynced", CERT_ISSUED: "certissued"
4242
};
4343
const ERROR = {
44-
INFO: "alert alert-info", DANGER: "alert alert-danger", SUCCESS: "alert alert-success", WARNING: "alert alert-warning"
44+
INFO: "alert alert-info",
45+
DANGER: "alert alert-danger",
46+
SUCCESS: "alert alert-success",
47+
WARNING: "alert alert-warning"
4548
};
4649

4750
/**
4851
* Initializes event listeners for issue certificate button
4952
*/
50-
const initializeEventListeners = async() => {
53+
const initializeEventListeners = async () => {
5154
document.getElementById("wasAwardedBy").value = window.location.origin;
5255
if (isAuthorized) {
5356
document.getElementById("connectFlow").hidden = true;
5457
document.getElementById("issueCertificate").addEventListener('click', () => {
5558
issueCredentials();
5659
});
57-
document.getElementById('students').addEventListener('change', function() {
58-
document.getElementById("issueCertificate").disabled = document.getElementById("students").selectedOptions.length <= 0;
59-
});
6060
document.getElementById("issueAnotherButton").addEventListener("click", () => {
6161
document.getElementById("issueAnother").hidden = true;
6262
updateUI(UI.AP);
@@ -77,12 +77,12 @@ define(["mod_eductx/main",
7777
}
7878
document.getElementById("connectFlow").hidden = false;
7979
// Connect Masca button
80-
document.getElementById("connectButton").addEventListener('click', async() => {
80+
document.getElementById("connectButton").addEventListener('click', async () => {
8181
document.getElementById("connectButton").disabled = true;
82-
await w3d.connectClientProvider(async(success) => {
82+
await w3d.connectClientProvider(async (success) => {
8383
if (!success) {
84-
updateErrorReporting("Wallet not installed", "Please consider installing" +
85-
"<a href='https://metamask.io/'>Metamask</a>.", ERROR.DANGER);
84+
updateErrorReporting("Wallet not installed", "Please consider installing " +
85+
"<a href='https://metamask.io/'>Metamask</a>.", ERROR.DANGER);
8686
document.getElementById("connectButton").disabled = false;
8787
return;
8888
}
@@ -102,7 +102,7 @@ define(["mod_eductx/main",
102102
});
103103
};
104104

105-
const initMasca = async() => {
105+
const initMasca = async () => {
106106
const address = await w3d.getAddressInUse();
107107
const enableResult = await connector.enableMasca(address, {
108108
snapId: "npm:@blockchain-lab-um/masca",
@@ -132,7 +132,7 @@ define(["mod_eductx/main",
132132
updateCurrentAccountData();
133133
});
134134
// On Provider Account Change
135-
window.ethereum.on("accountsChanged", async(accounts) => {
135+
window.ethereum.on("accountsChanged", async (accounts) => {
136136
if (accounts.length === 0) {
137137
document.getElementById("connectButton").hidden = false;
138138
}
@@ -157,19 +157,24 @@ define(["mod_eductx/main",
157157
* Prompts Metamask to confirm transaction and issue certificate to selected user
158158
* @return {Promise<void>}
159159
*/
160-
const issueCredentials = async() => {
161-
const url = `${endpoint}/issue-deferred/batch`;
162-
let credentialSubjects = await buildCredentialSubjects();
163-
const headers = {
164-
'Content-Type': 'application/json',
165-
'schemaType': '#educationCredentialBatch',
166-
"x-api-key": apiKey
167-
};
160+
const issueCredentials = async () => {
161+
const url = `${endpoint}/api/issue-oidc`;
162+
let {credentialSubject, email} = await buildCredentialSubjects();
163+
if (credentialSubject === null) {
164+
return;
165+
}
166+
const headers = new Headers();
167+
headers.append('Content-Type', 'application/json');
168+
headers.append('schemaType', '#educationCredential');
169+
headers.append("x-api-key", apiKey);
168170
try {
169171
const response = await fetch(url, {
170172
method: 'POST',
171-
headers: headers,
172-
body: JSON.stringify(credentialSubjects),
173+
headers,
174+
body: JSON.stringify({
175+
email: email,
176+
data: {credentialSubject}
177+
}),
173178
});
174179

175180
if (!response.ok) {
@@ -186,7 +191,7 @@ define(["mod_eductx/main",
186191
* Updates user data
187192
* @return {Promise<void>}
188193
*/
189-
const updateCurrentAccountData = async() => {
194+
const updateCurrentAccountData = async () => {
190195
// Validate network
191196
if (window.ethereum && window.ethereum.isConnected()) {
192197
updateUI(UI.NONE);
@@ -195,7 +200,7 @@ define(["mod_eductx/main",
195200
document.getElementById("connectFlow").hidden = false;
196201
document.getElementById("issueFlow").hidden = true;
197202
updateErrorReporting("Metamask not installed",
198-
"Please consider installing <a href='https://metamask.io/'>Metamask</a>", ERROR.DANGER);
203+
"Please consider installing <a href='https://metamask.io/'> Metamask</a>", ERROR.DANGER);
199204
return;
200205
}
201206
did = (await masca.getDID()).data;
@@ -222,7 +227,7 @@ define(["mod_eductx/main",
222227
if (did !== moodleDid && moodleDid !== null) {
223228
updateUI(UI.NOT_IN_SYNC);
224229
updateErrorReporting("Connected DID not linked to your Moodle account",
225-
`Currently connected account is not linked to your Moodle account.
230+
`Currently connected account is not linked to your Moodle account.
226231
Please change your account in MetaMask to <b>${moodleDid}</b>
227232
or link the connected account.`, ERROR.WARNING);
228233
return;
@@ -261,7 +266,7 @@ define(["mod_eductx/main",
261266
updateErrorReporting("", "", ERROR.SUCCESS);
262267
};
263268

264-
const proofOfPossession = async(url) => {
269+
const proofOfPossession = async (url) => {
265270
const response = await fetch(url);
266271
const signedData = await masca.signData({
267272
type: "JWT",
@@ -275,12 +280,12 @@ define(["mod_eductx/main",
275280

276281
if (connector.isError(signedData)) {
277282
updateErrorReporting("Failed to sign the data",
278-
"There has been an error signing the proof of possession data.", ERROR.DANGER);
283+
"There has been an error signing the proof of possession data.", ERROR.DANGER);
279284
}
280285
return signedData.data;
281286
};
282287

283-
const getCredentialsWithPop = async() => {
288+
const getCredentialsWithPop = async () => {
284289
const url = `${endpoint}/query`;
285290
const proof = await proofOfPossession(`${url}/nonce/${did}`);
286291
const claimResponse = await fetch(`${url}/claim`, {
@@ -296,18 +301,16 @@ define(["mod_eductx/main",
296301
/**
297302
* Fetches and shows current user's certificates
298303
*/
299-
const showCredentials = async() => {
304+
const showCredentials = async () => {
300305
const vcs = await getCredentialsWithPop();
301306
did = (await masca.getDID()).data;
302-
// eslint-disable-next-line no-console
303-
console.log(did);
304307
if (vcs.length === 0) {
305308
document.getElementById("viewCertFlow").innerHTML = "<h2>No credentials yet</h2>\n";
306309
return;
307310
}
308311
document.getElementById("viewCertFlow").innerHTML = buildCredentialsTable(vcs);
309312
document.querySelectorAll(".claim-credential").forEach(btn => {
310-
btn.addEventListener('click', async function() {
313+
btn.addEventListener('click', async function () {
311314
// eslint-disable-next-line no-console
312315
this.disabled = true;
313316
const credObj = JSON.parse(this.dataset.cred);
@@ -323,7 +326,7 @@ define(["mod_eductx/main",
323326
});
324327

325328
document.querySelectorAll(".reject-credential").forEach(btn => {
326-
btn.addEventListener('click', async function() {
329+
btn.addEventListener('click', async function () {
327330
this.disabled = true;
328331
const credObj = JSON.parse(this.dataset.cred);
329332
const rejected = await requestDeletion(credObj.id);
@@ -378,9 +381,9 @@ define(["mod_eductx/main",
378381
} else {
379382
credentialsTable = "<h2 id='shownCertsTitle'>My Credentials</h2>";
380383
credentialsTable += "<div style=\"overflow-x: scroll;\"><table class='table'>" +
381-
"<thead><th>Type</th>" +
382-
"<th>Title</th><th>Achievement</th><th>Grade</th><th>Awarding Body</th><th>ECTS</th><th></th><th></th></thead>" +
383-
"<tbody>";
384+
"<thead><th>Type</th>" +
385+
"<th>Title</th><th>Achievement</th><th>Grade</th><th>Awarding Body</th><th>ECTS</th><th></th><th></th></thead>" +
386+
"<tbody>";
384387
vcs.forEach((credObj) => {
385388
const cred = credObj.credential;
386389
credentialsTable += `<tr id='${credObj.id}'>`;
@@ -403,7 +406,7 @@ define(["mod_eductx/main",
403406
return credentialsTable;
404407
};
405408

406-
const saveCredential = async(credObj) => {
409+
const saveCredential = async (credObj) => {
407410
const credential = credObj.credential;
408411
const res = await masca.saveCredential(credential, {
409412
store: ['snap'],
@@ -416,7 +419,7 @@ define(["mod_eductx/main",
416419
return true;
417420
};
418421

419-
const requestDeletion = async(id) => {
422+
const requestDeletion = async (id) => {
420423
const url = `${endpoint}/query`;
421424
const proof = await proofOfPossession(`${endpoint}/query/nonce/${did}`);
422425
try {
@@ -491,7 +494,7 @@ define(["mod_eductx/main",
491494
const updateUI = (option) => {
492495
if (did) {
493496
document.getElementById("addressElement").innerHTML =
494-
did.substring(0, 12) + "..." + did.substring(did.length - 4, did.length);
497+
did.substring(0, 12) + "..." + did.substring(did.length - 4, did.length);
495498
}
496499
switch (option) {
497500
case UI.STUDENT:
@@ -583,49 +586,46 @@ define(["mod_eductx/main",
583586
* Return JSON object, data from input fields
584587
* @return {Promise<{obj}>}
585588
*/
586-
const buildCredentialSubjects = async() => {
589+
const buildCredentialSubjects = async () => {
587590
let dropDown = document.getElementById('students');
588-
const selectedValues = [];
589-
for (let i = 0; i < dropDown.options.length; i++) {
590-
if (dropDown.options[i].selected) {
591-
const value = JSON.parse(dropDown.options[i].value);
592-
selectedValues.push({
593-
credentialSubject: {
594-
currentFamilyName: value.lastName,
595-
currentGivenName: value.firstName,
596-
id: value.did,
597-
dateOfBirth: null,
598-
personIdentifier: null,
599-
achieved: {
591+
if (dropDown.value) {
592+
const value = JSON.parse(dropDown.value);
593+
return {
594+
credentialSubject: {
595+
currentFamilyName: value.lastName,
596+
currentGivenName: value.firstName,
597+
id: value.did || null,
598+
dateOfBirth: null,
599+
personIdentifier: null,
600+
achieved: {
601+
id: null,
602+
title: document.getElementById("achievement").value || null,
603+
specifiedBy: {
600604
id: null,
601-
title: document.getElementById("achievement").value,
602-
specifiedBy: {
603-
id: null,
604-
title: "Example", // Course iz moodla ime
605-
volumeOfLearning: null,
606-
iSCEDFCode: null,
607-
eCTSCreditPoints: document.getElementById("ects").value
608-
},
609-
wasAwardedBy: {
610-
id: document.getElementById("wasAwardedBy").value,
611-
awardingBody: null,
612-
awardingBodyDescription: document.getElementById("awardingBodyDescription").value,
613-
awardingDate: null,
614-
awardingLocation: null
615-
},
616-
wasDerivedFrom: {
617-
id: null,
618-
title: document.getElementById("title").value,
619-
grade: document.getElementById("grade").value,
620-
issuedDate: Date.now().toString()
621-
},
622-
associatedLearningOpportunity: null
623-
}
605+
title: "Example", // Course iz moodla ime
606+
volumeOfLearning: null,
607+
iSCEDFCode: null,
608+
eCTSCreditPoints: document.getElementById("ects").value
609+
},
610+
wasAwardedBy: {
611+
id: document.getElementById("wasAwardedBy").value,
612+
awardingBody: null,
613+
awardingBodyDescription: document.getElementById("awardingBodyDescription").value,
614+
awardingDate: null,
615+
awardingLocation: null
616+
},
617+
wasDerivedFrom: {
618+
id: null,
619+
title: document.getElementById("title").value,
620+
grade: document.getElementById("grade").value,
621+
issuedDate: Date.now().toString()
622+
},
623+
associatedLearningOpportunity: null
624624
}
625-
});
626-
}
625+
}, email: value.email
626+
};
627627
}
628-
return selectedValues;
628+
return {credentialSubject: null, email: null};
629629
};
630630

631631
/**

templates/home.mustache

+3-3
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@
105105
<h2 id="receiverHeading">Student</h2>
106106
<div id="issueCertReceiver">
107107
<label for="students">Name and Surname</label>
108-
<select class="form-control" style="width: 35%;" name="students[]" id="students" multiple>
108+
<select class="form-control" style="width: 35%;" name="students[]" id="students">
109109
{{#students}}
110-
<option value='{ "did": "{{did}}", "mid": "{{id}}", "idNumber": "{{idnumber}}",
110+
<option value='{ "did": "{{did}}", "mid": "{{id}}", "email": "{{email}}", "idNumber": "{{idnumber}}",
111111
"firstName": "{{firstname}}{{#middlename}} {{middlename}}{{/middlename}}", "lastName": "{{lastname}}" }'>
112112
{{firstname}} {{middlename}} {{lastname}}</option>
113113
{{/students}}
114114
</select><br>
115115
<hr>
116-
<button class="btn btn-primary" id="issueCertificate" disabled>Issue VC</button>
116+
<button class="btn btn-primary" id="issueCertificate">Issue VC</button>
117117
<hr>
118118
<h2>Save Template</h2>
119119
<label for="templateName">Template name</label>

view.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,10 @@
131131
$did = $didobj->did;
132132
if ($did != NULL) {
133133
$student->did = $did;
134-
$eligiblestudents[] = $student;
134+
} else {
135+
$student->did = NULL;
135136
}
137+
$eligiblestudents[] = $student;
136138
}
137139
}
138140

0 commit comments

Comments
 (0)