Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

About rendering action URLs

This document is about the is_on_main_actor attribute of an action request.

Side note: Code snippets (lines starting with >>>) in this document get tested as part of our development workflow. The following initialization snippet tells you which demo project is being used in this document.

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

Screenshot

This document uses the detail view of ticket #57 in the noi1e demo. Here is how this window looks in ExtJS (left) and in React (right):

The “Working sessions” panel in this screen shows that two users are currently working on this ticket. For each of these open sessions, you can either open the session itself (its detail window), or invoke its end_session action.

This summary panel is defined by the SessionsByTicket table, which has its default display mode set to ‘summary’. The summary is customized, it the total durations and a list of active sessions. In case you wonder how this summary is being generated, consult the source code of the table_as_summary method of SessionsByTicket.

Why we need is_on_main_actor

When you render an action button, you get a clickable html element that, when clicked, executes a given action.

The lino_xl.lib.working.Session.end_session action on a ticket is a window-less custom action. Such an action has no parameters and therefore does not open any popup window before executing. Clicking on such a button should immediately execute it.

In ExtJS, these actions are implemented as global JS functions that just call the global Lino.run_row_action function, which is defined in linoweb.js.

The action itself still runs on the server, so Lino.run_row_action sends an AJAX request.

That AJAX request needs to pass certain information that is available only in the JS client, e.g. the selected row, the requesting panel or the base parameters. But this should not happen (and would cause errors) for actions on a slave table.

We do not want Lino.run_row_action to add the parameter values of its requesting panel when it is being run from within a “non-main context”. A “non-main context” is for example in lino.core.actors.Actor.slave_as_html_meth() (i.e. when display_mode is html.

Note that currently we also don’t pass the mk and mt parameters (master key and master type). I think that this is correct.

Let A be our slave table (an actor) and obj the master instance for this slave table.

>>> A = working.SessionsByTicket
>>> obj = tickets.Ticket.objects.get(pk=59)

Here is how to simulate the content of this panel in a doctest.

>>> ses = rt.login('robin')
>>> ses.show(A, obj)
Total 64:54 hours.
Active sessions: `Luc since 09:00:00 <…>`__ **■**

In order to look at the href links generated by a JavaScript renderer, we simulate a web session by specifying the default renderer, which is an instance of lino.modlib.extjs.ext_renderer.ExtRenderer, a subclass of lino.core.renderer.JsRenderer.

>>> settings.SITE.kernel.default_renderer  
<lino.modlib.extjs.ext_renderer.ExtRenderer object at ...>
>>> ses = rt.login('robin', renderer=settings.SITE.kernel.default_renderer)

Now the lino.core.requests.BaseRequest.show() method no longer prints plain text to the stdout but a HTML string:

>>> ses.show(A, obj)  
'<div class="htmlText"><p>Total 64:54 hours.</p><p>Active sessions: <span><a
href="javascript:Lino.working.SessionsByTicket.detail.run(null,{
&quot;base_params&quot;: { &quot;mk&quot;: 59, &quot;mt&quot;: 43 },
&quot;param_values&quot;: { &quot;company&quot;: null,
&quot;companyHidden&quot;: null, &quot;end_date&quot;: null,
&quot;observed_event&quot;: &quot;Is active&quot;,
&quot;observed_eventHidden&quot;: &quot;20&quot;, &quot;session_type&quot;:
null, &quot;session_typeHidden&quot;: null, &quot;start_date&quot;: null,
&quot;ticket&quot;: &quot;#59 (Misc optimizations in Baz)&quot;,
&quot;ticketHidden&quot;: 59, &quot;user&quot;: null, &quot;userHidden&quot;:
null }, &quot;record_id&quot;: 1579 })" style="text-decoration:none">Luc since
09:00:00</a> <a
href="javascript:Lino.working.Sessions.end_session(null,false,1579,{
&quot;base_params&quot;: {  }, &quot;param_values&quot;: { &quot;company&quot;:
null, &quot;companyHidden&quot;: null, &quot;end_date&quot;: null,
&quot;observed_event&quot;: &quot;Is active&quot;,
&quot;observed_eventHidden&quot;: &quot;20&quot;, &quot;session_type&quot;:
null, &quot;session_typeHidden&quot;: null, &quot;start_date&quot;: null,
&quot;ticket&quot;: null, &quot;ticketHidden&quot;: null, &quot;user&quot;:
null, &quot;userHidden&quot;: null } })" title="Tell Lino that you stop this
session for now. This will simply set the end_time to the current time."
style="text-decoration:none">■</a></span></p></div>'
>>> ses.is_on_main_actor
True
>>> ar = rt.models.tickets.Tickets.request(parent=ses)
>>> ar.is_on_main_actor
True
>>> ar.actor
lino_xl.lib.tickets.ui.Tickets
>>> # ar.request = 123
>>> html = A.get_table_summary(obj, ar)

