Switch backend interface to boostrap
authorMagnus Hagander <magnus@hagander.net>
Tue, 22 Oct 2019 12:34:40 +0000 (14:34 +0200)
committerMagnus Hagander <magnus@hagander.net>
Tue, 22 Oct 2019 12:35:59 +0000 (14:35 +0200)
Make it look less bad and opens up for some further improvements

12 files changed:
gitadmin/gitadmin/adm/migrations/0001_initial.py
gitadmin/gitadmin/adm/models.py
gitadmin/gitadmin/adm/templates/base.html
gitadmin/gitadmin/adm/templates/deleterepo.html
gitadmin/gitadmin/adm/templates/form_field.html [new file with mode: 0644]
gitadmin/gitadmin/adm/templates/index.html
gitadmin/gitadmin/adm/templates/repoview.html
gitadmin/gitadmin/adm/templatetags/__init__.py [new file with mode: 0644]
gitadmin/gitadmin/adm/templatetags/alertmap.py [new file with mode: 0644]
gitadmin/gitadmin/adm/templatetags/formutil.py [new file with mode: 0644]
gitadmin/gitadmin/adm/views.py
gitadmin/gitadmin/static/pggit.css

index 31c292c8e080fd8be68ae3af8125dc091cc29898..60046d6de1c681842c4c72d0a1e26b0808311eb5 100644 (file)
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
             name='RepositoryPermission',
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('userid', models.CharField(max_length=255)),
+                ('userid', models.CharField(max_length=255, verbose_name="User id")),
                 ('level', models.IntegerField(default=0, verbose_name='Permission', choices=[(0, 'Read'), (1, 'Write'), (2, 'Owner')])),
                 ('repository', models.ForeignKey(to='adm.Repository', db_column='repository')),
             ],
index d2daa9e94356c5d9d9120435771a4cbc2360a0c5..f7245e495ef4150a379f26683ac107377ed996d0 100644 (file)
@@ -45,7 +45,7 @@ class Repository(models.Model):
                                          verbose_name='Remote repository')
 
     def ValidateOwnerPermissions(self, user):
-        if self.repositorypermission_set.filter(userid=user.username, level=2).count() != 1:
+        if not self.repositorypermission_set.filter(userid=user.username, level=2).exists():
             raise Exception('You need owner permissions to do that!')
 
     def __str__(self):
@@ -58,7 +58,7 @@ class Repository(models.Model):
 
 class RepositoryPermission(models.Model):
     repository = models.ForeignKey(Repository, db_column='repository')
-    userid = models.CharField(max_length=255, blank=False)
+    userid = models.CharField(max_length=255, blank=False, verbose_name="User id")
     level = models.IntegerField(default=0, verbose_name='Permission', choices=PERMISSION_CHOICES)
 
     @property
index bdbbf4913f737ca0b35f77cd7d0a2e93490d4c6e..ddd85e78ab8ca2e178510daef9c53cf40d2fb5fb 100644 (file)
@@ -1,31 +1,50 @@
-<!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" lang="en" xml:lang="en" dir="ltr">
<head>
-  <title>PostgreSQL Git Repository</title>
-  <meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8" />
-  <link rel="shortcut icon" href="/favicon.ico" />
-  <style type="text/css" media="screen" title="Normal Text">@import url("/pgstatic/pggit.css");</style>
-  <script language="javascript" src="/pgstatic/pggit.js"></script>
+<!doctype html>
+<html lang="en">
+  <head>
   <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
+
+    <link rel="stylesheet" href="/pgstatic/pggit.css">
  </head>
  <body>
-  <div id="pggitWrap">
-   <div id="pggitHeader">
-    <div class="fl"><img src="https://www.postgresql.org/layout/images/hdr_left.png" alt="PostgreSQL" /></div>
-    <div class="fr"><img width="210" height="80" src="https://www.postgresql.org/layout/images/hdr_right.png" alt="The world's most advanced open source database" /></div>
-    <div class="cb"></div>
-   </div> <!-- pggitHeader -->
-   <div id="pggitMain">
-{%if user.is_authenticated %}
-<div style="float:right;"><a href="/adm/logout">Log out</a></div>
-{%endif%}
+    <div class="container">
+      <div class="row justify-content-md-center">
+         <div class="col">
+           <nav class="navbar fixed-top navbar-dark bg-dark navbar-expand-lg">
+            <a class="navbar-brand" href="/adm/">Git Administration</a>
+             <div class="collapse navbar-collapse" id="pgNavbar">
+               <ul class="navbar-nav">
+{%block headlinks%}{%endblock%}
+               </ul>
+             </div> <!-- pgNavBar -->
+          </nav>
+         </div> <!-- col -->
+      </div> <!-- row -->
+      <div class="row">
+       <div class="col-md-12">
 {%if missing_sshkey %}
-<p><b>Note!</b> Your ssh key has not yet been registered with the system, or it has not yet replicated
-from the main server. Please upload your keys using the
-<a href="https://www.postgresql.org/account/">community account system</a>.</p>
+          <div class="alert alert-danger">Your ssh key has not yet been registered with the system,
+or it has not yet replicated from the main server. Please upload your keys using the
+<a href="https://www.postgresql.org/account/">community account system</a>.
+         </div>
+{%endif%}
+{%block content%}{%endblock%}
+       </div>
+      </div>
+      <div class="row mt-4 mb-2">
+       <div class="col-md-12">
+         <a href="/adm/logout/" class="btn btn-secondary mr-3">Log out</a>
+{%if user.is_superuser %}
+         <a href="/adm/admin/" class="btn btn-secondary mr-3">Django Admin</a>
 {%endif%}
-{% block content %} {% endblock %}
-   </div> <!-- pggitMain -->
-  </div> <!-- pggitWrap -->
+       </div>
+      </div>
+    </div> <!-- container-fluid -->
+
+    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
+    <script language="javascript" src="/pgstatic/pggit.js"></script>
  </body>
 </html>
index 4e35b4fcccc00a98ccea79e2916e24d2ac103613..29306cbf14d4f40ea2fd3cd449638d32ed20e13b 100644 (file)
@@ -5,11 +5,12 @@
 <p>Are you <b>sure</b> you want to delete this repository? The repository contents will also be deleted!</p>
 
 <form method="POST" action=".">
-<table class="leftalign">
-{{form}}
-</table>
-<input type="submit" value="Delete">
+{% csrf_token %}
+{%for field in form%}
+{%include "form_field.html"%}
+{%endfor%}
+<input type="submit" value="Delete" class="btn btn-danger">
 </form>
-<a href="..">Back</a>
+<a class="btn btn-primary mt-2" href="..">Back</a>
 {%endblock%}
 
