changeset 5479:56d30f09f205

address Unicode encoding issues in jinja2 template issue2550811
author Christof Meerwald <cmeerw@cmeerw.org>
date Tue, 31 Jul 2018 21:48:37 +0100
parents c8902d398434
children e8f6a1df73e3
files CHANGES.txt share/roundup/templates/jinja2/html/_generic.404.html share/roundup/templates/jinja2/html/_generic.collision.html share/roundup/templates/jinja2/html/_generic.help-empty.html share/roundup/templates/jinja2/html/_generic.index.html share/roundup/templates/jinja2/html/file.index.html share/roundup/templates/jinja2/html/file.item.html share/roundup/templates/jinja2/html/home.classlist.html share/roundup/templates/jinja2/html/home.html share/roundup/templates/jinja2/html/issue.index.html share/roundup/templates/jinja2/html/issue.item.edit.html share/roundup/templates/jinja2/html/issue.item.html share/roundup/templates/jinja2/html/issue.item.readonly.html share/roundup/templates/jinja2/html/keyword.item.html share/roundup/templates/jinja2/html/layout/banner.html share/roundup/templates/jinja2/html/layout/footer.html share/roundup/templates/jinja2/html/layout/navigation.html share/roundup/templates/jinja2/html/layout/page.html share/roundup/templates/jinja2/html/layout/pagination.html share/roundup/templates/jinja2/html/layout/permission.html share/roundup/templates/jinja2/html/layout/sort.html share/roundup/templates/jinja2/html/msg.index.html share/roundup/templates/jinja2/html/msg.item.html share/roundup/templates/jinja2/html/user.forgotten.html share/roundup/templates/jinja2/html/user.index.html share/roundup/templates/jinja2/html/user.item.html share/roundup/templates/jinja2/html/user.register.html share/roundup/templates/jinja2/html/user.rego_progress.html
diffstat 28 files changed, 274 insertions(+), 223 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Sun Jul 29 15:52:15 2018 +0100
+++ b/CHANGES.txt	Tue Jul 31 21:48:37 2018 +0100
@@ -11,6 +11,15 @@
 v2.7.2 is required to run newer releases of Roundup.
 
 
+2018-??-?? ?.?.0
+
+Fixed:
+
+- issue2550811: work around Unicode encoding issues in jinja2 template
+  by explicitly converting data to Unicode; also fixed pagination and
+  selecting columns to display in the issues list (Christof Meerwald)
+
+
 2018-07-13 1.6.0
 
 Features:
--- a/share/roundup/templates/jinja2/html/_generic.404.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/_generic.404.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,12 +1,12 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  404 - {{ i18n.gettext('page not found') }}
+  404 - {{ i18n.gettext('page not found')|u }}
 {% endblock %}
 
 {% block page_content %}
   <div class='container-fluid'>
-    <h1 class='text-error'>{{ i18n.gettext('404 - page not found') }}</h1>
-    <p style='text-align: center'>{{ i18n.gettext('What were you looking for anyways?') }}</p>
+    <h1 class='text-error'>{{ i18n.gettext('404 - page not found')|u }}</h1>
+    <p style='text-align: center'>{{ i18n.gettext('What were you looking for anyways?')|u }}</p>
   </div>
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/_generic.collision.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/_generic.collision.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Edit Collision') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Edit Collision')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('Edit Collision') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Edit Collision')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block content %}
@@ -13,7 +13,7 @@
     {{ i18n.gettext('There has been a collision.
     Another user updated this node
     while you were editing. Please') }}
-    <a href='{{ context }}'>{{ i18n.gettext('reload') }}</a>
-    {{ i18n.gettext('the node and review your edits.') }}
+    <a href='{{ context }}'>{{ i18n.gettext('reload')|u }}</a>
+    {{ i18n.gettext('the node and review your edits.')|u }}
   </p>
-</tal:block>
+{% endblock %}
--- a/share/roundup/templates/jinja2/html/_generic.help-empty.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/_generic.help-empty.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Empty page') }}
+  {{ i18n.gettext('Empty page')|u }}
 {% endblock %}
 
 {% block page_content %}
   <p>
-    {{ i18n.gettext('Please specify your search parameters!') }}
+    {{ i18n.gettext('Please specify your search parameters!')|u }}
   </p>
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/_generic.index.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/_generic.index.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ context._classname.capitalize() }} {{ i18n.gettext('editing') }}
+  {{ context._classname.capitalize() }} {{ i18n.gettext('editing')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ context._classname.capitalize() }} {{ i18n.gettext('editing') }}
+  {{ context._classname.capitalize() }} {{ i18n.gettext('editing')|u }}
 {% endblock %}
 
 {% block content %}
@@ -32,11 +32,11 @@
      removed item and you know its id then just put that id in the id column.
     ') }}</p>
     <form method="POST" action='{{ context.designator() }}'>
-      <textarea rows="15" style="width:90%" name="rows">{{ context.csv }}</textarea>
+      <textarea rows="15" style="width:90%" name="rows">{{ context.csv() }}</textarea>
       <br>
       <input name="@csrf" type="hidden" value="{{ utils.anti_csrf_nonce() }}">
-      <input type="hidden" name="@action" value="{{ i18n.gettext('editCSV') }}">
-      <input type="submit" value="{{ i18n.gettext('Edit Items') }}">
+      <input type="hidden" name="@action" value="{{ i18n.gettext('editCSV')|u }}">
+      <input type="submit" value="{{ i18n.gettext('Edit Items')|u }}">
     </form>
   {% endif %}
 
--- a/share/roundup/templates/jinja2/html/file.index.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/file.index.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,22 +1,22 @@
-<h4>{{ i18n.gettext('Files') }}</h4>
+<h4>{{ i18n.gettext('Files')|u }}</h4>
 <table class='table'>
   <tr>
-    <th>{{ i18n.gettext('File name') }}</th>
-    <th>{{ i18n.gettext('Uploaded') }}</th>
-    <th>{{ i18n.gettext('Type') }}</th>
-    <th>{{ i18n.gettext('Edit') }}</th>
-    <th>{{ i18n.gettext('Remove') }}</th>
+    <th>{{ i18n.gettext('File name')|u }}</th>
+    <th>{{ i18n.gettext('Uploaded')|u }}</th>
+    <th>{{ i18n.gettext('Type')|u }}</th>
+    <th>{{ i18n.gettext('Edit')|u }}</th>
+    <th>{{ i18n.gettext('Remove')|u }}</th>
   </tr>
   {% for file in context.files %}
     <tr>
       <td>
-        <a href='{{ file.download_url() }}'>{{ file.name }}</a>
+        <a href='{{ file.download_url() }}'>{{ file.name|u|e }}</a>
       </td>
-      <td>{{ file.creator }}, {{ file.creation }}</td>
-      <td>{{ file.type }}</td>
+      <td>{{ file.creator }}, {{ file.creation|u|e }}</td>
+      <td>{{ file.type|u|e }}</td>
       {% if file.is_edit_ok %}
         <td>
-          <a href='file{{ file.id }}'>{{ i18n.gettext('edit') }}</a>
+          <a href='file{{ file.id }}'>{{ i18n.gettext('edit')|u }}</a>
         </td>
       {% endif %}
       {% if context.is_edit_ok %}
@@ -24,8 +24,8 @@
           <form method="POST" action='issue{{ context.id }}'>
             <input type="hidden" name="@remove@files" value='{{ file.id }}'>
             <input name="@csrf" type="hidden" value="{{ utils.anti_csrf_nonce() }}">
-            <input type="hidden" name="@action" value="{{ i18n.gettext('edit') }}">
-            <input type="submit" value="{{ i18n.gettext('remove') }}">
+            <input type="hidden" name="@action" value="{{ i18n.gettext('edit')|u }}">
+            <input type="submit" value="{{ i18n.gettext('remove')|u }}">
           </form>
         </td>
       {% endif %}
--- a/share/roundup/templates/jinja2/html/file.item.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/file.item.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('File display') }}
+  {{ i18n.gettext('File display')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('File display') }}
+  {{ i18n.gettext('File display')|u }}
 {% endblock %}
 
 {% block content %}
