Skip to main content

Course members

Your tool can show who is in the course — a roster, a gradebook, "Your instructor is Dr. Lisa Simpson" — without ever holding a name or an email. You place an element, and Voshi draws the value inside it:

<p>Welcome back, <voshi-member mid="IMbr21" format="given"></voshi-member>.</p>

The tool's code works with member ids only. Names and emails are drawn by the <voshi-member> element in a closed shadow root, where your code cannot read them, log them, or save them by accident. Schools ask what a tool does with student information; this is the answer that lets them say yes.

Ask for it in voshi.json​

A tool shows a member's name or email only if its manifest asks for that field, with a sentence saying why:

{
"manifest_version": 2,
"app": { "entry": "app.js", "tag": "my-tool" },
"members": [
{ "field": "name", "reason": "The gradebook lists students by name." },
{ "field": "email", "reason": "The instructor's roster shows each student's email." }
]
}

The ask is part of the version and is reviewed with it. A field the manifest does not ask for draws nothing.

Who can see whom​

The launch isIt may see
Course staff (instructor, assistant, manager)Every member of the course
A student, or a mentorThemselves and the course staff

So a student's view can name the instructor, and an instructor's gradebook can name every student, but no student sees another student. A member who has never launched a Voshi tool in the course is not known to Voshi yet and cannot be listed or shown.

listMembers()​

api.listMembers()Promise<CourseMember[]>

Every member this launch may see, in id order. The order says nothing about their names, and stays the same when a name changes.

CourseMember
idstring

The member's id in this course, what mid takes.

groupsstring[]

The member's LTI roles: student, instructor, assistant, manager, mentor.

hasGroup(group)boolean

Whether the member holds group.

isStaffboolean

Course staff: an instructor, assistant, or manager.

isStudentboolean

A student, and not staff.

A member's role is not personal, so your code holds it; the name and email stay in the element.

api.user.member is the launching member's own id.

async renderRoster() {
const list = this.shadowRoot.querySelector('#roster')
list.replaceChildren()
for (const member of await this.api.listMembers()) {
if (member.isStudent) {
const row = document.createElement('li')
row.innerHTML = `<voshi-member mid="${member.id}" format="reverse"></voshi-member>`
list.append(row)
}
}
}

Keep what you store about a member keyed by their id (storage.member already is), and look the name up by placing an element when you draw.

The <voshi-member> element​

midstringrequired

The member's id.

fieldstring

name (the default) or email. The manifest's members must ask for it.

formatstring

For a name: full (the default, "Homer Jay Simpson"), reverse ("Simpson, Homer"), given ("Homer"), family ("Simpson"), or initials ("HS").

linkboolean

For an email: draw it as a mailto: link. Style the link from your stylesheet as voshi-member::part(link).

The element is defined on the page before your tool loads, so place it anywhere: in a template, with innerHTML, or with document.createElement('voshi-member'). Change mid and it redraws. Elements placed together are read from Voshi in one request, and a member already read is drawn at once.

When it draws nothing​

A field the manifest does not ask for, or a member this launch may not see, draws nothing, and the element logs a warning to the console. On a launch from the AI Builder the warning is also reported to the Test Log, under the Running Tool. A name or email the school's LMS never sent draws nothing, silently.

warning

Don't try to read the value back out of the element, sort by it, or copy it into storage. Use member ids for everything you keep.

Where the page may connect​

The launch page lets your tool reach Voshi and nothing else. A fetch to another site, an image or font from another host, or an <iframe> of another page is refused by the browser, unless the manifest's external lists that URL. That is what keeps a name on the page from leaving it.