teacup/schemas/bloomsbury-vra.sch

897 lines
71 KiB
Text
Raw Normal View History

2021-12-15 11:29:23 +00:00
<schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:bmy="http://www.bloomsbury.com/ns/xsl" queryBinding="xslt2" schemaVersion="ISO19757-3">
<ns uri="http://www.vraweb.org/vracore4.htm" prefix="vra"/>
<ns uri="http://www.bloomsbury.com/ns/xsl" prefix="bmy"/>
<!--
Add test for @source on material (materialsTechniques)
-->
<xsl:key name="isbn" match="vra:relation[@rules='isbn'][not(@extent = ('set', 'pack', 'otherVolume'))]" use="@refid"/>
<let name="allowed-taxonomies" value="doc(resolve-uri('../../SKOS/taxonomy-lookup.xml'))//taxonomy/@vraId"/>
<phase id="ssp-platform">
<active pattern="ids"/>
<active pattern="collection"/>
<active pattern="worktypeSet"/>
<active pattern="worktypeSetValues"/>
<active pattern="worktypeSetTimelineFilters"/>
<active pattern="worktypeSetNonTimelineCategory"/>
<active pattern="description"/>
<active pattern="location"/>
<active pattern="ISBNs"/>
<active pattern="validISBN"/>
<active pattern="workRefidISBN"/>
<active pattern="titleSet"/>
<active pattern="worktypeSetmuseumExhibImages"/>
<active pattern="museumImages"/>
<active pattern="exhibitionImages"/>
<active pattern="collectionImage"/>
<active pattern="workTitle"/>
<active pattern="itemTitle"/>
<active pattern="personRelationship"/>
<active pattern="personRelationshipDetailed"/>
<active pattern="earliestLatestDatesFormat"/>
<active pattern="validDates"/>
<active pattern="rightsTypes"/>
<active pattern="rightsOther"/>
<active pattern="rights"/>
<active pattern="imageRefid"/>
<active pattern="ancillariesID"/>
<active pattern="quiz"/>
<active pattern="imageSubjectRules"/>
<active pattern="preferredImage"/>
<active pattern="workItemWorktypeSet"/>
<active pattern="imageNotSequence"/>
<active pattern="contentStorage"/>
<active pattern="bookContentType"/>
<active pattern="bookWorkTypeOptions"/>
<active pattern="ancillariesTests"/>
<active pattern="ancillaryTypes"/>
<active pattern="journals"/>
<active pattern="timeline"/>
<active pattern="earliestLatestDateTest"/>
<active pattern="timelineChaptertimePeriod"/>
<active pattern="distinctSubjects"/>
<active pattern="magesSameISBN"/>
<active pattern="videos"/>
<active pattern="taxonomyLookup"/>
<active pattern="buy-link"/>
<active pattern="performance-rights"/>
<active pattern="performance-rights-ooc"/>
</phase>
<phase id="propagator-platform">
<active pattern="ids"/>
<active pattern="collection"/>
<active pattern="worktypeSet"/>
<active pattern="worktypeSetValues"/>
<active pattern="worktypeSetTimelineFilters"/>
<active pattern="worktypeSetNonTimelineCategory"/>
<active pattern="description"/>
<active pattern="location"/>
<active pattern="ISBNs"/>
<active pattern="validISBN"/>
<active pattern="workRefidISBN"/>
<active pattern="titleSet"/>
<active pattern="worktypeSetmuseumExhibImages"/>
<active pattern="museumImages"/>
<active pattern="exhibitionImages"/>
<active pattern="collectionImage"/>
<active pattern="FPAdate"/>
<active pattern="workTitle"/>
<active pattern="itemTitle"/>
<active pattern="FPAimages"/>
<active pattern="FPADescriptionWorktype"/>
<active pattern="FPArelatedImages"/>
<active pattern="personRelationship"/>
<active pattern="personRelationshipDetailed"/>
<active pattern="FPArefid"/>
<active pattern="earliestLatestDatesFormat"/>
<active pattern="validDates"/>
<active pattern="rightsTypes"/>
<active pattern="rightsOther"/>
<active pattern="rights"/>
<active pattern="imageRefid"/>
<active pattern="ancillariesID"/>
<active pattern="quiz"/>
<active pattern="imageSubjectRules"/>
<active pattern="preferredImage"/>
<active pattern="workItemWorktypeSet"/>
<active pattern="imageNotSequence"/>
<active pattern="contentStorage"/>
<active pattern="bookContentType-prop"/>
<active pattern="bookWorkTypeOptions"/>
<active pattern="ancillariesTests"/>
<active pattern="ancillaryTypes"/>
<active pattern="journals"/>
<active pattern="timeline"/>
<active pattern="earliestLatestDateTest"/>
<active pattern="timelineChaptertimePeriod"/>
<active pattern="distinctSubjects"/>
<active pattern="magesSameISBN"/>
<active pattern="videos"/>
<active pattern="taxonomyLookup"/>
<active pattern="buy-link"/>
<active pattern="performance-rights"/>
<active pattern="performance-rights-ooc"/>
</phase>
<pattern id="ids">
<!-- Main elements must have ids -->
<rule id="ids-rule1" context="vra:collection | vra:work | vra:image | vra:item">
<assert id="ids-rule1-test1" test="@id">A <name/> must have an ID, which should be the Librios ID, or derived therefrom</assert>
</rule>
</pattern>
<pattern id="collection">
<title>Required content</title>
<rule id="collection-rule1" context="vra:vra">
<assert id="collection-rule1-test1" test="count(vra:collection) = 1">The root <name/> element must contain exactly one collection element.</assert>
<report id="collection-rule1-test2" test="vra:work or vra:image or vra:item">The root <name/> must only contain a collection.</report>
</rule>
</pattern>
<pattern id="worktypeSet">
<title>Non-required content</title>
<rule id="worktypeSet-rule1" context="vra:collection">
<!-- Some collections need a title, but this isn't where it belongs -->
<report id="worktypeSet-rule1-test1" test="@source">A <name/> must not have a @source</report>
<assert id="worktypeSet-rule1-test2" test="vra:worktypeSet">A <name/> must have a worktypeSet</assert>
</rule>
</pattern>
<pattern id="worktypeSetValues">
<!-- Constrain the worktype of a collection -->
<rule id="worktypeSetValues-rule1" context="vra:collection/vra:worktypeSet/vra:worktype[not(@extent)]">
<let name="allowed-values" value="('museumImages', 'books', 'fpa', 'fpaArticles', 'ancillaries', 'exhibitionImages', 'journal', 'timeline', 'book-images', 'audio', 'video', 'screenplay', 'article', 'play', 'image', 'museum', 'monographAsReference', 'building', 'person')"/>
<assert id="worktypeSetValues-rule1-test1" test=". = $allowed-values"><name/> does not contain one of the defined values (<value-of select="$allowed-values"/>).</assert>
</rule>
<rule id="worktypeSetValues-rule2" context="vra:collection/vra:worktypeSet/vra:worktype[@extent]">
<let name="allowed-values" value="('product', 'category1', 'category2', 'category3', 'category4', 'category5', 'category6', 'category7', 'filter', 'filter1-title', 'filter1-default', 'contentTypeBrowsePageListView', 'contentTypeBrowsePageIndexView', 'customBrowsePageListView', 'customBrowsePageIndexView')"/>
<assert id="worktypeSetValues-rule2-test1" test="@extent = $allowed-values"><name/>/@<name path="@extent"/> does not contain one of the defined values (<value-of select="$allowed-values"/>).</assert>
</rule>
</pattern>
<pattern id="worktypeSetTimelineFilters">
<title>Timeline filters</title>
<rule id="worktypeSetTimelineFilters-rule1" context="vra:collection/vra:worktypeSet/vra:worktype[@extent = 'filter']">
<assert id="worktypeSetTimelineFilters-rule1-test1" test=". = ('filter1')">A worktype[@extent=filter] must be filter1</assert>
</rule>
<rule id="worktypeSetTimelineFilters-rule2" context="vra:collection/vra:worktypeSet[vra:worktype[@extent = 'filter'] = 'filter1']">
<assert id="worktypeSetTimelineFilters-rule2-test1" test="vra:worktype[@extent = 'filter1-title']">If there is a worktype[@extent=filter] = 'filter1' there must also be a filter1-title</assert>
<assert id="worktypeSetTimelineFilters-rule2-test2" test="vra:worktype[@extent = 'filter1-default']">If there is a worktype[@extent=filter] = 'filter1' there must also be a filter1-default</assert>
</rule>
</pattern>
<pattern id="worktypeSetNonTimelineCategory">
<title>Non-timelines</title>
<rule id="worktypeSetNonTimelineCategory-rule1" context="vra:collection/vra:worktypeSet[vra:worktype[not(@extent)][not(. = 'timeline')]]/vra:worktype">
<report id="worktypeSetNonTimelineCategory-rule1-test1" test="@extent = ('category1', 'category2', 'category3', 'category4', 'category5', 'category6', 'category7', 'filter')"><name/> in a collection that is not a timeline must not have @extent = (filter, category1 ... category7)</report>
</rule>
</pattern>
<pattern id="description">
<title>General tests</title>
<rule id="description-rule1" context="@refid">
<assert id="description-rule1-test1" test="matches(., '\w+')">A @refid must have some content</assert>
</rule>
<rule id="description-rule2" context="vra:collection/vra:descriptionSet/vra:description[@extent]">
<assert id="description-rule2-test1" test="@extent = ('dressObjects', 'general', 'numberOfObjects', 'strengths', 'description', 'additionalInformation', 'curator', 'primarySources')">A collection description/@extent must be one of the following: 'dressObjects', 'general', 'numberOfObjects', 'strengths', 'description', 'additionalInformation', 'curator', 'primarySources'</assert>
</rule>
<rule id="description-rule3" context="vra:work/vra:descriptionSet/vra:description[@extent]">
<let name="allowed-values" value="('description', 'season', 'filter1', 'representationOf', 'source', 'color', 'marking', 'general')"/>
<assert id="description-rule3-test1" test="@extent = $allowed-values">A work description/@extent must be one of the following: <value-of select="string-join($allowed-values, ', ')"/></assert>
</rule>
</pattern>
<pattern id="location">
<rule id="location-rule1" context="vra:location[@extent]">
<assert id="location-rule1-test1" test="@extent = ('placeForTimeline', 'designed', 'distributed', 'distribution', 'made', 'manufactured', 'owned', 'used', 'worn', 'Retailed', 'altered', 'customised', 'discovered', 'embroidered', 'hand painted', 'hand sewing', 'hand weaving', 'hand woven', 'painted', 'patented', 'printed', 'retailed', 'sold', 'woven', 'venue' )">A location/@extent must be one of the following: 'placeForTimeline', 'designed', 'distributed', 'distribution', 'made', 'manufactured', 'owned', 'used', 'worn', 'Retailed', 'altered', 'customised', 'discovered', 'embroidered', 'hand painted', 'hand sewing', 'hand weaving', 'hand woven', 'painted', 'patented', 'printed', 'retailed', 'sold', 'woven', 'venue'</assert>
</rule>
</pattern>
<pattern id="ISBNs">
<!-- Book ISBN relations -->
<rule id="ISBNs-rule1" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']/vra:work | vra:collection[vra:worktypeSet/vra:worktype = 'fpaArticles']/vra:work">
<assert id="ISBNs-rule1-test1" test="vra:relationSet/vra:relation[@rules='isbn']/@extent='xml'">A book or fpaArticles work must have an xml ISBN relation</assert>
</rule>
<rule id="ISBNs-rule2" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']/vra:work[worktypeSet/worktype[@extent='product']='FairchildBooks']">
<assert id="ISBNs-rule2-test1" test="vra:relationSet/vra:relation[@rules='isbn']/@extent='epdf'">A FairchildBooks book must have an epdf ISBN relation</assert>
</rule>
<rule id="ISBNs-rule3" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']/vra:work/vra:relationSet/vra:relation[@rules='isbn']/@extent">
<assert id="ISBNs-rule3-test1" test=". = ('xml', 'epdf', 'epub', 'studio', 'epdf+studio', 'hb', 'pb', 'print+accessCard', 'otherVolume', 'set', 'pack')">An isbn relation for a work in a book must have @extent=xml or epdf or epub or studio or epdf+studio or hb or pb or print+accessCard or otherVolume or set or pack</assert>
</rule>
</pattern>
<pattern id="validISBN">
<!-- Book ISBN tests -->
<rule id="validISBN-rule1" context="vra:relation[@rules='isbn']">
<let name="d1" value="number(substring(@refid,1,1))"/>
<let name="d2" value="number(substring(@refid,2,1)) * 3"/>
<let name="d3" value="number(substring(@refid,3,1))"/>
<let name="d4" value="number(substring(@refid,4,1)) * 3"/>
<let name="d5" value="number(substring(@refid,5,1))"/>
<let name="d6" value="number(substring(@refid,6,1)) * 3"/>
<let name="d7" value="number(substring(@refid,7,1))"/>
<let name="d8" value="number(substring(@refid,8,1)) * 3"/>
<let name="d9" value="number(substring(@refid,9,1))"/>
<let name="d10" value="number(substring(@refid,10,1)) * 3"/>
<let name="d11" value="number(substring(@refid,11,1))"/>
<let name="d12" value="number(substring(@refid,12,1)) * 3"/>
<let name="d13" value="number(substring(@refid,13,1))"/>
<let name="sum" value="$d1 + $d2 + $d3 + $d4 + $d5 + $d6 + $d7 + $d8 + $d9 + $d10 + $d11 + $d12 + $d13"/>
<assert id="validISBN-rule1-test1" test="string-length(@refid) = 13">An ISBN must be 13 digits long</assert>
<assert test="matches(string($sum), '0$')" id="isbn-check">ISBN is invalid - checksum incorrect [<value-of select="."/>]</assert>
<report test="count(key('isbn', @refid)) ge 2" id="dupe-id">Duplicated isbn - "<value-of select="@refid"/>".</report>
</rule>
</pattern>
<pattern id="workRefidISBN">
<!-- More specific ISBN tests -->
<rule id="workRefidISBN-rule1" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']/vra:work/vra:relationSet/vra:relation[@rules='isbn'][@extent='xml']">
<let name="myValue" value="@refid"/>
<let name="myWorkRefid" value="ancestor::vra:work/@refid"/>
<assert id="workRefidISBN-rule1-test1" test="$myValue = substring-after($myWorkRefid, '-')">The book xml ISBN does not match that in the work/@refid</assert>
</rule>
</pattern>
<pattern id="titleSet">
<!-- Some collections need a title (though we may not need to use it for FPA) -->
<rule id="titleSet-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]">
<assert id="titleSet-rule1-test1" test="vra:titleSet/vra:title">A museumImages collection must have a title</assert>
</rule>
<rule id="titleSet-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]">
<assert id="titleSet-rule2-test1" test="vra:titleSet/vra:title">A fpa collection must have a title</assert>
</rule>
<rule id="titleSet-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]">
<assert id="titleSet-rule3-test1" test="vra:titleSet/vra:title">A journal collection must have a title</assert>
</rule>
</pattern>
<pattern id="worktypeSetmuseumExhibImages">
<!-- museumImages and exhibition images -->
<rule id="worktypeSetmuseumExhibImages-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']] | vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]">
<assert id="worktypeSetmuseumExhibImages-rule1-test1" test="vra:worktypeSet/vra:worktype[@extent='product']">A museumImages or exhibitionImages collection must have a product worktype</assert>
<assert id="worktypeSetmuseumExhibImages-rule1-test2" test="vra:relationSet/vra:relation[@rules='timeline']">A museumImages or exhibitionImages collection must have a timeline relation</assert>
</rule>
<rule id="worktypeSetmuseumExhibImages-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:worktypeSet/vra:worktype[@extent='product'] | vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]/vra:worktypeSet/vra:worktype[@extent='product']">
<assert id="worktypeSetmuseumExhibImages-rule2-test1" test=". ='BergFashionLibrary'">A museumImages or exhibitionImages product worktype must be BergFashionLibrary</assert>
</rule>
</pattern>
<pattern id="museumImages">
<!-- Rules for museum images collections -->
<rule id="museumImages-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]">
<assert id="museumImages-rule1-test1" test="vra:descriptionSet/vra:description[@extent='dressObjects']" role="warning">A museumImages collection should usually contain details of the number of dress objects</assert>
<assert id="museumImages-rule1-test2" test="vra:descriptionSet/vra:description[@extent='strengths']" role="warning">A museumImages collection should usually contain details of the strengths of the collection</assert>
<assert id="museumImages-rule1-test3" test="vra:descriptionSet/vra:description[@extent='description']">A museumImages collection must contain a general description of the collection</assert>
<assert id="museumImages-rule1-test4" test="vra:descriptionSet/vra:description[@extent='additionalInformation']" role="warning">A museumImages collection should usually contain additional information about the collection</assert>
<assert id="museumImages-rule1-test5" test="vra:locationSet/vra:location[@type='repository']">A museumImages collection must contain the address of the repository</assert>
<assert id="museumImages-rule1-test6" test="vra:locationSet/vra:location[@type='repository']/@href">A museumImages collection must contain the web address of the repository</assert>
<assert id="museumImages-rule1-test7" test="starts-with(vra:locationSet/vra:location[@type='repository']/@href, 'http://')">A web address for a museumImages collection repository must start with http://</assert>
<assert id="museumImages-rule1-test8" test="vra:work/vra:dateSet/vra:notes[@extent='exclude']" role="warning">A museumImages collection should contain some works that are excluded from the timeline</assert>
</rule>
</pattern>
<pattern id="exhibitionImages">
<!-- Rules for exhibition images collections -->
<rule id="exhibitionImages-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]">
<assert id="exhibitionImages-rule1-test1" test="vra:locationSet/vra:location[@type='exhibition']">A exhibitionImages collection must contain the address of the organisation</assert>
<assert id="exhibitionImages-rule1-test2" test="vra:locationSet/vra:location[@type='exhibition']/vra:name[@type='corporate']">A exhibitionImages collection must contain the name of the organisation</assert>
<assert id="exhibitionImages-rule1-test3" test="vra:locationSet/vra:location[@type='exhibition']/@href">A exhibitionImages collection must contain the web address of the organisation</assert>
<assert id="exhibitionImages-rule1-test4" test="starts-with(vra:locationSet/vra:location[@type='exhibition']/@href, 'http://')">A web address for a exhibitionImages collection organisation must start with http://</assert>
</rule>
<rule id="exhibitionImages-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]/vra:work">
<assert id="exhibitionImages-rule2-test1" test="vra:dateSet/vra:notes[@extent='exclude']">A work in an exhibitionImages collection must be excluded from the timeline</assert>
</rule>
<rule id="exhibitionImages-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:locationSet/vra:location[@type='repository'] | vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:work/vra:locationSet/vra:location[@type='exhibition']">
<assert id="exhibitionImages-rule3-test1" test="vra:name[@type='geographic']">The address for a museum collection repository or exhibition must have each line in a name[@type=geographic]</assert>
<report id="exhibitionImages-rule3-test2" test="not(vra:name[@type='geographic'])">The address for a museum collection repository or exhibition must have each line in a name[@type=geographic]</report>
</rule>
<!-- Addresses can occur in museum collections and exhibition works -->
<rule id="exhibitionImages-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:locationSet/vra:location[@type='repository']/vra:name | vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]/vra:work/vra:locationSet/vra:location[@type='exhibition']/vra:name">
<assert id="exhibitionImages-rule4-test1" test="@extent = ('address1', 'address2', 'address3', 'country', 'postcode') or (@type = 'corporate')">The address lines in a museum collection repository must have @type=corporate or @extent = address1, address2, address3, country, or postcode</assert>
</rule>
</pattern>
<pattern id="collectionImage">
<!-- Some collection types shouldn't contain images, or items, or neither -->
<rule id="collectionImage-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:work">
<report id="collectionImage-rule1-test1" test="vra:item">A work in a museumImages collection must not contain items (ie only images are allowed)</report>
<assert id="collectionImage-rule1-test2" test="vra:image">A work in a museumImages collection must contain one or more image</assert>
</rule>
<rule id="collectionImage-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work">
<report id="collectionImage-rule2-test1" test="vra:item">A work in a fpa collection must not contain items (ie only images are allowed)</report>
<assert id="collectionImage-rule2-test2" test="vra:image">A work in a fpa collection must contain one or more image</assert>
</rule>
<rule id="collectionImage-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work">
<report id="collectionImage-rule3-test1" test="vra:image">A work in a fpaArticles collection must not contain images</report>
<assert id="collectionImage-rule3-test2" test="vra:item">A work in a fpaArticles collection must contain one or more items</assert>
</rule>
<rule id="collectionImage-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work">
<report id="collectionImage-rule4-test1" test="vra:image">A work in a books collection must not contain images (ie only items are allowed)</report>
</rule>
<rule id="collectionImage-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work">
<report id="collectionImage-rule5-test1" test="vra:image">A work in an ancillaries collection must not contain images (ie only items are allowed)</report>
<assert id="collectionImage-rule5-test2" test="vra:item">A work in a ancillaries collection must contain one or more item</assert>
</rule>
<rule id="collectionImage-rule6" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]/vra:work">
<report id="collectionImage-rule6-test1" test="vra:item">A work in a exhibitionImages collection must not contain items (ie only images are allowed)</report>
<assert id="collectionImage-rule6-test2" test="vra:image">A work in a museumImages collection must contain one or more image</assert>
</rule>
<rule id="collectionImage-rule7" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work">
<report id="collectionImage-rule7-test1" test="vra:item or vra:image">A work in a journal collection must not contain item or images</report>
</rule>
<rule id="collectionImage-rule8" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work">
<assert id="collectionImage-rule8-test1" test="vra:item">A work in a timeline collection must contain items</assert>
<report id="collectionImage-rule8-test2" test="vra:image">A work as a direct child of collection in an timeline collection must not contain images (ie only items are allowed)</report>
</rule>
</pattern>
<pattern id="FPAdate">
<!-- Some works and items types must contain date ranges to support timelines -->
<rule id="FPAdate-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'museumImages']]/vra:work | vra:collection[vra:worktypeSet/vra:worktype[. = 'exhibitionImages']]/vra:work | vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work">
<assert id="FPAdate-rule1-test1" test="if (not(vra:dateSet/vra:notes[@extent='exclude'])) then (vra:dateSet/vra:date) else 1" role="warning">A work in a fpa, museumImages or museumImages collection should usually contain a date range (it will need one if it must be in a timeline), or is it a work that must be excluded from the timeline?</assert>
</rule>
<rule id="FPAdate-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:item">
<assert id="FPAdate-rule2-test1" test="vra:dateSet/vra:date">A work in a timeline collection must contain a date range to support timelines functionality</assert>
</rule>
</pattern>
<pattern id="workTitle">
<title>Titles in works</title>
<rule id="workTitle-rule1" context="vra:work">
<!-- We must have a title for a work -->
<assert id="workTitle-rule1-test1" test="vra:titleSet">A titleSet must be present for a <name/></assert>
</rule>
<rule id="workTitle-rule2" context="vra:work/vra:titleSet/vra:title">
<assert id="workTitle-rule2-test1" test="string-length(.) gt 0">A title in a work must have some content</assert>
</rule>
<rule id="workTitle-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work/vra:titleSet/vra:title">
<assert id="workTitle-rule3-test1" test="matches(., '[0-9][0-9][0-9][0-9]$')" role="warning">A FPA work <name/> should usually end with a year (yyyy)</assert>
</rule>
</pattern>
<pattern id="itemTitle">
<title>Item titles, other than flashcards and quizzes</title>
<rule id="itemTitle-rule1" context="vra:item[not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = ('Quiz', 'Video Quiz', 'Flashcards', 'Visual Flashcards', 'runtime'))]">
<!-- We must have a title for an item -->
<assert id="itemTitle-rule1-test1" test="vra:titleSet">A titleSet must be present for a <name/></assert>
</rule>
<rule id="itemTitle-rule2" context="vra:item/vra:titleSet[not(../vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = ('Quiz', 'Video Quiz', 'Flashcards', 'Visual Flashcards', 'runtime'))]/vra:title">
<assert id="itemTitle-rule2-test1" test="string-length(.) gt 0">A title in an item must have some content</assert>
</rule>
</pattern>
<pattern id="FPAimages">
<title>FPA images</title>
<rule id="FPAimages-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]//vra:image">
<!-- The FPA images are available in three sizes, so we need to know which is which -->
<assert id="FPAimages-rule1-test1" test="vra:worktypeSet/vra:worktype[@extent='size'] = ('fullSize', 'thumbnail', 'preview')">An image in a fpa collection must contain a worktype[@extent=size]</assert>
</rule>
<rule id="FPAimages-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]">
<assert id="FPAimages-rule2-test1" test="vra:worktypeSet/vra:worktype[@extent='product']">An fpa collection must have a product worktype</assert>
<assert id="FPAimages-rule2-test2" test="vra:work/vra:dateSet/vra:notes[@extent='exclude']" role="warning">An fpa collection should contain some works that are excluded from the timeline</assert>
</rule>
<rule id="FPAimages-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[not(. = 'fpa')]]">
<report id="FPAimages-rule3-test1" test="//vra:description[@extent='filter1']">Only fpa collections are permitted to contain works with filter1</report>
</rule>
<rule id="FPAimages-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work">
<assert id="FPAimages-rule4-test1" test="if (not(vra:dateSet/vra:notes[@extent='exclude'])) then vra:descriptionSet/vra:description[@extent='filter1'] else 1" role="warning">If an fpa work is not excluded from the timeline it may need a filter1, but only if it is to appear in the designer timeline AND NOT the default timeline</assert>
<assert id="FPAimages-rule4-test2" test="if ((not(vra:dateSet/vra:notes[@extent='exclude'])) or (vra:descriptionSet/vra:description[@extent='filter1'])) then ((vra:dateSet/vra:date/vra:earliestDate) and (vra:dateSet/vra:date/vra:latestDate)) else 1">If a FPA work is not excluded from a timeline OR has a filter1 (ie is in a designer timeline), then it MUST have an earliestdate and a latestDate</assert>
<assert id="FPAimages-rule4-test3" test="if ((not(vra:dateSet/vra:notes[@extent='exclude'])) or (vra:descriptionSet/vra:description[@extent='filter1'])) then (vra:worktypeSet/vra:worktype[@extent='category']) else 1">If a FPA work is not excluded from a timeline OR has a filter1 (ie is in a designer timeline), then it MUST have a worktype[@extent='category']</assert>
</rule>
<rule id="FPAimages-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:worktypeSet/vra:worktype[@extent='product'][not(@rules)]">
<assert id="FPAimages-rule5-test1" test=". = 'FashionPhotographyArchive'">The fpa general product worktype must be FashionPhotographyArchive</assert>
</rule>
<rule id="FPAimages-rule6" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:worktypeSet/vra:worktype[@extent='product'][@rules]">
<assert id="FPAimages-rule5-test2" test="@refid = ('FPAMainCollection', 'FPAValerieSteeleCuratedCollection')">The fpa collection product worktype must be one of the permitted values.</assert>
</rule>
</pattern>
<pattern id="FPADescriptionWorktype">
<!-- Rules for other selected FPA works -->
<rule id="FPADescriptionWorktype-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work[vra:dateSet/vra:notes[@extent='exclude']][vra:worktypeSet/vra:worktype[@extent='category'] = 'Designers']">
<assert id="FPADescriptionWorktype-rule1-test1" test="vra:descriptionSet/vra:description[@extent='filter1']">An FPA image which has a category and is excluded from the default timeline (ie it is for Designers timeline) must have a description[@extent=filter1]</assert>
</rule>
<rule id="FPADescriptionWorktype-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpa']]/vra:work/vra:worktypeSet/vra:worktype[@extent='category']">
<assert id="FPADescriptionWorktype-rule2-test1" test=". = ('Designers', 'Designer and Brand Events', 'Models', 'Art and Music', 'Film and Television', 'Historical and Cultural Events')">An FPA work with worktype[@extent=category] must have a value of: 'Designers', 'Designer and Brand Events', 'Models', 'Art and Music', 'Film and Television', 'Historical and Cultural Events'</assert>
</rule>
</pattern>
<pattern id="FPArelatedImages">
<!-- FPA images also need to indicate which are related -->
<rule id="FPArelatedImages-rule1" context="vra:image[vra:worktypeSet/vra:worktype[@extent='size'][. = 'fullSize']]">
<assert id="FPArelatedImages-rule1-test1" test="vra:relationSet/vra:relation[@type='sourceFor'][. = 'preview']">A fullSize image must also have a related preview image</assert>
<assert id="FPArelatedImages-rule1-test2" test="vra:relationSet/vra:relation[@type='sourceFor'][. = 'thumbnail']">A fullSize image must also have a related thumbnail image</assert>
</rule>
<rule id="FPArelatedImages-rule2" context="vra:image[vra:worktypeSet/vra:worktype[@extent='size'][. = 'preview']]">
<assert id="FPArelatedImages-rule2-test1" test="vra:relationSet/vra:relation[@type='derivedFrom']">A preview image must also have a derivedFrom related image</assert>
</rule>
<rule id="FPArelatedImages-rule3" context="vra:image[vra:worktypeSet/vra:worktype[@extent='size'][. = 'thumbnail']]">
<assert id="FPArelatedImages-rule3-test1" test="vra:relationSet/vra:relation[@type='derivedFrom']">A thumbnail image must also have a derivedFrom related image</assert>
</rule>
<rule id="FPArelatedImages-rule4" context="vra:relation[not(@rules='personRelationship') and not(@rules='docID') and not(@rules='ancillaryID') and not(@rules='isbn') and not(@rules='museum') and not(@rules='exhibition') and not(@rules='timeline') and not(@rules='doi') and not (@rules='buyLink')]">
<!-- XXX handle this so it works for images etc where relids is required, but not for items -->
<assert id="FPArelatedImages-rule4-test1" test="@relids">A relation element must have @relids (unless it is a person relationship, when it needs to have @rules=personRelationship)</assert>
</rule>
<rule id="FPArelatedImages-rule5" context="vra:relation[@rules='personRelationship']">
<assert id="FPArelatedImages-rule5-test1" test="@type='relatedTo'">A relation element with @rules=personRelationship must have @type=relatedTo</assert>
<assert id="FPArelatedImages-rule5-test2" test="@refid">A relation element with @rules=personRelationship must have @refid</assert>
<assert id="FPArelatedImages-rule5-test3" test="@extent">A relation element with @rules=personRelationship must have @extent</assert>
</rule>
<rule id="FPArelatedImages-rule6" context="vra:relation[@rules='museum']">
<assert id="FPArelatedImages-rule6-test1" test="@type='relatedTo'">A relation element with @rules=museum must have @type=relatedTo</assert>
<assert id="FPArelatedImages-rule6-test2" test="@refid">A relation element with @rules=museum must have @refid</assert>
<assert id="FPArelatedImages-rule6-test3" test="ancestor::vra:collection/vra:worktypeSet/vra:worktype = 'exhibitionImages'">A relation element with @rules=museum can only occur in a exhibitionImages collection</assert>
</rule>
<rule id="FPArelatedImages-rule7" context="vra:relation[@rules='exhibition']">
<assert id="FPArelatedImages-rule7-test1" test="@type='relatedTo'">A relation element with @rules=exhibition must have @type=relatedTo</assert>
<assert id="FPArelatedImages-rule7-test2" test="@refid">A relation element with @rules=exhibition must have @refid</assert>
<assert id="FPArelatedImages-rule7-test3" test="ancestor::vra:collection/vra:worktypeSet/vra:worktype = 'museumImages'">A relation element with @rules=exhibition can only occur in a museumImages collection</assert>
</rule>
<rule id="FPArelatedImages-rule8" context="vra:relation[@rules='timeline']">
<assert id="FPArelatedImages-rule8-test1" test="@refid">A relation element with @rules=timeline must have @refid</assert>
<assert id="FPArelatedImages-rule8-test2" test="(ancestor::vra:collection/vra:worktypeSet/vra:worktype = 'museumImages') or (ancestor::vra:collection/vra:worktypeSet/vra:worktype = 'exhibitionImages') or (ancestor::vra:collection/vra:worktypeSet/vra:worktype = 'fpa')">A relation element with @rules=timeline can only occur in a fpa, museumImages or a exhibitionImages collection</assert>
</rule>
</pattern>
<pattern id="personRelationship">
<!-- person relationship types -->
<rule id="personRelationship-rule1" context="vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationship-rule1-test1" test=". = ('makerOrDesignerOf', 'representedIn', 'photographerOf', 'subjectOf', 'authorOf', 'editorOf', 'curatorOf', 'madeOrDesignedBy', 'representationOf', 'photographedBy', 'contentAbout', 'authoredBy', 'editedBy', 'curatedBy')">A relation element with @rules=personRelationship must have correct value of @extent</assert>
</rule>
</pattern>
<pattern id="personRelationshipDetailed">
<!-- More specific person relationship tests -->
<!-- Museum images -->
<rule id="personRelationshipDetailed-rule1" context="vra:collection[vra:worktypeSet/vra:worktype = 'museumImages']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule1-test1" test=". = 'madeOrDesignedBy'">A person relation for a work in a museum collection must have @extent=madeOrDesignedBy</assert>
</rule>
<!-- FPA -->
<rule id="personRelationshipDetailed-rule2" context="vra:collection[vra:worktypeSet/vra:worktype = 'fpa']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule2-test1" test=". = ('madeOrDesignedBy', 'photographedBy', 'representationOf')">A person relation for a work in FPA must have @extent=madeOrDesignedBy or photographedBy or representationOf</assert>
</rule>
<rule id="personRelationshipDetailed-rule3" context="vra:collection[vra:worktypeSet/vra:worktype = 'fpa']//vra:image/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule3-test1" test=". = ('photographedBy', 'representationOf')">A person relation for an image in FPA must have @extent=photographedBy or representationOf</assert>
</rule>
<!-- FPA articles -->
<rule id="personRelationshipDetailed-rule4" context="vra:collection[vra:worktypeSet/vra:worktype = 'fpaArticles']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule4-test1" test=". = ('authoredBy', 'contentAbout')">A person relation for a work in FPA must have @extent=authoredBy or contentAbout</assert>
</rule>
<!-- Books -->
<rule id="personRelationshipDetailed-rule5" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule5-test1" test=". = ('authoredBy', 'editedBy', 'contentAbout')">A person relation for a work in a book must have @extent=authoredBy or editedBy or contentAbout</assert>
</rule>
<rule id="personRelationshipDetailed-rule6" context="vra:collection[vra:worktypeSet/vra:worktype = 'books']//vra:item/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule6-test1" test=". = ('authoredBy', 'contentAbout')">A person relation for an item in a book must have @extent=authoredBy or contentAbout</assert>
</rule>
<!-- Ancillaries -->
<rule id="personRelationshipDetailed-rule7" context="vra:collection[vra:worktypeSet/vra:worktype = 'ancillaries']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule7-test1" test=". = ('authoredBy', 'editedBy', 'contentAbout')">A person relation for a work in an ancillary collection must have @extent=authoredBy or editedBy or contentAbout</assert>
</rule>
<!-- Exhibition Images -->
<rule id="personRelationshipDetailed-rule8" context="vra:collection[vra:worktypeSet/vra:worktype = 'exhibitionImages']/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule8-test1" test=". = ('curatedBy', 'contentAbout')">A person relation for an exhibition collection must have @extent=curatedBy or contentAbout</assert>
</rule>
<rule id="personRelationshipDetailed-rule9" context="vra:collection[vra:worktypeSet/vra:worktype = 'exhibitionImages']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule9-test1" test=". = 'madeOrDesignedBy'">A person relation for an image in a exhibition collection must have @extent=madeOrDesignedBy</assert>
</rule>
<!-- Journals -->
<rule id="personRelationshipDetailed-rule10" context="vra:collection[vra:worktypeSet/vra:worktype = 'journal']/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule10-test1" test=". = 'editedBy'">A person relation for a journal collection must have @extent=editedBy</assert>
</rule>
<rule id="personRelationshipDetailed-rule11" context="vra:collection[vra:worktypeSet/vra:worktype = 'journal']/vra:work/vra:relationSet/vra:relation[@rules='personRelationship']/@extent">
<assert id="personRelationshipDetailed-rule11-test1" test=". = ('authoredBy', 'contentAbout')">A person relation for a work in a journal collection must have @extent=authoredBy or contentAbout</assert>
</rule>
</pattern>
<pattern id="FPArefid">
<!-- Rules for FPA Articles -->
<rule id="FPArefid-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work/vra:item">
<assert id="FPArefid-rule1-test1" test="@refid">An FPA article must have a @refid, which is the ID of the target document</assert>
<assert id="FPArefid-rule1-test2" test="vra:worktypeSet/vra:worktype[@extent='type']">An FPA Article must have a worktype[@extent=type]</assert>
</rule>
</pattern>
<pattern id="earliestLatestDatesFormat">
<!-- Ensure the ealiest and latest dates are in yyyy-mm-dd form -->
<rule id="earliestLatestDatesFormat-rule1" context="vra:earliestDate[starts-with(.,'-')] | vra:latestDate[starts-with(.,'-')]">
<assert id="earliestLatestDatesFormat-rule1-test1" test="string-length(.) ge 11">The <name/> must be at least 11 characters long if it starts with '-'</assert>
<assert id="earliestLatestDatesFormat-rule1-test2" test="substring(substring-after(substring-after(., '-'), '-'),1,2) = ('01','02','03','04','05','06','07','08','09','10','11','12')">A <name/> must be in the format (-)yyyy-mm-dd and the mm component must be one of 01 to 12 (ie a month)</assert>
<assert id="earliestLatestDatesFormat-rule1-test3" test="substring(substring-after(substring-after(., '-'), '-'),4,2) = ('01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31')">A <name/> must be in the format (-)yyyy-mm-dd and the dd component must be one of 01 to 31 (ie a day)</assert>
</rule>
<rule id="earliestLatestDatesFormat-rule2" context="vra:earliestDate | vra:latestDate">
<assert id="earliestLatestDatesFormat-rule2-test1" test="string-length(.)=10">The <name/> must be ten characters long</assert>
<assert id="earliestLatestDatesFormat-rule2-test2" test="substring(.,6,2) = ('01','02','03','04','05','06','07','08','09','10','11','12')">The sixth and seventh digits in <name/> must be one of 01 to 12 (ie a month) [see <value-of select="ancestor::element()[@id][1]/@id"/>]</assert>
<assert id="earliestLatestDatesFormat-rule2-test3" test="substring(.,9,2) = ('01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31')">The ninth and tenth digits in <name/> must be one of 01 to 31 (ie a day)</assert>
</rule>
</pattern>
<pattern id="validDates">
<!-- Valid dates -->
<rule id="validDates-rule1" context="vra:earliestDate | vra:latestDate">
<assert id="validDates-rule1-test1" test=". castable as xs:date">The <name/> is not a valid date</assert>
</rule>
</pattern>
<pattern id="rightsTypes">
<!-- rights types -->
<rule id="rightsTypes-rule1" context="vra:rights">
<assert id="rightsTypes-rule1-test1" test="@type"><name/> must have a @type attribute</assert>
<let name="allowed-values" value="('copyrighted', 'publicDomain', 'other', 'territorial', 'performance')"/>
<assert id="rightsTypes-rule1-test2" test="@type = $allowed-values"><name/>/@type must have a value of <value-of select="$allowed-values"/></assert>
</rule>
</pattern>
<pattern id="rightsOther">
<!-- other rights -->
<rule id="rightsOther-rule1" context="vra:rights[@type='other']">
<assert id="rightsOther-rule1-test1" test="@extent">If <name/>[@type=other] there must be a @extent as well</assert>
<assert id="rightsOther-rule1-test2" test="@extent='online' or @extent='print' or @extent='accessRights' ">@extent must take one of the values: online, print, accessRights</assert>
</rule>
</pattern>
<pattern id="rights">
<!-- constraints for various other rights situations -->
<rule id="rights-rule1" context="vra:rights[@type='other'][@extent='online'] | vra:rights[@type='other'][@extent='print']">
<assert id="rights-rule1-test1" test="vra:text = ('no', 'yes')">If <name/>[@extent=online|print] text must be either yes or no</assert>
</rule>
<rule id="rights-rule2" context="vra:rights[@type='other'][@extent='accessRights']">
<assert id="rights-rule2-test1" test="vra:text = ('instructorsOnly', 'students', 'free')">If <name/>[@extent=accessRights] text must be either instructorsOnly or students or free</assert>
</rule>
</pattern>
<pattern id="imageRefid">
<!-- images and items must have @refid -->
<rule id="imageRefid-rule1" context="vra:image | vra:work/vra:item[not(ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'timeline'])]">
<assert id="imageRefid-rule1-test1" test="@refid"><name/> must have @refid</assert>
</rule>
<!-- @refids -->
<!-- In the context of certain specific Bmy uses of elements with @refid, @rules is not required. Otherwise, @rules is normally expected on any element with @refid, to define the type of identifier referenced by @refid. -->
<rule id="imageRefid-rule2" context="element()[@refid][not(self::vra:term)][not(self::vra:relation[@extent = ('docID', 'playerID')])][not(self::vra:name[@type = 'geographic'][@vocab = 'geoLocation'])]">
<assert id="imageRefid-rule2-test1" test="@rules">A <name/> with @refid must have @rules</assert>
</rule>
</pattern>
<pattern id="ancillariesID">
<!-- More relation constraints -->
<rule id="ancillariesID-rule1" context="vra:work/vra:item[ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'ancillaries']]/vra:relationSet/vra:relation[matches(@refid, '^b-978\d\d\d\d\d\d\d\d\d\d')][not(@rules='ancillaryID')]">
<assert id="ancillariesID-rule1-test1" test="@rules='docID'">A <name/> in ancillaries with @refid starting with b-nnnnnnnnnnnnn must have @rules=docID</assert>
</rule>
</pattern>
<pattern id="quiz">
<!-- Quiz ancillary relationship constraints -->
<rule id="quiz-rule1" context="vra:work/vra:item[ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'ancillaries']][vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Quiz']">
<assert id="quiz-rule1-test1" test="vra:relationSet/vra:relation[@rules='ancillaryID']">A <name/> in a Quiz ancillary must have a relation[@rules=ancillaryID]</assert>
</rule>
<rule id="quiz-rule2" context="vra:work/vra:item[ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'ancillaries']][vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Flashcards']">
<assert id="quiz-rule2-test1" test="vra:relationSet/vra:relation[@rules='ancillaryID']">A <name/> in a Flashcards ancillary must have a relation[@rules=ancillaryID]</assert>
</rule>
<rule id="quiz-rule3" context="vra:relation[@rules='ancillaryID']">
<assert id="quiz-rule3-test1" test="@refid">A relation with @rules=ancillaryID must have @refid (which must match the ID of the target MCQ)</assert>
</rule>
</pattern>
<pattern id="imageSubjectRules">
<!-- @rules -->
<rule id="imageSubjectRules-rule1" context="vra:image | vra:subject">
<assert id="imageSubjectRules-rule1-test1" test="@rules">A <name/> must have @rules</assert>
</rule>
<!-- All images need to indicate the MIME type -->
<rule id="imageSubjectRules-rule2" context="vra:image/@rules">
<assert id="imageSubjectRules-rule2-test1" test=". = ('image/jpeg', 'image/png', 'image/gif')">An image/@rules must be a valid image MIME type</assert>
</rule>
<!-- Image @refid must be a valid file extension -->
<rule id="imageSubjectRules-rule3" context="vra:image/@refid">
<assert id="imageSubjectRules-rule3-test1" test="ends-with(.,'.jpg') or ends-with(.,'.jpeg') or ends-with(.,'.JPG') or ends-with(.,'.JPEG') or ends-with(.,'.png') or ends-with(.,'.PNG') or ends-with(.,'.gif') or ends-with(.,'.GIF')">A image/@refid must end with a valid file extension</assert>
</rule>
<!-- subject rules value -->
<rule id="imageSubjectRules-rule4" context="vra:subject/@rules">
<assert id="imageSubjectRules-rule4-test1" test=". = $allowed-taxonomies">A subject/@rules must be one of: <value-of select="string-join($allowed-taxonomies, ', ')"/></assert>
</rule>
<!-- book rules values -->
<rule id="imageSubjectRules-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/@rules | vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]//vra:item/@rules">
<assert id="imageSubjectRules-rule5-test1" test=". = 'docID'">A work or item @rules in a book must = docIDs</assert>
</rule>
<!-- ancillary rules values -->
<rule id="imageSubjectRules-rule6" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]//vra:item[not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Quiz') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Video Quiz') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Flashcards') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Image Flashcards')]/@rules">
<assert id="imageSubjectRules-rule6-test1" test=". = ('application/msword', 'application/excel', 'application/pdf', 'application/mspowerpoint', 'application/powerpoint', 'image/png', 'image/gif', 'image/jpeg', 'video/mpeg', 'application/zip', 'application/xml', 'application/postscript', 'application/octet-stream')">An item @rules in an ancillary must equal the MIME type of the object</assert>
</rule>
<!-- journal rules values -->
<rule id="imageSubjectRules-rule7" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]">
<assert id="imageSubjectRules-rule7-test1" test="@rules">A journal collection must have @rules</assert>
<assert id="imageSubjectRules-rule7-test2" test="@rules='issn'">A journal collection's @rules must = issn</assert>
<assert id="imageSubjectRules-rule7-test3" test="string-length(@refid)=9">A journal collection's @refid must be nine characters long</assert>
<assert id="imageSubjectRules-rule7-test4" test="matches(@refid, '^[0-9][0-9][0-9][0-9]\-[0-9][0-9][0-9][0-9]$')">A journal collection's @refid must be of the form nnnn-nnnn</assert>
</rule>
<rule id="imageSubjectRules-rule8" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work">
<assert id="imageSubjectRules-rule8-test1" test="@rules">A journal work must have @rules</assert>
<assert id="imageSubjectRules-rule8-test2" test="@rules='doi'">A journal collection's @rules must = doi</assert>
<assert id="imageSubjectRules-rule8-test3" test="matches(@refid, '^10\.[0-9][0-9][0-9][0-9]/[0-z]+')">A journal work's @refid must be of the form 10.nnnn/d+</assert>
</rule>
<rule id="imageSubjectRules-rule9" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work/vra:locationSet/vra:location[@type='other']/@extent">
<assert id="imageSubjectRules-rule9-test1" test="matches(., 'volumeNumber') or matches(., 'issueNumber') or matches(., 'startPage') or matches(., 'endPage')">Journal location must include the citation components: volumeNumber, issueNumber, startPage, endPage</assert>
</rule>
</pattern>
<pattern id="preferredImage">
<!-- Preferred images -->
<rule id="preferredImage-rule1" context="vra:work[vra:image]">
<assert id="preferredImage-rule1-test1" test="count(vra:image[@pref='true']) = 1">A work with images must contain one and only one preferred image</assert>
</rule>
<rule id="preferredImage-rule2" context="vra:image[ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'fpa']][@pref='true']">
<assert id="preferredImage-rule2-test1" test="vra:worktypeSet/vra:worktype[@extent='size'] = 'fullSize'">An FPA preferred image must be fullsize</assert>
</rule>
</pattern>
<pattern id="workItemWorktypeSet">
<!-- items and images -->
<rule id="workItemWorktypeSet-rule1" context="vra:image | vra:item">
<!-- worktype is required -->
<assert id="workItemWorktypeSet-rule1-test1" test="vra:worktypeSet">An <name/> must contain a workTypeSet</assert>
</rule>
<rule id="workItemWorktypeSet-rule2" context="vra:image/vra:worktypeSet | vra:item/vra:worktypeSet[not(ancestor::vra:collection[vra:worktypeSet/vra:worktype = 'timeline'])]">
<!-- and it must have a source so we know where to get the file from -->
<assert id="workItemWorktypeSet-rule2-test1" test="vra:worktype/@extent='source'">An <name path="parent::node()"/>/<name/> must contain a worktype[@extent=source]</assert>
</rule>
</pattern>
<pattern id="imageNotSequence">
<!-- Images -->
<rule id="imageNotSequence-rule1" context="vra:image">
<report id="imageNotSequence-rule1-test1" test="@sequence">An <name/> is not permitted to have @sequence</report>
</rule>
<rule id="imageNotSequence-rule2" context="vra:item[@sequence]">
<assert id="imageNotSequence-rule2-test1" test="@sequence/ancestor::vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries' or . = 'audio' or . = 'video']]">The @sequence attribute is only permitted on item within an ancillary, audio or video collection</assert>
</rule>
<rule id="imageNotSequence-rule3" context="vra:item[@extent]">
<assert id="imageNotSequence-rule3-test1" test="@extent = 'searchable'">Item extent must be 'searchable'</assert>
</rule>
</pattern>
<pattern id="contentStorage">
<!-- Content locations
There are three scenarios:
1. content is on a remote location ie rackspace
2. books are loaded as regular DocBook zipped packages
3. other images (eg museum collections) are added to a defined location on the Propagator server via ftp
-->
<rule id="contentStorage-rule1" context="vra:image/vra:worktypeSet/vra:worktype[@extent='source']">
<assert id="contentStorage-rule1-test1" test=". = ('rackspace', 'local') ">An image/workTypeSet/worktype[@extent=source] must have value = local | rackspace</assert>
</rule>
<!-- This is just in case at some point we also allow ancillaries to be included in the book data package or elsewhere so we'd need to differentiate them -->
<rule id="contentStorage-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work/vra:worktypeSet/vra:worktype[@extent='source']">
<assert id="contentStorage-rule2-test1" test=". = 'ancillaryBundle'">An ancillary work/workTypeSet/worktype[@extent=source] must have value = ancillaryBundle</assert>
</rule>
<!-- book worktypes -->
<rule id="contentStorage-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:item/vra:worktypeSet/vra:worktype[@extent='source']">
<assert id="contentStorage-rule3-test1" test=". = 'docbook'">An item/workTypeSet/worktype[@extent=source] must have value = docbook</assert>
</rule>
</pattern>
<pattern id="bookContentType">
<!-- Book-specific rules - general -->
<rule id="bookContentType-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work">
<assert id="bookContentType-rule1-test1" test="vra:worktypeSet/vra:worktype[@extent='contentType']">A book work must contain a worktype[@extent='contentType']</assert>
</rule>
<rule id="bookContentType-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype">
<assert id="bookContentType-rule2-test1" test="@extent='product' or @extent='contentType'">A book worktype must be 'product' or 'contentType'</assert>
</rule>
</pattern>
<pattern id="bookContentType-prop">
<!-- Book-specific rules for BFC on Propagator -->
<rule id="bookContentType-prop-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work | vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work">
<assert id="bookContentType-prop-rule1-test1" test="vra:worktypeSet/vra:worktype[@extent='product']">A book or fpaArticles work must contain a worktype[@extent='product']</assert>
<assert id="bookContentType-prop-rule1-test2" test="vra:worktypeSet/vra:worktype[@extent='contentType']">A book or fpaArticles work must contain a worktype[@extent='contentType']</assert>
</rule>
<rule id="bookContentType-prop-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype">
<assert id="bookContentType-prop-rule2-test1" test="@extent='product' or @extent='contentType' or @extent='bfcDisplayType' or @extent='imageSearchType'">A book worktype must be 'product' or 'contentType' or 'bfcDisplayType' 'or imageSearchType'</assert>
</rule>
<rule id="bookContentType-prop-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work/vra:worktypeSet/vra:worktype">
<assert id="bookContentType-prop-rule3-test1" test="@extent='product' or @extent='contentType'">A fpaArticles worktype must be 'product' or 'contentType'</assert>
</rule>
</pattern>
<pattern id="bookWorkTypeOptions">
<!-- More book-specific rules -->
<rule id="bookWorkTypeOptions-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype[@extent='product']">
<assert id="bookWorkTypeOptions-rule1-test1" test=". = ('BergFashionLibrary', 'FairchildBooks', 'FairchildBooksLibrary', 'LaunchCollection')">A book's product worktype must be one of BergFashionLibrary, FairchildBooks, FairchildBooksLibrary, LaunchCollection</assert>
</rule>
<rule id="bookWorkTypeOptions-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype[@extent='contentType']">
<assert id="bookWorkTypeOptions-rule2-test1" test=". = ('monograph', 'reference', 'dictionary')">A book's contentType worktype must be one of monograph or reference or dictionary</assert>
</rule>
<rule id="bookWorkTypeOptions-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype[@extent='bfcDisplayType'] | vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work/vra:worktypeSet/vra:worktype[@extent='bfcDisplayType']">
<assert id="bookWorkTypeOptions-rule3-test1" test=". = ('toc', 'noToc')">A book's or fpaArticle's bfcDisplayType worktype must be one of toc or noToc</assert>
</rule>
<rule id="bookWorkTypeOptions-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'books']]/vra:work/vra:worktypeSet/vra:worktype[@extent='imageSearchType']">
<assert id="bookWorkTypeOptions-rule4-test1" test=". = 'searchImages'">A book's imageSearchType worktype must be searchImages</assert>
</rule>
<rule id="bookWorkTypeOptions-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work/vra:worktypeSet/vra:worktype[@extent='product']">
<assert id="bookWorkTypeOptions-rule5-test1" test=". = 'FashionPhotographyArchive'">A fpaArticle's product worktype must be FashionPhotographyArchive</assert>
</rule>
<rule id="bookWorkTypeOptions-rule6" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'fpaArticles']]/vra:work/vra:worktypeSet/vra:worktype[@extent='contentType']">
<assert id="bookWorkTypeOptions-rule6-test1" test=". = ('reference', 'dictionary')">A fpaArticle's contentType worktype must be one of reference or dictionary</assert>
</rule>
</pattern>
<pattern id="ancillariesTests">
<!-- Ancillary-specific rules -->
<rule id="ancillariesTests-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work">
<assert id="ancillariesTests-rule1-test1" test="vra:relationSet/vra:relation">An ancillaries work must have a relation</assert>
</rule>
<rule id="ancillariesTests-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work//vra:relation">
<assert id="ancillariesTests-rule2-test1" test="@refid">An ancillaries work//relation must have @refid</assert>
<!--<assert test="@relids">An ancillaries work//relation must have @relids</assert>-->
</rule>
<rule id="ancillariesTests-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work/vra:item[not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Quiz') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Video Quiz') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Flashcards') and not(vra:worktypeSet/vra:worktype[@extent='ancillaryType'] = 'Visual Flashcards')]">
<assert id="ancillariesTests-rule3-test1" test="vra:measurementsSet/vra:measurements">An ancillaries item must have measurements (filesize)</assert>
<assert id="ancillariesTests-rule3-test2" test="matches(vra:measurementsSet/vra:measurements, '\d+')">An ancillaries item//measurements must contain a numeric value</assert>
<assert id="ancillariesTests-rule3-test3" test="vra:measurementsSet/vra:measurements[@type='fileSize']">An ancillaries item must have a measurements (with @type=fileSize)</assert>
<assert id="ancillariesTests-rule3-test4" test="vra:measurementsSet/vra:measurements[@type='fileSize'][@unit='byte']">An ancillaries item must have a measurements (with @unit=byte)</assert>
</rule>
</pattern>
<pattern id="ancillaryTypes">
<!-- Ancillary worktypes -->
<rule id="ancillaryTypes-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work/vra:item/vra:worktypeSet/vra:worktype[@extent='source']">
<assert id="ancillaryTypes-rule1-test1" test=". = 'ancillaryZip'">An ancillary worktype[@extent=source] must equal ancillaryZip</assert>
</rule>
<rule id="ancillaryTypes-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'ancillaries']]/vra:work/vra:item/vra:worktypeSet/vra:worktype[@extent='ancillaryType']">
<assert id="ancillaryTypes-rule2-test1" test=". = ('Quiz', 'Video Quiz', 'Flashcards', 'Visual Flashcards', 'Instructor''s Materials', 'Document', 'Exercises &amp; Assignments', 'Lecture Slides / Power Point', 'Spreadsheet', 'Test Bank', 'Image Bank', 'Video', 'Media', 'Timeline', 'Image', 'Graphic', 'Form', 'Instructions', 'Software', 'List', 'Case Study')">An ancillary worktype[@extent=ancillaryType] must equal one of the defined values</assert>
</rule>
</pattern>
<pattern id="journals">
<!-- Journal-specific rules -->
<rule id="journals-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work">
<assert id="journals-rule1-test1" test="vra:descriptionSet">A journal work must have a description</assert>
<assert id="journals-rule1-test2" test="vra:locationSet/vra:location/@extent='startPage'">A journal work must have a startPage</assert>
<assert id="journals-rule1-test3" test="vra:locationSet/vra:location/@extent='endPage'">A journal work must have a endPage</assert>
</rule>
<rule id="journals-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work/vra:locationSet/vra:location[@extent='startPage'] | vra:collection[vra:worktypeSet/vra:worktype[. = 'journal']]/vra:work/vra:locationSet/vra:location[@extent='endPage']">
<assert id="journals-rule2-test1" test="vra:refid[@type='other']">A journal work must have a start or end page value</assert>
</rule>
</pattern>
<pattern id="timeline">
<!-- Timeline rules -->
<rule id="timeline-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:worktypeSet/vra:worktype[@extent='product']">
<assert id="timeline-rule1-test1" test=". = ('BergFashionLibrary', 'FashionPhotographyArchive', 'FairchildBooksLibrary', 'LaunchCollection')">A timeline product worktype must be one of BergFashionLibrary, FashionPhotographyArchive, FairchildBooksLibrary, LaunchCollection</assert>
</rule>
<rule id="timeline-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work">
<assert id="timeline-rule2-test1" test="vra:worktypeSet/vra:worktype[@extent='type']">A timeline work//worktype must have @extent=type</assert>
<assert id="timeline-rule2-test2" test="vra:worktypeSet/vra:worktype[@extent='group']" role="warning">A timeline work//worktype may need a @extent=group</assert>
</rule>
<rule id="timeline-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work/vra:worktypeSet/vra:worktype">
<assert id="timeline-rule3-test1" test="@extent='type' or @extent='group'">A timeline work//worktype must have @extent=type|group</assert>
</rule>
<rule id="timeline-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work/vra:item">
<assert id="timeline-rule4-test1" test="vra:worktypeSet/vra:worktype[@extent='category']">A timeline work/item must contain a worktype[@extent='category']</assert>
<assert id="timeline-rule4-test2" test="vra:titleSet/vra:title">A timeline work/item must contain a title</assert>
<assert id="timeline-rule4-test3" test="vra:dateSet/vra:display">A timeline work/item must contain a dateSet/display</assert>
<assert id="timeline-rule4-test4" test="vra:dateSet/vra:date">A timeline work/item must contain a dateSet/date</assert>
</rule>
<rule id="timeline-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work/vra:item/vra:worktypeSet/vra:worktype">
<assert id="timeline-rule5-test1" test="@extent='category'">A timeline work/item//worktype must have @extent=category</assert>
</rule>
<!--<rule context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work/vra:item/vra:sourceSet/vra:source">
<assert test="@extent='chapter'">A timeline source/@extent must have value of 'chapter'</assert>
<assert test="@extent">A timeline source must have @extent</assert>
<assert test="@rules='docID'">A timeline source must have @rules=docID</assert>
<assert test="@rules">A timeline source must have @rules</assert>
<assert test="@refid">A timeline source must have @refid</assert>
</rule>-->
<rule id="timeline-rule6" context="vra:date[vra:earliestDate[not(starts-with(., '-'))]][vra:latestDate[not(starts-with(., '-'))]]">
<assert id="timeline-rule6-test1" test="vra:latestDate &gt;= vra:earliestDate">earliestDate must be less than latestDate [see <value-of select="ancestor::element()[@id][1]/@id"/>]</assert></rule>
</pattern>
<pattern id="earliestLatestDateTest">
<!-- Fiddly date testing -->
<rule id="earliestLatestDateTest-rule1" context="vra:date[starts-with(vra:earliestDate, '-') and starts-with(vra:latestDate, '-')]">
<let name="earliestDateYear" value="number(substring-before(substring-after(vra:earliestDate, '-'), '-'))"/>
<let name="latestDateYear" value="number(substring-before(substring-after(vra:latestDate, '-'), '-'))"/>
<assert id="earliestLatestDateTest-rule1-test1" test="($earliestDateYear ge $latestDateYear)">For BC Dates integer value of earliestDate (<value-of select="$earliestDateYear"/>) must be greater than or equal to latestDate (<value-of select="$latestDateYear"/>)</assert></rule></pattern>
<pattern id="timelineChaptertimePeriod">
<!-- More timeline tests -->
<rule id="timelineChaptertimePeriod-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'timeline']]/vra:work/vra:worktypeSet/vra:worktype[@extent='type']">
<assert id="timelineChaptertimePeriod-rule1-test1" test=". = ('chapter', 'timePeriod')">A timeline work//worktype[@extent=type] must contain 'chapter' or 'timePeriod'</assert>
</rule>
</pattern>
<pattern id="distinctSubjects">
<!-- Each classification code should be distinct within the set of codes -->
<rule id="distinctSubjects-rule1" context="vra:subjectSet/vra:subject[@rules=$allowed-taxonomies and vra:term]">
<let name="dupe" value="bmy:duplicate-value(vra:term/@refid)"/>
<report id="distinctSubjects-rule1-test1" test="$dupe">Each taxonomy term should be distinct within the containing subjectSet: duplicate value term/@refid='<value-of select="$dupe"/>'</report>
</rule>
</pattern>
<pattern id="magesSameISBN">
<!-- in a collection of book images, all images should refer to the same ISBN -->
<rule id="magesSameISBN-rule1" context="vra:collection[vra:worktypeSet[vra:worktype[.='book-images']]]/vra:work/vra:item">
<let name="isbn" value="substring-before(substring-after(@refid, 'b-'), '-')"/>
<assert id="magesSameISBN-rule1-test1" test="$isbn eq parent::vra:work/@refid">work and item ISBNs should match: found '<value-of select="$isbn"/>'; expected '<value-of select="parent::vra:work/@refid"/>'</assert>
</rule>
</pattern>
<pattern id="videos">
<title>Videos</title>
<!--<rule id="videos-rule1" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work">
<assert id="videos-rule1-test1" test="vra:worktypeSet/vra:worktype[@extent='product']">A videos work must have a product worktype</assert>
</rule>-->
<rule id="videos-rule2" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work/vra:worktypeSet/vra:worktype[@extent='product']">
<assert id="videos-rule2-test1" test=". = 'FashionPhotographyArchive'">A videos product worktype must be FashionPhotographyArchive</assert>
</rule>
<rule id="videos-rule3" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work/vra:item">
<assert id="videos-rule3-test1" test="@refid">A videos item must have a @refid</assert>
<assert id="videos-rule3-test2" test="@rules">A videos item must have a @rules</assert>
<assert id="videos-rule3-test3" test="vra:relationSet/vra:relation">A video item must have a relation</assert>
<assert id="videos-rule7-test1" test="@refid = ./vra:relationSet/vra:relation[@type='brightcove']/@refid">A videos item's @refid must match the filename in the corresponding video relation</assert>
<assert id="videos-rule8-test1" test="@rules='video/mp4'">A videos item's @rules must be video/mp4, since the target of @refid is an mp4 file</assert>
</rule>
<rule id="videos-rule4" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work/vra:item/vra:worktypeSet">
<assert id="videos-rule4-test1" test="vra:worktype[@extent='source']">A videos item must contain a source worktype</assert>
<assert id="videos-rule4-test2" test="vra:worktype[@extent='ancillaryType']">A videos item must contain an ancillaryType worktype</assert>
</rule>
<rule id="videos-rule5" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work/vra:item/vra:worktypeSet/vra:worktype[@extent='ancillaryType']">
<assert id="videos-rule5-test1" test=".='runtime'">A video item must have an ancillaryType worktype = 'runtime'</assert>
</rule>
<rule id="videos-rule6" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'video']]/vra:work/vra:item/vra:worktypeSet/vra:worktype[@extent='source']">
<assert id="videos-rule6-test1" test=".='files'">A videos item must have a source worktype = 'files'</assert>
</rule>
<rule id="videos-rule9" role="warn" context="//vra:collection[vra:worktypeSet/vra:worktype[. = 'video']][vra:titleSet/vra:title[.!='video']]">
<assert id="videos-rule9-test1" test="./vra:relationSet/vra:relation[@rules='projectID']/@type='3play'">Please check if a 3Play project ID is needed at collection level.</assert>
</rule>
</pattern>
<!-- work out which taxonomy to use based on the value of the first group of facets -->
<let name="taxonomy-name" value="(//vra:subjectSet/vra:subject)[1]/@rules"/>
<!-- taxonomy lookup -->
<let name="taxonomy-lookup" value="resolve-uri('../../SKOS/taxonomy-lookup.xml')"/>
<let name="taxonomy-loc" value="if (doc-available($taxonomy-lookup)) then resolve-uri(concat('../../SKOS/',doc($taxonomy-lookup)//taxonomy[@vraId = $taxonomy-name]/node())) else error(xs:QName('err'), 'Taxonomy lookup not found. Expected at ../../SKOS/taxonomy-lookup.xml')"/>
<let name="taxonomy" value="if(doc-available($taxonomy-loc)) then doc($taxonomy-loc) else error(xs:QName('err'), concat('This taxonomy is not present in the lookup: ', $taxonomy-name) )"/>
<let name="taxonomy-codes" value="$taxonomy//facet/@xml:id | $taxonomy//id[@type='Taxonomy_BFL'] | $taxonomy//id[@type='Taxonomy_FPA']"/>
<!-- all classification codes should be present in the XML representation of the taxonomy -->
<pattern id="taxonomyLookup">
<rule id="taxonomyLookup-rule1" context="vra:subjectSet/vra:subject[@rules = $taxonomy-name]/vra:term">
<assert id="taxonomyLookup-rule1-test1" test="@refid = $taxonomy-codes">taxonomy code <value-of select="@refid"/> not found in taxonomy</assert>
</rule>
</pattern>
<!-- warn if no buy links are present for a book -->
<pattern id="buy-link">
<rule id="missing-buy-link" context="vra:collection[vra:worktypeSet/vra:worktype[. = ('books', 'monographAsReference')]]/vra:work/vra:relationSet">
<assert test="vra:relation[@rules eq 'buyLink']" role="warning" id="check-buy-link">This work has no buy link: is this correct?</assert>
</rule>
</pattern>
<!-- performance rights -->
<pattern id="performance-rights">
<rule id="performance-rights-types" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'play']]/vra:work/vra:rightsSet/vra:rights[@type='performance']">
<let name="allowed-values" value="('professional', 'amateur', 'outOfCopyright', 'all')"/>
<assert test="@extent = $allowed-values" id='performance-rights-allowed-values'>rights[@type='performance']/@extent must be one of the following: <value-of select="string-join($allowed-values, ', ')"/></assert>
</rule>
</pattern>
<pattern id="performance-rights-ooc">
<rule id="performance-rights-ooc-rule" context="vra:collection[vra:worktypeSet/vra:worktype[. = 'play']]/vra:work/vra:rightsSet/vra:rights[@type='performance'][@extent='outOfCopyright']">
<assert test="not(@rules or @refid)" id="ooc-rules-refid">rights[@type='performance'][@extent='outOfCopyright'] must not have @rules or @refid</assert>
</rule>
</pattern>
<!-- CUSTOM UTILITY FUNCTIONS -->
<!-- for the sequence passed in, return the first duplicate value (if any) occurring
within that sequence -->
<xsl:function name="bmy:duplicate-value" as="item()*">
<xsl:param name="seq" as="item()+"/>
<xsl:sequence select="$seq[index-of($seq,.)[2]]"/>
</xsl:function>
</schema>