The href links in this html must be javascript links, not external ones.

>>> print(tostring(html))  
<div class="htmlText"><p>Total 64:54 hours.</p><p>Active sessions: <span><a
href="javascript:Lino.working.SessionsByTicket.detail.run(null,{
&quot;base_params&quot;: { &quot;mk&quot;: 59, &quot;mt&quot;: 43 },
&quot;param_values&quot;: { &quot;company&quot;: null,
&quot;companyHidden&quot;: null, &quot;end_date&quot;: null,
&quot;observed_event&quot;: &quot;Is active&quot;,
&quot;observed_eventHidden&quot;: &quot;20&quot;, &quot;session_type&quot;:
null, &quot;session_typeHidden&quot;: null, &quot;start_date&quot;: null,
&quot;ticket&quot;: &quot;#59 (Misc optimizations in Baz)&quot;,
&quot;ticketHidden&quot;: 59, &quot;user&quot;: null, &quot;userHidden&quot;:
null }, &quot;record_id&quot;: 1579 })" style="text-decoration:none">Luc since
09:00:00</a> <a
href="javascript:Lino.working.Sessions.end_session(null,false,1579,{
&quot;base_params&quot;: {  }, &quot;param_values&quot;: { &quot;company&quot;:
null, &quot;companyHidden&quot;: null, &quot;end_date&quot;: null,
&quot;observed_event&quot;: &quot;Is active&quot;,
&quot;observed_eventHidden&quot;: &quot;20&quot;, &quot;session_type&quot;:
null, &quot;session_typeHidden&quot;: null, &quot;start_date&quot;: null,
&quot;ticket&quot;: null, &quot;ticketHidden&quot;: null, &quot;user&quot;:
null, &quot;userHidden&quot;: null } })" title="Tell Lino that you stop this
session for now. This will simply set the end_time to the current time."
style="text-decoration:none">■</a></span></p></div>

Let’s make this more readable:

>>> soup = BeautifulSoup(tostring(html), 'lxml')
>>> # print(soup.body.prettify())
>>> links = soup.body.find_all('a')
>>> len(links)
2
>>> for lnk in links:
...    print(lnk['href'])  
javascript:Lino.working.SessionsByTicket.detail.run(null,{ "base_params": { "mk": 59, "mt": 43 }, "param_values": { "company": null, "companyHidden": null, "end_date": null, "observed_event": "Is active", "observed_eventHidden": "20", "session_type": null, "session_typeHidden": null, "start_date": null, "ticket": "#59 (Misc optimizations in Baz)", "ticketHidden": 59, "user": null, "userHidden": null }, "record_id": 1579 })
javascript:Lino.working.Sessions.end_session(null,false,1579,{ "base_params": {  }, "param_values": { "company": null, "companyHidden": null, "end_date": null, "observed_event": "Is active", "observed_eventHidden": "20", "session_type": null, "session_typeHidden": null, "start_date": null, "ticket": null, "ticketHidden": null, "user": null, "userHidden": null } })