Roundup Tracker - Issues

Issue 2551270

Better templating support for JavaScript
Type: rfe Severity: normal
Components: Web interface Versions: 2.4.0
Status: fixed fixed
: rouilj : asavchuk, rouilj
Priority: : Effort-Medium

Created on 2023-03-28 22:48 by rouilj, last changed 2024-03-26 20:49 by rouilj.

msg7745 Author: [hidden] (rouilj) Date: 2023-03-28 22:48
I sent this to roundup-devel. Got no replies. I think it is worth doing.

>I have been working on trackers that need more javascript than a
>classic tracker. Currently javascript is used via onclick or other onX
>handlers on elements (buttons, inputs) in the generated HTML. If you
>are using a content security policy, using onX inline javascript is a
>bad idea.  Instead you want a javascript function to run that adds
>event listeners to the elements. This will result in more required
>I would like to totally remove javascript from the Python code and
>move it to external file(s). These files could be loaded via:
>   <script src="@@file/...">
>tag or be included inline like base_javascript().  Inserting the
>javascript into the HTML will make the HTML work even if loading a
>script fails (network issues ...).
>When base_javascript() is called, it inserts values from Roundup
>(self._client.client_nonce, self.base) into the returned
>javascript/html. (Note that the client_nonce must never be exposed
>anywhere except in the HTML processed by the browser.)  Some
>javascript I have written needs replaced values as well.
>The tokens (except for security nonces) could be specified using a
>json formatted variable created by a templating extension. But some
>way to embed an external javascript file into the returned HTML is
>still required.
>My thought is to provide a templating method:
>  inline_javascript("filename", values={}, options={})
>This would:
>  1 read the contents of filename (relative to the tracker home
>    unless it starts with /) into a string. (Optional: if started
>    with @@file, it's relative to the HTML directory.)
>  2 use string interpolation with only named values '%(name)s' on the
>    string. (Optional: scan and  autofix '%' not followed by a '('
>    if there is a TypeError when interpolating.)
>  3 have a predefined set of values that can be updated by passing new
>    values using the values={} parameter. At the very least:
>      * client_nonce
>      * base
>    would be defined. If values=None is supplied, interpolation is
>    not done.
>  4 return the (optionally) interpolated string
>This should allow the file to:
>   * be externally stored,
>   * substitute Roundup variable values,
>   * be inlined in the returned HTML
>The options dict is for future expansion. E.G. the file could be
>minimized on the fly or something. (Note it would probably be better
>to use a .js.min and inline that to remove the processing overhead.)
>I chose string interpolation because '%(' is a unique marker. In
>javascript '%' is used for remainder operations. It is not heavily
>used IMO. If a parenthesized expression is needed, '% (' can be used
>as a workaround (although minimizing the file might be an issue).
>I decided not to use the string.format method because the specifiers
>"{...}" can be used in javascript and I am concerned this will cause
>syntax errors. (That will be difficult to auto-correct.)
>I am not sure if I could write a custom Formatter class to replace the
>'{' and '}' but that's more involved/fragile/requires testing. For
>output that is to be consumed by humans (e.g. to provide a template
>for email messages) I think format makes sense. But broken formatting
>here breaks the application.
>Similarly template strings require replacing $ with $$. Since a lot of
>javascript uses $ as a variable/function name (e.g. jQuery) template
>strings are a nonstarter as well.
msg7967 Author: [hidden] (rouilj) Date: 2024-03-26 20:49
In changeset:   7836:219fc5804345 I committed readfile and expandfile.

    def readfile(self, name, optional=False):
    def expandfile(self, name, values=None, optional=False):

Docs are in the code with mentions in reference.txt and CHANGES.txt.
If optional is True, the methods return an empty string if the 
file can't be found. Raise NoTemplate exception otherwise.

Changes from original discussion.

Only the template directory is searched, not tracker home.

No options={} are available.

Names changed since it can be used for lots of things besides javascript.

Autofixing bare %'s in the file on TypeError is not done. When using named parameters
and a dict, that becomes a ValueError not TypeError if the following char is not a '(' or valid
format char. So '4 % 5 is a value error unsupported format character ' ' (0x20) at index 9.
It's treated like any other value error: returning n empty string and logging an Error.
Date User Action Args
2024-03-26 20:49:06rouiljsetstatus: open -> fixed
resolution: fixed
messages: + msg7967
components: + Web interface
versions: + 2.4.0
2024-03-26 03:18:32rouiljsetstatus: new -> open
assignee: rouilj
2023-12-27 06:20:18asavchuksetnosy: + asavchuk
2023-03-28 22:48:18rouiljcreate