Mercurial > libervia-backend
comparison sat/plugins/plugin_misc_forums.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | 989b622faff6 |
children | 9d0df638c8b4 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT plugin for pubsub forums | 4 # SAT plugin for pubsub forums |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
27 from twisted.internet import defer | 27 from twisted.internet import defer |
28 import shortuuid | 28 import shortuuid |
29 import json | 29 import json |
30 log = getLogger(__name__) | 30 log = getLogger(__name__) |
31 | 31 |
32 NS_FORUMS = u'org.salut-a-toi.forums:0' | 32 NS_FORUMS = 'org.salut-a-toi.forums:0' |
33 NS_FORUMS_TOPICS = NS_FORUMS + u'#topics' | 33 NS_FORUMS_TOPICS = NS_FORUMS + '#topics' |
34 | 34 |
35 PLUGIN_INFO = { | 35 PLUGIN_INFO = { |
36 C.PI_NAME: _("forums management"), | 36 C.PI_NAME: _("forums management"), |
37 C.PI_IMPORT_NAME: "forums", | 37 C.PI_IMPORT_NAME: "forums", |
38 C.PI_TYPE: "EXP", | 38 C.PI_TYPE: "EXP", |
40 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0277"], | 40 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0277"], |
41 C.PI_MAIN: "forums", | 41 C.PI_MAIN: "forums", |
42 C.PI_HANDLER: "no", | 42 C.PI_HANDLER: "no", |
43 C.PI_DESCRIPTION: _("""forums management plugin""") | 43 C.PI_DESCRIPTION: _("""forums management plugin""") |
44 } | 44 } |
45 FORUM_ATTR = {u'title', u'name', u'main-language', u'uri'} | 45 FORUM_ATTR = {'title', 'name', 'main-language', 'uri'} |
46 FORUM_SUB_ELTS = (u'short-desc', u'desc') | 46 FORUM_SUB_ELTS = ('short-desc', 'desc') |
47 FORUM_TOPICS_NODE_TPL = u'{node}#topics_{uuid}' | 47 FORUM_TOPICS_NODE_TPL = '{node}#topics_{uuid}' |
48 FORUM_TOPIC_NODE_TPL = u'{node}_{uuid}' | 48 FORUM_TOPIC_NODE_TPL = '{node}_{uuid}' |
49 | 49 |
50 | 50 |
51 class forums(object): | 51 class forums(object): |
52 | 52 |
53 def __init__(self, host): | 53 def __init__(self, host): |
54 log.info(_(u"forums plugin initialization")) | 54 log.info(_("forums plugin initialization")) |
55 self.host = host | 55 self.host = host |
56 self._m = self.host.plugins['XEP-0277'] | 56 self._m = self.host.plugins['XEP-0277'] |
57 self._p = self.host.plugins['XEP-0060'] | 57 self._p = self.host.plugins['XEP-0060'] |
58 self._node_options = { | 58 self._node_options = { |
59 self._p.OPT_ACCESS_MODEL: self._p.ACCESS_OPEN, | 59 self._p.OPT_ACCESS_MODEL: self._p.ACCESS_OPEN, |
65 } | 65 } |
66 host.registerNamespace('forums', NS_FORUMS) | 66 host.registerNamespace('forums', NS_FORUMS) |
67 host.bridge.addMethod("forumsGet", ".plugin", | 67 host.bridge.addMethod("forumsGet", ".plugin", |
68 in_sign='ssss', out_sign='s', | 68 in_sign='ssss', out_sign='s', |
69 method=self._get, | 69 method=self._get, |
70 async=True) | 70 async_=True) |
71 host.bridge.addMethod("forumsSet", ".plugin", | 71 host.bridge.addMethod("forumsSet", ".plugin", |
72 in_sign='sssss', out_sign='', | 72 in_sign='sssss', out_sign='', |
73 method=self._set, | 73 method=self._set, |
74 async=True) | 74 async_=True) |
75 host.bridge.addMethod("forumTopicsGet", ".plugin", | 75 host.bridge.addMethod("forumTopicsGet", ".plugin", |
76 in_sign='ssa{ss}s', out_sign='(aa{ss}a{ss})', | 76 in_sign='ssa{ss}s', out_sign='(aa{ss}a{ss})', |
77 method=self._getTopics, | 77 method=self._getTopics, |
78 async=True) | 78 async_=True) |
79 host.bridge.addMethod("forumTopicCreate", ".plugin", | 79 host.bridge.addMethod("forumTopicCreate", ".plugin", |
80 in_sign='ssa{ss}s', out_sign='', | 80 in_sign='ssa{ss}s', out_sign='', |
81 method=self._createTopic, | 81 method=self._createTopic, |
82 async=True) | 82 async_=True) |
83 | 83 |
84 @defer.inlineCallbacks | 84 @defer.inlineCallbacks |
85 def _createForums(self, client, forums, service, node, forums_elt=None, names=None): | 85 def _createForums(self, client, forums, service, node, forums_elt=None, names=None): |
86 """Recursively create <forums> element(s) | 86 """Recursively create <forums> element(s) |
87 | 87 |
92 @param parent_elt(domish.Element, None): element where the forum must be added | 92 @param parent_elt(domish.Element, None): element where the forum must be added |
93 if None, the root <forums> element will be created | 93 if None, the root <forums> element will be created |
94 @return (domish.Element): created forums | 94 @return (domish.Element): created forums |
95 """ | 95 """ |
96 if not isinstance(forums, list): | 96 if not isinstance(forums, list): |
97 raise ValueError(_(u"forums arguments must be a list of forums")) | 97 raise ValueError(_("forums arguments must be a list of forums")) |
98 if forums_elt is None: | 98 if forums_elt is None: |
99 forums_elt = domish.Element((NS_FORUMS, u'forums')) | 99 forums_elt = domish.Element((NS_FORUMS, 'forums')) |
100 assert names is None | 100 assert names is None |
101 names = set() | 101 names = set() |
102 else: | 102 else: |
103 if names is None or forums_elt.name != u'forums': | 103 if names is None or forums_elt.name != 'forums': |
104 raise exceptions.InternalError(u'invalid forums or names') | 104 raise exceptions.InternalError('invalid forums or names') |
105 assert names is not None | 105 assert names is not None |
106 | 106 |
107 for forum in forums: | 107 for forum in forums: |
108 if not isinstance(forum, dict): | 108 if not isinstance(forum, dict): |
109 raise ValueError(_(u"A forum item must be a dictionary")) | 109 raise ValueError(_("A forum item must be a dictionary")) |
110 forum_elt = forums_elt.addElement('forum') | 110 forum_elt = forums_elt.addElement('forum') |
111 | 111 |
112 for key, value in forum.iteritems(): | 112 for key, value in forum.items(): |
113 if key == u'name' and key in names: | 113 if key == 'name' and key in names: |
114 raise exceptions.ConflictError(_(u"following forum name is not unique: {name}").format(name=key)) | 114 raise exceptions.ConflictError(_("following forum name is not unique: {name}").format(name=key)) |
115 if key == u'uri' and not value.strip(): | 115 if key == 'uri' and not value.strip(): |
116 log.info(_(u"creating missing forum node")) | 116 log.info(_("creating missing forum node")) |
117 forum_node = FORUM_TOPICS_NODE_TPL.format(node=node, uuid=shortuuid.uuid()) | 117 forum_node = FORUM_TOPICS_NODE_TPL.format(node=node, uuid=shortuuid.uuid()) |
118 yield self._p.createNode(client, service, forum_node, self._node_options) | 118 yield self._p.createNode(client, service, forum_node, self._node_options) |
119 value = uri.buildXMPPUri(u'pubsub', | 119 value = uri.buildXMPPUri('pubsub', |
120 path=service.full(), | 120 path=service.full(), |
121 node=forum_node) | 121 node=forum_node) |
122 if key in FORUM_ATTR: | 122 if key in FORUM_ATTR: |
123 forum_elt[key] = value.strip() | 123 forum_elt[key] = value.strip() |
124 elif key in FORUM_SUB_ELTS: | 124 elif key in FORUM_SUB_ELTS: |
125 forum_elt.addElement(key, content=value) | 125 forum_elt.addElement(key, content=value) |
126 elif key == u'sub-forums': | 126 elif key == 'sub-forums': |
127 sub_forums_elt = forum_elt.addElement(u'forums') | 127 sub_forums_elt = forum_elt.addElement('forums') |
128 yield self._createForums(client, value, service, node, sub_forums_elt, names=names) | 128 yield self._createForums(client, value, service, node, sub_forums_elt, names=names) |
129 else: | 129 else: |
130 log.warning(_(u"Unknown forum attribute: {key}").format(key=key)) | 130 log.warning(_("Unknown forum attribute: {key}").format(key=key)) |
131 if not forum_elt.getAttribute(u'title'): | 131 if not forum_elt.getAttribute('title'): |
132 name = forum_elt.getAttribute(u'name') | 132 name = forum_elt.getAttribute('name') |
133 if name: | 133 if name: |
134 forum_elt[u'title'] = name | 134 forum_elt['title'] = name |
135 else: | 135 else: |
136 raise ValueError(_(u"forum need a title or a name")) | 136 raise ValueError(_("forum need a title or a name")) |
137 if not forum_elt.getAttribute(u'uri') and not forum_elt.children: | 137 if not forum_elt.getAttribute('uri') and not forum_elt.children: |
138 raise ValueError(_(u"forum need uri or sub-forums")) | 138 raise ValueError(_("forum need uri or sub-forums")) |
139 defer.returnValue(forums_elt) | 139 defer.returnValue(forums_elt) |
140 | 140 |
141 def _parseForums(self, parent_elt=None, forums=None): | 141 def _parseForums(self, parent_elt=None, forums=None): |
142 """Recursivly parse a <forums> elements and return corresponding forums data | 142 """Recursivly parse a <forums> elements and return corresponding forums data |
143 | 143 |
144 @param item(domish.Element): item with <forums> element | 144 @param item(domish.Element): item with <forums> element |
145 @param parent_elt(domish.Element, None): element to parse | 145 @param parent_elt(domish.Element, None): element to parse |
146 @return (list): parsed data | 146 @return (list): parsed data |
147 @raise ValueError: item is invalid | 147 @raise ValueError: item is invalid |
148 """ | 148 """ |
149 if parent_elt.name == u'item': | 149 if parent_elt.name == 'item': |
150 forums = [] | 150 forums = [] |
151 try: | 151 try: |
152 forums_elt = next(parent_elt.elements(NS_FORUMS, u'forums')) | 152 forums_elt = next(parent_elt.elements(NS_FORUMS, 'forums')) |
153 except StopIteration: | 153 except StopIteration: |
154 raise ValueError(_(u"missing <forums> element")) | 154 raise ValueError(_("missing <forums> element")) |
155 else: | 155 else: |
156 forums_elt = parent_elt | 156 forums_elt = parent_elt |
157 if forums is None: | 157 if forums is None: |
158 raise exceptions.InternalError(u'expected forums') | 158 raise exceptions.InternalError('expected forums') |
159 if forums_elt.name != 'forums': | 159 if forums_elt.name != 'forums': |
160 raise ValueError(_(u'Unexpected element: {xml}').format(xml=forums_elt.toXml())) | 160 raise ValueError(_('Unexpected element: {xml}').format(xml=forums_elt.toXml())) |
161 for forum_elt in forums_elt.elements(): | 161 for forum_elt in forums_elt.elements(): |
162 if forum_elt.name == 'forum': | 162 if forum_elt.name == 'forum': |
163 data = {} | 163 data = {} |
164 for attrib in FORUM_ATTR.intersection(forum_elt.attributes): | 164 for attrib in FORUM_ATTR.intersection(forum_elt.attributes): |
165 data[attrib] = forum_elt[attrib] | 165 data[attrib] = forum_elt[attrib] |
166 unknown = set(forum_elt.attributes).difference(FORUM_ATTR) | 166 unknown = set(forum_elt.attributes).difference(FORUM_ATTR) |
167 if unknown: | 167 if unknown: |
168 log.warning(_(u"Following attributes are unknown: {unknown}").format(unknown=unknown)) | 168 log.warning(_("Following attributes are unknown: {unknown}").format(unknown=unknown)) |
169 for elt in forum_elt.elements(): | 169 for elt in forum_elt.elements(): |
170 if elt.name in FORUM_SUB_ELTS: | 170 if elt.name in FORUM_SUB_ELTS: |
171 data[elt.name] = unicode(elt) | 171 data[elt.name] = str(elt) |
172 elif elt.name == u'forums': | 172 elif elt.name == 'forums': |
173 sub_forums = data[u'sub-forums'] = [] | 173 sub_forums = data['sub-forums'] = [] |
174 self._parseForums(elt, sub_forums) | 174 self._parseForums(elt, sub_forums) |
175 if not u'title' in data or not {u'uri', u'sub-forums'}.intersection(data): | 175 if not 'title' in data or not {'uri', 'sub-forums'}.intersection(data): |
176 log.warning(_(u"invalid forum, ignoring: {xml}").format(xml=forum_elt.toXml())) | 176 log.warning(_("invalid forum, ignoring: {xml}").format(xml=forum_elt.toXml())) |
177 else: | 177 else: |
178 forums.append(data) | 178 forums.append(data) |
179 else: | 179 else: |
180 log.warning(_(u"unkown forums sub element: {xml}").format(xml=forum_elt)) | 180 log.warning(_("unkown forums sub element: {xml}").format(xml=forum_elt)) |
181 | 181 |
182 return forums | 182 return forums |
183 | 183 |
184 def _get(self, service=None, node=None, forums_key=None, profile_key=C.PROF_KEY_NONE): | 184 def _get(self, service=None, node=None, forums_key=None, profile_key=C.PROF_KEY_NONE): |
185 client = self.host.getClient(profile_key) | 185 client = self.host.getClient(profile_key) |
198 if service is None: | 198 if service is None: |
199 service = client.pubsub_service | 199 service = client.pubsub_service |
200 if node is None: | 200 if node is None: |
201 node = NS_FORUMS | 201 node = NS_FORUMS |
202 if forums_key is None: | 202 if forums_key is None: |
203 forums_key = u'default' | 203 forums_key = 'default' |
204 items_data = yield self._p.getItems(client, service, node, item_ids=[forums_key]) | 204 items_data = yield self._p.getItems(client, service, node, item_ids=[forums_key]) |
205 item = items_data[0][0] | 205 item = items_data[0][0] |
206 # we have the item and need to convert it to json | 206 # we have the item and need to convert it to json |
207 forums = self._parseForums(item) | 207 forums = self._parseForums(item) |
208 defer.returnValue(forums) | 208 defer.returnValue(forums) |
239 if service is None: | 239 if service is None: |
240 service = client.pubsub_service | 240 service = client.pubsub_service |
241 if node is None: | 241 if node is None: |
242 node = NS_FORUMS | 242 node = NS_FORUMS |
243 if forums_key is None: | 243 if forums_key is None: |
244 forums_key = u'default' | 244 forums_key = 'default' |
245 forums_elt = yield self._createForums(client, forums, service, node) | 245 forums_elt = yield self._createForums(client, forums, service, node) |
246 yield self._p.sendItem(client, service, node, forums_elt, item_id=forums_key) | 246 yield self._p.sendItem(client, service, node, forums_elt, item_id=forums_key) |
247 | 247 |
248 def _getTopics(self, service, node, extra=None, profile_key=C.PROF_KEY_NONE): | 248 def _getTopics(self, service, node, extra=None, profile_key=C.PROF_KEY_NONE): |
249 client = self.host.getClient(profile_key) | 249 client = self.host.getClient(profile_key) |
250 extra = self._p.parseExtra(extra) | 250 extra = self._p.parseExtra(extra) |
251 d = self.getTopics(client, jid.JID(service), node, rsm_request=extra.rsm_request, extra=extra.extra) | 251 d = self.getTopics(client, jid.JID(service), node, rsm_request=extra.rsm_request, extra=extra.extra) |
252 d.addCallback(lambda(topics, metadata): (topics, {k: unicode(v) for k,v in metadata.iteritems()})) | 252 d.addCallback(lambda topics_metadata: (topics_metadata[0], {k: str(v) for k,v in topics_metadata[1].items()})) |
253 return d | 253 return d |
254 | 254 |
255 @defer.inlineCallbacks | 255 @defer.inlineCallbacks |
256 def getTopics(self, client, service, node, rsm_request=None, extra=None): | 256 def getTopics(self, client, service, node, rsm_request=None, extra=None): |
257 """Retrieve topics data | 257 """Retrieve topics data |
260 """ | 260 """ |
261 topics_data = yield self._p.getItems(client, service, node, rsm_request=rsm_request, extra=extra) | 261 topics_data = yield self._p.getItems(client, service, node, rsm_request=rsm_request, extra=extra) |
262 topics = [] | 262 topics = [] |
263 item_elts, metadata = topics_data | 263 item_elts, metadata = topics_data |
264 for item_elt in item_elts: | 264 for item_elt in item_elts: |
265 topic_elt = next(item_elt.elements(NS_FORUMS, u'topic')) | 265 topic_elt = next(item_elt.elements(NS_FORUMS, 'topic')) |
266 title_elt = next(topic_elt.elements(NS_FORUMS, u'title')) | 266 title_elt = next(topic_elt.elements(NS_FORUMS, 'title')) |
267 topic = {u'uri': topic_elt[u'uri'], | 267 topic = {'uri': topic_elt['uri'], |
268 u'author': topic_elt[u'author'], | 268 'author': topic_elt['author'], |
269 u'title': unicode(title_elt)} | 269 'title': str(title_elt)} |
270 topics.append(topic) | 270 topics.append(topic) |
271 defer.returnValue((topics, metadata)) | 271 defer.returnValue((topics, metadata)) |
272 | 272 |
273 def _createTopic(self, service, node, mb_data, profile_key): | 273 def _createTopic(self, service, node, mb_data, profile_key): |
274 client = self.host.getClient(profile_key) | 274 client = self.host.getClient(profile_key) |
275 return self.createTopic(client, jid.JID(service), node, mb_data) | 275 return self.createTopic(client, jid.JID(service), node, mb_data) |
276 | 276 |
277 @defer.inlineCallbacks | 277 @defer.inlineCallbacks |
278 def createTopic(self, client, service, node, mb_data): | 278 def createTopic(self, client, service, node, mb_data): |
279 try: | 279 try: |
280 title = mb_data[u'title'] | 280 title = mb_data['title'] |
281 if not u'content' in mb_data: | 281 if not 'content' in mb_data: |
282 raise KeyError(u'content') | 282 raise KeyError('content') |
283 except KeyError as e: | 283 except KeyError as e: |
284 raise exceptions.DataError(u"missing mandatory data: {key}".format(key=e.args[0])) | 284 raise exceptions.DataError("missing mandatory data: {key}".format(key=e.args[0])) |
285 | 285 |
286 topic_node = FORUM_TOPIC_NODE_TPL.format(node=node, uuid=shortuuid.uuid()) | 286 topic_node = FORUM_TOPIC_NODE_TPL.format(node=node, uuid=shortuuid.uuid()) |
287 yield self._p.createNode(client, service, topic_node, self._node_options) | 287 yield self._p.createNode(client, service, topic_node, self._node_options) |
288 self._m.send(client, mb_data, service, topic_node) | 288 self._m.send(client, mb_data, service, topic_node) |
289 topic_uri = uri.buildXMPPUri(u'pubsub', | 289 topic_uri = uri.buildXMPPUri('pubsub', |
290 subtype=u'microblog', | 290 subtype='microblog', |
291 path=service.full(), | 291 path=service.full(), |
292 node=topic_node) | 292 node=topic_node) |
293 topic_elt = domish.Element((NS_FORUMS, 'topic')) | 293 topic_elt = domish.Element((NS_FORUMS, 'topic')) |
294 topic_elt[u'uri'] = topic_uri | 294 topic_elt['uri'] = topic_uri |
295 topic_elt[u'author'] = client.jid.userhost() | 295 topic_elt['author'] = client.jid.userhost() |
296 topic_elt.addElement(u'title', content = title) | 296 topic_elt.addElement('title', content = title) |
297 yield self._p.sendItem(client, service, node, topic_elt) | 297 yield self._p.sendItem(client, service, node, topic_elt) |