plugin misc lists: templates:
Templates are a way to create lists with pre-filled schemas.
This commit is contained in:
parent
8aebf00194
commit
bdf4759d21
2 changed files with 278 additions and 4 deletions
|
|
@ -15,11 +15,16 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import shortuuid
|
||||
from typing import Tuple
|
||||
from twisted.internet import defer
|
||||
from twisted.words.xish import domish
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from wokkel import data_form
|
||||
from sat.core.i18n import _, D_
|
||||
from sat.core.constants import Const as C
|
||||
from twisted.internet import defer
|
||||
from sat.tools.common import uri
|
||||
import shortuuid
|
||||
from sat.tools.common import data_format
|
||||
from sat.core.log import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
|
@ -27,6 +32,7 @@ log = getLogger(__name__)
|
|||
# XXX: this plugin was formely named "tickets", thus the namespace keeps this
|
||||
# name
|
||||
APP_NS_TICKETS = "org.salut-a-toi.tickets:0"
|
||||
NS_TICKETS_TYPE = "org.salut-a-toi.tickets#type:0"
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: _("Pubsub Lists"),
|
||||
|
|
@ -39,6 +45,160 @@ PLUGIN_INFO = {
|
|||
C.PI_DESCRIPTION: _("""Pubsub lists management plugin"""),
|
||||
}
|
||||
|
||||
TEMPLATES = {
|
||||
"todo": {
|
||||
"name": D_("TODO List"),
|
||||
"icon": "check",
|
||||
"fields": [
|
||||
{"name": "title"},
|
||||
{"name": "author"},
|
||||
{"name": "created"},
|
||||
{"name": "updated"},
|
||||
{"name": "time_limit"},
|
||||
{"name": "labels", "type": "text-multi"},
|
||||
{
|
||||
"name": "status",
|
||||
"label": D_("status"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("to do"),
|
||||
"value": "todo"
|
||||
},
|
||||
{
|
||||
"label": D_("in progress"),
|
||||
"value": "in_progress"
|
||||
},
|
||||
{
|
||||
"label": D_("done"),
|
||||
"value": "done"
|
||||
},
|
||||
],
|
||||
"value": "todo"
|
||||
},
|
||||
{
|
||||
"name": "priority",
|
||||
"label": D_("priority"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("major"),
|
||||
"value": "major"
|
||||
},
|
||||
{
|
||||
"label": D_("normal"),
|
||||
"value": "normal"
|
||||
},
|
||||
{
|
||||
"label": D_("minor"),
|
||||
"value": "minor"
|
||||
},
|
||||
],
|
||||
"value": "normal"
|
||||
},
|
||||
{"name": "body", "type": "xhtml"},
|
||||
{"name": "comments_uri"},
|
||||
]
|
||||
},
|
||||
"shopping": {
|
||||
"name": D_("Shopping List"),
|
||||
"icon": "basket",
|
||||
"fields": [
|
||||
{"name": "name", "label": D_("name")},
|
||||
{"name": "quantity", "label": D_("quantity")},
|
||||
{
|
||||
"name": "status",
|
||||
"label": D_("status"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("to buy"),
|
||||
"value": "to_buy"
|
||||
},
|
||||
{
|
||||
"label": D_("bought"),
|
||||
"value": "bought"
|
||||
},
|
||||
],
|
||||
"value": "to_buy"
|
||||
},
|
||||
]
|
||||
},
|
||||
"tickets": {
|
||||
"name": D_("Tickets"),
|
||||
"icon": "clipboard",
|
||||
"fields": [
|
||||
{"name": "title"},
|
||||
{"name": "author"},
|
||||
{"name": "created"},
|
||||
{"name": "updated"},
|
||||
{"name": "labels", "type": "text-multi"},
|
||||
{
|
||||
"name": "type",
|
||||
"label": D_("type"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("bug"),
|
||||
"value": "bug"
|
||||
},
|
||||
{
|
||||
"label": D_("feature request"),
|
||||
"value": "feature"
|
||||
},
|
||||
],
|
||||
"value": "bug"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"label": D_("status"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("queued"),
|
||||
"value": "queued"
|
||||
},
|
||||
{
|
||||
"label": D_("started"),
|
||||
"value": "started"
|
||||
},
|
||||
{
|
||||
"label": D_("review"),
|
||||
"value": "review"
|
||||
},
|
||||
{
|
||||
"label": D_("closed"),
|
||||
"value": "closed"
|
||||
},
|
||||
],
|
||||
"value": "queued"
|
||||
},
|
||||
{
|
||||
"name": "priority",
|
||||
"label": D_("priority"),
|
||||
"type": "list-single",
|
||||
"options": [
|
||||
{
|
||||
"label": D_("major"),
|
||||
"value": "major"
|
||||
},
|
||||
{
|
||||
"label": D_("normal"),
|
||||
"value": "normal"
|
||||
},
|
||||
{
|
||||
"label": D_("minor"),
|
||||
"value": "minor"
|
||||
},
|
||||
],
|
||||
"value": "normal"
|
||||
},
|
||||
{"name": "body", "type": "xhtml"},
|
||||
{"name": "comments_uri"},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PubsubLists:
|
||||
|
||||
|
|
@ -69,6 +229,7 @@ class PubsubLists:
|
|||
"author": self._s.valueOrPublisherFilter,
|
||||
"created": self._s.dateFilter,
|
||||
"updated": self._s.dateFilter,
|
||||
"time_limit": self._s.dateFilter,
|
||||
},
|
||||
profile_key=profile_key),
|
||||
async_=True,
|
||||
|
|
@ -91,6 +252,28 @@ class PubsubLists:
|
|||
profile_key=profile_key),
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"listTemplatesNamesGet",
|
||||
".plugin",
|
||||
in_sign="ss",
|
||||
out_sign="s",
|
||||
method=self._getTemplatesNames,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"listTemplateGet",
|
||||
".plugin",
|
||||
in_sign="sss",
|
||||
out_sign="s",
|
||||
method=self._getTemplate,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"listTemplateCreate",
|
||||
".plugin",
|
||||
in_sign="ssss",
|
||||
out_sign="(ss)",
|
||||
method=self._createTemplate,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
def _set(self, service, node, values, schema=None, item_id=None, extra='',
|
||||
profile_key=C.PROF_KEY_NONE):
|
||||
|
|
@ -103,8 +286,10 @@ class PubsubLists:
|
|||
d.addCallback(lambda ret: ret or "")
|
||||
return d
|
||||
|
||||
async def set(self, client, service, node, values, schema=None, item_id=None, extra=None,
|
||||
deserialise=False, form_ns=APP_NS_TICKETS):
|
||||
async def set(
|
||||
self, client, service, node, values, schema=None, item_id=None, extra=None,
|
||||
deserialise=False, form_ns=APP_NS_TICKETS
|
||||
):
|
||||
"""Publish a tickets
|
||||
|
||||
@param node(unicode, None): Pubsub node to use
|
||||
|
|
@ -152,3 +337,88 @@ class PubsubLists:
|
|||
return await self._s.set(
|
||||
client, service, node, values, schema, item_id, extra, deserialise, form_ns
|
||||
)
|
||||
|
||||
def _getTemplatesNames(self, language, profile):
|
||||
client = self.host.getClient(profile)
|
||||
return data_format.serialise(self.getTemplatesNames(client, language))
|
||||
|
||||
def getTemplatesNames(self, client, language: str) -> list:
|
||||
"""Retrieve well known list templates"""
|
||||
|
||||
templates = [{"id": tpl_id, "name": d["name"], "icon": d["icon"]}
|
||||
for tpl_id, d in TEMPLATES.items()]
|
||||
return templates
|
||||
|
||||
def _getTemplate(self, name, language, profile):
|
||||
client = self.host.getClient(profile)
|
||||
return data_format.serialise(self.getTemplate(client, name, language))
|
||||
|
||||
def getTemplate(self, client, name: str, language: str) -> dict:
|
||||
"""Retrieve a well known template"""
|
||||
return TEMPLATES[name]
|
||||
|
||||
def _createTemplate(self, template_id, name, access_model, profile):
|
||||
client = self.host.getClient(profile)
|
||||
d = defer.ensureDeferred(self.createTemplate(
|
||||
client, template_id, name, access_model
|
||||
))
|
||||
d.addCallback(lambda node_data: (node_data[0].full(), node_data[1]))
|
||||
return d
|
||||
|
||||
async def createTemplate(
|
||||
self, client, template_id: str, name: str, access_model: str
|
||||
) -> Tuple[jid.JID, str]:
|
||||
"""Create a list from a template"""
|
||||
name = name.strip()
|
||||
if not name:
|
||||
name = shortuuid.uuid()
|
||||
template = TEMPLATES[template_id]
|
||||
|
||||
fields = [
|
||||
data_form.Field(fieldType="hidden", var=NS_TICKETS_TYPE, value=template_id)
|
||||
]
|
||||
for field_data in template['fields']:
|
||||
field_type = field_data.get('type', 'text-single')
|
||||
kwargs = {
|
||||
"fieldType": field_type,
|
||||
"var": field_data["name"],
|
||||
"label": field_data.get('label'),
|
||||
"value": field_data.get("value"),
|
||||
}
|
||||
if field_type == "xhtml":
|
||||
kwargs.update({
|
||||
"fieldType": None,
|
||||
"ext_type": "xml",
|
||||
})
|
||||
if kwargs["value"] is None:
|
||||
kwargs["value"] = domish.Element((C.NS_XHTML, "div"))
|
||||
elif "options" in field_data:
|
||||
kwargs["options"] = [
|
||||
data_form.Option(o["value"], o.get("label"))
|
||||
for o in field_data["options"]
|
||||
]
|
||||
field = data_form.Field(**kwargs)
|
||||
fields.append(field)
|
||||
|
||||
schema = data_form.Form(
|
||||
"form",
|
||||
formNamespace=APP_NS_TICKETS,
|
||||
fields=fields
|
||||
).toElement()
|
||||
|
||||
service = client.jid.userhostJID()
|
||||
node = self._s.getSubmittedNS(f"{APP_NS_TICKETS}_{name}")
|
||||
options = {
|
||||
self._p.OPT_ACCESS_MODEL: access_model
|
||||
}
|
||||
await self._p.createNode(client, service, node, options)
|
||||
await self._s.setSchema(client, service, node, schema)
|
||||
list_elt = domish.Element((APP_NS_TICKETS, "list"))
|
||||
list_elt["type"] = template_id
|
||||
try:
|
||||
await self.host.plugins['LIST_INTEREST'].registerPubsub(
|
||||
client, APP_NS_TICKETS, service, node, creator=True,
|
||||
name=name, element=list_elt)
|
||||
except Exception as e:
|
||||
log.warning(f"Can't add list to interests: {e}")
|
||||
return service, node
|
||||
|
|
|
|||
|
|
@ -321,6 +321,10 @@ class PubsubSchema(object):
|
|||
for x_elt in item_elt.elements((data_form.NS_X_DATA, "x")):
|
||||
form = data_form.Form.fromElement(x_elt)
|
||||
if form_ns and form.formNamespace != form_ns:
|
||||
log.debug(
|
||||
f"form's namespace ({form.formNamespace!r}) differs from expected"
|
||||
f"{form_ns!r}"
|
||||
)
|
||||
continue
|
||||
prepend = [
|
||||
("label", "id"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue