163 lines
6.0 KiB
Python
163 lines
6.0 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2014 Entr'ouvert
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from django.utils.encoding import force_str
|
|
from quixote import get_publisher
|
|
|
|
from .misc import indent_xml, xml_node_text
|
|
from .storage import Equal, Or, StorableObject
|
|
|
|
|
|
class XmlStorableObject(StorableObject):
|
|
XML_NODES = []
|
|
|
|
@classmethod
|
|
def storage_load(cls, fd):
|
|
first_byte = fd.read(1)
|
|
fd.seek(0)
|
|
if first_byte == b'<':
|
|
return cls.import_from_xml(fd, include_id=True)
|
|
else:
|
|
obj = StorableObject.storage_load(fd)
|
|
obj._upgrade_must_store = True
|
|
return obj
|
|
|
|
@classmethod
|
|
def storage_dumps(cls, object):
|
|
return object.export_to_xml_string(include_id=True)
|
|
|
|
def export_to_xml(self, include_id=False):
|
|
charset = get_publisher().site_charset
|
|
root = ET.Element(self.xml_root_node)
|
|
if include_id and self.id:
|
|
root.attrib['id'] = str(self.id)
|
|
for attribute in self.XML_NODES:
|
|
attribute_name, attribute_type = attribute
|
|
if not getattr(self, attribute_name, None):
|
|
continue
|
|
element = ET.SubElement(root, attribute_name)
|
|
export_method = getattr(self, 'export_%s_to_xml' % attribute_type)
|
|
export_method(element, attribute_name, charset=charset, include_id=include_id)
|
|
return root
|
|
|
|
def export_str_to_xml(self, element, attribute_name, charset, **kwargs):
|
|
element.text = force_str(getattr(self, attribute_name), charset)
|
|
|
|
def export_int_to_xml(self, element, attribute_name, **kwargs):
|
|
element.text = str(getattr(self, attribute_name))
|
|
|
|
def export_bool_to_xml(self, element, attribute_name, **kwargs):
|
|
element.text = 'true' if getattr(self, attribute_name) else 'false'
|
|
|
|
def export_datetime_to_xml(self, element, attribute_name, **kwargs):
|
|
element.text = getattr(self, attribute_name).isoformat()
|
|
|
|
def export_str_list_to_xml(self, element, attribute_name, **kwargs):
|
|
for item in getattr(self, attribute_name, None) or []:
|
|
ET.SubElement(element, 'item').text = item
|
|
|
|
def export_to_xml_string(self, include_id=False):
|
|
x = self.export_to_xml(include_id=include_id)
|
|
indent_xml(x)
|
|
return ET.tostring(x)
|
|
|
|
def export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs):
|
|
for role in getattr(self, attribute_name, None) or []:
|
|
sub = ET.SubElement(element, 'role')
|
|
if include_id:
|
|
sub.attrib['role-id'] = role.id
|
|
sub.attrib['role-slug'] = role.slug
|
|
sub.text = role.name
|
|
|
|
@classmethod
|
|
def import_from_xml(cls, fd, charset=None, include_id=False):
|
|
try:
|
|
tree = ET.parse(fd)
|
|
except Exception:
|
|
raise ValueError()
|
|
return cls.import_from_xml_tree(tree, charset=charset, include_id=include_id)
|
|
|
|
@classmethod
|
|
def import_from_xml_tree(cls, tree, include_id=False, charset=None, **kwargs):
|
|
if charset is None:
|
|
charset = get_publisher().site_charset
|
|
obj = cls()
|
|
|
|
# if the tree we get is actually a ElementTree for real, we get its
|
|
# root element and go on happily.
|
|
if not ET.iselement(tree):
|
|
tree = tree.getroot()
|
|
|
|
if tree.tag not in (cls.xml_root_node, cls._names):
|
|
# note: cls._names is allowed for compatibility with legacy files.
|
|
raise ValueError('root element mismatch (%s vs %s)' % (tree.tag, cls.xml_root_node))
|
|
|
|
if include_id and tree.attrib.get('id'):
|
|
obj.id = tree.attrib.get('id')
|
|
|
|
for attribute in cls.XML_NODES:
|
|
attribute_name, attribute_type = attribute
|
|
element = tree.find(attribute_name)
|
|
if element is None:
|
|
continue
|
|
import_method = getattr(obj, 'import_%s_from_xml' % attribute_type)
|
|
setattr(
|
|
obj,
|
|
attribute_name,
|
|
import_method(element, charset=charset, include_id=include_id),
|
|
)
|
|
return obj
|
|
|
|
def import_str_from_xml(self, element, **kwargs):
|
|
return xml_node_text(element)
|
|
|
|
def import_int_from_xml(self, element, **kwargs):
|
|
return int(element.text)
|
|
|
|
def import_bool_from_xml(self, element, **kwargs):
|
|
return bool(element.text == 'true')
|
|
|
|
def import_datetime_from_xml(self, element, **kwargs):
|
|
return datetime.datetime.strptime(element.text[:19], '%Y-%m-%dT%H:%M:%S')
|
|
|
|
def import_str_list_from_xml(self, element, **kwargs):
|
|
value = []
|
|
for item in element.findall('item'):
|
|
value.append(item.text)
|
|
return value
|
|
|
|
def import_roles_from_xml(self, element, include_id=False, **kwargs):
|
|
criterias = []
|
|
for sub in element:
|
|
if include_id and 'role-id' in sub.attrib:
|
|
criterias.append(Equal('id', sub.attrib['role-id']))
|
|
elif 'role-slug' in sub.attrib:
|
|
criterias.append(Equal('slug', sub.attrib['role-slug']))
|
|
else:
|
|
role_name = xml_node_text(sub)
|
|
if role_name:
|
|
criterias.append(Equal('name', role_name))
|
|
if not criterias:
|
|
return []
|
|
|
|
def lazy_roles():
|
|
return get_publisher().role_class.select([Or(criterias)], order_by='name')
|
|
|
|
return lazy_roles
|