@@ -17,10 +17,10 @@
       enctype="multipart/form-data" action='{{ context.designator() }}'>
 
       <dl class='dl-horizontal'>
-        <dt>{{ i18n.gettext('Name') }}</dt>
-        <dd>{{ context.name.field() }}</dd>
-        <dt>{{ i18n.gettext('Content Type') }}</dt>
-        <dd>{{ context.type.field() }}</dd>
+        <dt>{{ i18n.gettext('Name')|u }}</dt>
+        <dd>{{ context.name.field()|u|e }}</dd>
+        <dt>{{ i18n.gettext('Content Type')|u }}</dt>
+        <dd>{{ context.type.field()|u|e }}</dd>
       </dl>
 
       <input type="hidden" name="@template" value="item">
@@ -37,11 +37,11 @@
 
   {% if context.id and context.is_view_ok() %}
     <div class='row-fluid'>
-      <a href='file{{ context.id }}/{{ context.name }}'>{{ i18n.gettext('Download') }}</a>
+      <a href='file{{ context.id }}/{{ context.name|u|e }}'>{{ i18n.gettext('Download')|u }}</a>
     </div>
   {% endif %}
 
   <div class='vspace-five'></div>
-  {{ context.history() }}
+  {{ context.history()|u }}
 
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/home.classlist.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/home.classlist.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('List of classes') }}
+  {{ i18n.gettext('List of classes')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('List of classes') }}
+  {{ i18n.gettext('List of classes')|u }}
 {% endblock %}
 
 {% block content %}
--- a/share/roundup/templates/jinja2/html/home.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/home.html	Tue Jul 31 21:48:37 2018 +0100
@@ -4,5 +4,5 @@
     filter=['status'],
     columns=['id','activity','title','creator','assignedto', 'status'],
     filterspec={'status':['-1','1','2','3','4','5','6','7']}
-  )
+  )|u
 }}
--- a/share/roundup/templates/jinja2/html/issue.index.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/issue.index.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('List of issues') }}
+  {{ i18n.gettext('List of issues')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('List of issues') }}
+  {{ i18n.gettext('List of issues')|u }}
 {% endblock %}
 
 {% block content %}
@@ -16,38 +16,74 @@
     {% if context.list() %}
       <table class='table'>
         <tr class='info'>
-          <td>{{ i18n.gettext('Priority') }}</td>
-          <td>{{ i18n.gettext('ID') }}</td>
-          <td>{{ i18n.gettext('Activity') }}</td>
-          <td>{{ i18n.gettext('Actor') }}</td>
-          <td>{{ i18n.gettext('Keyword') }}</td>
-          <td>{{ i18n.gettext('Title') }}</td>
-          <td>{{ i18n.gettext('Status') }}</td>
-          <td>{{ i18n.gettext('Creator') }}</td>
-          <td>{{ i18n.gettext('Assigned To') }}</td>
+		  {% if request.show.priority %}
+          <td>{{ i18n.gettext('Priority')|u }}</td>
+		  {% endif %}
+		  {% if request.show.id %}
+          <td>{{ i18n.gettext('ID')|u }}</td>
+		  {% endif %}
+		  {% if request.show.activity %}
+          <td>{{ i18n.gettext('Activity')|u }}</td>
+		  {% endif %}
+		  {% if request.show.actor %}
+          <td>{{ i18n.gettext('Actor')|u }}</td>
+		  {% endif %}
+		  {% if request.show.keyword %}
+          <td>{{ i18n.gettext('Keyword')|u }}</td>
+		  {% endif %}
+		  {% if request.show.title %}
+          <td>{{ i18n.gettext('Title')|u }}</td>
+		  {% endif %}
+		  {% if request.show.status %}
+          <td>{{ i18n.gettext('Status')|u }}</td>
+		  {% endif %}
+		  {% if request.show.creator %}
+          <td>{{ i18n.gettext('Creator')|u }}</td>
+		  {% endif %}
+		  {% if request.show.assignedto %}
+          <td>{{ i18n.gettext('Assigned To')|u }}</td>
+		  {% endif %}
         </tr>
         {% for issue in context.list() %}
           <tr>
-           <td>{{ issue.priority.plain() }}</td>
+		   {% if request.show.priority %}
+           <td>{{ issue.priority.plain()|u|e }}</td>
+		   {% endif %}
+		   {% if request.show.id %}
            <td>{{ issue.id }}</td>
+		   {% endif %}
+		   {% if request.show.activity %}
            <td>{{ issue.activity }}</td>
-           <td>{{ issue.actor }}</td>
-           <td>{{ issue.keyword.plain() }}</td>
+		   {% endif %}
+		   {% if request.show.actor %}
+           <td>{{ issue.actor|u|e }}</td>
+		   {% endif %}
+		   {% if request.show.keyword %}
+           <td>{{ issue.keyword.plain()|u|e }}</td>
+		   {% endif %}
+		   {% if request.show.title %}
            <td>
-            <a href='issue{{ issue.id }}'>{{ issue.title.plain(hyperlink=0) }}</a>
+            <a href='issue{{ issue.id }}'>{{ issue.title.plain(hyperlink=0)|u|e }}</a>
            </td>
-           <td>{{ issue.status.plain() }}</td>
-           <td>{{ issue.creator.plain() }}</td>
+		   {% endif %}
+		   {% if request.show.status %}
+           <td>{{ issue.status.plain()|u|e }}</td>
+		   {% endif %}
+		   {% if request.show.creator %}
+           <td>{{ issue.creator.plain()|u|e }}</td>
+		   {% endif %}
+		   {% if request.show.assignedto %}
            <td>{{ issue.assignedto.plain() }}</td>
+		   {% endif %}
           </tr>
         {% endfor %}
       </table>
       {% include 'layout/pagination.html' %}
-  <div class='row-fluid'> 
+  <div class='row-fluid'>
     <div class='pull-right'>
       <a class='btn btn-success'
           href="{{ request.indexargs_url('issue', {'@action':'export_csv'}) }}">
-        {{ i18n.gettext('Download as CSV') }}
+        {{ i18n.gettext('Download as CSV')|u }}
       </a>
     </div>
   </div>
@@ -55,7 +91,7 @@
   {% include 'layout/sort.html' %}
 
     {% else %}
-      <p class='text-error'>{{ i18n.gettext('There are no issues yet!') }}</p>
+      <p class='text-error'>{{ i18n.gettext('There are no issues yet!')|u }}</p>
     {% endif %}
 
   {% endif %}
--- a/share/roundup/templates/jinja2/html/issue.item.edit.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/issue.item.edit.html	Tue Jul 31 21:48:37 2018 +0100
@@ -11,9 +11,9 @@
     <!-- title -->
     <div class='row-fluid'>
       <div class='control-group'>
-        <label class='control-label' for='title'>{{ i18n.gettext('Title') }}</label>
+        <label class='control-label' for='title'>{{ i18n.gettext('Title')|u }}</label>
         <div class='controls'>
-          <input name='title' id='title' type='text' class='input-xxlarge' value='{{ context.title }}' required>
+          <input name='title' id='title' type='text' class='input-xxlarge' value='{{ context.title.plain()|u|e }}' required>
         </div>
       </div>
     </div> <!-- row-fluid -->
@@ -21,15 +21,15 @@
     <!-- priority & Status -->
     <div class='row-fluid'>
       <div class='control-group span6'>
-        <label class='control-label' for='priority'>{{ i18n.gettext('Priority') }}</label>
+        <label class='control-label' for='priority'>{{ i18n.gettext('Priority')|u }}</label>
         <div class='controls'>
-          {{ context.priority.menu() }}
+          {{ context.priority.menu()|u }}
         </div>
       </div>
       <div class='control-group span6'>
-        <label class='control-label' for='status'>{{ i18n.gettext('Status') }}</label>
+        <label class='control-label' for='status'>{{ i18n.gettext('Status')|u }}</label>
         <div class='controls'>
-          {{ context.status.menu() }}
+          {{ context.status.menu()|u }}
         </div>
       </div>
     </div> <!-- row-fluid -->
@@ -37,15 +37,15 @@
     <!-- Superseder & nosy list -->
     <div class='row-fluid'>
       <div class='control-group span6'>
-        <label class='control-label' for='superseder'>{{ i18n.gettext('Superseder') }}</label>
+        <label class='control-label' for='superseder'>{{ i18n.gettext('Superseder')|u }}</label>
         <div class='controls'>
-          <input type='text' name='superseder'>
+          <input type='text' name='superseder' id='superseder' value='{{ context.superseder.plain()|u|e }}'>
         </div>
       </div>
       <div class='control-group span6'>
-        <label class='control-label' for='nosylist'>{{ i18n.gettext('Nosy list') }}</label>
+        <label class='control-label' for='nosylist'>{{ i18n.gettext('Nosy list')|u }}</label>
         <div class='controls'>
-          <input type='text' name='nosy'>
+          <input type='text' name='nosy' id='nosylist' value='{{ context.nosy.plain()|u|e }}'>
         </div>
       </div>
     </div> <!-- row-fluid -->
@@ -53,15 +53,15 @@
     <!-- Assigned to & keywords -->
     <div class='row-fluid'>
       <div class='control-group span6'>
-        <label class='control-label' for='assignedto'>{{ i18n.gettext('Assigned to') }}</label>
+        <label class='control-label' for='assignedto'>{{ i18n.gettext('Assigned to')|u }}</label>
         <div class='controls'>
-          {{ context.assignedto.menu() }}
+          {{ context.assignedto.menu()|u }}
         </div>
       </div>
       <div class='control-group span6'>
-        <label class='control-label' for='keywords'>{{ i18n.gettext('Keywords') }}</label>
+        <label class='control-label' for='keyword'>{{ i18n.gettext('Keywords')|u }}</label>
         <div class='controls'>
-          <input type='text' name='keywords' id='keywords'>
+          <input type='text' name='keyword' id='keyword' value='{{ context.keyword.plain()|u|e }}'>
         </div>
       </div>
     </div> <!-- row-fluid -->
@@ -69,7 +69,7 @@
     <!-- Note -->
     <div class='row-fluid'>
       <div class='control-group'>
-        <label class='control-label' for='change_note'>{{ i18n.gettext('Change note') }}</label>
+        <label class='control-label' for='change_note'>{{ i18n.gettext('Change note')|u }}</label>
         <div class='controls'>
           <textarea name="@note" rows="5" class='input-xxlarge' id='change_note'></textarea>
         </div>
@@ -79,7 +79,7 @@
     <!-- File upload -->
     <div class='row-fluid'>
       <div class='control-group'>
-        <label class='control-label' for='file_upload'>{{ i18n.gettext('File') }}</label>
+        <label class='control-label' for='file_upload'>{{ i18n.gettext('File')|u }}</label>
         <div class='controls'>
           <input type="file" name="@file" id='file_upload'>
         </div>
@@ -89,7 +89,7 @@
   <div class='form-actions'>
     {{ context.submit() }}
     {% if context.id %}
-      <a href='{{ context.copy_url() }}'>{{ i18n.gettext('Make a copy') }}</a>
+      <a href='{{ context.copy_url() }}'>{{ i18n.gettext('Make a copy')|u }}</a>
     {% endif %}
   </div>
   <input type="hidden" name="@template" value="item">
--- a/share/roundup/templates/jinja2/html/issue.item.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/issue.item.html	Tue Jul 31 21:48:37 2018 +0100
@@ -2,7 +2,7 @@
 
 {% block head_title %}
   {% if context.id %}
-    Issue {{ context.id }}: {{ context.title }} - {{ config.TRACKER_NAME }}
+    Issue {{ context.id }}: {{ context.title.plain()|u|e }} - {{ config.TRACKER_NAME }}
   {% else %}
     New Issue - {{ config.TRACKER_NAME }}
   {% endif %}
@@ -73,6 +73,6 @@
 
   <div class='vspace-five'></div>
 
-  {{ context.history() }}
+  {{ context.history()|u }}
 
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/issue.item.readonly.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/issue.item.readonly.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,16 +1,16 @@
 <dl class='dl-horizontal'>
