Memo commands in Lino Noi

>>> from lino import startup
>>> startup('lino_book.projects.noi1e.settings.demo')
>>> from lino.api.doctest import *

The description of a ticket and the text of a comment (short_text) are rich text fields.

And additionally they can contain memo markup commands (see memo : The memo parser).

Lino Noi memo command reference

See also Built-in memo commands.

ticket

Refer to a ticket. Usage example:

See [ticket 1].

company

Refer to a company. Usage example:

I met Joe from [company 1] and we agreed...

person

Refer to a person. Usage example:

I met [person 7 Joe] and we agreed...
>>> from lino.utils.diag import analyzer
>>> print(analyzer.show_memo_commands())
... 

- [cal_entry ...] :
  Insert a reference to the specified Calendar entry.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [comment ...] :
  Insert a reference to the specified Comment.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [company ...] :
  Insert a reference to the specified Organization.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [group ...] :
  Insert a reference to the specified Team.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [person ...] :
  Insert a reference to the specified Person.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [product ...] :
  Insert a reference to the specified Product.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [ticket ...] :
  Insert a reference to the specified Ticket.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

- [upload ...] :
  Insert a reference to the specified Upload file.

  The first argument is mandatory and specifies the primary key.
  All remaining arguments are used as the text of the link.

The obj2memo method

Sometimes you want to programmatically generate a text containing memo markup.

For example when your code knows some database object and you want to create a description that would refer to your object if rendered with memo:

>>> ar = rt.login('robin')
>>> obj = rt.models.tickets.Ticket.objects.get(pk=1)
>>> txt = obj.obj2memo()
>>> print(txt)
[ticket 1] (Föö fails to bar when baz)

Let's also check whether the produced text is valid:

>>> print(ar.parse_memo(txt))
<a title="Föö fails to bar when baz" href="Detail">#1</a> (Föö fails to bar when baz)

Suggesters

There are two suggesters in Lino Noi: when the user types a "#", they get a list of tickets. When they type a "@", they get a list with all users.

Every site instance has its global memo parser:

>>> mp = dd.plugins.memo.parser
>>> mp.suggesters.keys()
dict_keys(['@', '#'])

A suggester always returns a maximum of 5 suggestions:

>>> len(list(mp.suggesters['#'].get_suggestions()))
5
>>> list(mp.suggesters['#'].get_suggestions("12"))
... 
[{'value': 12, 'title': '#12 (Foo cannot bar)', 'link':
"javascript:window.App.runAction({'actorId': 'tickets.Tickets', 'an': 'detail',
'rp': null, 'status': {'record_id': 12}})"}]
>>> list(mp.suggesters['#'].get_suggestions("why"))
... 
[{'value': 20, 'title': '#20 (Why is foo so bar)', 'link':
"javascript:window.App.runAction({'actorId': 'tickets.Tickets', 'an': 'detail',
'rp': null, 'status': {'record_id': 20}})"}, {'value': 29, 'title': '#29 (Why is
foo so bar)', 'link': "javascript:window.App.runAction({'actorId':
'tickets.Tickets', 'an': 'detail', 'rp': null, 'status': {'record_id': 29}})"},
{'value': 38, 'title': '#38 (Why is foo so bar)', 'link':
"javascript:window.App.runAction({'actorId': 'tickets.Tickets', 'an': 'detail',
'rp': null, 'status': {'record_id': 38}})"}, {'value': 47, 'title': '#47 (Why is
foo so bar)', 'link': "javascript:window.App.runAction({'actorId':
'tickets.Tickets', 'an': 'detail', 'rp': null, 'status': {'record_id': 47}})"},
{'value': 56, 'title': '#56 (Why is foo so bar)', 'link':
"javascript:window.App.runAction({'actorId': 'tickets.Tickets', 'an': 'detail',
'rp': null, 'status': {'record_id': 56}})"}]
>>> list(mp.suggesters['@'].get_suggestions())
... 
[{'value': 'jean', 'title': 'Jean', 'link':
"javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': 'detail',
'rp': null, 'status': {'record_id': 7}})"}, {'value': 'luc', 'title': 'Luc',
'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an':
'detail', 'rp': null, 'status': {'record_id': 6}})"}, {'value': 'marc', 'title':
'Marc', 'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers',
'an': 'detail', 'rp': null, 'status': {'record_id': 4}})"}, {'value': 'mathieu',
'title': 'Mathieu', 'link': "javascript:window.App.runAction({'actorId':
'users.AllUsers', 'an': 'detail', 'rp': null, 'status': {'record_id': 5}})"},
{'value': 'robin', 'title': 'Robin Rood', 'link':
"javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': 'detail',
'rp': null, 'status': {'record_id': 1}})"}]
>>> list(mp.suggesters['@'].get_suggestions("ma"))
[{'value': 'marc', 'title': 'Marc', 'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': 'detail', 'rp': null, 'status': {'record_id': 4}})"}, {'value': 'mathieu', 'title': 'Mathieu', 'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': 'detail', 'rp': null, 'status': {'record_id': 5}})"}, {'value': 'romain', 'title': 'Romain Raffault', 'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': 'detail', 'rp': null, 'status': {'record_id': 3}})"}]
>>> mp.suggesters['#'].get_object("1")
Ticket #1 ('#1 (Föö fails to bar when baz)')
>>> mp.parse("#1", ar)
'<a title="#1 (F&#246;&#246; fails to bar when baz)" href="Detail">#1</a>'

Bleaching

Comments a being bleached by default.

Check whether content has been bleached

>>> print(comments.Comment.objects.filter(body="o:OfficeDocumentSettings").first())
None

>> obj = comments.Comment.objects.filter(body__contains="TODO: include a summary of the modifications.").first()

>>> obj  = comments.Comment.objects.filter(body__contains="and follow your welcome messages").first()
>>> txt = dd.plugins.memo.parser.parse(obj.body)
>>> from lino.modlib.memo.mixins import truncate_comment
>>> short = truncate_comment(txt)
>>> obj.body_short_preview == short
True
>>> short
... 
'breaking \n\n\n\nDe\xa0: lino@foo.net [mailto:foo@bar.com]  Envoyé\xa0: mardi
18 octobre 2016 08:52 À\xa0: eexample@foo.com Objet\xa0: [welcht] YOU modified
FOO BAR\n\n\xa0\n\nDear Aurélie ,\n\nthis is to notify  / BAR\n\nBAR modified
\n\nTODO: include a summary of the modifications.\n\nAny subsequent
notifications about foo/  un...'
>>> print(obj.body_short_preview)
... 
breaking



De : lino@foo.net [mailto:foo@bar.com]  Envoyé : mardi ... À : eexample@foo.com Objet : [welcht] YOU modified FOO BAR
...

Dear Aurélie ,

this is to notify  / BAR

BAR modified

TODO: include a summary of the modifications.

Any subsequent notifications about foo/  un...

Above comments were created by the demo2 fixture of lino.modlib.comments.