comparison sat/plugins/plugin_exp_list_of_interest.py @ 2912:a3faf1c86596

plugin events: refactored invitation and personal lists logic: - invitation logic has been moved to a new generic "plugin_exp_invitation" plugin - plugin_misc_invitations has be rename "plugin_exp_email_invitation" to avoid confusion - personal event list has be refactored to use a new experimental "list of interest", which regroup all interestings items, events or other ones
author Goffi <goffi@goffi.org>
date Sun, 14 Apr 2019 08:21:51 +0200
parents
children 672e6be3290f
comparison
equal deleted inserted replaced
2911:cd391ea847cb 2912:a3faf1c86596
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # SAT plugin to detect language (experimental)
5 # Copyright (C) 2009-2019 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 from sat.core.i18n import _
21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger
23 from wokkel import disco, iwokkel, pubsub
24 from zope.interface import implements
25 from twisted.internet import defer
26 from twisted.words.protocols.jabber import error as jabber_error
27 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
28 from twisted.words.xish import domish
29
30 log = getLogger(__name__)
31
32
33 PLUGIN_INFO = {
34 C.PI_NAME: "List of Interest",
35 C.PI_IMPORT_NAME: "LIST_INTEREST",
36 C.PI_TYPE: "EXP",
37 C.PI_PROTOCOLS: [],
38 C.PI_DEPENDENCIES: ["XEP-0060"],
39 C.PI_RECOMMENDATIONS: [],
40 C.PI_MAIN: "ListInterest",
41 C.PI_HANDLER: "yes",
42 C.PI_DESCRIPTION: _(u"Experimental handling of interesting XMPP locations"),
43 }
44
45 NS_LIST_INTEREST = "https://salut-a-toi/protocol/list-interest:0"
46
47
48 class ListInterest(object):
49 namespace = NS_LIST_INTEREST
50
51 def __init__(self, host):
52 log.info(_(u"List of Interest plugin initialization"))
53 self.host = host
54 self._p = self.host.plugins["XEP-0060"]
55
56 def getHandler(self, client):
57 return ListInterestHandler(self)
58
59 @defer.inlineCallbacks
60 def createNode(self, client):
61 try:
62 # TODO: check auto-create, no need to create node first if available
63 options = {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST}
64 yield self._p.createNode(
65 client,
66 client.jid.userhostJID(),
67 nodeIdentifier=NS_LIST_INTEREST,
68 options=options,
69 )
70 except jabber_error.StanzaError as e:
71 if e.condition == u"conflict":
72 log.debug(_(u"requested node already exists"))
73
74 @defer.inlineCallbacks
75 def registerPubsub(self, client, namespace, service, node, item_id=None,
76 creator=False, name=None, element=None):
77 """Register an interesting element in personal list
78
79 @param namespace(unicode): namespace of the interest
80 this is used as a cache, to avoid the need to retrieve the item only to get
81 its namespace
82 @param service(jid.JID): pubsub service of the
83 @param node(unicode): target pubsub node
84 @param item_id(unicode, None): target pubsub id
85 @param creator(bool): True if client's profile is the creator of the node
86 This is used a cache, to avoid the need to retrieve affiliations
87 @param name(unicode, None): name of the interest
88 @param element(domish.Element, None): element to attach
89 may be used to cache some extra data
90 """
91 yield self.createNode(client)
92 interest_elt = domish.Element((NS_LIST_INTEREST, u"interest"))
93 interest_elt[u"namespace"] = namespace
94 if name is not None:
95 interest_elt[u'name'] = name
96 pubsub_elt = interest_elt.addElement(u"pubsub")
97 pubsub_elt[u"service"] = service.full()
98 pubsub_elt[u"node"] = node
99 if item_id is not None:
100 pubsub_elt[u"item"] = item_id
101 if creator:
102 pubsub_elt[u"creator"] = C.BOOL_TRUE
103 if element is not None:
104 pubsub_elt.addChild(element)
105 item_elt = pubsub.Item(payload=interest_elt)
106 yield self._p.publish(
107 client, client.jid.userhostJID(), NS_LIST_INTEREST, items=[item_elt]
108 )
109
110 @defer.inlineCallbacks
111 def listInterests(self, client, service=None, node=None, namespace=None):
112 """Retrieve list of interests
113
114 @param service(jid.JID, None): service to use
115 None to use own PEP
116 @param node(unicode, None): node to use
117 None to use default node
118 @param namespace(unicode, None): filter interests of this namespace
119 None to retrieve all interests
120 @return: same as [XEP_0060.getItems]
121 """
122 # TODO: if a MAM filter were available, it would improve performances
123 if not node:
124 node = NS_LIST_INTEREST
125 items, metadata = yield self._p.getItems(client, service, node)
126 if namespace is not None:
127 filtered_items = []
128 for item in items:
129 try:
130 interest_elt = next(item.elements(NS_LIST_INTEREST, u"interest"))
131 except StopIteration:
132 log.warning(_(u"Missing interest element: {xml}").format(
133 xml=interest_elt.toXml()))
134 continue
135 if interest_elt.getAttribute(u"namespace") == namespace:
136 filtered_items.append(item)
137 items = filtered_items
138
139 defer.returnValue((items, metadata))
140
141
142 class ListInterestHandler(XMPPHandler):
143 implements(iwokkel.IDisco)
144
145 def __init__(self, plugin_parent):
146 self.plugin_parent = plugin_parent
147
148 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
149 return [
150 disco.DiscoFeature(NS_LIST_INTEREST),
151 ]
152
153 def getDiscoItems(self, requestor, target, nodeIdentifier=""):
154 return []