comparison sat/plugins/plugin_misc_lists.py @ 3460:d4a71a1dac88

plugin misc lists: templates: Templates are a way to create lists with pre-filled schemas.
author Goffi <goffi@goffi.org>
date Fri, 19 Feb 2021 15:49:58 +0100
parents 8dc26e5edcd3
children 02a8d227d5bb
comparison
equal deleted inserted replaced
3459:8dc26e5edcd3 3460:d4a71a1dac88
13 # GNU Affero General Public License for more details. 13 # GNU Affero General Public License for more details.
14 14
15 # You should have received a copy of the GNU Affero General Public License 15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 17
18 import shortuuid
19 from typing import Tuple
20 from twisted.internet import defer
21 from twisted.words.xish import domish
22 from twisted.words.protocols.jabber import jid
23 from wokkel import data_form
18 from sat.core.i18n import _, D_ 24 from sat.core.i18n import _, D_
19 from sat.core.constants import Const as C 25 from sat.core.constants import Const as C
20 from twisted.internet import defer
21 from sat.tools.common import uri 26 from sat.tools.common import uri
22 import shortuuid 27 from sat.tools.common import data_format
23 from sat.core.log import getLogger 28 from sat.core.log import getLogger
24 29
25 log = getLogger(__name__) 30 log = getLogger(__name__)
26 31
27 # XXX: this plugin was formely named "tickets", thus the namespace keeps this 32 # XXX: this plugin was formely named "tickets", thus the namespace keeps this
28 # name 33 # name
29 APP_NS_TICKETS = "org.salut-a-toi.tickets:0" 34 APP_NS_TICKETS = "org.salut-a-toi.tickets:0"
35 NS_TICKETS_TYPE = "org.salut-a-toi.tickets#type:0"
30 36
31 PLUGIN_INFO = { 37 PLUGIN_INFO = {
32 C.PI_NAME: _("Pubsub Lists"), 38 C.PI_NAME: _("Pubsub Lists"),
33 C.PI_IMPORT_NAME: "LISTS", 39 C.PI_IMPORT_NAME: "LISTS",
34 C.PI_TYPE: "EXP", 40 C.PI_TYPE: "EXP",
35 C.PI_PROTOCOLS: [], 41 C.PI_PROTOCOLS: [],
36 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0346", "XEP-0277", "IDENTITY"], 42 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0346", "XEP-0277", "IDENTITY"],
37 C.PI_MAIN: "PubsubLists", 43 C.PI_MAIN: "PubsubLists",
38 C.PI_HANDLER: "no", 44 C.PI_HANDLER: "no",
39 C.PI_DESCRIPTION: _("""Pubsub lists management plugin"""), 45 C.PI_DESCRIPTION: _("""Pubsub lists management plugin"""),
46 }
47
48 TEMPLATES = {
49 "todo": {
50 "name": D_("TODO List"),
51 "icon": "check",
52 "fields": [
53 {"name": "title"},
54 {"name": "author"},
55 {"name": "created"},
56 {"name": "updated"},
57 {"name": "time_limit"},
58 {"name": "labels", "type": "text-multi"},
59 {
60 "name": "status",
61 "label": D_("status"),
62 "type": "list-single",
63 "options": [
64 {
65 "label": D_("to do"),
66 "value": "todo"
67 },
68 {
69 "label": D_("in progress"),
70 "value": "in_progress"
71 },
72 {
73 "label": D_("done"),
74 "value": "done"
75 },
76 ],
77 "value": "todo"
78 },
79 {
80 "name": "priority",
81 "label": D_("priority"),
82 "type": "list-single",
83 "options": [
84 {
85 "label": D_("major"),
86 "value": "major"
87 },
88 {
89 "label": D_("normal"),
90 "value": "normal"
91 },
92 {
93 "label": D_("minor"),
94 "value": "minor"
95 },
96 ],
97 "value": "normal"
98 },
99 {"name": "body", "type": "xhtml"},
100 {"name": "comments_uri"},
101 ]
102 },
103 "shopping": {
104 "name": D_("Shopping List"),
105 "icon": "basket",
106 "fields": [
107 {"name": "name", "label": D_("name")},
108 {"name": "quantity", "label": D_("quantity")},
109 {
110 "name": "status",
111 "label": D_("status"),
112 "type": "list-single",
113 "options": [
114 {
115 "label": D_("to buy"),
116 "value": "to_buy"
117 },
118 {
119 "label": D_("bought"),
120 "value": "bought"
121 },
122 ],
123 "value": "to_buy"
124 },
125 ]
126 },
127 "tickets": {
128 "name": D_("Tickets"),
129 "icon": "clipboard",
130 "fields": [
131 {"name": "title"},
132 {"name": "author"},
133 {"name": "created"},
134 {"name": "updated"},
135 {"name": "labels", "type": "text-multi"},
136 {
137 "name": "type",
138 "label": D_("type"),
139 "type": "list-single",
140 "options": [
141 {
142 "label": D_("bug"),
143 "value": "bug"
144 },
145 {
146 "label": D_("feature request"),
147 "value": "feature"
148 },
149 ],
150 "value": "bug"
151 },
152 {
153 "name": "status",
154 "label": D_("status"),
155 "type": "list-single",
156 "options": [
157 {
158 "label": D_("queued"),
159 "value": "queued"
160 },
161 {
162 "label": D_("started"),
163 "value": "started"
164 },
165 {
166 "label": D_("review"),
167 "value": "review"
168 },
169 {
170 "label": D_("closed"),
171 "value": "closed"
172 },
173 ],
174 "value": "queued"
175 },
176 {
177 "name": "priority",
178 "label": D_("priority"),
179 "type": "list-single",
180 "options": [
181 {
182 "label": D_("major"),
183 "value": "major"
184 },
185 {
186 "label": D_("normal"),
187 "value": "normal"
188 },
189 {
190 "label": D_("minor"),
191 "value": "minor"
192 },
193 ],
194 "value": "normal"
195 },
196 {"name": "body", "type": "xhtml"},
197 {"name": "comments_uri"},
198 ]
199 }
40 } 200 }
41 201
42 202
43 class PubsubLists: 203 class PubsubLists:
44 204
67 form_ns=APP_NS_TICKETS, 227 form_ns=APP_NS_TICKETS,
68 filters={ 228 filters={
69 "author": self._s.valueOrPublisherFilter, 229 "author": self._s.valueOrPublisherFilter,
70 "created": self._s.dateFilter, 230 "created": self._s.dateFilter,
71 "updated": self._s.dateFilter, 231 "updated": self._s.dateFilter,
232 "time_limit": self._s.dateFilter,
72 }, 233 },
73 profile_key=profile_key), 234 profile_key=profile_key),
74 async_=True, 235 async_=True,
75 ) 236 )
76 host.bridge.addMethod( 237 host.bridge.addMethod(
89 method=lambda service, nodeIdentifier, profile_key: self._s._getUISchema( 250 method=lambda service, nodeIdentifier, profile_key: self._s._getUISchema(
90 service, nodeIdentifier, default_node=self.namespace, 251 service, nodeIdentifier, default_node=self.namespace,
91 profile_key=profile_key), 252 profile_key=profile_key),
92 async_=True, 253 async_=True,
93 ) 254 )
255 host.bridge.addMethod(
256 "listTemplatesNamesGet",
257 ".plugin",
258 in_sign="ss",
259 out_sign="s",
260 method=self._getTemplatesNames,
261 )
262 host.bridge.addMethod(
263 "listTemplateGet",
264 ".plugin",
265 in_sign="sss",
266 out_sign="s",
267 method=self._getTemplate,
268 )
269 host.bridge.addMethod(
270 "listTemplateCreate",
271 ".plugin",
272 in_sign="ssss",
273 out_sign="(ss)",
274 method=self._createTemplate,
275 async_=True,
276 )
94 277
95 def _set(self, service, node, values, schema=None, item_id=None, extra='', 278 def _set(self, service, node, values, schema=None, item_id=None, extra='',
96 profile_key=C.PROF_KEY_NONE): 279 profile_key=C.PROF_KEY_NONE):
97 client, service, node, schema, item_id, extra = self._s.prepareBridgeSet( 280 client, service, node, schema, item_id, extra = self._s.prepareBridgeSet(
98 service, node, schema, item_id, extra, profile_key 281 service, node, schema, item_id, extra, profile_key
101 client, service, node, values, schema, item_id, extra, deserialise=True 284 client, service, node, values, schema, item_id, extra, deserialise=True
102 )) 285 ))
103 d.addCallback(lambda ret: ret or "") 286 d.addCallback(lambda ret: ret or "")
104 return d 287 return d
105 288
106 async def set(self, client, service, node, values, schema=None, item_id=None, extra=None, 289 async def set(
107 deserialise=False, form_ns=APP_NS_TICKETS): 290 self, client, service, node, values, schema=None, item_id=None, extra=None,
291 deserialise=False, form_ns=APP_NS_TICKETS
292 ):
108 """Publish a tickets 293 """Publish a tickets
109 294
110 @param node(unicode, None): Pubsub node to use 295 @param node(unicode, None): Pubsub node to use
111 None to use default tickets node 296 None to use default tickets node
112 @param values(dict[key(unicode), [iterable[object]|object]]): values of the ticket 297 @param values(dict[key(unicode), [iterable[object]|object]]): values of the ticket
150 ) 335 )
151 336
152 return await self._s.set( 337 return await self._s.set(
153 client, service, node, values, schema, item_id, extra, deserialise, form_ns 338 client, service, node, values, schema, item_id, extra, deserialise, form_ns
154 ) 339 )
340
341 def _getTemplatesNames(self, language, profile):
342 client = self.host.getClient(profile)
343 return data_format.serialise(self.getTemplatesNames(client, language))
344
345 def getTemplatesNames(self, client, language: str) -> list:
346 """Retrieve well known list templates"""
347
348 templates = [{"id": tpl_id, "name": d["name"], "icon": d["icon"]}
349 for tpl_id, d in TEMPLATES.items()]
350 return templates
351
352 def _getTemplate(self, name, language, profile):
353 client = self.host.getClient(profile)
354 return data_format.serialise(self.getTemplate(client, name, language))
355
356 def getTemplate(self, client, name: str, language: str) -> dict:
357 """Retrieve a well known template"""
358 return TEMPLATES[name]
359
360 def _createTemplate(self, template_id, name, access_model, profile):
361 client = self.host.getClient(profile)
362 d = defer.ensureDeferred(self.createTemplate(
363 client, template_id, name, access_model
364 ))
365 d.addCallback(lambda node_data: (node_data[0].full(), node_data[1]))
366 return d
367
368 async def createTemplate(
369 self, client, template_id: str, name: str, access_model: str
370 ) -> Tuple[jid.JID, str]:
371 """Create a list from a template"""
372 name = name.strip()
373 if not name:
374 name = shortuuid.uuid()
375 template = TEMPLATES[template_id]
376
377 fields = [
378 data_form.Field(fieldType="hidden", var=NS_TICKETS_TYPE, value=template_id)
379 ]
380 for field_data in template['fields']:
381 field_type = field_data.get('type', 'text-single')
382 kwargs = {
383 "fieldType": field_type,
384 "var": field_data["name"],
385 "label": field_data.get('label'),
386 "value": field_data.get("value"),
387 }
388 if field_type == "xhtml":
389 kwargs.update({
390 "fieldType": None,
391 "ext_type": "xml",
392 })
393 if kwargs["value"] is None:
394 kwargs["value"] = domish.Element((C.NS_XHTML, "div"))
395 elif "options" in field_data:
396 kwargs["options"] = [
397 data_form.Option(o["value"], o.get("label"))
398 for o in field_data["options"]
399 ]
400 field = data_form.Field(**kwargs)
401 fields.append(field)
402
403 schema = data_form.Form(
404 "form",
405 formNamespace=APP_NS_TICKETS,
406 fields=fields
407 ).toElement()
408
409 service = client.jid.userhostJID()
410 node = self._s.getSubmittedNS(f"{APP_NS_TICKETS}_{name}")
411 options = {
412 self._p.OPT_ACCESS_MODEL: access_model
413 }
414 await self._p.createNode(client, service, node, options)
415 await self._s.setSchema(client, service, node, schema)
416 list_elt = domish.Element((APP_NS_TICKETS, "list"))
417 list_elt["type"] = template_id
418 try:
419 await self.host.plugins['LIST_INTEREST'].registerPubsub(
420 client, APP_NS_TICKETS, service, node, creator=True,
421 name=name, element=list_elt)
422 except Exception as e:
423 log.warning(f"Can't add list to interests: {e}")
424 return service, node