Roundup Tracker

Javascript has the ability to make roundup much easier to use. However progressive enhancement is not always capable of producing the best ui efficiently.

The normal way of editing multilink relationships (like supersedes, or nosy lists) uses a text input where comma separated id numbers are used to indicate links. UseSelect2andRestForIssueMultilink replaces the standard text input. The replacement is a select input enhanced with a javascript library. The result provides interactive searching using REST. It can handle selections from thousands of values. The selection/edit of the list is much easier than the native select input.

Using progressive enhancement in case javascript was unavailable would require the select box to have every possible option loaded. This sends significant data to the client slowing generation, transmission and rendering.

It would be nice to be able to determine that javascript was unavailable. If javascript is disabled (or doesn't exist e.g. in the w3m, lynx browsers), the tracker can fall back to the classic text input with a comma separated list of ids. This produces a leaner interface for remote access over poor connections.

To do this, roundup has to be able to determine if the client has javascript enabled/available. The basic idea for how to do this was taken from: https://www.codeproject.com/Tips/1217469/How-to-Detect-if-Client-has-JavaScript-Enabled-Dis (The method in that post no longer works because using meta to set cookies is deprecated and doesn't work in chrome anymore.)

This implementation does the following:

  1. In the response to the client, roundup sets a cookie

    jsdisabled=true (using a header not meta). If this cookie is received in a request, roundup believes javascript is disabled.

  2. If roundup believes the client has javascript enabled, send out a

    meta refresh inside a <noscript> tag in the head of the document. If roundup was wrong and the client does have javascript disabled, a new request will be sent to roundup to re-generate the page without javascript enhancements.

  3. Generate a javascript function that deletes the jsdisabled cookie and if roundup thinks javascript was disabled, reload the page to get a new copy with javascript enhancements.

To do this a few new templating functions need to be created.

Note: it is probably a good idea to make the cookie secure. Do not make it httponly as client side javascript has to be able to access it. If you implement this and it works please update this article.

Add the following to a python file in the extensions directory of the tracker:

   1 def AddHtmlHeaders(client, header_dict={}):
   2     ''' Generate https headers from dict.
   3     '''
   4     headers = {}
   5     headers.update(header_dict)
   6 
   7     client_headers = client.additional_headers
   8 
   9     for header, value in list(headers.items()):
  10         if value is None:
  11             continue
  12         client_headers[header] = value
  13 
  14 def IsJsEnabled(client):
  15     return 'jsdisabled' not in client.cookie
  16 
  17 def JsEnabledScript(client):
  18     enabled = 'jsdisabled' not in client.cookie
  19     script='''
  20         // delete jsdisabled cookie
  21         document.cookie = "jsdisabled=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=%(path)s; SameSite=Strict";
  22         if ( ! %(enabled)s ) {
  23            // server thinks js is not enabled
  24            // reload page to update server
  25            window.location.reload() };
  26     '''% {  'path': client.cookie_path,
  27             'enabled': "true" if enabled else "false" }
  28     return script
  29 
  30 def init(instance):
  31     # add these lines to existing init if already defined.
  32     instance.registerUtil('AddHtmlHeaders', AddHtmlHeaders)
  33     instance.registerUtil('jsenabled', IsJsEnabled)
  34     instance.registerUtil('jsenabledscript', JsEnabledScript)

Then add this block to page.html:

<!-- (1) emit cookie always -->
<tal:code tal:content="python:utils.AddHtmlHeaders(request.client,
            {'Set-Cookie': 'jsdisabled=true; path=%s; SameSite=Strict' % request.client.cookie_path})" />


<noscript tal:condition="python:utils.jsenabled(request.client)" >
  <!-- (2) emitted only if we think it will not run (js enabled) -->
  <meta http-equiv="Refresh" content="0">
</noscript>

<script tal:attributes="nonce request/client/client_nonce"
        <!-- (3) emit always. If roundup thinks client has
         javascript disabled, generated function will cause
         a refresh -->
        tal:content="python:utils.jsenabledscript(request.client)">
</script>

before the closing <head>. The cookie_path parameter is extracted from the web attribute of the [tracker] section of your tracker's config.ini

Putting this in page.html makes it available to all templates. Using: tal:condition="python:utils.jsenabled(request.client)" in your tal will allow you to send out different content based on javascript availability.


CategoryInterfaceWeb CategoryJavascript