diff --git a/gitadmin/gitadmin/adm/templates/form_field.html b/gitadmin/gitadmin/adm/templates/form_field.html
new file mode 100644 (file)
index 0000000..626d6f1
--- /dev/null
@@ -0,0 +1,21 @@
+{%load formutil%}
+{%if not field.is_hidden%}
+<div class="form-group row">
+  {{field|label_class:"col-sm-3 col-form-label"}}
+ <div class="col-sm-9">
+  {%if field.errors %}
+   {%for e in field.errors%}
+ <div class="alert alert-danger">{{e}}</div>
+   {%endfor%}
+  {%endif%}
+{%if field|ischeckbox%}
+    <div class="form-check">{{field|field_class:"form-check-input"}}</div>
+{%else%}
+{{field|field_class:"form-control"}}
+{%endif%}
+{%if field.help_text%}<small class="form-text text-muted">{{field.help_text|safe}}</small>{%endif%}
+ </div>
+</div>
+ {%else%}{# field.is_hidden #}
+{{field}}
+ {%endif%}
index c8e8f292300d549f58c9785b86494167994ea7a1..0fe98f214925118b9a8a22e01e34354ab97b8f99 100644 (file)
@@ -1,21 +1,24 @@
 {%extends "base.html"%}
 {%block content%}
 <p>You have access to the following git repositories:</p>
-<table border="1" cellspacing="0" cellpadding="1">
+<table class="table table-striped table-hover table-sm">
 <tr>
  <th>Name</th>
  <th>Description</th>
- <th>gitweb</th>
- <th>gitserve</th>
+ <th>Gitweb</th>
+ <th>Git</th>
  <th>Approved</th>
 </tr>
 {% for r in repos %}
 <tr>
  <td>{%if r.perm %}<a href="repo/{{r.repoid}}/">{{r.name}}</a>{%else%}{{r.name}}{%endif%}</td>
  <td>{{r.description}}</td>
- <td>{%if r.web %}<a href="/gitweb?p={{r.name}}.git;a=summary">url</a>{%endif%}</td>
- <td>{%if r.anonymous %}<a href="git://git.postgresql.org/git/{{r.name}}">url</a>{%endif%}</td>
- <td>{{r.approved|yesno:"Yes,No"}}</td>
+ <td class="text-center">{%if r.web %}<a class="btn btn-primary btn-sm" href="/gitweb?p={{r.name}}.git;a=summary">Gitweb</a>{%else%}<span class="badge badge-secondary">Disabled</span>{%endif%}</td>
+ <td class="text-center">
+   <a class="badge badge-primary" href="ssh://git@git.postgresql.org/{{r.name}}.git" title="ssh://git@git.postgresql.org/{{r.name}},git">ssh</a>
+   {%if r.anonymous %}<br/><a class="badge badge-info" href="https://git.postgresql.org/git/{{r.name}}.git">http</a>{%endif%}
+ </td>
+ <td class="text-center"><span class="badge badge-{{r.approved|yesno:"primary,danger"}}">{{r.approved|yesno:"Yes,No"}}</span></td>
 </tr>
 {% endfor %}
 </table>
@@ -30,9 +33,9 @@ the naming conversions as listed on the <a href="help/">help page</a>.</p>
 
 <p>To request a new project, enter a name here. The name has to be 5-64 characters long and contain only lowercase
 letters and numbers.</p>
-<form method="post" action="new/">
-<input type="text" name="reponame" maxlength="64">
-<input type="submit" value="Request new repository">
+<form class="form-inline" method="post" action="new/">
+  <input type="text" class="form-control mr-sm-2" name="reponame" maxlength="64" placeholder="Enter name of repository" required="true">
+  <input type="submit" class="btn btn-primary" value="Request new repository">
 </form>
 {%endblock%}
 
index 31a4fc3aff9cee49659883bc6a539152fb571148..b1112e8baa97f291e29c1f29159b441a82326d2a 100644 (file)
@@ -1,12 +1,15 @@
 {%extends "base.html"%}
 {%block content%}
 
-<h2>Repository: {{repo.name}}</h2>
+<h1>Repository: {{repo.name}}</h1>
 {% if not repo.approved %}
-<p><strong>This repository has not yet been approved.</strong> This means you cannot access it through git or web yet.
+<h2>This repository has not yet been approved.</h2>
+<p>
+This means you cannot access it through git or web yet.
 You can still update the description and set permissions - they will all start working automatically
 when the repository is approved. Until approval you can also set it up to clone another repository
-automatically upon creation.</p>
+automatically upon creation.
+</p>
 {%endif%}
 {% if form_saved_at %}
 <p>Your changes were successfully saved at {{form_saved_at|date:"Y-m-d H:i:s"}}.</p>
@@ -15,24 +18,30 @@ automatically upon creation.</p>
 <p><b>Error:</b> The submitted form contains errors and could not be saved.</p>
 {%endif%}
 <form method="POST" action=".">
-<table class="leftalign">
-{{form}}
-</table>
-<p><a href="delete/">Delete this repository</a></p>
+{% csrf_token %}
+{%if form.non_field_errors%}
+ <div class="alert alert-danger">{{form.non_field_errors}}</div>
+{%endif%}
+{%for field in form%}
+{%include "form_field.html"%}
+{%endfor%}
 
 <h3>Permissions</h3>
 {{formset.management_form}}
-<table border="0" cellspacing="0" cellpadding="1">
-{% for f in formset.forms %}
+
+<table class="table table-striped table-sm">
+{% for form in formset.forms %}
 <tr>
- <td class="rowform">
-  {{f.as_p}}
- </td>
+  {%for field in form %}
+  <td>{%include "form_field.html"%}</td>
+  {%endfor%}
 </tr>
 {% endfor %}
 </table>
-<input type="submit" value="Save">
+<input type="submit" value="Save" class="btn btn-primary mb-2">
 </form>
-<a href="../..">Back</a>
+
+<a class="btn btn-warning mr-2" href="delete/">Delete this repository</a>
+<a class="btn btn-secondary" href="../..">Back</a>
 {%endblock%}
 
diff --git a/gitadmin/gitadmin/adm/templatetags/__init__.py b/gitadmin/gitadmin/adm/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gitadmin/gitadmin/adm/templatetags/alertmap.py b/gitadmin/gitadmin/adm/templatetags/alertmap.py
new file mode 100644 (file)
index 0000000..c1cd5fd
--- /dev/null
@@ -0,0 +1,17 @@
+from django.template.defaultfilters import stringfilter
+from django import template
+
+register = template.Library()
+
+
+@register.filter(name='alertmap')
+@stringfilter
+def alertmap(value):
+        if value == 'error':
+                return 'alert-danger'
+        elif value == 'warning':
+                return 'alert-warning'
+        elif value == 'success':
+                return 'alert-success'
+        else:
+                return 'alert-info'
diff --git a/gitadmin/gitadmin/adm/templatetags/formutil.py b/gitadmin/gitadmin/adm/templatetags/formutil.py
new file mode 100644 (file)
index 0000000..37f7b48
--- /dev/null
@@ -0,0 +1,22 @@
+from django import template
+
+register = template.Library()
+
+
+@register.filter(is_safe=True)
+def label_class(value, arg):
+    return value.label_tag(attrs={'class': arg})
+
+
+@register.filter(is_safe=True)
+def field_class(value, arg):
+    prevclass = value.field.widget.attrs.get('class', '')
+    if prevclass:
+        newclass = "{0} {1}".format(arg, prevclass)
+    else:
+        newclass = arg
+    return value.as_widget(attrs={"class": newclass})
+
+@register.filter(is_safe=True)
+def ischeckbox(obj):
+    return obj.field.widget.__class__.__name__ in ("CheckboxInput", "CheckboxSelectMultiple") and not getattr(obj.field, 'regular_field', False)
index ef1ad3cb8e19af13e2587da55a4cd1b1edbe94d0..83ee27bd77f5e4d85a5b8b703abcbac7ed7f6d27 100644 (file)
@@ -60,7 +60,6 @@ def help(request):
 def editrepo(request, repoid):
     repo = get_object_or_404(Repository, repoid=repoid)
     repo.ValidateOwnerPermissions(request.user)
-    savedat = None
     form = None
 
     formfactory = inlineformset_factory(Repository, RepositoryPermission, extra=1, fields=['userid', 'level'])
@@ -95,9 +94,7 @@ def editrepo(request, repoid):
 
                 form.save()
                 formset.save()
-                savedat = datetime.datetime.now()
-                # Get a new copy of the repository to make sure it refreshes!
-                repo = get_object_or_404(Repository, repoid=repoid)
+                return HttpResponseRedirect("../../")
             except FormIsNotValid:
                 # Just continue as if the form wasn't valid, expect the caller
                 # to have set the required error fields
@@ -118,7 +115,6 @@ def editrepo(request, repoid):
         'formset': formset,
         'repo': repo,
         'repoperm': perm,
-        'form_saved_at': savedat,
     })
 
 