-  <dt>{{ i18n.gettext('Title') }}</dt>
-  <dd>{{ context.title.plain() }}</dd>
-  <dt>{{ i18n.gettext('Priority') }}</dt>
-  <dd>{{ context.priority.plain() }}</dd>
-  <dt>{{ i18n.gettext('Status') }}</dt>
-  <dd>{{ context.status.plain() }}</dd>
-  <dt>{{ i18n.gettext('Superseder') }}</dt>
-  <dd>{{ context.superseder.plain() }}</dd>
-  <dt>{{ i18n.gettext('Nosy list') }}</dt>
-  <dd>{{ context.nosy.plain() }}</dd>
-  <dt>{{ i18n.gettext('Assigned to') }}</dt>
-  <dd>{{ context.assignedto.plain() }}</dd>
-  <dt>{{ i18n.gettext('Keywords') }}</dt>
-  <dd>{{ context.keyword.plain() }}</dd>
+  <dt>{{ i18n.gettext('Title')|u }}</dt>
+  <dd>{{ context.title.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Priority')|u }}</dt>
+  <dd>{{ context.priority.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Status')|u }}</dt>
+  <dd>{{ context.status.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Superseder')|u }}</dt>
+  <dd>{{ context.superseder.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Nosy list')|u }}</dt>
+  <dd>{{ context.nosy.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Assigned to')|u }}</dt>
+  <dd>{{ context.assignedto.plain()|u|e }}</dd>
+  <dt>{{ i18n.gettext('Keywords')|u }}</dt>
+  <dd>{{ context.keyword.plain()|u|e }}</dd>
 </dl>
--- a/share/roundup/templates/jinja2/html/keyword.item.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/keyword.item.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Keyword editing') }}
+  {{ i18n.gettext('Keyword editing')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('Keyword editing') }}
+  {{ i18n.gettext('Keyword editing')|u }}
 {% endblock %}
 
 {% block content %}
@@ -19,10 +19,10 @@
   <ul class='nav nav-pills nav-stacked'>
     {% for keyword in db.keyword.list() %}
       <li>
-        <a href='keyword{{ keyword.id }}'>{{ keyword.name }}</a>
+        <a href='keyword{{ keyword.id }}'>{{ keyword.name.plain()|u|e }}</a>
       </li>
     {% else %}
-      <p class='text-mute'>{{ i18n.gettext('There are no keywords yet.  Be the first -') }}</p>
+      <p class='text-mute'>{{ i18n.gettext('There are no keywords yet.  Be the first -')|u }}</p>
     {% endfor %}
   </ul>
 
@@ -33,6 +33,6 @@
     <input name="@csrf" type="hidden"
            value="{{ utils.anti_csrf_nonce() }}">
     <input type="hidden" name="@action" value="new">
-    <input type='submit' class='btn btn-primary' value="{{ i18n.gettext('Create keyword') }}">
+    <input type='submit' class='btn btn-primary' value="{{ i18n.gettext('Create keyword')|u }}">
   </form>
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/layout/banner.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/banner.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,7 +1,7 @@
 <div class="navbar">
   <div class='container-fluid'>
     <div class='navbar-inner'>
-       <a href="/" class='brand'><i class='icon-home'></i> {{ i18n.gettext('Roundup Demo Tracker') }}</a>
+       <a href="/" class='brand'><i class='icon-home'></i> {{ i18n.gettext('Roundup Demo Tracker')|u }}</a>
        <form name="searchform" method="get" action="bug" class='navbar-search pull-right'>
          <input type="hidden" name="@columns" value="{{ bug_columns_showall }}"/>
          <input type="hidden" name="@sort" value="-activity">
@@ -9,7 +9,7 @@
          <input type="hidden" name="@filter" value="status">
          <input type="hidden" name="status" value="{{ bug_status_notclosed }}"/>
          <input class="input-medium search-query" id="search-text" type="search" name="@search_text">
-         <input type="submit" value="{{ i18n.gettext('search in open bugs') }}" name="submit" class='btn'>
+         <input type="submit" value="{{ i18n.gettext('search in open bugs')|u }}" name="submit" class='btn'>
        </form>
     </div> <!-- navbar-inner -->
   </div> <!-- container-fluid -->
--- a/share/roundup/templates/jinja2/html/layout/footer.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/footer.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,8 +1,8 @@
 <footer>
   <div class='container-fluid'>
     <hr>
-    {{ i18n.gettext('A') }}
-    <a href='http://www.roundup-tracker.org/'>{{ i18n.gettext('roundup') }}</a>
-    {{ i18n.gettext('production') }}. 
+    {{ i18n.gettext('A')|u }}
+    <a href='http://www.roundup-tracker.org/'>{{ i18n.gettext('roundup')|u }}</a>
+    {{ i18n.gettext('production')|u }}.
   </div>
 </footer>
--- a/share/roundup/templates/jinja2/html/layout/navigation.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/navigation.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,17 +1,20 @@
+{% set columns = 'id,activity,title,creator,status' %}
+{% set columns_showall = 'id,activity,title,creator,assignedto,status' %}
+
 {% if request.user.hasPermission('View', 'query') %}
   <p>
-    <b>{{ i18n.gettext('Your Queries') }}</b> (<a href="query?@template=edit">{{ i18n.gettext('edit') }}</a>)
+    <b>{{ i18n.gettext('Your Queries')|u }}</b> (<a href="query?@template=edit">{{ i18n.gettext('edit')|u }}</a>)
   </p>
 {% endif %}
 
 {% if request.user.hasPermission('View', 'issue') %}
   <ul class='nav nav-list'>
     <li class='nav-header'>
-      <i class='icon-fire'></i>{{ i18n.gettext('Issues') }}
+      <i class='icon-fire'></i>{{ i18n.gettext('Issues')|u }}
     </li>
     {% if request.user.hasPermission('Create', 'issue') %}
       <li>
-        <a href="issue?@template=item">{{ i18n.gettext('Create New') }}</a>
+        <a href="issue?@template=item">{{ i18n.gettext('Create New')|u }}</a>
       </li>
     {% endif %}
 
@@ -24,8 +27,8 @@
         '@search_text': '',
         'status': status_notresolved,
         'assignedto': '-1',
-        '@dispname': i18n.gettext('Show Unassigned'),
-       }) }}">{{ i18n.gettext('Show Unassigned') }}</a>
+        '@dispname': i18n.gettext('Show Unassigned')|u,
+       }) }}">{{ i18n.gettext('Show Unassigned')|u }}</a>
     </li>
     <li>
       <a href="{{ request.indexargs_url('issue', {
@@ -35,18 +38,18 @@
         '@columns': columns_showall,
         '@search_text': '',
         'status': status_notresolved,
-        '@dispname': i18n.gettext('Show All'),
-       }) }}">{{ i18n.gettext('Show All') }}</a>
+        '@dispname': i18n.gettext('Show All')|u,
+       }) }}">{{ i18n.gettext('Show All')|u }}</a>
     </li>
     <li>
