Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 178 additions & 49 deletions shapes/README.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,235 @@
# Solid User Profile

We don't currently have a specific Shape defined for user profiles. But the
original Solid specification provides good guidance on the basic requirements
for the Profile Document expected to be returned when de-referencing a WebID.

See here for details: https://github.com/solid/solid-spec#webid-profile-documents

We'd certainly be open to ideas on how to model these basic discovery
requirements differently, and also on how to model the actual human user profile
information (such as date-of-birth, gender, address, etc.).

## The 'country' problem?
An interesting discussion point would be how to model something as seemingly
trivial as the 'country' field of a user's address. If we take the ISO as just
an example of a recognized 'authority' when referring to "countries of the
world" (of course they are by no means the only such authority), then a problem
arises in that the ISO do not provide IRIs for the countries they otherwise
standardize identifiers for (e.g., 'ie' for Ireland, 'de' for Germany).

(About 7 years ago I specifically asked the ISO if they might consider minting
official IRIs for countries. Their reply was, literally, "**_We do not product
(sic) any RDF formats, I am sorry"_**!)

By way of example of how long-running a fundamental modelling question this is,
see this mailing list thread I started 6 years ago (and specifically see the
guidance from Martin Hepp, re-iterated in the final email from Bernard Vatant),
which basically recommends just using the standardized string values for
countries, and not using IRIs at all(!):
- Martin Hepp: https://lists.w3.org/Archives/Public/public-vocabs/2015Mar/0165.html
- Bernard Vatant (last email): https://lists.w3.org/Archives/Public/public-vocabs/2015Mar/0181.html

# Shapes

The remainder of this document describes the overall contents of this
directory, and attempts to explain some of the rationale for the modelling.

This directory also includes a series of Turtle files demonstrating expected
usage.

### Note: Use of triples versus quads for user data

Currently our modelling does not use quads for user data at all.

Instead we propose using Named Graphs to support various forms of meta-data
that might be associated with any resource, for example, server-managed
meta-data (e.g., the date/time the resource was created or last modified, or
the backend storage node used, etc.), or Access Control List (ACL) data
associated with the resource, or perhaps ShEx shapes the resource must conform
to, or client-side meta-data associated with Non-RDF resources (e.g., the 'name'
of a JPEG image resource, or the camera aperture settings of the image), etc.

## Structure of the Shapes, and example Turtle contained here.

This directory contains a `bookmark` directory and a `contact` directory. Each
contains ShEx Shapes and an `/example` folder showing demonstration Turtle files
conforming to the relevant Shapes.

The Turtle files of the `contact` examples also demonstrate how instances of Pod
Resources are currently interlinked (e.g., it demonstrates groups of people
referenced from address books).

## Use of the `#this` fragment identifier

This modelling follows a naming convention whereby the fragment identifier
`#this` can be used to denote the main RDF Subject in the dataset that makes up
all the triples of a single Pod resource.

For example, in `shapes/contact/example/group/group3-soccer-team.ttl` we use the
`#this` fragment identifier on the RDF Subject of the triples associated with
the Soccer Team Group itself. The resource is free to include triples using as
many other RDF Subjects as it likes, but `#this` is intended to identify the
main entity of the resource.

## Indexes

We use the term 'index' throughout our modelling with two very different
interpretations:

- Index in the sense of a single Pod resource containing a collection of
entities. E.g., a single Person-Index resource might contain a list of 50
Person instances, where each instance contains a reference (or link) to the
actual Person resource elsewhere in my Pod, but may also contain a copy of
the Person's name and profile image perhaps, directly in the index too.
- Index in the sense of a '_default resource to look for when navigating to a
Container_'. In much the same way as browsers will automatically look for a
resource named 'index.html' when browsing to a website, our modelling can
store resources named 'index.ttl' in the root of containers, which our
applications can then expect to contain app-specific information.

### Indexes as 'efficient containers for a collection of entities'

We chose not to model these collections using either native RDF lists (e.g.,
`rdf:Seq`), nor LDP containers, nor some existing list or set vocabulary (e.g.,
such as Schema.org's [ItemList](https://schema.org/ItemList), or the Ordered
List Ontology ([OLO](http://smiy.sourceforge.net/olo/spec/orderedlistontology.html))).
The reasons for this were:
- This was originally modelled around limitations of the first Solid server
(NSS).
- Efficiency reasons, i.e., where we wish to retrieve a collection of entities
in a single HTTP resource request (e.g., a list of people, perhaps with just
their first name and date-of-birth values), as opposed to needing to make
multiple HTTP requests, one for each Person resource in our list.


#### Example of index use

For example, a single AddressBook resource may reference two indexes, one for
the collection of email addresses in that AddressBook (i.e., using the
predicate `vcard:nameEmailIndex`), and one for the collection of groups in that
AddressBook (i.e., using the predicate `vcard:groupIndex`).


## Bookmarks

We've decided to keep the structure of bookmarks simple, only storing `terms:created`, `terms:title`, and
`bookmark:recalls`. Note that some Solid bookmarking apps also use `foaf:maker`, but we didn't think it necessary in the
We've decided to keep the structure of bookmarks simple, only storing
`dcterms:created`, `dcterms:title`, and `bookmark:recalls`. Note that some Solid
bookmarking apps also use `foaf:maker`, but we didn't think it necessary in the
context of the PodBrowser, so we've chosen to make it optional.

We've also decided to omit the use of an index using `dct:references`, as this would require us to keep the index
up-to-date manually, and we think it's better to find the bookmarks using the `bookmark:Bookmark` class.
We've also decided to omit the use of an index using `dcterms:references`, as
this would require us to keep the index up-to-date manually, and we think it's
better to find the bookmarks using the `bookmark:Bookmark` class.

At some point we might need to group bookmarks, but at this point we've chosen to keep it simple and simply
implement bookmarks as a set of bookmarks that are stored in one file.
At some point we might need to group bookmarks, but at this point we've chosen
to keep it simple and simply implement bookmarks as a set of bookmarks that are
stored in one file.

## Contacts

Because there are some differences between the ShEx shapes we've described and previous work on SHACL shapes we wanted
to explain the differences those shapes. Once they have been resolved, we might remove this README altogether.
Because there are some differences between the ShEx shapes we've described and
previous work on SHACL shapes, we wanted to explain the differences between
those shapes. Once these differences have all been resolved, we might remove
this README altogether.

### Address Book: Differences between ShEx and SHACL

The following is how the SHACL shape in `https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl` differs from the ShEx shape in `contacts/address-book.shex`.
The following is how the SHACL shape in
`https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl`
differs from the ShEx shape in `contacts/address-book.shex`.

- vcard:AddressBook
- vcard:fn (instead of dc:title)
- There can be one or more instances
- There can be one or more instances.
- vcard:nameEmailIndex
- There can be one or more instances
- There is not a specific datatype set
- vcard:vcard:groupIndex
- Using terms (sh:count, sh:FollowMe) that isn't formalized
- There can be one or more instances.
- There is no specific datatype set.
- vcard:groupIndex
- Uses terms (e.g., sh:count, sh:FollowMe) that aren't formalized.

### Group Index: Differences between ShEx and SHACL

The following is how the SHACL shape in `https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl` differ from the ShEx shape in `contacts/group-index.shex`.
The following is how the SHACL shape in
`https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl`
differs from the ShEx shape in `contacts/group-index.shex`.

- vcard:AddressBook
- vcard:fn
- Different cardinality ({1,m} vs {1,1})
- Different cardinality ({1,m} vs {1,1}).
- vcard:includesGroup
- We used an inverse triple constraint instead (putting this term under vcard:Group shape instead)
- We used an inverse triple constraint instead (putting this term under
vcard:Group shape instead).

### Group: Differences between ShEx and SHACL

The following is how the SHACL shape in `https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl` differ from the ShEx shape in `contacts/group.shex`.
The following is how the SHACL shape in
`https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl`
differs from the ShEx shape in `contacts/group.shex`.

- vcard:AddressBook
- vcard:includesGroup
- Using informal terms (sh:count, sh:FollowMe)
- Using informal terms (sh:count, sh:FollowMe).
- vcard:Group
- vcard:fn cardinality difference (one or more instead of just one)
- vcard:member (informal term) instead of vcard:hasMember
- vcard:fn cardinality difference (one or more instead of just one).
- vcard:member (informal term) instead of vcard:hasMember.

### People Index: Differences between ShEx and SHACL

The following is how the SHACL shape in `https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl` differ from the ShEx shape in `contacts/people-index.shex`.
The following is how the SHACL shape in
`https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl`
differs from the ShEx shape in `contacts/person-index.shex`.

- vcard:Individual
- vcard:inAddressBook
- uses informal term sh:BackwardLink
- minimum and maximum one instance of IRI (can be part of only one address book)
- uses informal term sh:BackwardLink.
- minimum and maximum one instance of IRI (i.e., can only be referenced from
one address book).

### Person: Differences between ShEx and SHACL

The following is how the SHACL shape in `https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl` differ from the ShEx shape in `contacts/person.shex`.
The following is how the SHACL shape in
`https://raw.githubusercontent.com/solid/contacts-pane/master/shapes/contacts-shapes.ttl`
differs from the ShEx shape in `contacts/person.shex`.

- vcard:Individual
- is closed
- using informal term sh:count
- is closed.
- using informal term sh:count.
- vcard:fn
- uses sh:pattern ".* .*"
- uses sh:pattern ".* .*".
- vcard:hasUID
- different cardinality ({1, 1} vs {0, 1})
- uses sh:pattern "^urn:uuid:"
- different cardinality ({1, 1} vs {0, 1}).
- uses sh:pattern "^urn:uuid:".
- vcard:hasName
- has a shape of its own (:NameShape)
- has a shape of its own (:NameShape):
- vcard:family-name
- vcard:given-name
- vcard:additional-name
- vcard:honorific-prefix
- vcard:honorific-suffix
- we used string, but added a comment
- we used string, but added a comment.
- vcard:hasImage
- don't specify type dc:Image
- don't specify type dc:Image.
- vcard:hasRelated
- don't specify node or sub-shape (we specified a shape for vcard:RelatedType in ShEx)
- don't specify node or sub-shape (we specified a shape for
vcard:RelatedType in ShEx).
- vcard:url
- has a shape of its own (:WebPageShape)
- uses vcard:value (vs rdf:value)
- uses sh:pattern "^https?:"
- uses informal term sh:count
- we specified type rdf:Resource in the ShEx shape
- has a shape of its own (:WebPageShape):
- uses vcard:value (vs rdf:value).
- uses sh:pattern "^https?:".
- uses informal term sh:count.
- we specified type rdf:Resource in the ShEx shape.
- vcard:hasAddress
- don't specify node or sub-shape (vcard:Address)
- don't specify node or sub-shape (vcard:Address).
- vcard:anniversary
- we used vcard:bday in the ShEx shape
- we used vcard:bday in the ShEx shape.
- vcard:hasEmail
- uses sh:pattern "^mailto:" (vs /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/ in ShEx)
- uses vcard:value (vs rdf:value)
- uses informal term sh:count
- different cardinality (ShEx is *)
- uses sh:pattern "^mailto:" (vs /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/ in ShEx).
- uses vcard:value (vs rdf:value).
- uses informal term sh:count.
- different cardinality (ShEx is *).
- vcard:hasTelephone
- uses sh:pattern "^tel:" (vs /^\+?[0-9]+[0-9-]*[0-9]$/ in ShEx)
- uses vcard:value (vs rdf:value)
- uses informal term sh:count
- different cardinality (ShEx is *)
- uses sh:pattern "^tel:" (vs /^\+?[0-9]+[0-9-]*[0-9]$/ in ShEx).
- uses vcard:value (vs rdf:value).
- uses informal term sh:count.
- different cardinality (ShEx is *).
5 changes: 5 additions & 0 deletions shapes/bookmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Bookmark-related Turtle files

Bookmarks are modelled very simply. Due to this simplicity at the moment, we've
choosen to model a collection of bookmarks as simply being entities contained
within a single resource (as opposed to defining a resource-per-bookmark).
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX terms: <http://purl.org/dc/terms/>
PREFIX xsd: <https://www.w3.org/2001/XMLSchema#>

#
# This shape describes the information we store for a bookmark.
# In Pod Browser we store all bookmarks within the one resource (as opposed to
# storing one bookmark per resource and containing them all within an
# `ldp:Container`).
#
bookmark:Bookmark {
terms:created xsd:dateTime {1} ;
terms:title xsd:string {1} ;
Expand Down
16 changes: 16 additions & 0 deletions shapes/bookmark/example/bookmark/index.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@prefix : <#>.
@prefix bookm: <http://www.w3.org/2002/01/bookmark#>.
@prefix terms: <http://purl.org/dc/terms/>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

:16115748945888512870652861511
a bookm:Bookmark;
terms:created "2021-01-25T11:41:34.589Z"^^xsd:dateTime;
terms:title "Solid Project";
bookm:recalls <https://solidproject.org/>.

:12391230834289702394820340982
a bookm:Bookmark;
terms:created "2021-01-30T12:30:21.321Z"^^xsd:dateTime;
terms:title "Inrupt";
bookm:recalls <https://inrupt.com/>.
24 changes: 24 additions & 0 deletions shapes/contact/address-book.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
PREFIX acl: <http://www.w3.org/ns/auth/acl#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

# This shape describes some of the information we store for an address book.
# It's intended to represent a single address book. We use the ID "#this" to
# refer to the subject of the address book.

# This model was originally developed as part of the
# SolidOS/Data Browser project, and we strive to keep it interoperable with
# their work. Some custom terms have been introduced to vocabularies we think
# they could belong to (e.g. vcard:nameEmailIndex). We'll make a note of the
# custom term when used.

# vcard:AddressBook is a custom class
vcard:AddressBook {
dc:title xsd:string {1} ;
# vcard:nameEmailIndex is a custom term
vcard:nameEmailIndex IRI {1} ;
# vcard:groupIndex is a custom term
vcard:groupIndex IRI {1} ;
acl:owner IRI +
}
47 changes: 47 additions & 0 deletions shapes/contact/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Contacts-related Turtle files

The example Turtle files here describe the structure and modelling of the
following Contact-related concepts:

### Two address books

Modelled as **_containers_**:

- Work colleagues (the `/addressBookWork` container).
- Friends (the `/addressBookFriend` container).

### Four groups

Modelled as individual **_resources_** within the `/group` container, and with
that `/group` container having an `index.ttl` Group-Index.

- Work colleagues (`/group/group1-work-colleague.ttl`).
- Friends (`/group/group2-friend.ttl`).
- Local soccer team (`/group/group3-soccer-team.ttl`).
- Just an empty group (`/group/group4-empty.ttl`).

And the Group-Index - `/group/index.ttl`.

### Three people

Each person modelled as a **_child container_** within the `/person` container,
with the parent `/person` container containing an `index.ttl` Person-Index.

- Arne H, who I work with, is a friend I play D&G, and is the striker on my
local soccer team!
- Vincent T, who I work with.
- Tommy Mc, who is a friend.

And the Person-Index - `/person/index.ttl`.


# Notes:

- The use of `index.ttl` is a convention, but it seems to be used for both
'indexes' (i.e., a Group index (e.g., `/group/index.ttl`) listing a number of
contained groups), and the 'default resource' of a container (e.g., the
`index.ttl` resource within the individual person container (e.g.,
`/person/347429c1-e6b5-40c0-bd6f-61ba1265d357/index.ttl`).

- In Group-Indexes, is it necessary to duplicate the triple stating that each
contained group is of `rdf:type` `vcard:Group`?
Loading