From 36a826afc2634d0d91d3b6d8d5f96fa2492eb061 Mon Sep 17 00:00:00 2001
From: Ralph Bean <rbean@redhat.com>
Date: Mar 17 2016 14:47:20 +0000
Subject: Move jinja templates out into their own files.


---

diff --git a/hubs/widgets/about.py b/hubs/widgets/about.py
index 84b8de7..061ec6e 100755
--- a/hubs/widgets/about.py
+++ b/hubs/widgets/about.py
@@ -1,16 +1,13 @@
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
-
-import jinja2
+from hubs.widgets import templating
 
 import hubs.validators as validators
 
 
-template = jinja2.Template("""
-    <p>{{text}}</p>
-    """)
 chrome = panel("<span class='glyphicon glyphicon-info-sign' aria-hidden='true'></span> About")
+template = templating.environment.get_template('templates/about.html')
 
 
 @argument(name="text", default="I am a Fedora user, and this is my about",
diff --git a/hubs/widgets/badges.py b/hubs/widgets/badges.py
index 1018c3d..f65c4fb 100755
--- a/hubs/widgets/badges.py
+++ b/hubs/widgets/badges.py
@@ -4,24 +4,13 @@ import requests
 
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
-
-import jinja2
-
+from hubs.widgets import templating
 
 import hubs.validators as validators
 
-
-template = jinja2.Template("""
-<div class="flex-container">
-{% for badge in assertions[:10] %}
-  <a width="60px" href="https://badges.fedoraproject.org/badge/{{badge['id']}}">
-    <img width="60px" src="{{badge['image']}}"/></a>
-{%endfor%}
-</div>
-""")
-
 from hubs.widgets.chrome import panel
 chrome = panel("Badges")
+template = templating.environment.get_template('templates/badges.html')
 
 
 @argument(name="username",
diff --git a/hubs/widgets/bugzilla.py b/hubs/widgets/bugzilla.py
index 5482829..d524833 100644
--- a/hubs/widgets/bugzilla.py
+++ b/hubs/widgets/bugzilla.py
@@ -1,36 +1,14 @@
 import requests
-import jinja2
 import pkgwat.api
 
 import hubs.validators as validators
 from hubs.widgets.base import argument
 from hubs.widgets.chrome import panel
+from hubs.widgets import templating
 from hubs.hinting import hint, prefixed as _
 
 chrome = panel("Bugzilla: Issues")
-
-template = jinja2.Template("""
-<ul class="media-list">
-  {% for item in issues %}
-    <li class="media">
-      <div class="media-left">
-        <a href="https://bugzilla.redhat.com/show_bug.cgi?id=
-        {{ item['id'] }}" target="_blank">
-        <span class="label label-default">#{{ item['id'] }}</span>
-        </a>
-      </div>
-      <div class="media-body">
-        <h4 class="media-heading">
-          <a href="https://admin.fedoraproject.org/pkgdb/
-          package/rpms/{{ item['pkg_name'] }}" target=
-          "_blank">{{ item['pkg_name'] }}</a>:
-          {{ item['title'] }}
-        </h4>
-      </div>
-    </li>
-  {% endfor %}
-</ul>
-""")
+template = templating.environment.get_template('templates/bugzilla.html')
 
 
 @argument(name="username",
diff --git a/hubs/widgets/chrome.py b/hubs/widgets/chrome.py
index 7dd7a96..f74b32c 100755
--- a/hubs/widgets/chrome.py
+++ b/hubs/widgets/chrome.py
@@ -1,29 +1,8 @@
 from hubs.widgets.base import wraps
+from hubs.widgets import templating
 
-import jinja2
-
-_panel_template = jinja2.Template("""
-<div class="panel {{klass}}">
-  {{heading}}
-  <div class="pull-right widget-buttons">
-    <!-- the AGPLv3 wrapper puts the source url in all responses -->
-    <a href="{{source_url}}"><span class="glyphicon glyphicon-eye-open"></span></a>
-    <a href="{{widget_url}}"><span class="glyphicon glyphicon-new-window"></span></a>
-    <a href="#"><span class="glyphicon glyphicon-edit"></span></a>
-  </div>
-  <div class="panel-body">
-    {{content}}
-  </div> <!-- end panel-body -->
-</div> <!-- end panel -->
-""")
-
-_panel_heading_template = jinja2.Template("""
-  <div class="panel-heading">
-    <h3 class="panel-title">
-      {{title}}
-    </h3>
-  </div> <!-- end panel-heading -->
-""")
+_panel_template = templating.environment.get_template('templates/panel.html')
+_panel_heading_template = templating.environment.get_template('templates/panel_heading.html')
 
 
 def panel(title=None, klass="panel-default", key=None):
diff --git a/hubs/widgets/cobwebs.py b/hubs/widgets/cobwebs.py
index 890fb18..f03aa86 100755
--- a/hubs/widgets/cobwebs.py
+++ b/hubs/widgets/cobwebs.py
@@ -2,30 +2,12 @@ from hubs.hinting import hint, prefixed as _
 
 import arrow
 import datetime
-import jinja2
 
+from hubs.widgets import templating
 from hubs.widgets.chrome import panel
 klass_switcher = lambda data: 'panel-danger' if data['archived'] else 'panel-info'
 chrome = panel(None, key='old', klass=klass_switcher)
-
-template = jinja2.Template("""
-{% if old %}
-<ul class="media-list">
-    <li class="media">
-        <div class="media-left">
-            <img class="media-object square-32" src="{{icon}}"/>
-        </div>
-        <div class="media-body">
-        {% if archived %}
-            This hub has been archived and locked.
-        {% else %}
-            Cobweb alert!  This hub was last active {{then}}.
-        {% endif %}
-        </div>
-    </li>
-</ul>
-{% endif %}
-""")
+template = templating.environment.get_template('templates/cobwebs.html')
 
 
 def data(session, widget):
diff --git a/hubs/widgets/dummy.py b/hubs/widgets/dummy.py
index 28f48fb..c6fbb90 100755
--- a/hubs/widgets/dummy.py
+++ b/hubs/widgets/dummy.py
@@ -1,14 +1,12 @@
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
-
-import jinja2
+from hubs.widgets import templating
 
 import hubs.validators as validators
 
-
-template = jinja2.Template("{{text}}")
 chrome = panel("This is a dummy widget")
+template = templating.environment.get_template('templates/dummy.html')
 
 @argument(name="text", default="Lorem ipsum dolor...",
           validator=validators.text,
diff --git a/hubs/widgets/fedmsgstats.py b/hubs/widgets/fedmsgstats.py
index cbb920f..d06f398 100755
--- a/hubs/widgets/fedmsgstats.py
+++ b/hubs/widgets/fedmsgstats.py
@@ -1,10 +1,10 @@
 from hubs.hinting import hint
 from hubs.widgets.chrome import panel
+from hubs.widgets import templating
 
 from hubs.utils import commas
 
 import flask
-import jinja2
 import requests
 
 import fedmsg.config
@@ -13,29 +13,7 @@ import fedmsg.meta
 config = fedmsg.config.load_config()
 
 chrome = panel()
-template = jinja2.Template("""
-<div class="stats-container row">
-  <div class="col-sm-7 col-md-12 col-lg-7">
-  <table class="stats-table">
-    <tr><th>subscribers</th><th>subscribed to</th></tr>
-    <tr class="text-info"><td>{{subscribers_text}}</td><td class="text-center">{{subscribed_text}}</td></tr>
-  </table>
-  </div>
-  <div class="col-sm-5 col-md-12 col-lg-5">
-  {% if session['nickname'] != username %}
-    {% if session['nickname'] in subscribers %}
-    <li><form action="{{hub_unsubscribe_url}}" method="POST">
-        <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unsubscribe</button>
-    </form></li>
-    {% else %}
-    <li><form action="{{hub_subscribe_url}}" method="POST">
-        <button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Subscribe</button>
-    </form></li>
-    {% endif %}
-  {% endif %}
-  </div>
-</div>
-""")
+template = templating.environment.get_template('templates/fedmsgstats.html')
 
 
 def data(session, widget, username):
diff --git a/hubs/widgets/feed.py b/hubs/widgets/feed.py
index e7cd4da..486ef68 100755
--- a/hubs/widgets/feed.py
+++ b/hubs/widgets/feed.py
@@ -2,6 +2,7 @@ from __future__ import print_function
 
 from hubs.hinting import hint
 from hubs.widgets.base import argument
+from hubs.widgets import templating
 
 import fmn.lib
 import fmn.lib.hinting
@@ -9,7 +10,6 @@ import fmn.lib.hinting
 import datanommer.models
 
 import arrow
-import jinja2
 import munch
 import requests
 
@@ -29,54 +29,10 @@ fmn_url = config['fmn.url']
 bazillion = 1000
 paths = fmn.lib.load_rules(root='fmn.rules')
 
-
-template = jinja2.Template("""
-{% for match in matches %}
-<div class="panel panel-default">
-  <div class="panel-body">
-    <div class="media">
-      <div class="media-left">
-        <a href="{{match['link']}}" target="_blank">
-          <img class="media-object square-32 img-circle" src="{{match['secondary_icon']}}">
-        </a>
-      </div>
-      <div class="media-body">
-        <h4 class="media-heading">{{match['markup']}}</h4>
-        {{match['human_time']}}
-        {% if match['msg_ids'] | length > 1 %}
-            <div class="pull-right">
-                <a id="expand-{{match['dom_id']}}" href="javascript:expand_feed_entries('{{match['dom_id']}}');">
-                    <span class="glyphicon glyphicon-chevron-down"></span>
-                    expand
-                </a>
-                <a id="collapse-{{match['dom_id']}}" href="javascript:collapse_feed_entries('{{match['dom_id']}}');" class="hidden">
-                    <span class="glyphicon glyphicon-chevron-up"></span>
-                    collapse
-                </a>
-            </div>
-            <ul id="content-{{match['dom_id']}}" class="hidden list-unstyled">
-                {% for idx, constituent in match['msg_ids'].items() %}
-                <li>
-                    {{ constituent['markup'] }}
-                    {% if constituent['link'] %}
-                    <a href="{{constituent['link']}}" target="_blank">
-                        <span class="glyphicon glyphicon-new-window"></span>
-                    </a>
-                    {% endif %}
-                </li>
-                {% endfor %}
-            </ul>
-        {% endif %}
-      </div>
-    </div>
-  </div>
-</div>
-{% endfor %}
-""")
-
 # No chrome around the feed.
 #from hubs.widgets.chrome import panel
 #chrome = panel()
+template = templating.environment.get_template('templates/feed.html')
 
 def apply_markup(match):
     markup = match['subtitle']
diff --git a/hubs/widgets/github_pr.py b/hubs/widgets/github_pr.py
index ff890aa..905924c 100755
--- a/hubs/widgets/github_pr.py
+++ b/hubs/widgets/github_pr.py
@@ -3,6 +3,7 @@ import logging
 from hubs.widgets.chrome import panel
 from hubs.hinting import hint
 from hubs.widgets.base import argument
+from hubs.widgets import templating
 
 import hubs.validators as validators
 import hubs.utils
@@ -10,60 +11,10 @@ import hubs.utils
 import fedmsg.config
 config = fedmsg.config.load_config()
 
-import jinja2
-
 log = logging.getLogger(__name__)
 
 chrome = panel("Github: Pull Requests")
-
-template = jinja2.Template("""
-<a class="btn btn-success" target="_blank"
-    href="https://github.com/{{ organization }}" >
-  Organization Github Page
-</a>
-<hr/>
-
-  {% for pr in pulls[:display_number] %}
-    <li class="media">
-      <div class="media-left">
-        <a  href="{{ pr['url'] }}" 
-            target="_blank">
-        <span class="label label-default">#{{ pr['number'] }}</span>
-        </a>
-      </div>
-      <div class="media-body">
-        <h4 class="media-heading">
-          {{ organization }} -
-          <span title="{{ pr['title'] }}">
-            {{ pr['title'] }} {% if
-              pr['title'] | length > 45 %} ... {% endif %}
-          </span>
-        </h4>
-        Opened by:
-          <a href="https://github.com/{{ pr['user'] }}" 
-              target="_blank">
-            {{ pr['user'] }}
-          </a>
-        --
-        {% if pr['assignee'] %}
-          Assigned to:
-          <a href="https://github.com/{{ pr['assignee'] }}"
-              target="_blank">
-            {{ pr['assignee'] }}
-          </a>
-        {% else %}
-          <span class="text-muted">Unassigned</span>
-        {% endif %}
-      </div>
-    </li>
-  {% endfor %}
-  {% if all_pr | length > display_number %}
-  <li>
-    And {{ all_pr | length - display_number }} more ...
-  </li>
-  {% endif %}
-</ul>
-""")
+template = templating.environment.get_template('templates/github_pr.html')
 
 
 @argument(name="display_number",
diff --git a/hubs/widgets/githubissues.py b/hubs/widgets/githubissues.py
index feabd5e..35536f7 100755
--- a/hubs/widgets/githubissues.py
+++ b/hubs/widgets/githubissues.py
@@ -1,53 +1,13 @@
 from hubs.widgets.chrome import panel
 from hubs.hinting import hint
 from hubs.widgets.base import argument
+from hubs.widgets import templating
 import hubs.validators as validators
-import jinja2
 import requests
 
 chrome = panel("Github: Newest Open Tickets")
+template = templating.environment.get_template('templates/githubissues.html')
 
-template = jinja2.Template("""
-  <br />
-  {% for i in range(display_number) %}
-    <div>
-      <table class="table-responsive table-condensed">
-        <tr>
-          <td valign="top" class="panel-heading">
-            <b>#{{ all_issues[i]['num'] }}</b>
-          </td>
-          <td width="300px">
-            <b>{{ all_issues[i]['title'] }}</b>
-          </td>
-        </tr>
-      </table>
-      <table class="table-responsive table-condensed">
-        <tr>
-          <td width="300px">
-            <font size="2">
-              <i>Opened by <a href="https://github.com/{{ all_issues[i]['openedby'] }}" target="_blank">{{ all_issues[i]['openedby'] }}</a></i>
-              {% if all_issues[i]['assignee']|string() == "None" %}
-                <br /><i>Unclaimed</i>
-              {% else %}
-                 <br /><i>Assigned by <a href="https://github.com/{{ all_issues[i]['assignee'] }}" target="_blank">{{ all_issues[i]['assignee'] }}</a></i>
-              {% endif %}
-            </font>
-          </td>
-          <td>
-             <a href="https://github.com/{{ org }}/{{ repo }}/issues/{{ all_issues[i]['num'] }}" target="_blank">
-               <button type="button" class="btn btn-default" aria-label="Left Align">
-                 <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-               </button>
-             </a>
-          </td>
-        </tr>
-      </table>
-    </div>
-    {% endfor %}
-    <div class="row">
-      <a href="https://github.com/{{ org }}/{{ repo }}/issues" target="_blank"><center>All Issues</center></a>
-    </div>
-""")
 
 @argument(name="repo",
           default=None,
diff --git a/hubs/widgets/linechart.py b/hubs/widgets/linechart.py
index 0a3fb91..ee47d85 100755
--- a/hubs/widgets/linechart.py
+++ b/hubs/widgets/linechart.py
@@ -1,24 +1,15 @@
 from hubs.hinting import hint
 from hubs.widgets.base import argument
-
-import jinja2
+from hubs.widgets import templating
 
 import hubs.validators as validators
 
 import fedmsg.config
 config = fedmsg.config.load_config()
 
-
-template = jinja2.Template("""
-<div class="flex-container">
-
-    <img src="{{url}}" />
-
-</div>
-""")
-
 from hubs.widgets.chrome import panel
 chrome = panel("Weekly Activity")
+template = templating.environment.get_template('templates/linechart.html')
 
 
 @argument(name="username",
diff --git a/hubs/widgets/meetings.py b/hubs/widgets/meetings.py
index c3a8382..37c11ee 100755
--- a/hubs/widgets/meetings.py
+++ b/hubs/widgets/meetings.py
@@ -1,6 +1,7 @@
 #from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
 from hubs.widgets.chrome import panel
+from hubs.widgets import templating
 
 from hubs import validators
 from hubs import utils
@@ -8,10 +9,8 @@ from hubs import utils
 import arrow
 import collections
 import datetime
-import jinja2
 import requests
 
-chrome = panel('Reminders', key='meetings')
 
 def next_meeting(meetings):
     now = datetime.datetime.utcnow()
@@ -29,7 +28,7 @@ def next_meeting(meetings):
             meeting['start_date'] = start_dt.strftime('%b %d')
             meeting['stop_date'] = stop_dt.strftime('%b %d')
             meeting['display_time'] = True
-            meeting['display_duration'] = False            
+            meeting['display_duration'] = False
             # Just UTC for now until we do this on the client and can access
             # their local with moment.js.
             meeting['start_time'] = meeting['meeting_time_start'][:-3] + " UTC"
@@ -45,33 +44,10 @@ def next_meeting(meetings):
 
     return None
 
-jinja2_environment = jinja2.Environment()
-jinja2_environment.filters['next_meeting'] = next_meeting
-template = jinja2_environment.from_string("""
-{% if meetings %}
-    {% for title, items in meetings.items() %}
-        {% set next = items | next_meeting %}
-        {% if next %}
-        <p>The {{ next.meeting_name }} is {{ next.human_time }}</p>
-        {% if next.display_duration %}
-        <h1 class='inline'>{{next.start_date}} - {{next.stop_date}}</h1>
-        {% else %}
-        <h1 class='inline'>{{next.start_date}}</h1>
-        {% endif %}
-        <div class='inline'>
-            {% if next.display_time %}
-            @{{next.start_time}}<br/>
-            {% endif %}
-            {{next.location}}<br/>
-            {{next.meeting_information_html}}
-        </div>
-        {% endif %}
-        {% if meetings | length > 1 %}
-        <hr/>
-        {% endif %}
-    {% endfor %}
-{% endif %}
-""")
+
+chrome = panel('Reminders', key='meetings')
+templating.environment.filters['next_meeting'] = next_meeting
+template = templating.environment.get_template('templates/meetings.html')
 
 
 @argument(name="calendar",
diff --git a/hubs/widgets/pagure_pr.py b/hubs/widgets/pagure_pr.py
index 0ee7de9..59444db 100755
--- a/hubs/widgets/pagure_pr.py
+++ b/hubs/widgets/pagure_pr.py
@@ -1,63 +1,16 @@
 from hubs.hinting import hint
 from hubs.widgets.chrome import panel
-import jinja2
-import requests
 from hubs.widgets.base import argument
-import hubs.validators as validators
-chrome = panel("Newest Open Pull Requests on Pagure")
+from hubs.widgets import templating
 
-pagure_url = "https://pagure.io/api/0"
+import hubs.validators as validators
 
-template = jinja2.Template("""
-<ul class="media-list">
-  {% for pr in all_pr[:10] %}
-    <li class="media">
-      <div class="media-left">
-        <a  href="https://pagure.io/{{ repo }}/pull-request/{{ pr['pr_id'] }}"
-            target="_blank">
-        <span class="label label-default">#{{ pr['pr_id'] }}</span>
-        </a>
-      </div>
-      <div class="media-body">
-        <h4 class="media-heading">
-          {{ pr['pr_project_name'] }} -
-          <span title="{{ pr['pr_title_full'] }}">
-            {{ pr['pr_title'] }} {% if
-              pr['pr_title_full'] | length > 45 %} ... {% endif %}
-          </span>
-        </h4>
-        Opened by:
-          <a href="https://pagure.io/user/{{ pr['pr_openedby'] }}"
-              target="_blank">
-            {{ pr['pr_openedby'] }}
-          </a>
-        --
-        {% if pr['pr_assignee'] %}
-          Assigned to:
-          <a href="https://pagure.io/user/{{ pr['pr_assignee'] }}"
-              target="_blank">
-            {{ pr['pr_assignee'] }}
-          </a>
-        {% else %}
-          <span class="text-muted">Unassigned</span>
-        {% endif %}
-      </div>
-    </li>
-  {% endfor %}
-  {% if all_pr | length > 10 %}
-  <li>
-    And {{ all_pr | length - 10 }} more ...
-  </li>
-  {% endif %}
-</ul>
+import requests
 
-<hr/>
+pagure_url = "https://pagure.io/api/0"
 
-<a class="btn btn-success" target="_blank"
-    href="https://pagure.io/{{ repo }}/pull-requests">
-  All Pull-Requests
-</a>
-""")
+chrome = panel("Newest Open Pull Requests on Pagure")
+template = templating.environment.get_template('templates/pagure_pr.html')
 
 
 @argument(name="repo",
diff --git a/hubs/widgets/pagureissues.py b/hubs/widgets/pagureissues.py
index 6eecf9b..ab13903 100755
--- a/hubs/widgets/pagureissues.py
+++ b/hubs/widgets/pagureissues.py
@@ -1,63 +1,14 @@
 from hubs.hinting import hint
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
+from hubs.widgets import templating
 import hubs.validators as validators
-import jinja2
 import requests
 
-chrome = panel("Newest Open Tickets on Pagure")
-
 pagure_url = "https://pagure.io/api/0"
 
-template = jinja2.Template("""
-<a class="btn btn-success" target="_blank"
-    href="https://pagure.io/{{ repo }}/issues">
-  All Issues
-</a>
-<hr/>
-
-<ul class="media-list">
-  {% for issue in all_issues[:10] %}
-    <li class="media">
-      <div class="media-left">
-        <a  href="https://pagure.io/{{ repo }}/issue/{{ issue['issue_id'] }}"
-            target="_blank">
-          {{ issue['issue_project_name'] }}
-        <span class="label label-default">#{{ issue['issue_id'] }}</span>
-        </a>
-      </div>
-      <div class="media-body">
-        <h4 class="media-heading">
-          <span title="{{ issue['issue_title_full'] }}">
-            {{ issue['issue_title'] }} {% if
-              issue['issue_title_full'] | length > 45 %} ... {% endif %}
-          </span>
-        </h4>
-        Opened by:
-          <a href="https://pagure.io/user/{{ issue['issue_openedby'] }}"
-              target="_blank">
-            {{ issue['issue_openedby'] }}
-          </a>
-        --
-        {% if issue['issue_assignee'] %}
-          Assigned to:
-          <a href="https://pagure.io/user/{{issue['issue_assignee'] }}"
-              target="_blank">
-            {{issue['issue_assignee'] }}
-          </a>
-        {% else %}
-          <span class="text-muted">Unassigned</span>
-        {% endif %}
-      </div>
-    </li>
-  {% endfor %}
-  {% if all_issues | length > 10 %}
-  <li>
-    And {{ all_issues | length - 10 }} more ...
-  </li>
-  {% endif %}
-</ul>
-""")
+chrome = panel("Newest Open Tickets on Pagure")
+template = templating.environment.get_template('templates/pagureissues.html')
 
 
 @argument(name="repo",
diff --git a/hubs/widgets/rules.py b/hubs/widgets/rules.py
index 9793207..4b17931 100755
--- a/hubs/widgets/rules.py
+++ b/hubs/widgets/rules.py
@@ -1,58 +1,14 @@
 from collections import OrderedDict as ordereddict
 
-import jinja2
-
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument, avatar
+from hubs.widgets import templating
 from hubs import validators
 
 chrome = panel()
-template = jinja2.Template("""
-<div class="rules-container">
-  {% if link %}
-  <h6>community rules</h6>
-  <p><a href="{{link}}">Community Rules and Guidelines</a></h6>
-  {% endif %}
-  <h6>group owners</h6>
-  <div class="row">
-    {% for owner in owners %}
-        <div class="col-sm-6">
-            <img class="img-circle" src="{{owners[owner]}}"/>
-            <a href="{{ url_for('hub', name=owner)}}">{{owner}}</a>
-        </div>
-    {% endfor %}
-  </div>
-  {% if schedule_text or schedule_link or minutes_link %}
-  <h6>meetings</h6>
-  {% if schedule_text %}
-  <p>{{schedule_text}}</p>
-  {% endif %}
-  <p>
-  {% if schedule_link %}
-  <a target="_blank" href="{{schedule_link}}">Meeting Schedule</a>
-  {% endif %}
-  {% if schedule_link and minutes_link %}
-  &#8214;
-  {% endif %}
-  {% if minutes_link %}
-  <a target="_blank" href="{{minutes_link}}">Past Meeting Minutes</a>
-  {% endif %}
-  </p>
-  {% endif %}
-</div>
-<style>
-  .rules-table {
-    width: 180px
-    padding: 0px;
-    margin: 0px;
-    display: inline-block;
-  }
-  .rules-table td {
-    font-size: 32pt;
-  }
-</style>
-""")
+template = templating.environment.get_template('templates/rules.html')
+
 
 @argument(name='link', default=None,
           validator=validators.link,
diff --git a/hubs/widgets/stats.py b/hubs/widgets/stats.py
index 3523e84..7f329cf 100755
--- a/hubs/widgets/stats.py
+++ b/hubs/widgets/stats.py
@@ -1,55 +1,13 @@
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
+from hubs.widgets import templating
 
 from hubs.utils import commas
 
 import flask
-import jinja2
 
 chrome = panel()
-template = jinja2.Template("""
-<div class="stats-container row">
-<div class="col-md-7">
-  <table class="stats-table">
-    <tr><th>Members</th><th>Subscribers</th></tr>
-    <tr class="text-info"><td>{{members_text}}</td><td class="text-right">{{subscribers_text}}</td></tr>
-  </table>
-</div>
-<div class="col-md-5">
-  <ul class="list-unstyled">
-  {% if g.auth.nickname in subscribers %}
-  <li><form action="{{hub_unsubscribe_url}}" method="POST">
-      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unsubscribe</button>
-  </form></li>
-  {% else %}
-  <li><form action="{{hub_subscribe_url}}" method="POST">
-      <button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Subscribe</button>
-  </form></li>
-  {% endif %}
-
-  {% if g.auth.nickname in stargazers %}
-  <li><form action="{{hub_unstar_url}}" method="POST">
-      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unstar Hub</button>
-  </form></li>
-  {% else %}
-  <li><form action="{{hub_star_url}}" method="POST">
-      <button class="btn btn-default"><span class="glyphicon glyphicon-star" aria-hidden="true"></span> Star Hub</button>
-  </form></li>
-  {% endif %}
-
-  {% if g.auth.nickname in members %}
-  <li><form action="{{hub_leave_url}}" method="POST">
-      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Leave Hub</button>
-  </form></li>
-  {% else %}
-  <li><form action="{{hub_join_url}}" method="POST">
-      <button class="btn btn-default"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Join Hub</button>
-  </form></li>
-  {% endif %}
-  </ul>
-</div>
-</div>
-""")
+template = templating.environment.get_template('templates/stats.html')
 
 
 def data(session, widget):
diff --git a/hubs/widgets/sticky.py b/hubs/widgets/sticky.py
index 885108d..95bd2dc 100755
--- a/hubs/widgets/sticky.py
+++ b/hubs/widgets/sticky.py
@@ -1,14 +1,13 @@
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
-
-import jinja2
+from hubs.widgets import templating
 
 import hubs.validators as validators
 
-template = jinja2.Template("{{text}}")
-
 chrome = panel(title='Sticky Note', klass="panel-info")
+template = templating.environment.get_template('templates/sticky.html')
+
 
 @argument(name="text", default="Lorem ipsum dolor...",
           validator=validators.text,
diff --git a/hubs/widgets/subscriptions.py b/hubs/widgets/subscriptions.py
index 395ee0c..6d7e52f 100755
--- a/hubs/widgets/subscriptions.py
+++ b/hubs/widgets/subscriptions.py
@@ -1,6 +1,7 @@
 # from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
 from hubs.widgets.chrome import panel
+from hubs.widgets import templating
 
 from hubs import validators
 
@@ -9,24 +10,9 @@ import hubs.models
 from fedmsg.meta.base import BaseConglomerator as BC
 
 import flask
-import jinja2
 
 chrome = panel('Hubs', key='associations')
-template = jinja2.Template("""
-{% if associations %}
-    {% if memberships %}
-        <p><strong>Belongs to: </strong> {{memberships_text}}</p>
-    {% endif %}
-    <hr/>
-    {% if subscriptions %}
-        <p><strong>Subscribes to:</strong>
-        {% for subscription in subscriptions %}
-        <a href="{{ url_for('hub', name=subscription)}}">
-        {{subscription}}</a>
-        {% endfor %}</p>
-    {% endif %}
-{% endif %}
-""")
+template = templating.environment.get_template('templates/subscriptions.html')
 
 
 @argument(name="username",
diff --git a/hubs/widgets/templates/about.html b/hubs/widgets/templates/about.html
new file mode 100644
index 0000000..f73bbab
--- /dev/null
+++ b/hubs/widgets/templates/about.html
@@ -0,0 +1 @@
+<p>{{text}}</p>
diff --git a/hubs/widgets/templates/badges.html b/hubs/widgets/templates/badges.html
new file mode 100644
index 0000000..9901f43
--- /dev/null
+++ b/hubs/widgets/templates/badges.html
@@ -0,0 +1,6 @@
+<div class="flex-container">
+{% for badge in assertions[:10] %}
+  <a width="60px" href="https://badges.fedoraproject.org/badge/{{badge['id']}}">
+    <img width="60px" src="{{badge['image']}}"/></a>
+{%endfor%}
+</div>
diff --git a/hubs/widgets/templates/bugzilla.html b/hubs/widgets/templates/bugzilla.html
new file mode 100644
index 0000000..21eceb7
--- /dev/null
+++ b/hubs/widgets/templates/bugzilla.html
@@ -0,0 +1,20 @@
+<ul class="media-list">
+  {% for item in issues %}
+    <li class="media">
+      <div class="media-left">
+        <a href="https://bugzilla.redhat.com/show_bug.cgi?id=
+        {{ item['id'] }}" target="_blank">
+        <span class="label label-default">#{{ item['id'] }}</span>
+        </a>
+      </div>
+      <div class="media-body">
+        <h4 class="media-heading">
+          <a href="https://admin.fedoraproject.org/pkgdb/
+          package/rpms/{{ item['pkg_name'] }}" target=
+          "_blank">{{ item['pkg_name'] }}</a>:
+          {{ item['title'] }}
+        </h4>
+      </div>
+    </li>
+  {% endfor %}
+</ul>
diff --git a/hubs/widgets/templates/cobwebs.html b/hubs/widgets/templates/cobwebs.html
new file mode 100644
index 0000000..d4b2e7b
--- /dev/null
+++ b/hubs/widgets/templates/cobwebs.html
@@ -0,0 +1,16 @@
+{% if old %}
+<ul class="media-list">
+    <li class="media">
+        <div class="media-left">
+            <img class="media-object square-32" src="{{icon}}"/>
+        </div>
+        <div class="media-body">
+        {% if archived %}
+            This hub has been archived and locked.
+        {% else %}
+            Cobweb alert!  This hub was last active {{then}}.
+        {% endif %}
+        </div>
+    </li>
+</ul>
+{% endif %}
diff --git a/hubs/widgets/templates/dummy.html b/hubs/widgets/templates/dummy.html
new file mode 100644
index 0000000..8d15644
--- /dev/null
+++ b/hubs/widgets/templates/dummy.html
@@ -0,0 +1 @@
+{{text}}
diff --git a/hubs/widgets/templates/fedmsgstats.html b/hubs/widgets/templates/fedmsgstats.html
new file mode 100644
index 0000000..37d6c42
--- /dev/null
+++ b/hubs/widgets/templates/fedmsgstats.html
@@ -0,0 +1,21 @@
+<div class="stats-container row">
+  <div class="col-sm-7 col-md-12 col-lg-7">
+  <table class="stats-table">
+    <tr><th>subscribers</th><th>subscribed to</th></tr>
+    <tr class="text-info"><td>{{subscribers_text}}</td><td class="text-center">{{subscribed_text}}</td></tr>
+  </table>
+  </div>
+  <div class="col-sm-5 col-md-12 col-lg-5">
+  {% if session['nickname'] != username %}
+    {% if session['nickname'] in subscribers %}
+    <li><form action="{{hub_unsubscribe_url}}" method="POST">
+        <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unsubscribe</button>
+    </form></li>
+    {% else %}
+    <li><form action="{{hub_subscribe_url}}" method="POST">
+        <button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Subscribe</button>
+    </form></li>
+    {% endif %}
+  {% endif %}
+  </div>
+</div>
diff --git a/hubs/widgets/templates/feed.html b/hubs/widgets/templates/feed.html
new file mode 100644
index 0000000..a0dd732
--- /dev/null
+++ b/hubs/widgets/templates/feed.html
@@ -0,0 +1,41 @@
+{% for match in matches %}
+<div class="panel panel-default">
+  <div class="panel-body">
+    <div class="media">
+      <div class="media-left">
+        <a href="{{match['link']}}" target="_blank">
+          <img class="media-object square-32 img-circle" src="{{match['secondary_icon']}}">
+        </a>
+      </div>
+      <div class="media-body">
+        <h4 class="media-heading">{{match['markup']}}</h4>
+        {{match['human_time']}}
+        {% if match['msg_ids'] | length > 1 %}
+            <div class="pull-right">
+                <a id="expand-{{match['dom_id']}}" href="javascript:expand_feed_entries('{{match['dom_id']}}');">
+                    <span class="glyphicon glyphicon-chevron-down"></span>
+                    expand
+                </a>
+                <a id="collapse-{{match['dom_id']}}" href="javascript:collapse_feed_entries('{{match['dom_id']}}');" class="hidden">
+                    <span class="glyphicon glyphicon-chevron-up"></span>
+                    collapse
+                </a>
+            </div>
+            <ul id="content-{{match['dom_id']}}" class="hidden list-unstyled">
+                {% for idx, constituent in match['msg_ids'].items() %}
+                <li>
+                    {{ constituent['markup'] }}
+                    {% if constituent['link'] %}
+                    <a href="{{constituent['link']}}" target="_blank">
+                        <span class="glyphicon glyphicon-new-window"></span>
+                    </a>
+                    {% endif %}
+                </li>
+                {% endfor %}
+            </ul>
+        {% endif %}
+      </div>
+    </div>
+  </div>
+</div>
+{% endfor %}
diff --git a/hubs/widgets/templates/github_pr.html b/hubs/widgets/templates/github_pr.html
new file mode 100644
index 0000000..8b4a105
--- /dev/null
+++ b/hubs/widgets/templates/github_pr.html
@@ -0,0 +1,46 @@
+<a class="btn btn-success" target="_blank"
+    href="https://github.com/{{ organization }}" >
+  Organization Github Page
+</a>
+<hr/>
+<ul class="media-list">
+{% for pr in pulls[:display_number] %}
+  <li class="media">
+    <div class="media-left">
+      <a  href="{{ pr['url'] }}" 
+          target="_blank">
+      <span class="label label-default">#{{ pr['number'] }}</span>
+      </a>
+    </div>
+    <div class="media-body">
+      <h4 class="media-heading">
+        {{ organization }} -
+        <span title="{{ pr['title'] }}">
+          {{ pr['title'] }} {% if
+            pr['title'] | length > 45 %} ... {% endif %}
+        </span>
+      </h4>
+      Opened by:
+        <a href="https://github.com/{{ pr['user'] }}" 
+            target="_blank">
+          {{ pr['user'] }}
+        </a>
+      --
+      {% if pr['assignee'] %}
+        Assigned to:
+        <a href="https://github.com/{{ pr['assignee'] }}"
+            target="_blank">
+          {{ pr['assignee'] }}
+        </a>
+      {% else %}
+        <span class="text-muted">Unassigned</span>
+      {% endif %}
+    </div>
+  </li>
+{% endfor %}
+{% if all_pr | length > display_number %}
+  <li>
+    And {{ all_pr | length - display_number }} more ...
+  </li>
+{% endif %}
+</ul>
diff --git a/hubs/widgets/templates/githubissues.html b/hubs/widgets/templates/githubissues.html
new file mode 100644
index 0000000..cd5de9d
--- /dev/null
+++ b/hubs/widgets/templates/githubissues.html
@@ -0,0 +1,38 @@
+{% for i in range(display_number) %}
+<div>
+  <table class="table-responsive table-condensed">
+    <tr>
+      <td valign="top" class="panel-heading">
+        <b>#{{ all_issues[i]['num'] }}</b>
+      </td>
+      <td width="300px">
+        <b>{{ all_issues[i]['title'] }}</b>
+      </td>
+    </tr>
+  </table>
+  <table class="table-responsive table-condensed">
+    <tr>
+      <td width="300px">
+        <font size="2">
+          <i>Opened by <a href="https://github.com/{{ all_issues[i]['openedby'] }}" target="_blank">{{ all_issues[i]['openedby'] }}</a></i>
+          {% if all_issues[i]['assignee']|string() == "None" %}
+            <br /><i>Unclaimed</i>
+          {% else %}
+             <br /><i>Assigned by <a href="https://github.com/{{ all_issues[i]['assignee'] }}" target="_blank">{{ all_issues[i]['assignee'] }}</a></i>
+          {% endif %}
+        </font>
+      </td>
+      <td>
+         <a href="https://github.com/{{ org }}/{{ repo }}/issues/{{ all_issues[i]['num'] }}" target="_blank">
+           <button type="button" class="btn btn-default" aria-label="Left Align">
+             <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
+           </button>
+         </a>
+      </td>
+    </tr>
+  </table>
+</div>
+{% endfor %}
+<div class="row">
+  <a href="https://github.com/{{ org }}/{{ repo }}/issues" target="_blank"><center>All Issues</center></a>
+</div>
diff --git a/hubs/widgets/templates/linechart.html b/hubs/widgets/templates/linechart.html
new file mode 100644
index 0000000..5b4e756
--- /dev/null
+++ b/hubs/widgets/templates/linechart.html
@@ -0,0 +1,3 @@
+<div class="flex-container">
+    <img src="{{url}}" />
+</div>
diff --git a/hubs/widgets/templates/meetings.html b/hubs/widgets/templates/meetings.html
new file mode 100644
index 0000000..76a4c1b
--- /dev/null
+++ b/hubs/widgets/templates/meetings.html
@@ -0,0 +1,23 @@
+{% if meetings %}
+    {% for title, items in meetings.items() %}
+        {% set next = items | next_meeting %}
+        {% if next %}
+        <p>The {{ next.meeting_name }} is {{ next.human_time }}</p>
+        {% if next.display_duration %}
+        <h1 class='inline'>{{next.start_date}} - {{next.stop_date}}</h1>
+        {% else %}
+        <h1 class='inline'>{{next.start_date}}</h1>
+        {% endif %}
+        <div class='inline'>
+            {% if next.display_time %}
+            @{{next.start_time}}<br/>
+            {% endif %}
+            {{next.location}}<br/>
+            {{next.meeting_information_html}}
+        </div>
+        {% endif %}
+        {% if meetings | length > 1 %}
+        <hr/>
+        {% endif %}
+    {% endfor %}
+{% endif %}
diff --git a/hubs/widgets/templates/pagure_pr.html b/hubs/widgets/templates/pagure_pr.html
new file mode 100644
index 0000000..5508cb7
--- /dev/null
+++ b/hubs/widgets/templates/pagure_pr.html
@@ -0,0 +1,48 @@
+<ul class="media-list">
+  {% for pr in all_pr[:10] %}
+    <li class="media">
+      <div class="media-left">
+        <a  href="https://pagure.io/{{ repo }}/pull-request/{{ pr['pr_id'] }}"
+            target="_blank">
+        <span class="label label-default">#{{ pr['pr_id'] }}</span>
+        </a>
+      </div>
+      <div class="media-body">
+        <h4 class="media-heading">
+          {{ pr['pr_project_name'] }} -
+          <span title="{{ pr['pr_title_full'] }}">
+            {{ pr['pr_title'] }} {% if
+              pr['pr_title_full'] | length > 45 %} ... {% endif %}
+          </span>
+        </h4>
+        Opened by:
+          <a href="https://pagure.io/user/{{ pr['pr_openedby'] }}"
+              target="_blank">
+            {{ pr['pr_openedby'] }}
+          </a>
+        --
+        {% if pr['pr_assignee'] %}
+          Assigned to:
+          <a href="https://pagure.io/user/{{ pr['pr_assignee'] }}"
+              target="_blank">
+            {{ pr['pr_assignee'] }}
+          </a>
+        {% else %}
+          <span class="text-muted">Unassigned</span>
+        {% endif %}
+      </div>
+    </li>
+  {% endfor %}
+  {% if all_pr | length > 10 %}
+  <li>
+    And {{ all_pr | length - 10 }} more ...
+  </li>
+  {% endif %}
+</ul>
+
+<hr/>
+
+<a class="btn btn-success" target="_blank"
+    href="https://pagure.io/{{ repo }}/pull-requests">
+  All Pull-Requests
+</a>
diff --git a/hubs/widgets/templates/pagureissues.html b/hubs/widgets/templates/pagureissues.html
new file mode 100644
index 0000000..258d37e
--- /dev/null
+++ b/hubs/widgets/templates/pagureissues.html
@@ -0,0 +1,47 @@
+<a class="btn btn-success" target="_blank"
+    href="https://pagure.io/{{ repo }}/issues">
+  All Issues
+</a>
+<hr/>
+
+<ul class="media-list">
+  {% for issue in all_issues[:10] %}
+    <li class="media">
+      <div class="media-left">
+        <a  href="https://pagure.io/{{ repo }}/issue/{{ issue['issue_id'] }}"
+            target="_blank">
+          {{ issue['issue_project_name'] }}
+        <span class="label label-default">#{{ issue['issue_id'] }}</span>
+        </a>
+      </div>
+      <div class="media-body">
+        <h4 class="media-heading">
+          <span title="{{ issue['issue_title_full'] }}">
+            {{ issue['issue_title'] }} {% if
+              issue['issue_title_full'] | length > 45 %} ... {% endif %}
+          </span>
+        </h4>
+        Opened by:
+          <a href="https://pagure.io/user/{{ issue['issue_openedby'] }}"
+              target="_blank">
+            {{ issue['issue_openedby'] }}
+          </a>
+        --
+        {% if issue['issue_assignee'] %}
+          Assigned to:
+          <a href="https://pagure.io/user/{{issue['issue_assignee'] }}"
+              target="_blank">
+            {{issue['issue_assignee'] }}
+          </a>
+        {% else %}
+          <span class="text-muted">Unassigned</span>
+        {% endif %}
+      </div>
+    </li>
+  {% endfor %}
+  {% if all_issues | length > 10 %}
+  <li>
+    And {{ all_issues | length - 10 }} more ...
+  </li>
+  {% endif %}
+</ul>
diff --git a/hubs/widgets/templates/panel.html b/hubs/widgets/templates/panel.html
new file mode 100644
index 0000000..ba6dff2
--- /dev/null
+++ b/hubs/widgets/templates/panel.html
@@ -0,0 +1,12 @@
+<div class="panel {{klass}}">
+  {{heading}}
+  <div class="pull-right widget-buttons">
+    <!-- the AGPLv3 wrapper puts the source url in all responses -->
+    <a href="{{source_url}}"><span class="glyphicon glyphicon-eye-open"></span></a>
+    <a href="{{widget_url}}"><span class="glyphicon glyphicon-new-window"></span></a>
+    <a href="#"><span class="glyphicon glyphicon-edit"></span></a>
+  </div>
+  <div class="panel-body">
+    {{content}}
+  </div> <!-- end panel-body -->
+</div> <!-- end panel -->
diff --git a/hubs/widgets/templates/panel_heading.html b/hubs/widgets/templates/panel_heading.html
new file mode 100644
index 0000000..d7d26f3
--- /dev/null
+++ b/hubs/widgets/templates/panel_heading.html
@@ -0,0 +1,5 @@
+<div class="panel-heading">
+  <h3 class="panel-title">
+    {{title}}
+  </h3>
+</div> <!-- end panel-heading -->
diff --git a/hubs/widgets/templates/rules.html b/hubs/widgets/templates/rules.html
new file mode 100644
index 0000000..2d986d1
--- /dev/null
+++ b/hubs/widgets/templates/rules.html
@@ -0,0 +1,43 @@
+<div class="rules-container">
+  {% if link %}
+  <h6>community rules</h6>
+  <p><a href="{{link}}">Community Rules and Guidelines</a></h6>
+  {% endif %}
+  <h6>group owners</h6>
+  <div class="row">
+    {% for owner in owners %}
+        <div class="col-sm-6">
+            <img class="img-circle" src="{{owners[owner]}}"/>
+            <a href="{{ url_for('hub', name=owner)}}">{{owner}}</a>
+        </div>
+    {% endfor %}
+  </div>
+  {% if schedule_text or schedule_link or minutes_link %}
+  <h6>meetings</h6>
+  {% if schedule_text %}
+  <p>{{schedule_text}}</p>
+  {% endif %}
+  <p>
+  {% if schedule_link %}
+  <a target="_blank" href="{{schedule_link}}">Meeting Schedule</a>
+  {% endif %}
+  {% if schedule_link and minutes_link %}
+  &#8214;
+  {% endif %}
+  {% if minutes_link %}
+  <a target="_blank" href="{{minutes_link}}">Past Meeting Minutes</a>
+  {% endif %}
+  </p>
+  {% endif %}
+</div>
+<style>
+  .rules-table {
+    width: 180px
+    padding: 0px;
+    margin: 0px;
+    display: inline-block;
+  }
+  .rules-table td {
+    font-size: 32pt;
+  }
+</style>
diff --git a/hubs/widgets/templates/stats.html b/hubs/widgets/templates/stats.html
new file mode 100644
index 0000000..a8f0a8a
--- /dev/null
+++ b/hubs/widgets/templates/stats.html
@@ -0,0 +1,41 @@
+<div class="stats-container row">
+<div class="col-md-7">
+  <table class="stats-table">
+    <tr><th>Members</th><th>Subscribers</th></tr>
+    <tr class="text-info"><td>{{members_text}}</td><td class="text-right">{{subscribers_text}}</td></tr>
+  </table>
+</div>
+<div class="col-md-5">
+  <ul class="list-unstyled">
+  {% if g.auth.nickname in subscribers %}
+  <li><form action="{{hub_unsubscribe_url}}" method="POST">
+      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unsubscribe</button>
+  </form></li>
+  {% else %}
+  <li><form action="{{hub_subscribe_url}}" method="POST">
+      <button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Subscribe</button>
+  </form></li>
+  {% endif %}
+
+  {% if g.auth.nickname in stargazers %}
+  <li><form action="{{hub_unstar_url}}" method="POST">
+      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Unstar Hub</button>
+  </form></li>
+  {% else %}
+  <li><form action="{{hub_star_url}}" method="POST">
+      <button class="btn btn-default"><span class="glyphicon glyphicon-star" aria-hidden="true"></span> Star Hub</button>
+  </form></li>
+  {% endif %}
+
+  {% if g.auth.nickname in members %}
+  <li><form action="{{hub_leave_url}}" method="POST">
+      <button class="btn btn-info"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span> Leave Hub</button>
+  </form></li>
+  {% else %}
+  <li><form action="{{hub_join_url}}" method="POST">
+      <button class="btn btn-default"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Join Hub</button>
+  </form></li>
+  {% endif %}
+  </ul>
+</div>
+</div>
diff --git a/hubs/widgets/templates/sticky.html b/hubs/widgets/templates/sticky.html
new file mode 100644
index 0000000..8d15644
--- /dev/null
+++ b/hubs/widgets/templates/sticky.html
@@ -0,0 +1 @@
+{{text}}
diff --git a/hubs/widgets/templates/subscriptions.html b/hubs/widgets/templates/subscriptions.html
new file mode 100644
index 0000000..74e46f2
--- /dev/null
+++ b/hubs/widgets/templates/subscriptions.html
@@ -0,0 +1,13 @@
+{% if associations %}
+    {% if memberships %}
+        <p><strong>Belongs to: </strong> {{memberships_text}}</p>
+    {% endif %}
+    <hr/>
+    {% if subscriptions %}
+        <p><strong>Subscribes to:</strong>
+        {% for subscription in subscriptions %}
+        <a href="{{ url_for('hub', name=subscription)}}">
+        {{subscription}}</a>
+        {% endfor %}</p>
+    {% endif %}
+{% endif %}
diff --git a/hubs/widgets/templates/workflow/pendingacls.html b/hubs/widgets/templates/workflow/pendingacls.html
new file mode 100644
index 0000000..c419d61
--- /dev/null
+++ b/hubs/widgets/templates/workflow/pendingacls.html
@@ -0,0 +1,20 @@
+{% if pending_acls %}
+{% if session['nickname'] == username %}
+<a class="btn btn-success" target="_blank" href="https://admin.fedoraproject.org/pkgdb/acl/pending/">Manage requests</a>
+<hr/>
+{% endif %}
+<ul class="media-list">
+{% for acl in pending_acls %}
+    <li class="media">
+        <div class="media-left">
+            <img class="media-object img-circle" src="{{acl['avatar']}}"/>
+        </div>
+        <div class="media-body">
+            <h4 class="media-heading">{{acl['user']}} is {{acl['status']}}</h4>
+            for {{acl['acl']}} on
+            {{acl['package']}}({{acl['collection']}})
+        </div>
+    </li>
+{% endfor %}
+</ul>
+{% endif %}
diff --git a/hubs/widgets/templates/workflow/updates2stable.html b/hubs/widgets/templates/workflow/updates2stable.html
new file mode 100644
index 0000000..35e2d25
--- /dev/null
+++ b/hubs/widgets/templates/workflow/updates2stable.html
@@ -0,0 +1,21 @@
+{% if updates %}
+{% if session['nickname'] == username %}
+<a class="btn btn-success" target="_blank" href="https://admin.fedoraproject.org/updates/mine">Manage updates</a>
+<hr/>
+{% endif %}
+<ul class="media-list">
+{% for update in updates %}
+    <li class="media">
+        <div class="media-left">
+            <a href="{{update['link']}}" target="_blank">
+                <img class="media-object square-32" src="{{update['icon']}}"/>
+            </a>
+        </div>
+        <div class="media-body">
+            <h4 class="media-heading">{{update['title']}} (karma {{update['karma']}})</h4>
+            can be pushed to stable
+        </div>
+    </li>
+{% endfor %}
+</ul>
+{% endif %}
diff --git a/hubs/widgets/templating.py b/hubs/widgets/templating.py
new file mode 100644
index 0000000..98b29cd
--- /dev/null
+++ b/hubs/widgets/templating.py
@@ -0,0 +1,10 @@
+import os
+
+import jinja2
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+environment = jinja2.Environment(
+    loader=jinja2.FileSystemLoader(here),
+    trim_blocks=True,
+)
diff --git a/hubs/widgets/test.py b/hubs/widgets/test.py
deleted file mode 100755
index e69de29..0000000
--- a/hubs/widgets/test.py
+++ /dev/null
diff --git a/hubs/widgets/workflow/pendingacls.py b/hubs/widgets/workflow/pendingacls.py
index 958d034..6f0e678 100644
--- a/hubs/widgets/workflow/pendingacls.py
+++ b/hubs/widgets/workflow/pendingacls.py
@@ -2,40 +2,16 @@ import requests
 
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
+from hubs.widgets import templating
 from hubs.utils import username2avatar
 
-import jinja2
-
-
 import hubs.validators as validators
 
-# TODO -- add approve/deny buttons or just link through to pkgdb
-template = jinja2.Template("""
-{% if pending_acls %}
-{% if session['nickname'] == username %}
-<a class="btn btn-success" target="_blank" href="https://admin.fedoraproject.org/pkgdb/acl/pending/">Manage requests</a>
-<hr/>
-{% endif %}
-<ul class="media-list">
-{% for acl in pending_acls %}
-    <li class="media">
-        <div class="media-left">
-            <img class="media-object img-circle" src="{{acl['avatar']}}"/>
-        </div>
-        <div class="media-body">
-            <h4 class="media-heading">{{acl['user']}} is {{acl['status']}}</h4>
-            for {{acl['acl']}} on
-            {{acl['package']}}({{acl['collection']}})
-        </div>
-    </li>
-{% endfor %}
-</ul>
-{% endif %}
-""")
-
 from hubs.widgets.chrome import panel
 # If 'pending_acls' is empty, then don't render any chrome.
 chrome = panel('Pending ACL Requests', key='pending_acls')
+# TODO -- add approve/deny buttons or just link through to pkgdb
+template = templating.environment.get_template('templates/workflow/pendingacls.html')
 
 
 @argument(name="username",
diff --git a/hubs/widgets/workflow/updates2stable.py b/hubs/widgets/workflow/updates2stable.py
index 8b0d802..64a8316 100644
--- a/hubs/widgets/workflow/updates2stable.py
+++ b/hubs/widgets/workflow/updates2stable.py
@@ -2,42 +2,17 @@ import requests
 
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
-
-import jinja2
-
-
+from hubs.widgets import templating
 import hubs.validators as validators
 
-# TODO -- add approve/deny buttons or just link through to pkgdb
-template = jinja2.Template("""
-{% if updates %}
-{% if session['nickname'] == username %}
-<a class="btn btn-success" target="_blank" href="https://admin.fedoraproject.org/updates/mine">Manage updates</a>
-<hr/>
-{% endif %}
-<ul class="media-list">
-{% for update in updates %}
-    <li class="media">
-        <div class="media-left">
-            <a href="{{update['link']}}" target="_blank">
-                <img class="media-object square-32" src="{{update['icon']}}"/>
-            </a>
-        </div>
-        <div class="media-body">
-            <h4 class="media-heading">{{update['title']}} (karma {{update['karma']}})</h4>
-            can be pushed to stable
-        </div>
-    </li>
-{% endfor %}
-</ul>
-{% endif %}
-""")
 
 from hubs.widgets.chrome import panel
 # If 'updates' is empty, then don't render any chrome.
 chrome = panel('Updates Ready for Stable', key='updates')
+# TODO -- add approve/deny buttons or just link through to pkgdb
+template = templating.environment.get_template('templates/workflow/updates2stable.html')
 
-
+# TODO - the bodhi api exposes a new flag we can use instead of scraping comments...
 giveaway = 'can be pushed to stable now'
 
 