-      <a href="issue?@template=search">{{ i18n.gettext('Search') }}</a>
+      <a href="issue?@template=search">{{ i18n.gettext('Search')|u }}</a>
     </li>
     <li>
       <form method="POST" class='form-inline' action="{{ request.base }}">
         <input type="hidden" name="@type" value="issue">
         <input type="hidden" name="@action" value="show">
         <input class="input-mini" type="text" name="@number">
-        <input type="submit" class="btn" value="{{ i18n.gettext('Show issue') }}">
+        <input type="submit" class="btn" value="{{ i18n.gettext('Show issue')|u }}">
       </form>
     </li>
   </ul>
@@ -59,17 +62,17 @@
   {% if request.user.hasPermission('Create', 'keyword')
         or request.user.hasPermission('Edit', 'keyword') %}
     <li class='nav-header'>
-      <i class='icon-star'></i>{{ i18n.gettext('Keywords') }}
+      <i class='icon-star'></i>{{ i18n.gettext('Keywords')|u }}
     </li>
   {% endif %}
   {% if request.user.hasPermission('Create', 'keyword') %}
     <li>
-      <a href="keyword?@template=item">{{ i18n.gettext('Create New') }}</a>
+      <a href="keyword?@template=item">{{ i18n.gettext('Create New')|u }}</a>
     </li>
   {% endif %}
   {% if request.user.hasPermission('Edit', 'keyword') %}
     <li>
-      <a href="keyword?@template=item">{{ i18n.gettext('Edit Existing') }}</a>
+      <a href="keyword?@template=item">{{ i18n.gettext('Edit Existing')|u }}</a>
     </li>
   {% endif %}
 </ul>
@@ -81,24 +84,24 @@
 {% if request.user.hasPermission('View', 'user') %}
   <ul class='nav nav-list'>
     <li class='nav-header'>
-      <i class='icon-eye-open'></i>{{ i18n.gettext('Administration') }}
+      <i class='icon-eye-open'></i>{{ i18n.gettext('Administration')|u }}
     </li>
 
     {% if request.user.hasPermission('Edit', None) %}
       <li>
-        <a href="home?@template=classlist">{{ i18n.gettext('Class List') }}</a>
+        <a href="home?@template=classlist">{{ i18n.gettext('Class List')|u }}</a>
       </li>
     {% endif %}
 
     {% if request.user.hasPermission('View', 'user') or request.user.hasPermission('Edit', 'user') %}
       <li>
-        <a href="user">{{ i18n.gettext('User List') }}</a>
+        <a href="user">{{ i18n.gettext('User List')|u }}</a>
       </li>
     {% endif %}
 
     {% if request.user.hasPermission('Create', 'user') %}
       <li>
-        <a href="user?@template=item">{{ i18n.gettext('Add User') }}</a>
+        <a href="user?@template=item">{{ i18n.gettext('Add User')|u }}</a>
       </li>
     {% endif %}
   </ul>
@@ -109,7 +112,7 @@
   <form method="POST" action='{{ request.base }}'>
     <ul class='nav nav-list'>
       <li class='nav-header'>
-        <i class='icon-user'></i>{{ i18n.gettext('Login') }}
+        <i class='icon-user'></i>{{ i18n.gettext('Login')|u }}
       </li>
       <li>
         <input type='text' name="__login_name" placeholder='username'>
@@ -119,29 +122,29 @@
       </li>
       <li>
         <label class='checkbox'>
-          <input type="checkbox" name="remember"> {{ i18n.gettext('Remember me?') }}
+          <input type="checkbox" name="remember"> {{ i18n.gettext('Remember me?')|u }}
         </label>
       </li>
       <li>
-        <input type="submit" value="{{ i18n.gettext('Login') }}" class='btn btn-inverse'>
+        <input type="submit" value="{{ i18n.gettext('Login')|u }}" class='btn btn-inverse'>
       </li>
       <li class='vspace-one'>
         {% if request.user.hasPermission('Register', 'user') %}
-          <a href="user?@template=register">{{ i18n.gettext('Register') }}</a>
+          <a href="user?@template=register">{{ i18n.gettext('Register')|u }}</a>
         {% endif %}
       </li>
       <li>
-        <a href="user?@template=forgotten">{{ i18n.gettext('Lost your login?') }}</a>
+        <a href="user?@template=forgotten">{{ i18n.gettext('Lost your login?')|u }}</a>
       </li>
     </ul>
-    <input type="hidden" name="@action" value="{{ i18n.gettext('Login') }}">
+    <input type="hidden" name="@action" value="{{ i18n.gettext('Login')|u }}">
     <input type="hidden" name="__came_from" value='{{request.base}}{{request.env.PATH_INFO}}'>
   </form>
   <hr>
 {% else %}
   <ul class='nav nav-list'>
     <li class='nav-header'>
-      <i class='icon-user'></i>{{ i18n.gettext('Hello,') }}
+      <i class='icon-user'></i>{{ i18n.gettext('Hello,')|u }}
       <span class='username'>{{ request.user.username.plain(escape=1) }}</span>
     </li>
     <li>
@@ -153,14 +156,14 @@
         '@search_text': '',
         'status': status_notresolved,
         'assignedto': request.user.id,
-        '@dispname': i18n.gettext('Your Issues'),
-      }) }}">{{ i18n.gettext('Your Issues') }}</a>
+        '@dispname': i18n.gettext('Your Issues')|u,
+      }) }}">{{ i18n.gettext('Your Issues')|u }}</a>
     </li>
     <li>
-      <a href="user{{ request.user.id }}">{{ i18n.gettext('Your Details') }}</a>
+      <a href="user{{ request.user.id }}">{{ i18n.gettext('Your Details')|u }}</a>
     </li>
     <li>
-      <a href="{{ request.indexargs_url('', {'@action':'logout'}) }}">{{ i18n.gettext('Logout') }}</a>
+      <a href="{{ request.indexargs_url('', {'@action':'logout'}) }}">{{ i18n.gettext('Logout')|u }}</a>
     </li>
   </ul>
   <hr>
@@ -168,7 +171,7 @@
 
 <ul class='nav nav-list'>
   <li class='nav-header'>
-    <i class='icon-bookmark'></i>{{ i18n.gettext('Help') }}
+    <i class='icon-bookmark'></i>{{ i18n.gettext('Help')|u }}
   </li>
   <li>
     <a href="http://www.roundup-tracker.org">Roundup docs</a>
--- a/share/roundup/templates/jinja2/html/layout/page.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/page.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,4 +1,5 @@
-<!doctype html>
+{% set status_notresolved = '-1,1,2,3,4,5,6,7'
+%}<!doctype html>
 <html>
   <head>
     <meta charset="utf-8">
--- a/share/roundup/templates/jinja2/html/layout/pagination.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/pagination.html	Tue Jul 31 21:48:37 2018 +0100
@@ -3,17 +3,19 @@
   <ul>
     {% if batch and batch.previous %}
       <li>
