comparison sat/tools/common/data_objects.py @ 2562:26edcf3a30eb

core, setup: huge cleaning: - moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention - move twisted directory to root - removed all hacks from setup.py, and added missing dependencies, it is now clean - use https URL for website in setup.py - removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed - renamed sat.sh to sat and fixed its installation - added python_requires to specify Python version needed - replaced glib2reactor which use deprecated code by gtk3reactor sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author Goffi <goffi@goffi.org>
date Mon, 02 Apr 2018 19:44:50 +0200
parents src/tools/common/data_objects.py@8cf58a415126
children 56f94936df1e
comparison
equal deleted inserted replaced
2561:bd30dc3ffe5a 2562:26edcf3a30eb
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # SAT: a jabber client
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
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/>.
19
20 """Objects handling bridge data, with jinja2 safe markup handling"""
21
22 from sat.tools.common import data_format
23 try:
24 from jinja2 import Markup as safe
25 except ImportError:
26 safe = unicode
27
28 from sat.tools.common import uri as xmpp_uri
29 import urllib
30
31 q = lambda value: urllib.quote(value.encode('utf-8'), safe='@')
32
33
34 class BlogItem(object):
35
36 def __init__(self, mb_data, parent):
37 self.mb_data = mb_data
38 self.parent = parent
39 self._tags = None
40 self._groups = None
41 self._comments = None
42 self._comments_items_list = None
43
44 @property
45 def id(self):
46 return self.mb_data.get(u'id')
47
48 @property
49 def atom_id(self):
50 return self.mb_data.get(u'atom_id')
51
52 @property
53 def uri(self):
54 node = self.parent.node
55 service = self.parent.service
56 return xmpp_uri.buildXMPPUri(u'pubsub',
57 subtype=u'microblog',
58 path=service,
59 node=node,
60 item=self.id)
61
62 @property
63 def published(self):
64 return self.mb_data.get(u'published')
65
66 @property
67 def updated(self):
68 return self.mb_data.get(u'updated')
69
70 @property
71 def language(self):
72 return self.mb_data.get(u'language')
73
74 @property
75 def author(self):
76 return self.mb_data.get(u'author')
77
78 @property
79 def author_jid(self):
80 return self.mb_data.get(u'author_jid')
81
82 @property
83 def author_jid_verified(self):
84 return self.mb_data.get(u'author_jid_verified')
85
86 @property
87 def author_email(self):
88 return self.mb_data.get(u'author_email')
89
90 @property
91 def tags(self):
92 if self._tags is None:
93 self._tags = list(data_format.dict2iter('tag', self.mb_data))
94 return self._tags
95
96 @property
97 def groups(self):
98 if self._groups is None:
99 self._groups = list(data_format.dict2iter('group', self.mb_data))
100 return self._groups
101
102 @property
103 def title(self):
104 return self.mb_data.get(u'title')
105
106 @property
107 def title_xhtml(self):
108 try:
109 return safe(self.mb_data[u'title_xhtml'])
110 except KeyError:
111 return None
112
113 @property
114 def content(self):
115 return self.mb_data.get(u'content')
116
117 @property
118 def content_xhtml(self):
119 try:
120 return safe(self.mb_data[u'content_xhtml'])
121 except KeyError:
122 return None
123
124 @property
125 def comments(self):
126 if self._comments is None:
127 self._comments = data_format.dict2iterdict(u'comments', self.mb_data, (u'node', u'service'))
128 return self._comments
129
130 @property
131 def comments_service(self):
132 return self.mb_data.get(u'comments_service')
133
134 @property
135 def comments_node(self):
136 return self.mb_data.get(u'comments_node')
137
138 @property
139 def comments_items_list(self):
140 return [] if self._comments_items_list is None else self._comments_items_list
141
142 def appendCommentsItems(self, items):
143 """append comments items to self.comments_items"""
144 if self._comments_items_list is None:
145 self._comments_items_list = []
146 self._comments_items_list.append(items)
147
148
149 class BlogItems(object):
150
151 def __init__(self, mb_data):
152 self.items = [BlogItem(i, self) for i in mb_data[0]]
153 self.metadata = mb_data[1]
154
155 @property
156 def service(self):
157 return self.metadata[u'service']
158
159 @property
160 def node(self):
161 return self.metadata[u'node']
162
163 @property
164 def uri(self):
165 return self.metadata[u'uri']
166
167 def __len__(self):
168 return self.items.__len__()
169
170 def __missing__(self, key):
171 return self.items.__missing__(key)
172
173 def __getitem__(self, key):
174 return self.items.__getitem__(key)
175
176 def __iter__(self):
177 return self.items.__iter__()
178
179 def __reversed__(self):
180 return self.items.__reversed__()
181
182 def __contains__(self, item):
183 return self.items.__contains__(item)
184
185
186 class Message(object):
187
188 def __init__(self, msg_data):
189 self._uid = msg_data[0]
190 self._timestamp = msg_data[1]
191 self._from_jid = msg_data[2]
192 self._to_jid = msg_data[3]
193 self._message_data = msg_data[4]
194 self._subject_data = msg_data[5]
195 self._type = msg_data[6]
196 self._extra = msg_data[7]
197 self._html = dict(data_format.getSubDict('xhtml', self._extra))
198
199 @property
200 def id(self):
201 return self._uid
202
203 @property
204 def timestamp(self):
205 return self._timestamp
206
207 @property
208 def from_(self):
209 return self._from_jid
210
211 @property
212 def text(self):
213 try:
214 return self._message_data['']
215 except KeyError:
216 return next(self._message_data.itervalues())
217
218 @property
219 def subject(self):
220 try:
221 return self._subject_data['']
222 except KeyError:
223 return next(self._subject_data.itervalues())
224
225 @property
226 def type(self):
227 return self._type
228
229 @property
230 def thread(self):
231 return self._extra.get('thread')
232
233 @property
234 def thread_parent(self):
235 return self._extra.get('thread_parent')
236
237 @property
238 def received(self):
239 return self._extra.get('received_timestamp', self._timestamp)
240
241 @property
242 def delay_sender(self):
243 return self._extra.get('delay_sender')
244
245 @property
246 def info_type(self):
247 return self._extra.get('info_type')
248
249 @property
250 def html(self):
251 if not self._html:
252 return None
253 try:
254 return safe(self._html[''])
255 except KeyError:
256 return safe(next(self._html.itervalues()))
257
258
259 class Messages(object):
260
261 def __init__(self, msgs_data):
262 self.messages = [Message(m) for m in msgs_data]
263
264 def __len__(self):
265 return self.messages.__len__()
266
267 def __missing__(self, key):
268 return self.messages.__missing__(key)
269
270 def __getitem__(self, key):
271 return self.messages.__getitem__(key)
272
273 def __iter__(self):
274 return self.messages.__iter__()
275
276 def __reversed__(self):
277 return self.messages.__reversed__()
278
279 def __contains__(self, item):
280 return self.messages.__contains__(item)
281
282 class Room(object):
283
284 def __init__(self, jid, name=None, url=None):
285 self.jid = jid
286 self.name = name or jid
287 if url is not None:
288 self.url = url
289
290
291 class Identity(object):
292
293 def __init__(self, jid_str, data=None):
294 self.jid_str = jid_str
295 self.data = data if data is not None else {}
296
297 def __getitem__(self, key):
298 return self.data[key]
299
300 def __getattr__(self, key):
301 try:
302 return self.data[key]
303 except KeyError:
304 raise AttributeError(key)
305
306
307 class Identities(object):
308
309 def __init__(self):
310 self.identities = {}
311
312 def __getitem__(self, jid_str):
313 try:
314 return self.identities[jid_str]
315 except KeyError:
316 return None
317
318 def __setitem__(self, jid_str, data):
319 self.identities[jid_str] = Identity(jid_str, data)
320
321 def __contains__(self, jid_str):
322 return jid_str in self.identities
323
324
325 class ObjectQuoter(object):
326 """object wrapper which quote attribues (to be used in templates)"""
327
328 def __init__(self, obj):
329 self.obj = obj
330
331 def __unicode__(self):
332 return q(self.obj.__unicode__())
333
334 def __str__(self):
335 return self.__unicode__()
336
337 def __getattr__(self, name):
338 return q(self.obj.__getattr__(name))
339
340 def __getitem__(self, key):
341 return q(self.obj.__getitem__(key))
342
343
344 class OnClick(object):
345 """Class to handle clickable elements targets"""
346
347 def __init__(self, url=None):
348 self.url = url
349
350 def formatUrl(self, *args, **kwargs):
351 """format URL using Python formatting
352
353 values will be quoted before being used
354 """
355 return self.url.format(*[q(a) for a in args],
356 **{k: ObjectQuoter(v) for k,v in kwargs.iteritems()})