Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
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.
- [file ...] :
Insert an image tag of the specified upload file.
- [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 href="…" title="Föö fails to bar when baz">#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
Every item of the list is a dict with three keys value, title and link.
>>> pprint(list(mp.suggesters['#'].get_suggestions("12")))
...
[{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 12}})",
'title': '#12 (Foo cannot bar)',
'value': '12 (Foo cannot bar)'}]
>>> pprint(list(mp.suggesters['#'].get_suggestions("why")))
...
[{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 20}})",
'title': '#20 (Why is foo so bar)',
'value': '20 (Why is foo so bar)'},
{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 32}})",
'title': '#32 (Why is foo so bar)',
'value': '32 (Why is foo so bar)'},
{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 44}})",
'title': '#44 (Why is foo so bar)',
'value': '44 (Why is foo so bar)'},
{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 56}})",
'title': '#56 (Why is foo so bar)',
'value': '56 (Why is foo so bar)'},
{'link': "javascript:window.App.runAction({'actorId': 'tickets.Tickets', "
"'an': 'detail', 'rp': null, 'status': {'record_id': 68}})",
'title': '#68 (Why is foo so bar)',
'value': '68 (Why is foo so bar)'}]
>>> pprint(list(mp.suggesters['@'].get_suggestions()))
...
[{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 7}})",
'title': 'Jean',
'value': 'jean'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 6}})",
'title': 'Luc',
'value': 'luc'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 4}})",
'title': 'Marc',
'value': 'marc'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 5}})",
'title': 'Mathieu',
'value': 'mathieu'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 1}})",
'title': 'Robin Rood',
'value': 'robin'}]
>>> pprint(list(mp.suggesters['@'].get_suggestions("ma")))
[{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 4}})",
'title': 'Marc',
'value': 'marc'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 5}})",
'title': 'Mathieu',
'value': 'mathieu'},
{'link': "javascript:window.App.runAction({'actorId': 'users.AllUsers', 'an': "
"'detail', 'rp': null, 'status': {'record_id': 3}})",
'title': 'Romain Raffault',
'value': 'romain'}]
>>> mp.suggesters['#'].get_object("1")
Ticket #1 ('#1 (Föö fails to bar when baz)')
>>> mp.parse("#1", ar)
'<a href="…" title="#1 (Föö fails to bar when baz)">#1</a>'
Bleaching¶
Comments are 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\n\n\n<strong><span>De\xa0:</span></strong> <a
href="mailto:lino@foo.net">lino@foo.net</a> [<a
href="mailto:foo@bar.com">mailto:foo@bar.com</a>] <br/>
<strong>Envoyé\xa0:</strong> mardi 18 octobre 2016 08:52<br/>
<strong>À\xa0:</strong> <a href="mailto:Far@baz.net">eexample@foo.com</a><br/>
<strong>Objet\xa0:</strong> [welcht] YOU modified FOO BAR\n\n\n\xa0\n\n\nDear
Aurélie ,\n\n\nthis is to notify / BAR\n\n\nBAR modified \n\n\nTODO: include a
summary of the modifications.\n\n\nAny subsequent notifications about ...'
>>> print(obj.body_short_preview)
...
breaking
<strong><span>De :</span></strong> <a href="mailto:lino@foo.net">lino@foo.net</a> [<a href="mailto:foo@bar.com">mailto:foo@bar.com</a>] <br/> <strong>Envoyé :</strong> ...<br/> <strong>À :</strong> <a href="mailto:Far@baz.net">eexample@foo.com</a><br/> <strong>Objet :</strong> [welcht] YOU modified FOO BAR
<BLANKLINE> <BLANKLINE> Dear Aurélie , <BLANKLINE> <BLANKLINE> this is to notify / BAR <BLANKLINE> <BLANKLINE> BAR modified <BLANKLINE> <BLANKLINE> TODO: include a summary of the modifications. <BLANKLINE> <BLANKLINE> Any subsequent notifications about …
Above comments were created by the demo2
fixture of
lino.modlib.comments
.
Permalink URIs¶
Note that the URI of the link depends on the context.
Of course it depends on the site’s front end (specfied in the default_ui
setting). But when the front end is
lino.modlib.extjs
, then we also get a different URL depending on whether
lino.core.requests.BaseRequest.permalink_uris
is set or not: Usually we
want a “javascript:…” URI because we don’t want the page to reload when
executing an action.
For example when calling it e.g. from send_summary_emails
, we want a “permalink” whose
URI also works in the recipients email client where the JS application isn’t yet
loaded. In that case we must explicitly set
lino.core.requests.BaseRequest.permalink_uris
to True.
>>> ses = rt.login('robin',
... renderer=settings.SITE.kernel.default_renderer)
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="javascript:Lino.tickets.Tickets.detail.run(null,{ "record_id": 1 })" title="Föö fails to bar when baz" style="text-decoration:none">#1</a>.
>>> ses.permalink_uris = True
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="/api/tickets/Tickets/1" title="Föö fails to bar when baz" style="text-decoration:none">#1</a>.
While the lino.modlib.bootstrap3
front end will render it
like this:
>>> ses = rt.login(renderer=dd.plugins.bootstrap3.renderer)
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="/bs3/tickets/Tickets/1" title="Föö fails to bar when baz" style="text-decoration:none">#1</a>.
When using this front end, the permalink_uris
parameter has no effect:
>>> ses.permalink_uris = True
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="/bs3/tickets/Tickets/1" title="Föö fails to bar when baz" style="text-decoration:none">#1</a>.
Or the plain text renderer will render:
>>> ses = rt.login()
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="…" title="Föö fails to bar when baz">#1</a>.
>>> ses.permalink_uris = True
>>> print(ses.parse_memo("See [ticket 1]."))
See <a href="…" title="Föö fails to bar when baz">#1</a>.