-        <a href="#">{{ i18n.gettext('Previous') }}</a>
+        <a href="{{ request.indexargs_url(request.classname,
+                {'@startwith':batch.previous().first, '@pagesize':batch.previous().size}) }}"
+           >{{ i18n.gettext('Previous')|u }}</a>
       </li>
     {% else %}
       <li class='disabled'>
-        <a href="#">{{ i18n.gettext('Previous') }}</a>
+        <a href="#">{{ i18n.gettext('Previous')|u }}</a>
       </li>
     {% endif %}
     <li>
       <span>
         {{ batch.start }}...{{ batch.start + batch.length -1 }}
-        {{ i18n.gettext('out of') }}
+        {{ i18n.gettext('out of')|u }}
         {{ batch.sequence_length }}
       </span>
     </li>
@@ -21,11 +23,11 @@
       <li>
         <a href="{{ request.indexargs_url(request.classname,
                 {'@startwith':batch.next().first, '@pagesize':batch.next().size}) }}"
-        >{{ i18n.gettext('Next') }}</a>
+           >{{ i18n.gettext('Next')|u }}</a>
       </li>
     {% else %}
       <li class='disabled'>
-        <a href='#'>{{ i18n.gettext('Next') }}</a>
+        <a href="#">{{ i18n.gettext('Next')|u }}</a>
       </li>
     {% endif %}
   </ul>
--- a/share/roundup/templates/jinja2/html/layout/permission.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/permission.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,13 +1,13 @@
 {% if not (context.is_view_ok() or request.user.hasRole('Anonymous')) %}
   <p class='alert alert-block alert-error fade in'> 
-    {{ i18n.gettext('You are not allowed to view this page.') }}
+    {{ i18n.gettext('You are not allowed to view this page.')|u }}
     <button type='button' class='close' data-dismiss='alert'>X</button>
   </p>
 {% endif %}
 
 {% if not context.is_view_ok() and request.user.hasRole('Anonymous') %}
   <p class='alert alert-block alert-error fade in'> 
-    {{ i18n.gettext('Please login with your username and password.') }}
+    {{ i18n.gettext('Please login with your username and password.')|u }}
     <button type='button' class='close' data-dismiss='alert'>X</button>
   </p>
 {% endif %}
--- a/share/roundup/templates/jinja2/html/layout/sort.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/layout/sort.html	Tue Jul 31 21:48:37 2018 +0100
@@ -2,7 +2,7 @@
 <form method="GET" action='{{ request.classname }}'>
   <div class='row-fluid'>
     <div class='span6'>
-      <span class='label label-info'>{{ i18n.gettext('Sort on') }}</span>
+      <span class='label label-info'>{{ i18n.gettext('Sort on')|u }}</span>
       {% for i in range(2) %}
         <p>
           <select name='@sort{{i}}'>
@@ -16,7 +16,7 @@
       {% endfor %}
     </div>
     <div class='span6'>
-      <span class='label label-info'>{{ i18n.gettext('Group on') }}</span>
+      <span class='label label-info'>{{ i18n.gettext('Group on')|u }}</span>
       {% for i in range(2) %}
         <p>
           <select name='@group{{i}}'>
--- a/share/roundup/templates/jinja2/html/msg.index.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/msg.index.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('List of messages') }}
+  {{ i18n.gettext('List of messages')|u }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('List of messages') }}
+  {{ i18n.gettext('List of messages')|u }}
 {% endblock %}
 
 {% block content %}
@@ -13,15 +13,15 @@
   {% for msg in context.list() %}
     <div class='row-fluid'>
       <dl class='span5 dl-horizontal'>
-        <dt>{{ i18n.gettext('Author') }}</dt>
-        <dd>{{ msg.author }}</dd>
-        <dt>{{ i18n.gettext('Date') }}</dt>
+        <dt>{{ i18n.gettext('Author')|u }}</dt>
+        <dd>{{ msg.author|u|e }}</dd>
+        <dt>{{ i18n.gettext('Date')|u }}</dt>
         <dd>{{ msg.date }}</dd>
-        <dt>{{ i18n.gettext('Message id') }}</dt>
+        <dt>{{ i18n.gettext('Message id')|u }}</dt>
         <dd>{{ msg.id }}</dd>
       </dl>
       <div class='span7'>
-        <pre>{{ msg.content }}</pre>
+        <pre>{{ msg.content|u }}</pre>
       </div>
     </div>
     <hr>
--- a/share/roundup/templates/jinja2/html/msg.item.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/msg.item.html	Tue Jul 31 21:48:37 2018 +0100
@@ -2,21 +2,21 @@
 
 {% block head_title %}
   {% if context.id %}
-    {{ i18n.gettext('Message') }} {{ context.id }} - {{ config.TRACKER_NAME }}
+    {{ i18n.gettext('Message')|u }} {{ context.id }} - {{ config.TRACKER_NAME }}
   {% else %}
-    {{ i18n.gettext('New Message') }} - {{ config.TRACKER_NAME }}
+    {{ i18n.gettext('New Message')|u }} - {{ config.TRACKER_NAME }}
   {% endif %}
 {% endblock %}
 
 {% block page_header %}
   {% if not (context.id or context.is_edit_ok()) %}
-    {{ i18n.gettext('New Message') }}
+    {{ i18n.gettext('New Message')|u }}
   {% elif not context.id and context.is_edit_ok() %}
-    {{ i18n.gettext('New Message Editing') }}
+    {{ i18n.gettext('New Message Editing')|u }}
   {% elif context.id and not context.is_edit_ok() %}
-    {{ i18n.gettext('Message') }}{{ context.id }}
+    {{ i18n.gettext('Message')|u }}{{ context.id }}
   {% elif context.id and context.is_edit_ok() %}
-    {{ i18n.gettext('Message') }}{{ context.id }} {{ i18n.gettext('editing') }}
+    {{ i18n.gettext('Message')|u }}{{ context.id }} {{ i18n.gettext('editing')|u }}
   {% endif %}
 {% endblock %}
 
@@ -26,17 +26,17 @@
 
   {% if context.is_view_ok() %}
     <dl class='dl-horizontal'>
-      <dt>{{ i18n.gettext('Author') }}</dt>
-      <dd>{{ context.author }} </dd>
-      <dt>{{ i18n.gettext('Recipients') }}</dt>
-      <dd>{{ context.recipients }}</dd>
-      <dt>{{ i18n.gettext('Date') }}</dt>
+      <dt>{{ i18n.gettext('Author')|u }}</dt>
+      <dd>{{ context.author|u|e }} </dd>
+      <dt>{{ i18n.gettext('Recipients')|u }}</dt>
+      <dd>{{ context.recipients|u|e }}</dd>
+      <dt>{{ i18n.gettext('Date')|u }}</dt>
       <dd>{{ context.date }}</dd>
     </dl>
 
     <div class='row-fluid'>
       <h4>Content</h4>
