GEPS 011: Tagging - Add XML import and export

svn: r15938
This commit is contained in:
Nick Hall 2010-09-29 22:25:52 +00:00
parent 8b675ed1f2
commit 6b6da7dafe
6 changed files with 364 additions and 160 deletions

View File

@ -24,45 +24,46 @@
--> -->
<!-- <!--
This is the Document Type Definition file for v1.3.0 This is the Document Type Definition file for v1.4.0
of the GRAMPS XML genealogy data format. of the GRAMPS XML genealogy data format.
Please use the following formal public identifier to identify it: Please use the following formal public identifier to identify it:
"-//GRAMPS//DTD GRAMPS XML V1.3.0//EN" "-//GRAMPS//DTD GRAMPS XML V1.4.0//EN"
For example: For example:
<!DOCTYPE database PUBLIC "-//GRAMPS//DTD GRAMPS XML V1.3.0//EN" <!DOCTYPE database PUBLIC "-//GRAMPS//DTD GRAMPS XML V1.4.0//EN"
"http://gramps-project.org/xml/1.3.0/grampsxml.dtd" "http://gramps-project.org/xml/1.4.0/grampsxml.dtd"
[...]> [...]>
--> -->
<!-- <!--
DATABASE DATABASE
Defines an XML document which is a <database> consisting of a Defines an XML document which is a <database> consisting of a
header Information about the "owner" of the database header Information about the "owner" of the database
people people
families families
sources sources
places places
objects objects
repositories repositories
notes notes
bookmarks tags
bookmarks
--> -->
<!ELEMENT database (header, name-formats?, events?, people?, families?, <!ELEMENT database (header, name-formats?, tags?, events?, people?, families?,
sources?, places?, objects?, repositories?, notes?, sources?, places?, objects?, repositories?, notes?,
bookmarks?,namemaps?)> bookmarks?, namemaps?)>
<!ATTLIST database xmlns CDATA #FIXED "http://gramps-project.org/xml/1.3.0/"> <!ATTLIST database xmlns CDATA #FIXED "http://gramps-project.org/xml/1.4.0/">
<!-- ************************************************************ <!-- ************************************************************
HEADER HEADER
A <header> consists of <created> (information about this A <header> consists of <created> (information about this
genealogical database) and <researcher> (information about the genealogical database) and <researcher> (information about the
person who created this genealogical database) person who created this genealogical database)
--> -->
<!ELEMENT header (created, researcher?, mediapath?)> <!ELEMENT header (created, researcher?, mediapath?)>
@ -86,7 +87,7 @@ HEADER
<!ELEMENT mediapath (#PCDATA)> <!ELEMENT mediapath (#PCDATA)>
<!-- ************************************************************ <!-- ************************************************************
PEOPLE PEOPLE
--> -->
@ -98,12 +99,12 @@ PEOPLE
<!ELEMENT person (gender, name*, nick?, eventref*, lds_ord*, <!ELEMENT person (gender, name*, nick?, eventref*, lds_ord*,
objref*, address*, attribute*, url*, childof*, objref*, address*, attribute*, url*, childof*,
parentin*, personref*, noteref*, sourceref*)> parentin*, personref*, noteref*, sourceref*, tagref*)>
<!ATTLIST person <!ATTLIST person
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
@ -112,8 +113,8 @@ GENDER has values of M, F, or U.
--> -->
<!ELEMENT gender (#PCDATA)> <!ELEMENT gender (#PCDATA)>
<!ELEMENT name (first?,call?,last?,suffix?,patronymic?,title?, <!ELEMENT name (first?, call?, last?, suffix?, patronymic?, title?,
(daterange|datespan|dateval|datestr)?,noteref*,sourceref*)> (daterange|datespan|dateval|datestr)?, noteref*, sourceref*)>
<!ATTLIST name <!ATTLIST name
alt (0|1) #IMPLIED alt (0|1) #IMPLIED
type CDATA #IMPLIED type CDATA #IMPLIED
@ -133,7 +134,7 @@ GENDER has values of M, F, or U.
<!ELEMENT patronymic (#PCDATA)> <!ELEMENT patronymic (#PCDATA)>
<!ELEMENT title (#PCDATA)> <!ELEMENT title (#PCDATA)>
<!ELEMENT nick (#PCDATA)> <!ELEMENT nick (#PCDATA)>
<!ELEMENT childof EMPTY> <!ELEMENT childof EMPTY>
<!ATTLIST childof hlink IDREF #REQUIRED <!ATTLIST childof hlink IDREF #REQUIRED
@ -142,15 +143,16 @@ GENDER has values of M, F, or U.
<!ELEMENT parentin EMPTY> <!ELEMENT parentin EMPTY>
<!ATTLIST parentin hlink IDREF #REQUIRED> <!ATTLIST parentin hlink IDREF #REQUIRED>
<!ELEMENT personref (sourceref*,noteref*)> <!ELEMENT personref (sourceref*, noteref*)>
<!ATTLIST personref <!ATTLIST personref
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
rel CDATA #REQUIRED rel CDATA #REQUIRED
> >
<!ELEMENT address ((daterange|datespan|dateval|datestr)?,street?,city?, <!ELEMENT address ((daterange|datespan|dateval|datestr)?, street?, city?,
county?,state?,country?,postal?,phone?,noteref*,sourceref*)> county?, state?, country?, postal?, phone?, noteref*,
sourceref*)>
<!ATTLIST address priv (0|1) #IMPLIED> <!ATTLIST address priv (0|1) #IMPLIED>
<!ELEMENT street (#PCDATA)> <!ELEMENT street (#PCDATA)>
@ -162,20 +164,20 @@ GENDER has values of M, F, or U.
<!ELEMENT phone (#PCDATA)> <!ELEMENT phone (#PCDATA)>
<!-- ************************************************************ <!-- ************************************************************
FAMILY FAMILY
An element to record information about a family. An element to record information about a family.
--> -->
<!ELEMENT families (family)*> <!ELEMENT families (family)*>
<!ELEMENT family (rel?,father?,mother?,eventref*,lds_ord*, <!ELEMENT family (rel?, father?, mother?, eventref*, lds_ord*,
objref*,childref*,attribute*,noteref*,sourceref*)> objref*, childref*, attribute*, noteref*, sourceref*)>
<!ATTLIST family <!ATTLIST family
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
@ -187,10 +189,10 @@ FAMILY
<!ELEMENT childref (sourceref*,noteref*)> <!ELEMENT childref (sourceref*,noteref*)>
<!ATTLIST childref <!ATTLIST childref
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
mrel (None|Birth|Adopted|Stepchild|Sponsored|Foster|Other|Unknown) #IMPLIED mrel (None|Birth|Adopted|Stepchild|Sponsored|Foster|Other|Unknown) #IMPLIED
frel (None|Birth|Adopted|Stepchild|Sponsored|Foster|Other|Unknown) #IMPLIED frel (None|Birth|Adopted|Stepchild|Sponsored|Foster|Other|Unknown) #IMPLIED
> >
<!ELEMENT type (#PCDATA)> <!ELEMENT type (#PCDATA)>
@ -198,33 +200,33 @@ FAMILY
<!ELEMENT rel EMPTY> <!ELEMENT rel EMPTY>
<!ATTLIST rel type CDATA #REQUIRED> <!ATTLIST rel type CDATA #REQUIRED>
<!-- ************************************************************ <!-- ************************************************************
EVENT EVENT
--> -->
<!ELEMENT events (event)*> <!ELEMENT events (event)*>
<!ELEMENT event (type?,(daterange|datespan|dateval|datestr)?,place?,cause?, <!ELEMENT event (type?, (daterange|datespan|dateval|datestr)?, place?, cause?,
description?,attribute*,noteref*,sourceref*,objref*)> description?, attribute*, noteref*, sourceref*, objref*)>
<!ATTLIST event <!ATTLIST event
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
<!-- ************************************************************ <!-- ************************************************************
SOURCES SOURCES
--> -->
<!ELEMENT sources (source)*> <!ELEMENT sources (source)*>
<!ELEMENT source (stitle?,sauthor?,spubinfo?,sabbrev?, <!ELEMENT source (stitle?, sauthor?, spubinfo?, sabbrev?,
noteref*,objref*,data_item*,reporef*)> noteref*, objref*, data_item*, reporef*)>
<!ATTLIST source <!ATTLIST source
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
<!ELEMENT stitle (#PCDATA)> <!ELEMENT stitle (#PCDATA)>
@ -232,22 +234,23 @@ SOURCES
<!ELEMENT spubinfo (#PCDATA)> <!ELEMENT spubinfo (#PCDATA)>
<!ELEMENT sabbrev (#PCDATA)> <!ELEMENT sabbrev (#PCDATA)>
<!-- ************************************************************ <!-- ************************************************************
PLACES PLACES
--> -->
<!ELEMENT places (placeobj)*> <!ELEMENT places (placeobj)*>
<!ELEMENT placeobj (ptitle?,coord?,location*,objref*,url*,noteref*,sourceref*)> <!ELEMENT placeobj (ptitle?, coord?, location*, objref*, url*, noteref*,
sourceref*)>
<!ATTLIST placeobj <!ATTLIST placeobj
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
<!ELEMENT ptitle (#PCDATA)> <!ELEMENT ptitle (#PCDATA)>
<!ELEMENT coord EMPTY> <!ELEMENT coord EMPTY>
<!ATTLIST coord <!ATTLIST coord
@ -257,29 +260,29 @@ PLACES
<!ELEMENT location EMPTY> <!ELEMENT location EMPTY>
<!ATTLIST location <!ATTLIST location
street CDATA #IMPLIED street CDATA #IMPLIED
city CDATA #IMPLIED city CDATA #IMPLIED
parish CDATA #IMPLIED parish CDATA #IMPLIED
county CDATA #IMPLIED county CDATA #IMPLIED
state CDATA #IMPLIED state CDATA #IMPLIED
country CDATA #IMPLIED country CDATA #IMPLIED
postal CDATA #IMPLIED postal CDATA #IMPLIED
phone CDATA #IMPLIED phone CDATA #IMPLIED
> >
<!-- ************************************************************ <!-- ************************************************************
OBJECTS OBJECTS
--> -->
<!ELEMENT objects (object)*> <!ELEMENT objects (object)*>
<!ELEMENT object (file,attribute*,noteref*, <!ELEMENT object (file, attribute*, noteref*,
(daterange|datespan|dateval|datestr)?,sourceref*)> (daterange|datespan|dateval|datestr)?, sourceref*)>
<!ATTLIST object <!ATTLIST object
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
@ -290,56 +293,71 @@ OBJECTS
description CDATA #REQUIRED description CDATA #REQUIRED
> >
<!-- ************************************************************ <!-- ************************************************************
REPOSITORIES REPOSITORIES
--> -->
<!ELEMENT repositories (repository)*> <!ELEMENT repositories (repository)*>
<!ELEMENT repository (rname,type,address*,url*,noteref*)> <!ELEMENT repository (rname, type, address*, url*, noteref*)>
<!ATTLIST repository <!ATTLIST repository
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
> >
<!ELEMENT rname (#PCDATA)> <!ELEMENT rname (#PCDATA)>
<!-- ************************************************************ <!-- ************************************************************
NOTES NOTES
--> -->
<!ELEMENT notes (note)*> <!ELEMENT notes (note)*>
<!ELEMENT note (text,tag*)> <!ELEMENT note (text, style*)>
<!ATTLIST note <!ATTLIST note
id CDATA #REQUIRED id CDATA #REQUIRED
handle ID #REQUIRED handle ID #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
marker CDATA #IMPLIED marker CDATA #IMPLIED
change CDATA #REQUIRED change CDATA #REQUIRED
format (0|1) #IMPLIED format (0|1) #IMPLIED
type CDATA #REQUIRED type CDATA #REQUIRED
> >
<!ELEMENT text (#PCDATA)> <!ELEMENT text (#PCDATA)>
<!ELEMENT tag (range+)> <!ELEMENT style (range+)>
<!ATTLIST tag <!ATTLIST style
name (bold|italic|underline|fontface|fontsize| name (bold|italic|underline|fontface|fontsize|
fontcolor|highlight|superscript) #REQUIRED fontcolor|highlight|superscript) #REQUIRED
value CDATA #IMPLIED value CDATA #IMPLIED
> >
<!ELEMENT range EMPTY> <!ELEMENT range EMPTY>
<!ATTLIST range <!ATTLIST range
start CDATA #REQUIRED start CDATA #REQUIRED
end CDATA #REQUIRED end CDATA #REQUIRED
> >
<!-- ************************************************************ <!-- ************************************************************
TAGS
-->
<!ELEMENT tags (tag)*>
<!ELEMENT tag EMPTY>
<!ATTLIST tag
handle ID #REQUIRED
name CDATA #REQUIRED
color CDATA #REQUIRED
priority CDATA #REQUIRED
change CDATA #REQUIRED
>
<!-- ************************************************************
BOOKMARKS BOOKMARKS
--> -->
@ -350,7 +368,7 @@ BOOKMARKS
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
> >
<!-- ************************************************************ <!-- ************************************************************
NAME MAPS NAME MAPS
--> -->
<!ELEMENT namemaps (map)*> <!ELEMENT namemaps (map)*>
@ -361,7 +379,7 @@ NAME MAPS
value CDATA #REQUIRED value CDATA #REQUIRED
> >
<!-- ************************************************************ <!-- ************************************************************
NAME FORMATS NAME FORMATS
--> -->
@ -374,10 +392,10 @@ NAME FORMATS
active (0|1) #IMPLIED active (0|1) #IMPLIED
> >
<!-- ************************************************************ <!-- ************************************************************
SHARED ELEMENTS SHARED ELEMENTS
--> -->
<!ELEMENT daterange EMPTY> <!ELEMENT daterange EMPTY>
<!ATTLIST daterange <!ATTLIST daterange
start CDATA #REQUIRED start CDATA #REQUIRED
stop CDATA #REQUIRED stop CDATA #REQUIRED
@ -387,17 +405,17 @@ SHARED ELEMENTS
newyear CDATA #IMPLIED newyear CDATA #IMPLIED
> >
<!ELEMENT datespan EMPTY> <!ELEMENT datespan EMPTY>
<!ATTLIST datespan <!ATTLIST datespan
start CDATA #REQUIRED start CDATA #REQUIRED
stop CDATA #REQUIRED stop CDATA #REQUIRED
quality (estimated|calculated) #IMPLIED quality (estimated|calculated) #IMPLIED
cformat CDATA #IMPLIED cformat CDATA #IMPLIED
dualdated (0|1) #IMPLIED dualdated (0|1) #IMPLIED
newyear CDATA #IMPLIED newyear CDATA #IMPLIED
> >
<!ELEMENT dateval EMPTY> <!ELEMENT dateval EMPTY>
<!ATTLIST dateval <!ATTLIST dateval
val CDATA #REQUIRED val CDATA #REQUIRED
type (before|after|about) #IMPLIED type (before|after|about) #IMPLIED
@ -407,17 +425,17 @@ SHARED ELEMENTS
newyear CDATA #IMPLIED newyear CDATA #IMPLIED
> >
<!ELEMENT datestr EMPTY> <!ELEMENT datestr EMPTY>
<!ATTLIST datestr val CDATA #REQUIRED> <!ATTLIST datestr val CDATA #REQUIRED>
<!ELEMENT sourceref (spage?,noteref*,(daterange|datespan|dateval|datestr)?)> <!ELEMENT sourceref (spage? ,noteref*, (daterange|datespan|dateval|datestr)?)>
<!ATTLIST sourceref <!ATTLIST sourceref
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
conf CDATA #IMPLIED conf CDATA #IMPLIED
> >
<!ELEMENT eventref (attribute*,noteref*)> <!ELEMENT eventref (attribute*, noteref*)>
<!ATTLIST eventref <!ATTLIST eventref
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
@ -437,13 +455,18 @@ SHARED ELEMENTS
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
> >
<!ELEMENT spage (#PCDATA)> <!ELEMENT tagref EMPTY>
<!ATTLIST tagref
hlink IDREF #REQUIRED
>
<!ELEMENT attribute (sourceref*,noteref*)> <!ELEMENT spage (#PCDATA)>
<!ELEMENT attribute (sourceref*, noteref*)>
<!ATTLIST attribute <!ATTLIST attribute
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
type CDATA #REQUIRED type CDATA #REQUIRED
value CDATA #REQUIRED value CDATA #REQUIRED
> >
<!ELEMENT place EMPTY> <!ELEMENT place EMPTY>
@ -454,13 +477,13 @@ SHARED ELEMENTS
<!ELEMENT url EMPTY> <!ELEMENT url EMPTY>
<!ATTLIST url <!ATTLIST url
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
type CDATA #IMPLIED type CDATA #IMPLIED
href CDATA #REQUIRED href CDATA #REQUIRED
description CDATA #IMPLIED description CDATA #IMPLIED
> >
<!ELEMENT objref (region?,attribute*,sourceref*,noteref*)> <!ELEMENT objref (region?, attribute*, sourceref*, noteref*)>
<!ATTLIST objref <!ATTLIST objref
hlink IDREF #REQUIRED hlink IDREF #REQUIRED
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
@ -480,10 +503,10 @@ SHARED ELEMENTS
value CDATA #REQUIRED value CDATA #REQUIRED
> >
<!ELEMENT lds_ord ((daterange|datespan|dateval|datestr)?,temple?,place?, <!ELEMENT lds_ord ((daterange|datespan|dateval|datestr)?, temple?, place?,
status?,sealed_to?,noteref*,sourceref*)> status?, sealed_to?, noteref*, sourceref*)>
<!ATTLIST lds_ord <!ATTLIST lds_ord
priv (0|1) #IMPLIED priv (0|1) #IMPLIED
type CDATA #REQUIRED type CDATA #REQUIRED
> >

View File

@ -31,7 +31,7 @@
<grammar <grammar
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
ns="http://gramps-project.org/xml/1.3.0/" ns="http://gramps-project.org/xml/1.4.0/"
xmlns="http://relaxng.org/ns/structure/1.0"> xmlns="http://relaxng.org/ns/structure/1.0">
<start><element name="database"> <start><element name="database">
@ -53,6 +53,12 @@
</element></zeroOrMore> </element></zeroOrMore>
</element></optional> </element></optional>
<optional><element name="tags">
<zeroOrMore><element name="tag">
<ref name="tag-content"/>
</element></zeroOrMore>
</element></optional>
<optional><element name="events"> <optional><element name="events">
<zeroOrMore><element name="event"> <zeroOrMore><element name="event">
<ref name="event-content"/> <ref name="event-content"/>
@ -129,15 +135,19 @@
<optional><element name="resemail"><text/></element></optional> <optional><element name="resemail"><text/></element></optional>
</define> </define>
<define name="primary-object"> <define name="table-object">
<attribute name="id"><text/></attribute>
<attribute name="handle"><data type="ID"/></attribute> <attribute name="handle"><data type="ID"/></attribute>
<attribute name="change"><text/></attribute>
</define>
<define name="primary-object">
<ref name="table-object"/>
<attribute name="id"><text/></attribute>
<optional><attribute name="priv"><choice> <optional><attribute name="priv"><choice>
<value>0</value> <value>0</value>
<value>1</value> <value>1</value>
</choice></attribute></optional> </choice></attribute></optional>
<optional><attribute name="marker"><text/></attribute></optional> <optional><attribute name="marker"><text/></attribute></optional>
<attribute name="change"><text/></attribute>
</define> </define>
<define name="person-content"> <define name="person-content">
@ -188,6 +198,9 @@
<zeroOrMore><element name="sourceref"> <zeroOrMore><element name="sourceref">
<ref name="sourceref-content"/> <ref name="sourceref-content"/>
</element></zeroOrMore> </element></zeroOrMore>
<zeroOrMore><element name="tagref">
<ref name="tagref-content"/>
</element></zeroOrMore>
</define> </define>
<define name="child-rel"> <define name="child-rel">
@ -268,10 +281,10 @@
<value>calculated</value> <value>calculated</value>
</choice></attribute></optional> </choice></attribute></optional>
<optional><attribute name="cformat"><text/></attribute></optional> <optional><attribute name="cformat"><text/></attribute></optional>
<optional><attribute name="dualdated"> <optional><attribute name="dualdated">
<choice><value>0</value><value>1</value></choice> <choice><value>0</value><value>1</value></choice>
</attribute></optional> </attribute></optional>
<optional><attribute name="newyear"><text/></attribute></optional> <optional><attribute name="newyear"><text/></attribute></optional>
</element> </element>
<element name="datespan"> <element name="datespan">
<attribute name="start"><text/></attribute> <attribute name="start"><text/></attribute>
@ -281,10 +294,10 @@
<value>calculated</value> <value>calculated</value>
</choice></attribute></optional> </choice></attribute></optional>
<optional><attribute name="cformat"><text/></attribute></optional> <optional><attribute name="cformat"><text/></attribute></optional>
<optional><attribute name="dualdated"> <optional><attribute name="dualdated">
<choice><value>0</value><value>1</value></choice> <choice><value>0</value><value>1</value></choice>
</attribute></optional> </attribute></optional>
<optional><attribute name="newyear"><text/></attribute></optional> <optional><attribute name="newyear"><text/></attribute></optional>
</element> </element>
<element name="dateval"> <element name="dateval">
<attribute name="val"><text/></attribute> <attribute name="val"><text/></attribute>
@ -298,10 +311,10 @@
<value>estimated</value> <value>estimated</value>
<value>calculated</value> <value>calculated</value>
</choice></attribute></optional> </choice></attribute></optional>
<optional><attribute name="dualdated"> <optional><attribute name="dualdated">
<choice><value>0</value><value>1</value></choice> <choice><value>0</value><value>1</value></choice>
</attribute></optional> </attribute></optional>
<optional><attribute name="newyear"><text/></attribute></optional> <optional><attribute name="newyear"><text/></attribute></optional>
</element> </element>
<element name="datestr"> <element name="datestr">
<attribute name="val"><text/></attribute> <attribute name="val"><text/></attribute>
@ -504,7 +517,7 @@
<define name="styledtext"> <define name="styledtext">
<element name="text"><text/></element> <element name="text"><text/></element>
<zeroOrMore><element name="tag"> <zeroOrMore><element name="style">
<attribute name="name"><choice> <attribute name="name"><choice>
<value>bold</value> <value>bold</value>
<value>italic</value> <value>italic</value>
@ -650,4 +663,15 @@
<text/> <text/>
</define> </define>
<define name="tagref-content">
<attribute name="hlink"><data type="IDREF"/></attribute>
</define>
<define name="tag-content">
<ref name="table-object"/>
<attribute name="name"><text/></attribute>
<attribute name="color"><text/></attribute>
<attribute name="priority"><data type="integer"/></attribute>
</define>
</grammar> </grammar>

View File

@ -198,9 +198,10 @@ class GrampsXmlWriter(UpdateCallback):
repo_len = self.db.get_number_of_repositories() repo_len = self.db.get_number_of_repositories()
obj_len = self.db.get_number_of_media_objects() obj_len = self.db.get_number_of_media_objects()
note_len = self.db.get_number_of_notes() note_len = self.db.get_number_of_notes()
tag_len = self.db.get_number_of_tags()
total_steps = person_len + family_len + event_len + source_len \ total_steps = person_len + family_len + event_len + source_len \
+ place_len + repo_len + obj_len + note_len + place_len + repo_len + obj_len + note_len + tag_len
self.set_total(total_steps) self.set_total(total_steps)
@ -233,6 +234,16 @@ class GrampsXmlWriter(UpdateCallback):
# by the time we get to person's names # by the time we get to person's names
self.write_name_formats() self.write_name_formats()
# Write table objects
if tag_len > 0:
self.g.write(" <tags>\n")
for key in self.db.get_tag_handles():
tag = self.db.get_tag_from_handle(key)
self.write_tag(tag, 2)
self.update()
self.g.write(" </tags>\n")
# Write primary objects
if event_len > 0: if event_len > 0:
self.g.write(" <events>\n") self.g.write(" <events>\n")
for handle in self.db.get_event_handles(): for handle in self.db.get_event_handles():
@ -384,6 +395,19 @@ class GrampsXmlWriter(UpdateCallback):
escxml(name), escxml(fmt_str), int(active)) ) escxml(name), escxml(fmt_str), int(active)) )
self.g.write(" </name-formats>\n") self.g.write(" </name-formats>\n")
def write_tag(self, tag, index=2):
"""
Write a tag definition.
"""
if not tag:
return
self.write_table_tag('tag', tag, index, close=False)
self.g.write(' name="%s"' % escxml(tag.get_name()))
self.g.write(' color="%s"' % tag.get_color())
self.g.write(' priority="%d"' % tag.get_priority())
self.g.write('/>\n')
def fix(self,line): def fix(self,line):
try: try:
l = unicode(line) l = unicode(line)
@ -426,7 +450,7 @@ class GrampsXmlWriter(UpdateCallback):
name = tag.name.xml_str() name = tag.name.xml_str()
value = tag.value value = tag.value
self.g.write(' ' * index + '<tag name="%s"' % name) self.g.write(' ' * index + '<style name="%s"' % name)
if value: if value:
self.g.write(' value="%s"' % escxml(str(value))) self.g.write(' value="%s"' % escxml(str(value)))
self.g.write('>\n') self.g.write('>\n')
@ -435,7 +459,7 @@ class GrampsXmlWriter(UpdateCallback):
self.g.write((' ' * (index + 1)) + self.g.write((' ' * (index + 1)) +
'<range start="%d" end="%d"/>\n' % (start, end)) '<range start="%d" end="%d"/>\n' % (start, end))
self.g.write(' ' * index + '</tag>\n') self.g.write(' ' * index + '</style>\n')
def write_text(self, val, text, indent=0): def write_text(self, val, text, indent=0):
if not text: if not text:
@ -488,6 +512,10 @@ class GrampsXmlWriter(UpdateCallback):
for s in person.get_source_references(): for s in person.get_source_references():
self.dump_source_ref(s,index+2) self.dump_source_ref(s,index+2)
for tag_handle in person.get_tag_list():
self.write_ref("tagref", tag_handle, index+1)
self.g.write("%s</person>\n" % sp) self.g.write("%s</person>\n" % sp)
def write_family(self,family,index=1): def write_family(self,family,index=1):
@ -711,22 +739,37 @@ class GrampsXmlWriter(UpdateCallback):
self.g.write('%s<%s hlink="_%s"%s%s>\n' self.g.write('%s<%s hlink="_%s"%s%s>\n'
% (sp,tagname, handle,extra_text,close_tag)) % (sp,tagname, handle,extra_text,close_tag))
def write_primary_tag(self,tagname, obj,index=1,close=True): def write_primary_tag(self, tagname, obj, index=1, close=True):
"""
Write the tag attributes common to all primary objects.
"""
if not obj: if not obj:
return return
sp = " "*index
marker = obj.get_marker().xml_str() marker = obj.get_marker().xml_str()
if marker: if marker:
marker_text = ' marker="%s"' % escxml(marker) marker_text = ' marker="%s"' % escxml(marker)
else: else:
marker_text = '' marker_text = ''
priv_text = conf_priv(obj) priv_text = conf_priv(obj)
change_text = ' change="%d"' % obj.get_change_time() id_text = ' id="%s"' % escxml(obj.gramps_id)
handle_id_text = ' id="%s" handle="_%s"' % (escxml(obj.gramps_id), obj.handle)
obj_text = '%s<%s' % (sp,tagname)
self.g.write(obj_text + handle_id_text + priv_text + marker_text + self.write_table_tag(tagname, obj, index, False)
change_text) self.g.write(id_text + priv_text + marker_text)
if close:
self.g.write('>\n')
def write_table_tag(self, tagname, obj, index=1, close=True):
"""
Write the tag attributes common to all table objects.
"""
if not obj:
return
sp = " " * index
change_text = ' change="%d"' % obj.get_change_time()
handle_text = ' handle="_%s"' % obj.get_handle()
obj_text = '%s<%s' % (sp, tagname)
self.g.write(obj_text + handle_text + change_text)
if close: if close:
self.g.write('>\n') self.g.write('>\n')

View File

@ -46,7 +46,8 @@ import Utils
import DateHandler import DateHandler
from gen.display.name import displayer as name_displayer from gen.display.name import displayer as name_displayer
from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY,
MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, NOTE_KEY) MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, NOTE_KEY,
TAG_KEY)
from gen.updatecallback import UpdateCallback from gen.updatecallback import UpdateCallback
import const import const
import libgrampsxml import libgrampsxml
@ -198,7 +199,7 @@ class ImportInfo(object):
Class object that can hold information about the import Class object that can hold information about the import
""" """
keyorder = [PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY, keyorder = [PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY,
PLACE_KEY, REPOSITORY_KEY, NOTE_KEY] PLACE_KEY, REPOSITORY_KEY, NOTE_KEY, TAG_KEY]
key2data = { key2data = {
PERSON_KEY : 0, PERSON_KEY : 0,
FAMILY_KEY : 1, FAMILY_KEY : 1,
@ -207,7 +208,8 @@ class ImportInfo(object):
MEDIA_KEY: 4, MEDIA_KEY: 4,
PLACE_KEY: 5, PLACE_KEY: 5,
REPOSITORY_KEY: 6, REPOSITORY_KEY: 6,
NOTE_KEY: 7 NOTE_KEY: 7,
TAG_KEY: 8
} }
def __init__(self): def __init__(self):
@ -216,8 +218,8 @@ class ImportInfo(object):
This creates the datastructures to hold info This creates the datastructures to hold info
""" """
self.data_mergeoverwrite = [{},{},{},{},{},{},{},{}] self.data_mergeoverwrite = [{}] * 9
self.data_newobject = [0,0,0,0,0,0,0,0] self.data_newobject = [0] * 9
self.data_relpath = False self.data_relpath = False
@ -257,6 +259,8 @@ class ImportInfo(object):
return _(" Repository %(id)s\n") % {'id': obj.gramps_id} return _(" Repository %(id)s\n") % {'id': obj.gramps_id}
elif key == NOTE_KEY: elif key == NOTE_KEY:
return _(" Note %(id)s\n") % {'id': obj.gramps_id} return _(" Note %(id)s\n") % {'id': obj.gramps_id}
elif key == TAG_KEY:
return _(" Tag %(name)s\n") % {'name': obj.get_name()}
def info_text(self): def info_text(self):
""" """
@ -271,6 +275,7 @@ class ImportInfo(object):
PLACE_KEY : _(' Places: %d\n'), PLACE_KEY : _(' Places: %d\n'),
REPOSITORY_KEY : _(' Repositories: %d\n'), REPOSITORY_KEY : _(' Repositories: %d\n'),
NOTE_KEY : _(' Notes: %d\n'), NOTE_KEY : _(' Notes: %d\n'),
TAG_KEY : _(' Tags: %d\n'),
} }
txt = _("Number of new objects imported:\n") txt = _("Number of new objects imported:\n")
for key in self.keyorder: for key in self.keyorder:
@ -373,6 +378,7 @@ class GrampsParser(UpdateCallback):
self.in_note = 0 self.in_note = 0
self.in_stext = 0 self.in_stext = 0
self.in_scomments = 0 self.in_scomments = 0
self.note = None
self.note_text = None self.note_text = None
self.note_tags = [] self.note_tags = []
self.in_witness = False self.in_witness = False
@ -529,8 +535,11 @@ class GrampsParser(UpdateCallback):
"stext": (None, self.stop_stext), "stext": (None, self.stop_stext),
"stitle": (None, self.stop_stitle), "stitle": (None, self.stop_stitle),
"street": (None, self.stop_street), "street": (None, self.stop_street),
"style": (self.start_style, None),
"suffix": (None, self.stop_suffix), "suffix": (None, self.stop_suffix),
"tag": (self.start_tag, None), "tag": (self.start_tag, None),
"tagref": (self.start_tagref, None),
"tags": (None, None),
"text": (None, self.stop_text), "text": (None, self.stop_text),
"title": (None, self.stop_title), "title": (None, self.stop_title),
"url": (self.start_url, None), "url": (self.start_url, None),
@ -1321,7 +1330,10 @@ class GrampsParser(UpdateCallback):
self.name.prefix = attrs.get('prefix', '') self.name.prefix = attrs.get('prefix', '')
self.name.group_as = attrs.get('group', '') self.name.group_as = attrs.get('group', '')
def start_tag(self, attrs): def start_style(self, attrs):
"""
Styled text tag in notes (v1.4.0 onwards).
"""
tagtype = gen.lib.StyledTextTagType() tagtype = gen.lib.StyledTextTagType()
tagtype.set_from_xml_str(attrs['name']) tagtype.set_from_xml_str(attrs['name'])
@ -1335,6 +1347,42 @@ class GrampsParser(UpdateCallback):
self.note_tags.append(gen.lib.StyledTextTag(tagtype, tagvalue)) self.note_tags.append(gen.lib.StyledTextTag(tagtype, tagvalue))
def start_tag(self, attrs):
"""
Tag definition.
"""
if self.note is not None:
# Styled text tag in notes (prior to v1.4.0)
self.start_style(attrs)
return
# Tag defintion
self.tag, new = self.db.find_tag_from_handle(
attrs['handle'].replace('_', ''), self.trans)
if new:
#keep change time from xml file
self.tag.change = int(attrs.get('change', self.change))
self.info.add('new-object', TAG_KEY, self.tag)
else:
self.tag.change = self.change
self.info.add('merge-overwrite', TAG_KEY, self.tag)
self.tag.set_name(attrs['name'])
self.tag.set_color(attrs['color'])
self.tag.set_priority(int(attrs['priority']))
self.db.commit_tag(self.tag, self.trans, self.tag.get_change_time())
def start_tagref(self, attrs):
"""
Tag reference in a primary object.
"""
handle = attrs['hlink'].replace('_', '')
self.db.check_tag_from_handle(handle, self.trans)
if self.person:
self.person.add_tag(handle)
def start_range(self, attrs): def start_range(self, attrs):
self.note_tags[-1].ranges.append((int(attrs['start']), self.note_tags[-1].ranges.append((int(attrs['start']),
int(attrs['end']))) int(attrs['end'])))

View File

@ -35,5 +35,5 @@
# Public Constants # Public Constants
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
GRAMPS_XML_VERSION = "1.3.0" GRAMPS_XML_VERSION = "1.4.0"

View File

@ -29,8 +29,8 @@ Mixin for DbDir to enable find_from_handle and check_from_handle methods.
# Gramps Modules # Gramps Modules
# #
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
from gen.lib import (GenderStats, Person, Family, Event, Place, Source, from gen.lib import (Person, Family, Event, Place, Source,
MediaObject, Repository, Note) MediaObject, Repository, Note, Tag)
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# #
@ -50,10 +50,11 @@ class DbMixin(object):
where "database" is the object name of your instance of the gramps where "database" is the object name of your instance of the gramps
database. database.
""" """
def find_from_handle(self, handle, transaction, class_type, dmap, def __find_primary_from_handle(self, handle, transaction, class_type, dmap,
add_func): add_func):
""" """
Find a object of class_type in the database from the passed handle. Find a primary object of class_type in the database from the passed
handle.
If no object exists, a new object is added to the database. If no object exists, a new object is added to the database.
@ -74,14 +75,57 @@ class DbMixin(object):
add_func(obj, transaction) add_func(obj, transaction)
return obj, new return obj, new
def __check_from_handle(self, handle, transaction, class_type, dmap, def __find_table_from_handle(self, handle, transaction, class_type, dmap,
add_func):
"""
Find a table object of class_type in the database from the passed
handle.
If no object exists, a new object is added to the database.
@return: Returns a tuple, first the object, second a bool which is True
if the object is new
@rtype: tuple
"""
obj = class_type()
handle = str(handle)
if handle in dmap:
obj.unserialize(dmap.get(handle))
return obj, False
else:
obj.set_handle(handle)
add_func(obj, transaction)
return obj, True
def __check_primary_from_handle(self, handle, transaction, class_type, dmap,
add_func, set_gid=True): add_func, set_gid=True):
"""
Check whether a primary object of class_type with the passed handle
exists in the database.
If no such object exists, a new object is added to the database.
If set_gid then a new gramps_id is created, if not, None is used.
"""
handle = str(handle) handle = str(handle)
if handle not in dmap: if handle not in dmap:
obj = class_type() obj = class_type()
obj.set_handle(handle) obj.set_handle(handle)
add_func(obj, transaction, set_gid=set_gid) add_func(obj, transaction, set_gid=set_gid)
def __check_table_from_handle(self, handle, transaction, class_type, dmap,
add_func):
"""
Check whether a table object of class_type with the passed handle exists
in the database.
If no such object exists, a new object is added to the database.
"""
handle = str(handle)
if handle not in dmap:
obj = class_type()
obj.set_handle(handle)
add_func(obj, transaction)
def find_person_from_handle(self, handle, transaction): def find_person_from_handle(self, handle, transaction):
""" """
Find a Person in the database from the passed handle. Find a Person in the database from the passed handle.
@ -92,7 +136,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Person, return self.__find_primary_from_handle(handle, transaction, Person,
self.person_map, self.add_person) self.person_map, self.add_person)
def find_source_from_handle(self, handle, transaction): def find_source_from_handle(self, handle, transaction):
@ -105,7 +149,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Source, return self.__find_primary_from_handle(handle, transaction, Source,
self.source_map, self.add_source) self.source_map, self.add_source)
def find_event_from_handle(self, handle, transaction): def find_event_from_handle(self, handle, transaction):
@ -118,7 +162,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Event, return self.__find_primary_from_handle(handle, transaction, Event,
self.event_map, self.add_event) self.event_map, self.add_event)
def find_object_from_handle(self, handle, transaction): def find_object_from_handle(self, handle, transaction):
@ -131,7 +175,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, MediaObject, return self.__find_primary_from_handle(handle, transaction, MediaObject,
self.media_map, self.add_object) self.media_map, self.add_object)
def find_place_from_handle(self, handle, transaction): def find_place_from_handle(self, handle, transaction):
@ -144,7 +188,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Place, return self.__find_primary_from_handle(handle, transaction, Place,
self.place_map, self.add_place) self.place_map, self.add_place)
def find_family_from_handle(self, handle, transaction): def find_family_from_handle(self, handle, transaction):
@ -157,7 +201,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Family, return self.__find_primary_from_handle(handle, transaction, Family,
self.family_map, self.add_family) self.family_map, self.add_family)
def find_repository_from_handle(self, handle, transaction): def find_repository_from_handle(self, handle, transaction):
@ -170,7 +214,7 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Repository, return self.__find_primary_from_handle(handle, transaction, Repository,
self.repository_map, self.add_repository) self.repository_map, self.add_repository)
def find_note_from_handle(self, handle, transaction): def find_note_from_handle(self, handle, transaction):
@ -183,9 +227,22 @@ class DbMixin(object):
if the object is new if the object is new
@rtype: tuple @rtype: tuple
""" """
return self.find_from_handle(handle, transaction, Note, return self.__find_primary_from_handle(handle, transaction, Note,
self.note_map, self.add_note) self.note_map, self.add_note)
def find_tag_from_handle(self, handle, transaction):
"""
Find a Tag in the database from the passed handle.
If no such Tag exists, a new Tag is added to the database.
@return: Returns a tuple, first the object, second a bool which is True
if the object is new
@rtype: tuple
"""
return self.__find_table_from_handle(handle, transaction, Tag,
self.tag_map, self.add_tag)
def check_person_from_handle(self, handle, transaction, set_gid=True): def check_person_from_handle(self, handle, transaction, set_gid=True):
""" """
Check whether a Person with the passed handle exists in the database. Check whether a Person with the passed handle exists in the database.
@ -193,7 +250,7 @@ class DbMixin(object):
If no such Person exists, a new Person is added to the database. If no such Person exists, a new Person is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Person, self.__check_primary_from_handle(handle, transaction, Person,
self.person_map, self.add_person, self.person_map, self.add_person,
set_gid = set_gid) set_gid = set_gid)
@ -204,7 +261,7 @@ class DbMixin(object):
If no such Source exists, a new Source is added to the database. If no such Source exists, a new Source is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Source, self.__check_primary_from_handle(handle, transaction, Source,
self.source_map, self.add_source, self.source_map, self.add_source,
set_gid=set_gid) set_gid=set_gid)
@ -215,7 +272,7 @@ class DbMixin(object):
If no such Event exists, a new Event is added to the database. If no such Event exists, a new Event is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Event, self.__check_primary_from_handle(handle, transaction, Event,
self.event_map, self.add_event, self.event_map, self.add_event,
set_gid=set_gid) set_gid=set_gid)
@ -228,7 +285,7 @@ class DbMixin(object):
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, MediaObject, self.__check_primary_from_handle(handle, transaction, MediaObject,
self.media_map, self.add_object, self.media_map, self.add_object,
set_gid=set_gid) set_gid=set_gid)
@ -239,7 +296,7 @@ class DbMixin(object):
If no such Place exists, a new Place is added to the database. If no such Place exists, a new Place is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Place, self.__check_primary_from_handle(handle, transaction, Place,
self.place_map, self.add_place, self.place_map, self.add_place,
set_gid=set_gid) set_gid=set_gid)
@ -250,7 +307,7 @@ class DbMixin(object):
If no such Family exists, a new Family is added to the database. If no such Family exists, a new Family is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Family, self.__check_primary_from_handle(handle, transaction, Family,
self.family_map, self.add_family, self.family_map, self.add_family,
set_gid=set_gid) set_gid=set_gid)
@ -262,7 +319,7 @@ class DbMixin(object):
If no such Repository exists, a new Repository is added to the database. If no such Repository exists, a new Repository is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Repository, self.__check_primary_from_handle(handle, transaction, Repository,
self.repository_map, self.add_repository, self.repository_map, self.add_repository,
set_gid=set_gid) set_gid=set_gid)
@ -273,6 +330,15 @@ class DbMixin(object):
If no such Note exists, a new Note is added to the database. If no such Note exists, a new Note is added to the database.
If set_gid then a new gramps_id is created, if not, None is used. If set_gid then a new gramps_id is created, if not, None is used.
""" """
self.__check_from_handle(handle, transaction, Note, self.__check_primary_from_handle(handle, transaction, Note,
self.note_map, self.add_note, self.note_map, self.add_note,
set_gid=set_gid) set_gid=set_gid)
def check_tag_from_handle(self, handle, transaction):
"""
Check whether a Tag with the passed handle exists in the database.
If no such Tag exists, a new Tag is added to the database.
"""
self.__check_table_from_handle(handle, transaction, Tag,
self.tag_map, self.add_tag)