[#931] Jinja templates for ForgeClassic

Jenny Steele Jenny Steele 2010-09-17

added ForgeClassic/sfx/templates/sfx/mailman_admin.html
added ForgeClassic/sfx/templates/sfx/hosted_index.html
removed ForgeClassic/sfx/templates/new_list.html
removed ForgeClassic/sfx/templates/vhost_admin.html
changed Allura/allura/config/app_cfg.py
changed ForgeClassic/sfx/hosted_apps.py
changed ForgeClassic/sfx/mailman.py
changed ForgeClassic/sfx/prweb.py
changed ForgeClassic/sfx/widgets.py
copied ForgeClassic/sfx/templates/hosted_admin.html -> ForgeClassic/sfx/templates/sfx/hosted_admin.html
copied ForgeClassic/sfx/templates/hosted_index.html -> ForgeClassic/sfx/templates/sfx/mailman_admin_password.html
copied ForgeClassic/sfx/templates/list_admin_row.html -> ForgeClassic/sfx/templates/sfx/list_admin_row.html
copied ForgeClassic/sfx/templates/mailman_admin.html -> ForgeClassic/sfx/templates/sfx/mailman_subscriber_display.html
copied ForgeClassic/sfx/templates/mailman_admin_password.html -> ForgeClassic/sfx/templates/sfx/vhost_admin.html
copied ForgeClassic/sfx/templates/mailman_subscriber_display.html -> ForgeClassic/sfx/templates/sfx/new_list.html
copied ForgeClassic/sfx/templates/mailman_subscriber_query.html -> ForgeClassic/sfx/templates/sfx/mailman_subscriber_query.html
copied ForgeClassic/sfx/templates/mysql_admin.html -> ForgeClassic/sfx/templates/sfx/mysql_admin.html
ForgeClassic/sfx/templates/sfx/mailman_admin.html Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/templates/sfx/hosted_index.html Diff Switch to side-by-side view
Loading...
Allura/allura/config/app_cfg.py Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/hosted_apps.py Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/mailman.py Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/prweb.py Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/widgets.py Diff Switch to side-by-side view
Loading...
ForgeClassic/sfx/templates/hosted_admin.html to ForgeClassic/sfx/templates/sfx/hosted_admin.html
--- a/ForgeClassic/sfx/templates/hosted_admin.html
+++ b/ForgeClassic/sfx/templates/sfx/hosted_admin.html
@@ -1,41 +1,34 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
+{% extends 'jinja_master/master.html' %}
 
-  <xi:include href="${g.allura_templates}/master.html"/>
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}}{% endblock %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>$c.project.name / $c.app.config.options.mount_label</title>
-  </head>
+{% block header %}{{c.app.config.options.mount_label}}{% endblock %}
 
-  <body>
-    <h1 class="title">$c.app.config.options.mount_label</h1>
+{% block content %}
+  <h2>Enabled Apps</h2>
+  <ul>
+    {% for app in enabled_apps %}
+    <li>
+      <a href="{{app.application_docs_url}}"
+         title="{{app.application_desc}}">{{app.description}}</a>:
+      {{app.application_function}} &mdash;
+      <a href="{{app.format_column('application_admin_url')}}">Manage</a>
+      / <a href="grant_admin?ft={{app.feature_type}}">Grant Admin</a>
+      / <a href="disable?ft={{app.feature_type}}">Disable</a>
+    </li>
+    {% endfor %}
+  </ul>
 
-    <h2>Enabled Apps</h2>
-    <ul>
-      <li py:for="app in enabled_apps">
-        <a href="$app.application_docs_url"
-           title="$app.application_desc">$app.description</a>:
-        $app.application_function &mdash;
-        <a href="${app.format_column('application_admin_url')}">Manage</a>
-        / <a href="grant_admin?ft=$app.feature_type">Grant Admin</a>
-        / <a href="disable?ft=$app.feature_type">Disable</a>
-      </li>
-    </ul>
+  <h2>Available Apps</h2>
 
-    <h2>Available Apps</h2>
-
-    <ul>
-      <li py:for="app in disabled_apps">
-        <a href="$app.application_docs_url"
-           title="$app.application_desc">$app.description</a>:
-        $app.application_function &mdash;
-        <a href="enable?ft=$app.feature_type">Enable</a>
-      </li>
-    </ul>
-
-  </body>
-</html>
+  <ul>
+    {% for app in disabled_apps %}
+    <li>
+      <a href="{{app.application_docs_url}}"
+         title="{{app.application_desc}}">{{app.description}}</a>:
+      {{app.application_function}} &mdash;
+      <a href="enable?ft={{app.feature_type}}">Enable</a>
+    </li>
+    {% endfor %}
+  </ul>
+{% endblock %}
ForgeClassic/sfx/templates/hosted_index.html to ForgeClassic/sfx/templates/sfx/mailman_admin_password.html
--- a/ForgeClassic/sfx/templates/hosted_index.html
+++ b/ForgeClassic/sfx/templates/sfx/mailman_admin_password.html
@@ -1,26 +1,28 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
+{% extends 'jinja_master/master.html' %}
 
-  <xi:include href="${g.allura_templates}/master.html"/>
+{% block title %}Change List Password{% endblock %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>$c.project.name / $c.app.config.options.mount_label</title>
-  </head>
+{% block header %}Change List Password{% endblock %}
 
-  <body>
-    <h1 class="title">$c.app.config.options.mount_label</h1>
-    <ul>
-      <li py:for="app in enabled_apps">
-        <a href="${app.format_column('application_url')}"
-           title="$app.application_desc">
-          $app.description</a>:
-        $app.application_function
-        <a href="$app.application_docs_url">Documentation</a>
-      </li>
-    </ul>
-  </body>
-</html>
+{% block content %}
+<div>
+  <p>Each mailing list has one mailing list admin password. You will need to
+  provide this password to any other team members who perform list
+  administration. We recommend that at least two people on each project team be
+  aware of the list admin password, as that they may both tend to pending
+  administrative requests, if needed.
+  </p>
+  <h2>List Name: {{ml.name}}</h2>
+  {{c.form.display(action='save')}}
+</div>
+<div class="notice">
+  <b>Please note</b><br/>
+   Password must be 4-16 chars containing letters and numbers only. The same
+   password must be entered twice, verbatim, in the provided fields (above).
+</div>
+<div class="notice">
+  <b>Password should be unique</b><br/>
+  Do not use your SourceForge.net user account password as the list admin
+  password.
+</div>
+{% endblock %}
ForgeClassic/sfx/templates/list_admin_row.html to ForgeClassic/sfx/templates/sfx/list_admin_row.html
--- a/ForgeClassic/sfx/templates/list_admin_row.html
+++ b/ForgeClassic/sfx/templates/sfx/list_admin_row.html
@@ -1,20 +1,20 @@
-<tr xmlns="http://www.w3.org/1999/xhtml"
-     xmlns:py="http://genshi.edgewall.org/"
-     xmlns:xi="http://www.w3.org/2001/XInclude">
-  <td py:for="i, field in enumerate(fields)"
-      py:with="ctx=c.widget.context_for(field.name)">
-    <py:if test="i==0">
-      <py:for each="hidden_field in hidden_fields"
-              py:with="hidden_ctx=c.widget.context_for(hidden_field.name)">
-        ${hidden_field.display(**hidden_ctx)}
-      </py:for>
-    </py:if>
-    ${field.display(**ctx)}
-    <py:if test="i==1">
-      <br/>
-      <a href="$value.admin_url">Mailman Admin</a> |
-      <a href="$value.name/subscribers/">List Subscribers</a> |
-      <a href="$value.name/admin_password/">Change Password</a>
-    </py:if>
-  </td>
+<tr>
+  {% for field in fields %}
+    {% set ctx=c.widget.context_for(field.name) %}
+    <td>
+      {% if loop.index0 == 0 %}
+        {% for hidden_field in hidden_fields %}
+          {% set hidden_ctx=c.widget.context_for(hidden_field.name) %}
+          {{hidden_field.display(**hidden_ctx)}}
+        {% endfor %}
+      {% endif %}
+      {{field.display(**ctx)}}
+      {% if loop.index0 == 1 %}
+        <br/>
+        <a href="{{value.admin_url}}">Mailman Admin</a> |
+        <a href="{{value.name}}/subscribers/">List Subscribers</a> |
+        <a href="{{value.name}}/admin_password/">Change Password</a>
+      {% endif %}
+    </td>
+  {% endfor %}
 </tr>
ForgeClassic/sfx/templates/mailman_admin.html to ForgeClassic/sfx/templates/sfx/mailman_subscriber_display.html
--- a/ForgeClassic/sfx/templates/mailman_admin.html
+++ b/ForgeClassic/sfx/templates/sfx/mailman_subscriber_display.html
@@ -1,32 +1,28 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <?python from allura.lib import helpers as h?>
-  <?python from allura.lib.security import has_artifact_access?>
-  <xi:include href="master.html"/>
+{% extends 'jinja_master/master.html' %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>Mailing List Admin</title>
-    <style>
-      .hidden { display: None }
-    </style>
-  </head>
+{% block title %}Mailing List Subscribers{% endblock %}
 
-  <body>
-    <h1>Mailing List Admin</h1>
-    <py:if test="lists">
-      <h2 class="title">Existing Lists</h2>
-      <div class="content">
-        ${c.list.display(action='save', value=dict(lists=lists))}
-        <br style="clear:both"/>
-      </div>
-    </py:if>
-    <h2 class="title">New List</h2>
-    <div class="content">
-      ${c.new.display(action='create')}
-    </div>
-  </body>
-</html>
+{% block header %}Mailing List Subscribers: {{ml.name}}{% endblock %}
+
+{% block content %}
+  <div>
+    {% if search %}
+      <p>The following subscribers match the provided search criteria:</p>
+    {% endif %}
+    <p>
+      {% for s in subscribers %}
+        {{s.subscribed_email}}<br/>
+      {% endfor %}
+    </p>
+    <p>
+      Total subscriber count: {{subscribers.__len__()}}<br/>
+      Results have been sorted by {{sort_by}}.
+    </p>
+  </div>
+  <div class="notice">
+    <b>Delayed Reports</b><br/>
+     All subscriber information is refreshed each Sunday; the subscriber
+    listing for your mailing lists may have changed since the last time this
+    data was updated.
+  </div>
+{% endblock %}
ForgeClassic/sfx/templates/mailman_admin_password.html to ForgeClassic/sfx/templates/sfx/vhost_admin.html
--- a/ForgeClassic/sfx/templates/mailman_admin_password.html
+++ b/ForgeClassic/sfx/templates/sfx/vhost_admin.html
@@ -1,38 +1,51 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <?python from allura.lib import helpers as h?>
-  <?python from allura.lib.security import has_artifact_access?>
-  <xi:include href="master.html"/>
+{% extends 'jinja_master/master.html' %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>Change List Password</title>
-  </head>
+{% block title %}{{c.project.name}} project: Admin: VHOST Management{% endblock %}
 
-  <body>
-    <h1 class="title">Change List Administrator Password</h1>
-    <div class="content">
-      <p>Each mailing list has one mailing list admin password. You will need to
-      provide this password to any other team members who perform list
-      administration. We recommend that at least two people on each project team be
-      aware of the list admin password, as that they may both tend to pending
-      administrative requests, if needed.
-      </p>
-      <h2>List Name: ${ml.name}</h2>
-      ${c.form.display(action='save')}
-    </div>
-    <div class="notice">
-      <b>Please note</b><br/>
-       Password must be 4-16 chars containing letters and numbers only. The same
-       password must be entered twice, verbatim, in the provided fields (above).
-    </div>
-    <div class="notice">
-      <b>Password should be unique</b><br/>
-      Do not use your SourceForge.net user account password as the list admin
-      password.
-    </div>
-  </body>
-</html>
+{% block header %}VHOST Service{% endblock %}
+
+{% block content %}
+  <p>
+    Your project currently has {{vhosts.__len__()}} VHOSTs out of a
+    maximum 10 permitted for your project.
+  </p>
+  <h2>Add New Virtual Host</h2>
+  <p>
+
+    Please follow the instructions in the documentation titled,
+    "<a href="http://p.sf.net/sourceforge/vhost"
+       >VHOST Services for SourceForge.net Projects</a>", located in the
+    Site Documentation collection. This document covers the correct way to
+    configure your DNS services to direct traffic to your VHOST at SourceForge.net.
+  </p>
+  <p>
+    One VHOST should be added for each host and each domain, for which you
+    wish for us to answer traffic. For example, separate VHOST would need to
+    be added for yourhost.org and www.yourhost.org. Clicking on the "Create"
+    button will schedule your VHOST for creation; this is not an immediate
+    process (see the aforementioned documentation for further details).
+  </p>
+
+  {{c.new.display(action='create')}}<br/>
+
+  {% if vhosts %}
+    <table>
+      <thead>
+        <tr><th>Virtual Host</th><th>Operations</th></tr>
+      </thead>
+      <tbody>
+        {% for vhost in vhosts %}
+          <tr>
+            <td>{{vhost.name}}</td>
+            <td>
+              <form method="POST" action="delete">
+                <input type="hidden" name="vhostid" value="{{vhost.vhostid}}" />
+                <input type="submit" value="Delete" />
+              </form>
+            </td>
+          </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+  {% endif %}
+{% endblock %}
ForgeClassic/sfx/templates/mailman_subscriber_display.html to ForgeClassic/sfx/templates/sfx/new_list.html
--- a/ForgeClassic/sfx/templates/mailman_subscriber_display.html
+++ b/ForgeClassic/sfx/templates/sfx/new_list.html
@@ -1,32 +1,30 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="master.html"/>
+<form name="{{name}}"
+      id="{{name}}"
+      method="{{method}}"
+      action="{{action}}"
+      enctype="{{enctype}}">
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>Mailing List Subscribers</title>
-  </head>
+  {% set ctx=c.widget.context_for('name') %}
+  {% set field=fields.name %}
+  <label for="name">New List Name:</label>
+  {% if ctx.errors %}<span class="{{error_class}}">{{ctx.errors}}</span><br/>{% endif %}
+  {{c.app.project.shortname}}-{{field.display(**ctx)}}@lists.sourceforge.net
+  <br/>
 
-  <body>
-    <h1 class="title">Mailing List Subscribers: $ml.name</h1>
-    <div class="content">
-      <p py:if="search">The following subscribers match the provided search criteria:</p>
-      <p>
-        <py:for each="s in subscribers">$s.subscribed_email<br/></py:for>
-      </p>
-      <p>
-        Total subscriber count: ${len(subscribers)}<br/>
-        Results have been sorted by ${sort_by}.
-      </p>
-    </div>
-    <div class="notice">
-      <b>Delayed Reports</b><br/>
-       All subscriber information is refreshed each Sunday; the subscriber
-      listing for your mailing lists may have changed since the last time this
-      data was updated.
-    </div>
-  </body>
-</html>
+  {% set ctx=c.widget.context_for('description') %}
+  {% set field=fields.description %}
+  <label for="description">Description:</label>
+  {% if ctx.errors %}<span class="{{error_class}}">{{ctx.errors}}</span><br/>{% endif %}
+  {{field.display(**ctx)}}
+  <br/>
+
+  {% set ctx=c.widget.context_for('is_public') %}
+  {% set field=fields.is_public %}
+  <label for="is_public">Is this list public?</label>
+  {% if ctx.errors %}<span class="{{error_class}}">{{ctx.errors}}</span><br/>{% endif %}
+  {{field.display(**ctx)}}
+  <br/>
+
+  <label>&nbsp;</label>
+  {% for b in buttons %}{{b.display()}}{% endfor %}
+</form>
ForgeClassic/sfx/templates/mailman_subscriber_query.html to ForgeClassic/sfx/templates/sfx/mailman_subscriber_query.html
--- a/ForgeClassic/sfx/templates/mailman_subscriber_query.html
+++ b/ForgeClassic/sfx/templates/sfx/mailman_subscriber_query.html
@@ -1,28 +1,20 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="master.html"/>
+{% extends 'jinja_master/master.html' %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>Mailing List Subscribers</title>
-  </head>
+{% block title %}Mailing List Subscribers{% endblock %}
 
-  <body>
-    <h1 class="title">Mailing List Subscribers: $ml.name</h1>
-    <div class="content">
-      <p>Search through the subscription listing, or
-	<a href="display">click here</a> to see a full subscriber list.
-      </p>
-      ${c.search.display(action='display')}
-    </div>
-    <div class="notice">
-      <b>Delayed Reports</b><br/>
-       All subscriber information is refreshed each Sunday; the subscriber
-      listing for your mailing lists may have changed since the last time this
-      data was updated.
-    </div>
-  </body>
-</html>
+{% block header %}Mailing List Subscribers: {{ml.name}}{% endblock %}
+
+{% block content %}
+  <div>
+    <p>Search through the subscription listing, or
+    <a href="display">click here</a> to see a full subscriber list.
+    </p>
+    {{c.search.display(action='display')}}
+  </div>
+  <div class="notice">
+    <b>Delayed Reports</b><br/>
+    All subscriber information is refreshed each Sunday; the subscriber
+    listing for your mailing lists may have changed since the last time this
+    data was updated.
+  </div>
+{% endblock %}
ForgeClassic/sfx/templates/mysql_admin.html to ForgeClassic/sfx/templates/sfx/mysql_admin.html
--- a/ForgeClassic/sfx/templates/mysql_admin.html
+++ b/ForgeClassic/sfx/templates/sfx/mysql_admin.html
@@ -1,55 +1,48 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <?python from allura.lib import helpers as h?>
-  <?python from allura.lib.security import has_artifact_access?>
-  <xi:include href="master.html"/>
+{% extends 'jinja_master/master.html' %}
 
-  <head>
-    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
-    <title>${c.project.name} project: Admin: Editing Database Info</title>
-  </head>
+{% block title %}{{c.project.name}} project: Admin: Editing Database Info{% endblock %}
 
-  <body>
-    <h1 class="title">MySQL Service</h1>
-    <div class="content">
-      <p>Service details:</p>
-      <div>
-        <a href="${g.url('/apps/admin/%s/' % c.project.shortname)}"
-           >Access this project's databases over the internet</a><br/>
-        <a href="http://p.sf.net/sourceforge/mysql"
-           >Documentation: Guide to MySQL Database Services</a><br/>
-        Hostname: $value.hostname (exactly as shown, with no domain suffix)<br/>
-        Database name prefix: ${value.prefix}_ &mdash; i.e. "CREATE DATABASE
-        ${value.prefix}_myapp" as your ADMIN user.<br/>
-        RO User: ${value.prefix}ro (SELECT)<br/>
-        RW User: ${value.prefix}rw (SELECT, INSERT , DELETE, UPDATE)<br/>
-        ADMIN User: ${value.prefix}admin (has RW account privileges, and CREATE,
-        DROP, ALTER, INDEX, LOCK TABLES))<br/>
-        web-access URL: <a href="$value.url"
-                           >$value.url</a><br/>
-        <p py:if="value.passwd_rouser is not None">
-          Please note that your MySQL passwords are displayed here as plaintext
-          for your convenience. These passwords are readily available on the
-          project shell and web servers. Their display here allows for easy lookup
-          by project administrators.
-        </p>
-        <p py:if="value.passwd_rouser is None">
-          MySQL service will be available when you have set passwords on the provided
-          accounts.
-        </p>
-      </div>
-      <div>
-        <h2>Passwords</h2>
-        ${c.form.display(value=value, action='save')}
-	<br/>
-      </div>
-      <p py:if="value.modified_user is not None">
-        Password data last set ${h.ago_ts(value.row_modtime)} ago
-        by $value.modified_user
+{% block header %}MySQL Service{% endblock %}
+
+{% block content %}
+
+<p>Service details:</p>
+  <div>
+    <a href="{{g.url('/apps/admin/%s/' % c.project.shortname)}}"
+       >Access this project's databases over the internet</a><br/>
+    <a href="http://p.sf.net/sourceforge/mysql"
+       >Documentation: Guide to MySQL Database Services</a><br/>
+    Hostname: {{value.hostname}} (exactly as shown, with no domain suffix)<br/>
+    Database name prefix: {{value.prefix}}_ &mdash; i.e. "CREATE DATABASE
+    {{value.prefix}}_myapp" as your ADMIN user.<br/>
+    RO User: {{value.prefix}}ro (SELECT)<br/>
+    RW User: {{value.prefix}}rw (SELECT, INSERT , DELETE, UPDATE)<br/>
+    ADMIN User: {{value.prefix}}admin (has RW account privileges, and CREATE,
+    DROP, ALTER, INDEX, LOCK TABLES))<br/>
+    web-access URL: <a href="{{value.url}}"
+                       >{{value.url}}</a><br/>
+    {% if value.passwd_rouser %}
+      <p>
+        Please note that your MySQL passwords are displayed here as plaintext
+        for your convenience. These passwords are readily available on the
+        project shell and web servers. Their display here allows for easy lookup
+        by project administrators.
       </p>
-    </div>
-  </body>
-</html>
+    {% else %}
+      <p>
+        MySQL service will be available when you have set passwords on the provided
+        accounts.
+      </p>
+    {% endif %}
+  </div>
+  <div>
+    <h2>Passwords</h2>
+    {{c.form.display(value=value, action='save')}}<br/>
+  </div>
+  {% if value.modified_user %}
+    <p>
+      Password data last set {{h.ago_ts(value.row_modtime)}} ago
+      by {{value.modified_user}}
+    </p>
+  {% endif %}
+{% endblock %}