-      <pre>{{ context.content.hyperlinked|u }}</pre>
+      <pre>{{ context.content.hyperlinked()|u }}</pre>
     </div>
   {% endif %}
 
@@ -45,6 +45,6 @@
   {% endif %}
 
   <div class='vspace-five'></div>
-  {{ context.history() }}
+  {{ context.history()|u }}
 
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/user.forgotten.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/user.forgotten.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,22 +1,22 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Password reset request') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Password reset request')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('Password reset request') }}
+  {{ i18n.gettext('Password reset request')|u }}
 {% endblock %}
 
 {% block content %}
 
   <p>
-    {{ i18n.gettext('If you know the email address you registered with, enter it below.') }}
+    {{ i18n.gettext('If you know the email address you registered with, enter it below.')|u }}
   </p>
 
   <form method="POST" action='{{ context.designator() }}'>
     <div class='row-fluid'>
-      <label>{{ i18n.gettext('Email Address') }}:</label>
+      <label>{{ i18n.gettext('Email Address')|u }}:</label>
       <input type='text' name="address">
       <input name="@csrf" type="hidden"
              value="{{ utils.anti_csrf_nonce() }}">
@@ -24,14 +24,14 @@
              value="{{ utils.anti_csrf_nonce() }}">
       <input type="hidden" name="@action" value="passrst">
       <input type="hidden" name="@template" value="forgotten">
-      <input type="submit" value="{{ i18n.gettext('Password reset') }}" class='btn btn-primary'>
+      <input type="submit" value="{{ i18n.gettext('Password reset')|u }}" class='btn btn-primary'>
     </div>
 
     <div class='row-fluid vspace-two'> 
-      <p>{{ i18n.gettext('Or, if you know your username, then enter it below') }}.</p>
-      <label>{{ i18n.gettext('Username') }}:</label>
+      <p>{{ i18n.gettext('Or, if you know your username, then enter it below')|u }}.</p>
+      <label>{{ i18n.gettext('Username')|u }}:</label>
       <input type='text' name="username">
-      <input type="submit" value="{{ i18n.gettext('Password reset') }}" class='btn btn-info'>
+      <input type="submit" value="{{ i18n.gettext('Password reset')|u }}" class='btn btn-info'>
     </div>
   </form>
 
--- a/share/roundup/templates/jinja2/html/user.index.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/user.index.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('User listing') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('User listing')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('User listing') }}
+  {{ i18n.gettext('User listing')|u }}
 {% endblock %}
 
 {% block content %}
@@ -15,24 +15,24 @@
   {% if context.is_view_ok() %}
     <table class='table'>
       <tr class='info'>
-        <td>{{ i18n.gettext('Username') }}</td>
-        <td>{{ i18n.gettext('Real name') }}</td>
-        <td>{{ i18n.gettext('Organisation') }}</td>
-        <td>{{ i18n.gettext('Email address') }}</td>
-        <td>{{ i18n.gettext('Phone number') }}</td>
+        <td>{{ i18n.gettext('Username')|u }}</td>
+        <td>{{ i18n.gettext('Real name')|u }}</td>
+        <td>{{ i18n.gettext('Organisation')|u }}</td>
+        <td>{{ i18n.gettext('Email address')|u }}</td>
+        <td>{{ i18n.gettext('Phone number')|u }}</td>
         {% if context.is_retire_ok() %}
-          <td>{{ i18n.gettext('Retire') }}</td>
+          <td>{{ i18n.gettext('Retire')|u }}</td>
         {% endif %}
       </tr>
       {% for user in context.list() %}
         <tr>
           <td>
-            <a href='user{{ user.id }}'>{{ user.username }}</a>
+            <a href='user{{ user.id }}'>{{ user.username.plain()|u|e }}</a>
           </td>
-          <td>{{ user.realname.plain() }}</td>
-          <td>{{ user.organisation.plain() }}</td>
-          <td>{{ user.address.email() }}</td>
-          <td>{{ user.phone.plain() }}</td>
+          <td>{{ user.realname.plain()|u|e }}</td>
+          <td>{{ user.organisation.plain()|u|e }}</td>
+          <td>{{ user.address.email()|u|e }}</td>
+          <td>{{ user.phone.plain()|u|e }}</td>
           {% if context.is_retire_ok() %}
             <td>
               <form method="POST" action='user{{ user.id }}' class='form-inline'>
@@ -40,7 +40,7 @@
                 <input name="@csrf" type="hidden"
                        value="{{ utils.anti_csrf_nonce() }}">
                 <input type="hidden" name="@action" value="retire">
-                <input type="submit" value="{{ i18n.gettext('retire') }}" class='btn btn-small'>
+                <input type="submit" value="{{ i18n.gettext('retire')|u }}" class='btn btn-small'>
               </form>
             </td>
           {% endif %}
--- a/share/roundup/templates/jinja2/html/user.item.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/user.item.html	Tue Jul 31 21:48:37 2018 +0100
@@ -2,21 +2,21 @@
 
 {% block head_title %}
   {% if context.id %}
-    {{ i18n.gettext('User') }} {{ context.id }} - {{ config.TRACKER_NAME }}
+    {{ i18n.gettext('User')|u }} {{ context.id }} - {{ config.TRACKER_NAME }}
   {% else %}
-    {{ i18n.gettext('New User') }} - {{ config.TRACKER_NAME }}
+    {{ i18n.gettext('New User')|u }} - {{ config.TRACKER_NAME }}
   {% endif %}
 {% endblock %}
 
 {% block page_header %}
   {% if not (context.id or context.is_edit_ok()) %}
-    {{ i18n.gettext('New User') }}
+    {{ i18n.gettext('New User')|u }}
   {% elif not context.id and context.is_edit_ok() %}
-    {{ i18n.gettext('New User Editing') }}
+    {{ i18n.gettext('New User Editing')|u }}
   {% elif context.id and not context.is_edit_ok() %}
-    {{ i18n.gettext('User') }}{{ context.id }}
+    {{ i18n.gettext('User')|u }}{{ context.id }}
   {% elif context.id and context.is_edit_ok() %}
-    {{ i18n.gettext('User') }}{{ context.id }} {{ i18n.gettext('editing') }}
+    {{ i18n.gettext('User')|u }}{{ context.id }} {{ i18n.gettext('editing')|u }}
   {% endif %}
 {% endblock %}
 
@@ -31,26 +31,26 @@
           action ='{{  context.designator() }}'>
       <table>
         <tr>
-          <th>{{ i18n.gettext('Name') }}</th>
+          <th>{{ i18n.gettext('Name')|u }}</th>
           <td>
-            <input type='text' name="realname" value='{{ context.realname }}'>
+            <input type='text' name="realname" value='{{ context.realname.plain()|u|e }}'>
           </td>
         </tr>
         <tr>
-          <th>{{ i18n.gettext('Login Name') }}</th>
+          <th>{{ i18n.gettext('Login Name')|u }}</th>
           <td>
