|
1
|
|
|
# -*- coding: utf-8 -*- |
|
2
|
|
|
""" |
|
3
|
|
|
XML builder/parser |
|
4
|
|
|
""" |
|
5
|
|
|
import xml.etree.ElementTree as ET |
|
6
|
|
|
from nextcloud.compat import encode_string |
|
7
|
|
|
from nextcloud.common.properties import NAMESPACES_MAP |
|
8
|
|
|
|
|
9
|
|
|
|
|
10
|
|
|
def _prepare_xml_parsing(string): |
|
11
|
|
|
return encode_string(string) |
|
12
|
|
|
|
|
13
|
|
|
def _safe_xml_val(val): |
|
14
|
|
|
if isinstance(val, int): |
|
15
|
|
|
val = str(val) |
|
16
|
|
|
return val |
|
17
|
|
|
|
|
18
|
|
|
class SimpleXml: |
|
19
|
|
|
""" |
|
20
|
|
|
Static class to build and parse XML datas |
|
21
|
|
|
""" |
|
22
|
|
|
namespaces_map = NAMESPACES_MAP |
|
23
|
|
|
supported_field_types = list(NAMESPACES_MAP.keys()) |
|
24
|
|
|
xml_namespaces_map = {'xmlns:' + k: v for k, v in NAMESPACES_MAP.items()} |
|
25
|
|
|
|
|
26
|
|
|
@classmethod |
|
27
|
|
|
def _to_fields_list(cls, fields_hash): |
|
28
|
|
|
props_xml = [] |
|
29
|
|
|
for field_type in fields_hash: |
|
30
|
|
|
if field_type not in cls.supported_field_types: |
|
31
|
|
|
pass |
|
32
|
|
|
else: |
|
33
|
|
|
for field in fields_hash[field_type]: |
|
34
|
|
|
props_xml.append('{}:{}'.format(field_type, field)) |
|
35
|
|
|
|
|
36
|
|
|
return props_xml |
|
37
|
|
|
|
|
38
|
|
|
@classmethod |
|
39
|
|
|
def _to_field_vals_list(cls, fields_hash): |
|
40
|
|
|
props_xml = {} |
|
41
|
|
|
for field_type in fields_hash: |
|
42
|
|
|
if field_type not in cls.supported_field_types: |
|
43
|
|
|
pass |
|
44
|
|
|
else: |
|
45
|
|
|
vals = fields_hash[field_type] |
|
46
|
|
|
for field in vals: |
|
47
|
|
|
props_xml['{}:{}'.format(field_type, field)] = _safe_xml_val(vals[field]) |
|
48
|
|
|
|
|
49
|
|
|
return props_xml |
|
50
|
|
|
|
|
51
|
|
|
@classmethod |
|
52
|
|
|
def _tostring(cls, root): |
|
53
|
|
|
return ET.tostring(root) |
|
54
|
|
|
|
|
55
|
|
|
@classmethod |
|
56
|
|
|
def fromstring(cls, data): |
|
57
|
|
|
""" |
|
58
|
|
|
Fetch xml.etree.ElementTree for input data |
|
59
|
|
|
|
|
60
|
|
|
:param data: raw xml data |
|
61
|
|
|
:returns: :class:xml.etree.ElementTree |
|
62
|
|
|
""" |
|
63
|
|
|
return ET.fromstring(_prepare_xml_parsing(data)) |
|
64
|
|
|
|
|
65
|
|
|
@classmethod |
|
66
|
|
|
def build_propfind_datas(cls, instr=None, filter_rules=None, fields=None): |
|
67
|
|
|
""" |
|
68
|
|
|
Build XML datas for a PROPFIND querry. |
|
69
|
|
|
|
|
70
|
|
|
:param instr: http instruction (default: PROPFIND) |
|
71
|
|
|
:param filter_rules: a dict containing filter rules separated by |
|
72
|
|
|
namespace. e.g. {'oc': {'favorite': 1}} |
|
73
|
|
|
:param fields: a dict containing fields separated by namespace |
|
74
|
|
|
e.g. {'oc': ['id']} |
|
75
|
|
|
:returns: xml data (string) |
|
76
|
|
|
""" |
|
77
|
|
|
if not instr: |
|
78
|
|
|
instr = 'd:propfind' |
|
79
|
|
|
|
|
80
|
|
|
root = ET.Element(instr, cls.xml_namespaces_map) |
|
81
|
|
|
props = cls._to_fields_list(fields or {}) |
|
82
|
|
|
if props: |
|
83
|
|
|
prop_group = ET.SubElement(root, 'd:prop') |
|
84
|
|
|
for prop in props: |
|
85
|
|
|
ET.SubElement(prop_group, prop) |
|
86
|
|
|
|
|
87
|
|
|
rules = cls._to_field_vals_list(filter_rules or {}) |
|
88
|
|
|
if rules: |
|
89
|
|
|
rule_group = ET.SubElement(root, 'oc:filter-rules') |
|
90
|
|
|
for k in rules: |
|
91
|
|
|
rule = ET.SubElement(rule_group, k) |
|
92
|
|
|
val = rules[k] |
|
93
|
|
|
rule.text = _safe_xml_val(val) |
|
94
|
|
|
|
|
95
|
|
|
return cls._tostring(root) |
|
96
|
|
|
|
|
97
|
|
|
@classmethod |
|
98
|
|
|
def build_propupdate_datas(cls, values): |
|
99
|
|
|
""" |
|
100
|
|
|
Build XML datas for a PROPUPDATE querry. |
|
101
|
|
|
|
|
102
|
|
|
:param values: a dict containing values separated by namespace |
|
103
|
|
|
e.g. {'oc': {'favorite': 1}} |
|
104
|
|
|
:returns: xml data (string) |
|
105
|
|
|
""" |
|
106
|
|
|
root = ET.Element('d:propertyupdate', cls.xml_namespaces_map) |
|
107
|
|
|
vals = cls._to_field_vals_list(values) |
|
108
|
|
|
if vals: |
|
109
|
|
|
set_group = ET.SubElement(root, 'd:set') |
|
110
|
|
|
val_group = ET.SubElement(set_group, 'd:prop') |
|
111
|
|
|
for k in vals: |
|
112
|
|
|
val = ET.SubElement(val_group, k) |
|
113
|
|
|
val.text = vals[k] |
|
114
|
|
|
|
|
115
|
|
|
return cls._tostring(root) |
|
116
|
|
|
|