index caf231294941d53296befae8bd58ff2ae427a20f..e4045c16b24d223d9c86feeb0ff6763f347e6654 100644 (file)
@@ -1,50 +1,11 @@
 body {
-  font-family: verdana, sans-serif;
-  color: #000000;
-  background-color: #ffffff;
-  margin: 0 0 0 0;
-  padding: 0 0 0 0;
-  font-size: 13px;
-}
-
-div#pggitHeader {
-  width: 100%;
-  background: url(https://www.postgresql.org/layout/images/hdr_fill.png);
-  padding: 0 0 0 0;
-  height: 80px;
-  margin: 5px 0 2px 0;
-}
-
-div#pggitWrap {
-  margin-left: 20px;
-  margin-right: 20px;
-}
-
-div#pggitWrap img {
-  /* applies to all images */
-  border: none;
-}
-
-div#pggitMain {
-  width: 100%;
-}
-
-div.fl { float: left; border: none; text-align: left; }
-div.fr { float: right; }
-div.cb { clear: both;  }
-div.cl { clear: left; }
-
-a:link                          { color:#0085B0; text-decoration: underline; }
-a:visited                       { color:#004E66; text-decoration: underline; }
-a:active                        { color:#0085B0; text-decoration: underline; }
-a:hover                         { color:#000000; text-decoration: underline; }
-
+  padding-top: 4rem;
 
-td.rowform p {
-   display: inline;
-   padding-right: 20px;
+  font-weight: 400;
+  color: #515151;
+  font-size: 11.5pt;
 }
 
-table.leftalign tr th {
-   text-align: left;
+.table-nonfluid {
+   width: auto !important;
 }