-            <input type='text' name='username' value='{{ context.username }}'>
+            <input type='text' name='username' value='{{ context.username.plain()|u|e }}'>
           </td>
         </tr>
         {% if context.is_edit_ok() %}
           <tr>
-            <th>{{ i18n.gettext('Login Password') }}</th>
+            <th>{{ i18n.gettext('Login Password')|u }}</th>
             <td>
               <input type='password' name='password'>
             </td>
           </tr>
           <tr>
-            <th>{{ i18n.gettext('Confirm Password') }}</th>
+            <th>{{ i18n.gettext('Confirm Password')|u }}</th>
             <td>
               <input type='password' name='@confirm@password'>
             </td>
@@ -58,42 +58,42 @@
         {% endif %}
         {% if request.user.hasPermission('Web Roles') %}
           <tr>
-            <th>{{ i18n.gettext('Roles <br>(comma separated)') }}</th>
+            <th>{{ i18n.gettext('Roles <br>(comma separated)')|u }}</th>
             <td>
-              <input type='text' name='roles' value='{{ context.roles }}'>
+              <input type='text' name='roles' value='{{ context.roles|u|e }}'>
             </td>
           </tr>
         {% endif %}
         <tr>
-          <th>{{ i18n.gettext('Phone') }}</th>
+          <th>{{ i18n.gettext('Phone')|u }}</th>
           <td>
-            <input type='text' name='phone' value='{{ context.phone }}'>
+            <input type='text' name='phone' value='{{ context.phone.plain()|u|e }}'>
           </td>
         </tr>
         <tr>
-          <th>{{ i18n.gettext('Organisation') }}</th>
+          <th>{{ i18n.gettext('Organisation')|u }}</th>
           <td>
-            <input type='text' name='organisation' value='{{ context.organisation }}'>
+            <input type='text' name='organisation' value='{{ context.organisation.plain()|u|e }}'>
           </td>
         </tr>
         {% if context.timezone %}
           <tr>
-            <th>{{ i18n.gettext('Timezone') }}</th>
+            <th>{{ i18n.gettext('Timezone')|u }}</th>
             <td>
-              <input type='text' name='timezone' value='{{ context.timezone}}'>
+              <input type='text' name='timezone' value='{{ context.timezone|u|e }}'>
             </td>
           </tr>
         {% endif %}
         <tr>
-          <th>{{ i18n.gettext('Email') }}</th>
+          <th>{{ i18n.gettext('Email')|u }}</th>
           <td>
-            <input type='text' name='address' value='{{ context.address }}'>
+            <input type='text' name='address' value='{{ context.address.plain()|u|e }}'>
           </td>
         </tr>
         <tr>
-          <th>{{ i18n.gettext('Alternate email address <br>One address per line') }}</th>
+          <th>{{ i18n.gettext('Alternate email address <br>One address per line')|u }}</th>
           <td>
-            {{ context.alternate_addresses.multiline() }}
+            {{ context.alternate_addresses.multiline()|u }}
           </td>
         </tr>
         {% if context.is_edit_ok() %}
@@ -109,6 +109,6 @@
   {% endif %}
 
   <div class='vspace-five'></div>
-  {{ context.history() }}
+  {{ context.history()|u }}
 
 {% endblock %}
--- a/share/roundup/templates/jinja2/html/user.register.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/user.register.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Registration') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Registration')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('Registration') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Registration')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block content %}
@@ -16,65 +16,65 @@
         action ='{{  context.designator() }}'>
     <table>
       <tr>
-        <th>{{ i18n.gettext('Name') }}</th>
+        <th>{{ i18n.gettext('Name')|u }}</th>
         <td>
           <input type='text' name="realname">
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Login Name') }}</th>
+        <th>{{ i18n.gettext('Login Name')|u }}</th>
         <td>
           <input type='text' name='username' required>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Login Password') }}</th>
+        <th>{{ i18n.gettext('Login Password')|u }}</th>
         <td>
           <input type='password' name='password' required>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Confirm Password') }}</th>
+        <th>{{ i18n.gettext('Confirm Password')|u }}</th>
         <td>
           <input type='password' name='@confirm@password'>
         </td>
       </tr>
       {% if request.user.hasPermission('Web Roles') %}
         <tr>
-          <th>{{ i18n.gettext('Roles <br>(comma separated)') }}</th>
+          <th>{{ i18n.gettext('Roles <br>(comma separated)')|u }}</th>
           <td>
-            <input type='text' name='roles' value='{{ context.roles }}'>
+            <input type='text' name='roles' value='{{ context.roles|u|e }}'>
           </td>
         </tr>
       {% endif %}
       <tr>
-        <th>{{ i18n.gettext('Phone') }}</th>
+        <th>{{ i18n.gettext('Phone')|u }}</th>
         <td>
           <input type='text' name='phone'>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Organisation') }}</th>
+        <th>{{ i18n.gettext('Organisation')|u }}</th>
         <td>
           <input type='text' name='organisation'>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Timezone') }}</th>
+        <th>{{ i18n.gettext('Timezone')|u }}</th>
         <td>
           <input type='text' name='timezone'>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Email') }}</th>
+        <th>{{ i18n.gettext('Email')|u }}</th>
         <td>
           <input type='text' name='address' required>
         </td>
       </tr>
       <tr>
-        <th>{{ i18n.gettext('Alternate email address <br>One address per line') }}</th>
+        <th>{{ i18n.gettext('Alternate email address <br>One address per line')|u }}</th>
         <td>
-          {{ context.alternate_addresses.multiline() }}
+          {{ context.alternate_addresses.multiline()|u|e }}
         </td>
       </tr>
       <tr class='form-actions'>
@@ -83,7 +83,7 @@
         <input name="@csrf" type="hidden"
                value="{{ utils.anti_csrf_nonce() }}">
         <input type="hidden" name="@action" value="register">
-        <input type="submit" name="submit" value="{{ i18n.gettext('Register') }}">
+        <input type="submit" name="submit" value="{{ i18n.gettext('Register')|u }}">
         </td>
       </tr>
     </table>
--- a/share/roundup/templates/jinja2/html/user.rego_progress.html	Sun Jul 29 15:52:15 2018 +0100
+++ b/share/roundup/templates/jinja2/html/user.rego_progress.html	Tue Jul 31 21:48:37 2018 +0100
@@ -1,11 +1,11 @@
 {% extends 'layout/page.html' %}
 
 {% block head_title %}
-  {{ i18n.gettext('Registration in progress') }} - {{ config.TRACKER_NAME }}
+  {{ i18n.gettext('Registration in progress')|u }} - {{ config.TRACKER_NAME }}
 {% endblock %}
 
 {% block page_header %}
-  {{ i18n.gettext('Registration in progress...') }}
+  {{ i18n.gettext('Registration in progress...')|u }}
 {% endblock %}
 
 {% block content %}

Roundup Issue Tracker: http://roundup-tracker.org/