Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0277.py @ 615:6f4c31192c7c
plugins XEP-0060, XEP-0277, groupblog: comments implementation (first draft, not finished yet):
- PubSub options constants are moved to XEP-0060
- comments url are generated/parsed according to XEP-0277
- microblog data can now have the following keys:
- "comments", with the url as given in the <link> tag
- "comments_service", with the jid of the PubSub service hosting the comments
- "comments_node", with the parsed node
- comments nodes use different access_model according to parent microblog item access
- publisher is not verified yet, see FIXME warning
- so far, comments node are automatically subscribed
- some bug fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 20 May 2013 23:21:29 +0200 |
parents | 84a6e83157c2 |
children | 47d3a22b4629 |
comparison
equal
deleted
inserted
replaced
614:bef0f893482f | 615:6f4c31192c7c |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from logging import debug, info, error | 20 from logging import debug, info, warning, error |
21 from twisted.internet import protocol | 21 from twisted.words.protocols.jabber import jid |
22 from twisted.words.protocols.jabber import client, jid | 22 from sat.core import exceptions |
23 from twisted.words.protocols.jabber import error as jab_error | |
24 import twisted.internet.error | |
25 from twisted.words.xish import domish | |
26 from sat.tools.xml_tools import ElementParser | 23 from sat.tools.xml_tools import ElementParser |
27 | 24 |
28 from wokkel import disco, pubsub | 25 from wokkel import pubsub |
29 from feed.atom import Entry, Author | 26 from feed import atom |
30 import uuid | 27 import uuid |
31 from time import time | 28 from time import time |
29 import urlparse | |
32 | 30 |
33 NS_MICROBLOG = 'urn:xmpp:microblog:0' | 31 NS_MICROBLOG = 'urn:xmpp:microblog:0' |
34 OPT_ACCESS_MODEL = 'pubsub#access_model' | |
35 OPT_PERSIST_ITEMS = 'pubsub#persist_items' | |
36 OPT_MAX_ITEMS = 'pubsub#max_items' | |
37 OPT_DELIVER_PAYLOADS = 'pubsub#deliver_payloads' | |
38 OPT_SEND_ITEM_SUBSCRIBE = 'pubsub#send_item_subscribe' | |
39 | 32 |
40 PLUGIN_INFO = { | 33 PLUGIN_INFO = { |
41 "name": "Microblogging over XMPP Plugin", | 34 "name": "Microblogging over XMPP Plugin", |
42 "import_name": "XEP-0277", | 35 "import_name": "XEP-0277", |
43 "type": "XEP", | 36 "type": "XEP", |
71 host.bridge.addMethod("setMicroblogAccess", ".plugin", in_sign='ss', out_sign='', | 64 host.bridge.addMethod("setMicroblogAccess", ".plugin", in_sign='ss', out_sign='', |
72 method=self.setMicroblogAccess, | 65 method=self.setMicroblogAccess, |
73 async=True, | 66 async=True, |
74 doc={}) | 67 doc={}) |
75 | 68 |
69 def parseCommentUrl(self, node_url): | |
70 parsed_url = urlparse.urlparse(node_url, 'xmpp') | |
71 service = jid.JID(parsed_url.path) | |
72 queries = parsed_url.query.split(';') | |
73 parsed_queries = dict() | |
74 for query in queries: | |
75 parsed_queries.update(urlparse.parse_qs(query)) | |
76 node = parsed_queries.get('node',[''])[0] | |
77 | |
78 if not node: | |
79 raise exceptions.DataError('Invalid comments link') | |
80 | |
81 return (service, node) | |
82 | |
76 def item2mbdata(self, item): | 83 def item2mbdata(self, item): |
77 """Convert an XML Item to microblog data used in bridge API | 84 """Convert an XML Item to microblog data used in bridge API |
78 @param item: domish.Element of microblog item | 85 @param item: domish.Element of microblog item |
79 @return: microblog data (dictionary)""" | 86 @return: microblog data (dictionary)""" |
80 try: | 87 try: |
81 entry_elt = filter(lambda x: x.name == "entry", item.children)[0] | 88 entry_elt = filter(lambda x: x.name == "entry", item.children)[0] |
82 except KeyError: | 89 except KeyError: |
83 warning(_('No entry element in microblog item')) | 90 warning(_('No entry element in microblog item')) |
84 return | 91 return |
85 _entry = Entry().import_xml(entry_elt.toXml().encode('utf-8')) | 92 _entry = atom.Entry().import_xml(entry_elt.toXml().encode('utf-8')) |
86 microblog_data = {} | 93 microblog_data = {} |
87 try: | 94 try: |
88 microblog_data['content'] = _entry.title.text | 95 microblog_data['content'] = _entry.title.text |
89 if len(_entry.authors): | 96 if len(_entry.authors): |
90 microblog_data['author'] = _entry.authors[0].name.text | 97 microblog_data['author'] = _entry.authors[0].name.text |
91 microblog_data['timestamp'] = str(int(_entry.updated.tf)) | 98 microblog_data['timestamp'] = str(int(_entry.updated.tf)) |
92 microblog_data['id'] = item['id'] | 99 microblog_data['id'] = item['id'] |
100 for link in _entry.links: | |
101 try: | |
102 if link.attrs["title"] == "comments": | |
103 microblog_data['comments'] = link.attrs["href"] | |
104 service, node = self.parseCommentUrl(microblog_data["comments"]) | |
105 microblog_data['comments_service'] = service.full() | |
106 microblog_data['comments_node'] = node | |
107 break | |
108 except (KeyError, exceptions.DataError): | |
109 continue | |
110 | |
93 except (AttributeError, KeyError): | 111 except (AttributeError, KeyError): |
94 error(_('Error while parsing atom entry for microblogging event')) | 112 error(_('Error while parsing atom entry for microblogging event')) |
95 return {} | 113 return {} |
96 | 114 |
97 ##XXX: workaround for Jappix behaviour | 115 ##XXX: workaround for Jappix behaviour |
115 """Convert a data dict to en entry usable to create an item | 133 """Convert a data dict to en entry usable to create an item |
116 @param data: data dict as given by bridge method | 134 @param data: data dict as given by bridge method |
117 @return: domish.Element""" | 135 @return: domish.Element""" |
118 _uuid = unicode(uuid.uuid1()) | 136 _uuid = unicode(uuid.uuid1()) |
119 content = data['content'] | 137 content = data['content'] |
120 _entry = Entry() | 138 _entry = atom.Entry() |
121 #FIXME: need to escape html | 139 #FIXME: need to escape html |
122 _entry.title = unicode(content).encode('utf-8') | 140 _entry.title = unicode(content).encode('utf-8') |
123 _entry.author = Author() | 141 _entry.author = atom.Author() |
124 _entry.author.name = data.get('author', self.host.getJidNStream(profile)[0].userhost()).encode('utf-8') | 142 _entry.author.name = data.get('author', self.host.getJidNStream(profile)[0].userhost()).encode('utf-8') |
125 _entry.updated = float(data.get('timestamp', time())) | 143 _entry.updated = float(data.get('timestamp', time())) |
126 _entry.id = str(_uuid) | 144 _entry.id = str(_uuid) |
145 if 'comments' in data: | |
146 link = atom.Link() | |
147 link.attrs['href'] = data['comments'] | |
148 link.attrs['rel'] = 'replies' | |
149 link.attrs['title'] = 'comments' | |
150 _entry.links.append(link) | |
127 _entry_elt = ElementParser()(str(_entry).decode('utf-8')) | 151 _entry_elt = ElementParser()(str(_entry).decode('utf-8')) |
128 item = pubsub.Item(payload=_entry_elt) | 152 item = pubsub.Item(payload=_entry_elt) |
129 item['id'] = _uuid | 153 item['id'] = _uuid |
130 return item | 154 return item |
131 | 155 |
147 def getLastMicroblogs(self, pub_jid, max_items=10, profile_key='@DEFAULT@'): | 171 def getLastMicroblogs(self, pub_jid, max_items=10, profile_key='@DEFAULT@'): |
148 """Get the last published microblogs | 172 """Get the last published microblogs |
149 @param pub_jid: jid of the publisher | 173 @param pub_jid: jid of the publisher |
150 @param max_items: how many microblogs we want to get | 174 @param max_items: how many microblogs we want to get |
151 @param profile_key: profile key | 175 @param profile_key: profile key |
152 @param callback: used for the async answer | |
153 @param errback: used for the async answer | |
154 """ | 176 """ |
155 assert(callback) | |
156 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key) | 177 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key) |
157 d.addCallback(lambda items: map(self.item2mbdata, items)) | 178 d.addCallback(lambda items: map(self.item2mbdata, items)) |
158 | 179 |
159 def setMicroblogAccess(self, access="presence", profile_key='@DEFAULT@'): | 180 def setMicroblogAccess(self, access="presence", profile_key='@DEFAULT@'): |
160 """Create a microblog node on PEP with given access | 181 """Create a microblog node on PEP with given access |
164 | 185 |
165 _jid, xmlstream = self.host.getJidNStream(profile_key) | 186 _jid, xmlstream = self.host.getJidNStream(profile_key) |
166 if not _jid: | 187 if not _jid: |
167 error(_("Can't find profile's jid")) | 188 error(_("Can't find profile's jid")) |
168 return | 189 return |
169 _options = {OPT_ACCESS_MODEL: access, OPT_PERSIST_ITEMS: 1, OPT_MAX_ITEMS: -1, OPT_DELIVER_PAYLOADS: 1, OPT_SEND_ITEM_SUBSCRIBE: 1} | 190 C = self.host.plugins["XEP-0060"] |
191 _options = {C.OPT_ACCESS_MODEL: access, C.OPT_PERSIST_ITEMS: 1, C.OPT_MAX_ITEMS: -1, C.OPT_DELIVER_PAYLOADS: 1, C.OPT_SEND_ITEM_SUBSCRIBE: 1} | |
170 | 192 |
171 def cb(result): | 193 def cb(result): |
172 #Node is created with right permission | 194 #Node is created with right permission |
173 debug(_("Microblog node has now access %s") % access) | 195 debug(_("Microblog node has now access %s") % access) |
174 | 196 |