Zwischencommit
This commit is contained in:
parent
25f0e78cab
commit
baa2dcc38b
|
@ -26,7 +26,7 @@ SECRET_KEY = 'b96*sagnglcd8xe&8&cm6g-(onalk(ps3u!c1l3#_(-w64w35)'
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
|
@ -26,7 +26,7 @@ class RequestForm(forms.Form):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(RequestForm, self).clean()
|
cleaned_data = super(RequestForm, self).clean()
|
||||||
|
|
||||||
if not forms.errors:
|
if not self.errors:
|
||||||
mnts = self._user.maintainer_set.all()
|
mnts = self._user.maintainer_set.all()
|
||||||
if cleaned_data['applicant'] in mnts and cleaned_data['provider'] in mnts:
|
if cleaned_data['applicant'] in mnts and cleaned_data['provider'] in mnts:
|
||||||
raise forms.ValidationError("You could request resources from yourself, but this would actually not make that much sense.")
|
raise forms.ValidationError("You could request resources from yourself, but this would actually not make that much sense.")
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Request(models.Model):
|
||||||
return reverse("rrequests:show", args=(self.pk,))
|
return reverse("rrequests:show", args=(self.pk,))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "(%s -> %s) [%s] %s" % (self.applicant, self.provider, self.state, self.subject)
|
return "(%s -> %s) [%s] %s" % (self.applicant, self.provider, self.status, self.subject)
|
||||||
|
|
||||||
|
|
||||||
class RequestMessage(models.Model):
|
class RequestMessage(models.Model):
|
||||||
|
|
|
@ -15,8 +15,9 @@ def listRequests(request):
|
||||||
mnts = request.user.maintainer_set.all()
|
mnts = request.user.maintainer_set.all()
|
||||||
requestedFromMe = Request.objects.filter(applicant=mnts)
|
requestedFromMe = Request.objects.filter(applicant=mnts)
|
||||||
requestedToMe = Request.objects.filter(provider__in=mnts)
|
requestedToMe = Request.objects.filter(provider__in=mnts)
|
||||||
|
requests = (requestedFromMe | requestedToMe).order_by("-lastAction")
|
||||||
|
|
||||||
return render(request, "rrequests/list.html", {"requestedFromMe": requestedFromMe, "requestedToMe": requestedToMe})
|
return render(request, "rrequests/list.html", {"requests": requests, "requestedFromMe": requestedFromMe, "requestedToMe": requestedToMe})
|
||||||
|
|
||||||
|
|
||||||
class RrequestCreate(LoginRequiredMixin, FormView):
|
class RrequestCreate(LoginRequiredMixin, FormView):
|
||||||
|
@ -79,6 +80,10 @@ def rrequestDetail(request, pk):
|
||||||
|
|
||||||
msg.save()
|
msg.save()
|
||||||
|
|
||||||
|
if "createdResources" in form.cleaned_data and \
|
||||||
|
form.cleaned_data['createdResources'].strip() != "":
|
||||||
|
reqObj.grantedResources = form.cleaned_data["createdResources"].strip()
|
||||||
|
|
||||||
reqObj.lastAction = timezone.now()
|
reqObj.lastAction = timezone.now()
|
||||||
reqObj.save()
|
reqObj.save()
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<li class="dropdown{% if request.resolver_match.url_name == 'whoisdb' %} active{%endif%}">
|
<li class="dropdown{% if request.resolver_match.url_name == 'whoisdb' %} active{%endif%}">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Whois DB <span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Whois DB <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="{% url "whoisdb:dashboard" %}">Overview</a></li>
|
<li><a href="{% url "whoisdb:dashboard" %}">Whois DB</a></li>
|
||||||
<li><a href="{% url "rrequests:dashboard" %}">Resource Requests</a></li>
|
<li><a href="{% url "rrequests:dashboard" %}">Resource Requests</a></li>
|
||||||
<li><a href="{% url "domains:overview" %}">Domains</a></li>
|
<li><a href="{% url "domains:overview" %}">Domains</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
@ -25,7 +27,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for asn in asns %}
|
{% for asn in asns %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ asn.handle }}</td>
|
<td>{{ asn|linkObject }}</td>
|
||||||
<td>{{ asn.number }}</td>
|
<td>{{ asn.number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -42,13 +44,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for inetnum in inetnums %}
|
{% for inetnum in inetnums %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ inetnum.handle }}</td>
|
<td>{{ inetnum|linkObject }}</td>
|
||||||
<td>{{ inetnum.prefix }}</td>
|
<td>{{ inetnum.prefix }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>You don't have any AS numbers</p>
|
<p>You don't have any domains</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,12 +68,12 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for domain in domains %}
|
{% for domain in domains %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ domain.name }}</td>
|
<td>{{ domain|linkObject }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>You don't have any AS numbers</p>
|
<p>You don't have any open resource requests</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -89,7 +91,7 @@
|
||||||
{% for rrequest in rrequests %}
|
{% for rrequest in rrequests %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ rrequest.applicant }} <span class="glyphicon glyphicon-arrow-right"></span> {{ rrequest.provider }}</td>
|
<td>{{ rrequest.applicant }} <span class="glyphicon glyphicon-arrow-right"></span> {{ rrequest.provider }}</td>
|
||||||
<td>{{ rrequest.subject }}</td>
|
<td><a href="{{ rrequest.get_absolute_url }}">{{ rrequest.subject }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -4,25 +4,14 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">Participate!</h3></div>
|
|
||||||
<div class="panel-body">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">Login</h3></div>
|
|
||||||
<div class="panel-body">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><h3 class="panel-title">Empty landing page!</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
Click on dashboard above to get an overview.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
@ -7,20 +9,41 @@
|
||||||
<div class="panel-heading">Resource Requests</div>
|
<div class="panel-heading">Resource Requests</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url "rrequests:create" %}">Create resource request</a>
|
<a href="{% url "rrequests:create" %}">Start a new request for resources</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Requests to you
|
Current and old resource requests:
|
||||||
<ul>
|
|
||||||
{% for r in requestedFromMe %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
{% for r in requestedToMe %}
|
|
||||||
<li><a href="{% url "rrequests:show" r.pk %}">{{ r.subject }} {{ r.created }} {{ r.applicant }} {{ r.provider }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th>Applicant</th>
|
||||||
|
<th>Provider</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Subject</th>
|
||||||
|
<th>Created at</th>
|
||||||
|
<th>Last Modified</th>
|
||||||
|
<th>Resources</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
{% for request in requests %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ request.applicant|linkObject }}</td>
|
||||||
|
<td>{{ request.provider|linkObject }}</td>
|
||||||
|
<td>{{ request.get_status_display }}</td>
|
||||||
|
<td><a href="{% url "rrequests:show" request.pk %}">{{ request.subject }}</a></td>
|
||||||
|
<td>{{ request.created }}</td>
|
||||||
|
<td>{{ request.lastAction }}</td>
|
||||||
|
<td>{{ request.grantedResources|default:"-" }}</td>
|
||||||
|
<td><a href="{% url "rrequests:show" request.pk %}">View</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Start New Resource Request</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form method="post" action="#">
|
<form method="post" action="#">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -6,16 +6,22 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Resource Request - {{ request.subject }} - {{ request.applicant }} <span class="glyphicon glyphicon-arrow-right"></span> {{ request.provider }}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<h2>{{ request.subject }}</h2>
|
<h3>{{ request.subject }}</h3>
|
||||||
<p>
|
<p>
|
||||||
{% for message in request.requestmessage_set.all %}
|
{% for message in request.requestmessage_set.all %}
|
||||||
<h4>{{ message.creator }} {{ message.created }}</h4>
|
<div class="panel panel-default">
|
||||||
{{ message.message | linebreaks }}
|
<div class="panel-heading">From {{message.creator }} at {{ message.created }}</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ message.message | linebreaks }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if message.statusChanged %}
|
{% if message.statusChanged %}
|
||||||
Status changed to {{ message.statusChanged }}.
|
<div role="alert" class="alert alert-{% if message.statusChanged == "OPEN" %}info{% elif message.statusChanged == "RESOLVED" %}success{% elif message.statusChanged == "REJECTED" %}danger{% endif %}">
|
||||||
|
Status changed to {{ message.get_statusChanged_display }}.
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Contact {{ handle }}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{ contact }}
|
{{ contact }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Whois Database Objects</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h3>Create Database Objects</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{% url "whoisdb:mnt-create" %}">Create new Maintainer</a></li>
|
||||||
|
<li><a href="{% url "whoisdb:contact-create" %}">Create new Role/Person</a></li>
|
||||||
|
<li><a href="{% url "rrequests:dashboard" %}">Request resources</a></li>
|
||||||
|
{% if netblocks %}
|
||||||
|
<li><a href="{% url "whoisdb:inetnum-create" %}">Create Subnet</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if asblocks %}
|
||||||
|
<li><a href="{% url "whoisdb:asnumber-create" %}">Create AS</a></li>
|
||||||
|
<li><a href="{% url "whoisdb:asblock-create" %}">Create ASblock</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">{{ object }} <small>({{ object.getClassName }})</small></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
{% for field in object|getFields:user %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ field.0 }}</td>
|
||||||
|
<td>{% if field.1.through %}{{ field.1.all|linkObjects }}{% else %}{{ field.1 }}{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% if object|userCanEdit:user %}
|
||||||
|
<tr>
|
||||||
|
<td>Actions</td>
|
||||||
|
{% with "whoisdb:"|add:object.getClassName|lower|add:"-edit" as editView %}
|
||||||
|
{% with "whoisdb:"|add:object.getClassName|lower|add:"-delete" as deleteView %}
|
||||||
|
<td><a href="{% url editView handle=object.handle %}">Edit object<a/>, <a href="{% url deleteView handle=object.handle %}">Delete object</a></td>
|
||||||
|
{% endwith %}
|
||||||
|
{% endwith %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
{% with "whoisdb:"|add:prefix|add:"-detail" as detailView %}
|
{% with "whoisdb:"|add:prefix|add:"-detail" as detailView %}
|
||||||
{% with "whoisdb:"|add:prefix|add:"-delete" as deleteView %}
|
{% with "whoisdb:"|add:prefix|add:"-delete" as deleteView %}
|
||||||
{% with "whoisdb:"|add:prefix|add:"-edit" as editView %}
|
{% with "whoisdb:"|add:prefix|add:"-edit" as editView %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{% url detailView obj.handle %}">{{ obj.handle }}</a></td>
|
<td>{{ obj|linkObject }}</td>
|
||||||
<td>{{ objType }}</td>
|
<td>{{ objType }}</td>
|
||||||
<td>{{obj.name }}{{ obj.description }}</td>
|
<td>{{ obj.name }}{{ obj.description }}</td>
|
||||||
<td>{% for contact in obj.admin_c.all %}<a href="{% url "whoisdb:contact-detail" contact.handle %}">{{ contact }}</a> {% endfor %}</td>
|
<td>{{ obj.getResource|default:"-" }}</td>
|
||||||
|
<td>{{ obj.mnt_by.all|linkObjects|default:"-" }}{% if obj.mnt_lower.all %} / lower: {{ obj.mnt_lower.all|linkObjects }}{% endif %}</td>
|
||||||
|
<td>{{ obj.admin_c.all|linkObjects }}</td>
|
||||||
<td><a href="{% url editView obj.handle %}">Edit</a> <a href="{% url deleteView obj.handle %}">Delete</a></td>
|
<td><a href="{% url editView obj.handle %}">Edit</a> <a href="{% url deleteView obj.handle %}">Delete</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">{{ mnt }} <small>(Maintainer)</small></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{ mnt }}
|
{% for field in mnt|getFields %}
|
||||||
|
{{ field }}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Create New Whois Database Object</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form method="post" action="#">
|
<form method="post" action="#">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-{% if reasons %}danger{%else%}default{%endif%}">
|
<div class="panel panel-{% if reasons %}danger{%else%}default{%endif%}">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Delete {{ object }}?</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if reasons %}
|
{% if reasons %}
|
||||||
<p>
|
<p>
|
||||||
|
@ -19,10 +19,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ obj }}
|
<p>
|
||||||
|
Are you sure you want to delete {{ object }}?
|
||||||
|
</p>
|
||||||
<form method="post" action="#">
|
<form method="post" action="#">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-primary">Delete</button>
|
<button type="submit" class="btn btn-primary">Delete {{ object }}</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
{% load handletags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Header</div>
|
<div class="panel-heading">Whois Database Objects</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% if mnts %}
|
{% if mnts %}
|
||||||
|
<p>
|
||||||
|
You can create new database objects <a href="{% url "whoisdb:createObjectOverview" %}">here</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
Create <a href="{% url "whoisdb:mnt-create" %}">new Maintainer</a>, create <a href="{% url "whoisdb:contact-create" %}">new Contact</a>, <a href="{% url "rrequests:dashboard" %}">request resources</a>{% if netblocks %}, create <a href="{% url "whoisdb:inetnum-create" %}">Subnet</a>{% endif %}{% if asblocks %}, create <a href="{% url "whoisdb:asnumber-create" %}">AS</a>, create <a href="{% url "whoisdb:asblock-create" %}">ASblock</a>{% endif %}
|
||||||
|
</p>
|
||||||
|
<!--
|
||||||
|
<h3>Create Database Objects</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url "whoisdb:mnt-create" %}">Create new Maintainer</a></li>
|
<li><a href="{% url "whoisdb:mnt-create" %}">Create new Maintainer</a></li>
|
||||||
<li><a href="{% url "whoisdb:contact-create" %}">Create new Role/Person</a></li>
|
<li><a href="{% url "whoisdb:contact-create" %}">Create new Role/Person</a></li>
|
||||||
|
@ -22,14 +32,16 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
You have the following Maintainers associated with you
|
<h3>Your Database Objects</h3>
|
||||||
|
-->
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Handle</th>
|
<th>Handle</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Name?</th>
|
<th>Name</th>
|
||||||
|
<th>Resource</th>
|
||||||
|
<th>Maintained</th>
|
||||||
<th>Contact</th>
|
<th>Contact</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -97,7 +97,7 @@ class InetNumForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InetNum
|
model = InetNum
|
||||||
fields = ['handle', 'protocol', 'parent_range', 'prefix', 'name', 'description', 'mnt_by', 'mnt_lower']
|
fields = ['handle', 'protocol', 'parent_range', 'prefix', 'name', 'description', 'mnt_by', 'mnt_lower', 'admin_c']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(InetNumForm, self).__init__(*args, **kwargs)
|
super(InetNumForm, self).__init__(*args, **kwargs)
|
||||||
|
@ -167,7 +167,7 @@ class ASBlockForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
|
||||||
# FIXME: Filter blocks
|
# FIXME: Filter blocks
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ASBlock
|
model = ASBlock
|
||||||
fields = ['handle', 'parent_block', 'asBegin', 'asEnd', 'name', 'description', 'mnt_by', 'mnt_lower']
|
fields = ['handle', 'parent_block', 'asBegin', 'asEnd', 'name', 'description', 'mnt_by', 'mnt_lower', 'admin_c']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(ASBlockForm, self).__init__(*args, **kwargs)
|
super(ASBlockForm, self).__init__(*args, **kwargs)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import whoisdb.models
|
||||||
|
|
||||||
|
def _addFields(fields, obj, fieldNames):
|
||||||
|
for fieldName in fieldNames:
|
||||||
|
fields.append((fieldName.capitalize(), getattr(obj, fieldName)))
|
||||||
|
|
||||||
|
|
||||||
|
def getWhoisObjectFields(obj, owner):
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
if getattr(obj, "handle"):
|
||||||
|
_addFields(fields, obj, ["handle"])
|
||||||
|
|
||||||
|
c = type(obj)
|
||||||
|
if c == whoisdb.models.Maintainer:
|
||||||
|
_addFields(fields, obj, ["admin_c"])
|
||||||
|
if owner:
|
||||||
|
_addFields(fields, obj, ["auth"])
|
||||||
|
elif c == whoisdb.models.Contact:
|
||||||
|
_addFields(fields, obj, ["name", "mnt_by"])
|
||||||
|
elif c == whoisdb.models.ASBlock:
|
||||||
|
_addFields(fields, obj, ["name"])
|
||||||
|
fields.append(("AS Range", "%s - %s" % (obj.asBegin, obj.asEnd)))
|
||||||
|
_addFields(fields, obj, ["description", "mnt_by", "mnt_lower", "admin_c"])
|
||||||
|
elif c == whoisdb.models.ASNumber:
|
||||||
|
_addFields(fields, obj, ["name", "number", "description", "mnt_by", "mnt_lower", "admin_c"])
|
||||||
|
elif c == whoisdb.models.InetNum:
|
||||||
|
_addFields(fields, obj, ["name"])
|
||||||
|
fields.append(("Address CIDR", obj.prefix()))
|
||||||
|
_addFields(fields, obj, ["description", "mnt_by", "mnt_lower", "admin_c"])
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
|
@ -19,6 +19,9 @@ class WhoisObject(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.handle
|
return self.handle
|
||||||
|
|
||||||
|
def getClassName(self):
|
||||||
|
return self._meta.object_name
|
||||||
|
|
||||||
def genHandle(self, main=None):
|
def genHandle(self, main=None):
|
||||||
if not main:
|
if not main:
|
||||||
main = self.name
|
main = self.name
|
||||||
|
@ -74,7 +77,7 @@ class Maintainer(WhoisObject):
|
||||||
# autoInclude = models.BooleanField(default=True)
|
# autoInclude = models.BooleanField(default=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("whoisdb:mnt-detail", kwargs={"handle": self.handle})
|
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
|
||||||
|
|
||||||
def getNoDeleteReasons(self):
|
def getNoDeleteReasons(self):
|
||||||
reasons = []
|
reasons = []
|
||||||
|
@ -87,6 +90,9 @@ class Maintainer(WhoisObject):
|
||||||
|
|
||||||
return reasons
|
return reasons
|
||||||
|
|
||||||
|
def canEdit(self, user):
|
||||||
|
return user in self.auth.all()
|
||||||
|
|
||||||
|
|
||||||
class MntdObject(WhoisObject):
|
class MntdObject(WhoisObject):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -94,6 +100,17 @@ class MntdObject(WhoisObject):
|
||||||
|
|
||||||
mnt_by = models.ManyToManyField(Maintainer)
|
mnt_by = models.ManyToManyField(Maintainer)
|
||||||
|
|
||||||
|
def canEdit(self, user):
|
||||||
|
mnts = user.maintainer_set.all()
|
||||||
|
objmnts = self.mnt_by.all()
|
||||||
|
if hasattr(self, "mnt_lower"):
|
||||||
|
objmnts |= self.mnt_lower.all()
|
||||||
|
|
||||||
|
for objmnt in objmnts:
|
||||||
|
if objmnt in mnts:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Contact(MntdObject):
|
class Contact(MntdObject):
|
||||||
handleSuffix = "DN"
|
handleSuffix = "DN"
|
||||||
|
@ -106,7 +123,7 @@ class Contact(MntdObject):
|
||||||
type = models.CharField(max_length=10, choices=TYPE, default=TYPE_PERSON)
|
type = models.CharField(max_length=10, choices=TYPE, default=TYPE_PERSON)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("whoisdb:contact-detail", kwargs={"handle": self.handle})
|
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
|
||||||
|
|
||||||
def getNoDeleteReasons(self):
|
def getNoDeleteReasons(self):
|
||||||
reasons = []
|
reasons = []
|
||||||
|
@ -132,8 +149,11 @@ class ASBlock(MntdObject):
|
||||||
|
|
||||||
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
|
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
|
||||||
|
|
||||||
|
def getResource(self):
|
||||||
|
return "%s - %s" % (self.asBegin, self.asEnd)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("whoisdb:asblock-detail", kwargs={"handle": self.handle})
|
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
|
||||||
|
|
||||||
def getNoDeleteReasons(self):
|
def getNoDeleteReasons(self):
|
||||||
reasons = []
|
reasons = []
|
||||||
|
@ -160,13 +180,15 @@ class ASNumber(MntdObject):
|
||||||
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asnumber_set', blank=True)
|
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asnumber_set', blank=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("whoisdb:asnumber-detail", kwargs={"handle": self.handle})
|
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
|
||||||
|
|
||||||
def getNoDeleteReasons(self):
|
def getNoDeleteReasons(self):
|
||||||
reasons = []
|
reasons = []
|
||||||
|
|
||||||
return reasons
|
return reasons
|
||||||
|
|
||||||
|
def getResource(self):
|
||||||
|
return str(self.number)
|
||||||
|
|
||||||
class InetNum(MntdObject):
|
class InetNum(MntdObject):
|
||||||
handleSuffix = "NET"
|
handleSuffix = "NET"
|
||||||
|
@ -185,6 +207,9 @@ class InetNum(MntdObject):
|
||||||
|
|
||||||
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True)
|
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True)
|
||||||
|
|
||||||
|
def getResource(self):
|
||||||
|
return self.prefix()
|
||||||
|
|
||||||
def prefix(self):
|
def prefix(self):
|
||||||
""" Helper function, mainly used in templates """
|
""" Helper function, mainly used in templates """
|
||||||
return "%s/%s" % (self.address, self.netmask)
|
return "%s/%s" % (self.address, self.netmask)
|
||||||
|
@ -193,7 +218,7 @@ class InetNum(MntdObject):
|
||||||
return ipaddress.ip_network(self.prefix())
|
return ipaddress.ip_network(self.prefix())
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("whoisdb:inetnum-detail", kwargs={"handle": self.handle})
|
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
|
||||||
|
|
||||||
def getNoDeleteReasons(self):
|
def getNoDeleteReasons(self):
|
||||||
reasons = []
|
reasons = []
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from dncore.models import User
|
|
||||||
from .validators import HandleValidator
|
|
||||||
|
|
||||||
class ExtraFields(models.Model):
|
|
||||||
name = models.CharField(max_length=64)
|
|
||||||
value = models.CharField(max_length=128)
|
|
||||||
|
|
||||||
order = models.PositiveSmallIntegerField()
|
|
||||||
|
|
||||||
class WhoisObject(models.Model):
|
|
||||||
handle_suffix = None
|
|
||||||
whois_fields = None
|
|
||||||
whois_extra_field_names = None
|
|
||||||
|
|
||||||
handle = models.SlugField(max_length='32', unique=True, validators=[HandleValidator()])
|
|
||||||
created = models.DateTimeField(auto_add_now=True)
|
|
||||||
last_modified = models.DateTimeField(auto_add_now=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class Maintainer(WhoisObject):
|
|
||||||
auth = models.ManyToManyField(User)
|
|
||||||
|
|
||||||
admin_c = models.ManyToManyField("Contact")
|
|
||||||
|
|
||||||
class MntdObject(WhoisObject):
|
|
||||||
mnt_by = models.ManyToManyField(Maintainer)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class Contact(MntdObject):
|
|
||||||
TYPE = (('person', 'person'), ('role', 'role'))
|
|
||||||
|
|
||||||
name = models.CharField(max_length=128)
|
|
||||||
|
|
||||||
class MntdCObject(MntdObject)
|
|
||||||
admin_c = models.ManyToManyField("Contact")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class MntdObject(WhoisObject):
|
|
||||||
admin_c = models.ManyToManyField(Contact)
|
|
||||||
mnt_by = models
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ASNumber(WhoisObject):
|
|
||||||
number = models.PositiveIntegerField(unique=True, db_index=True)
|
|
||||||
|
|
||||||
|
|
||||||
class InetNum(WhoisObject):
|
|
||||||
PROTO = (('ipv4', 'ipv4'), ('ipv6', 'ipv6'))
|
|
||||||
protocol = models.CharField(max_length=4, choices=PROTO)
|
|
||||||
netmask = models.PositiveIntegerField()
|
|
||||||
|
|
||||||
#class ASBlock(WhoisObject):
|
|
||||||
# pass
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
from whoisdb.helpers import getWhoisObjectFields
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def linkObject(value):
|
||||||
|
return mark_safe('<a href="%s">%s</a>' % (value.get_absolute_url(), str(value)))
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def linkObjects(value):
|
||||||
|
links = []
|
||||||
|
for obj in value:
|
||||||
|
if hasattr(obj, "get_absolute_url"):
|
||||||
|
links.append('<a href="%s">%s</a>' % (obj.get_absolute_url(), str(obj)))
|
||||||
|
else:
|
||||||
|
links.append(str(obj))
|
||||||
|
|
||||||
|
return mark_safe(", ".join(links))
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def getFields(value, user):
|
||||||
|
print("User is", user)
|
||||||
|
|
||||||
|
owner = value.canEdit(user)
|
||||||
|
|
||||||
|
return getWhoisObjectFields(value, owner)
|
||||||
|
#return [("Handle", value.handle), ("Auth", value.auth)]
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def userCanEdit(value, user):
|
||||||
|
if hasattr(value, "canEdit"):
|
||||||
|
return value.canEdit(user)
|
||||||
|
return False
|
|
@ -4,11 +4,19 @@ from . import views as whoisdb_views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', whoisdb_views.dbDashboard, name='dashboard'),
|
url(r'^$', whoisdb_views.dbDashboard, name='dashboard'),
|
||||||
|
url(r'^create/$', whoisdb_views.createObjectOverview, name='createObjectOverview'),
|
||||||
|
url(r'^handle/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.showHandle, name='showhandle'),
|
||||||
|
url(r'^handle/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.showHandle, name='handle-detail'),
|
||||||
|
|
||||||
url(r'^mnt/create/$', whoisdb_views.MaintainerCreate.as_view(), name='mnt-create'),
|
url(r'^mnt/create/$', whoisdb_views.MaintainerCreate.as_view(), name='mnt-create'),
|
||||||
url(r'^mnt/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDetail.as_view(), name='mnt-detail'),
|
url(r'^mnt/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDetail.as_view(), name='mnt-detail'),
|
||||||
url(r'^mnt/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerEdit.as_view(), name='mnt-edit'),
|
url(r'^mnt/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerEdit.as_view(), name='mnt-edit'),
|
||||||
url(r'^mnt/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDelete.as_view(), name='mnt-delete'),
|
url(r'^mnt/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDelete.as_view(), name='mnt-delete'),
|
||||||
|
# FIXME hacky duplicates
|
||||||
|
url(r'^mnt/create/$', whoisdb_views.MaintainerCreate.as_view(), name='mnt-create'),
|
||||||
|
url(r'^mnt/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDetail.as_view(), name='maintainer-detail'),
|
||||||
|
url(r'^mnt/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerEdit.as_view(), name='maintainer-edit'),
|
||||||
|
url(r'^mnt/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.MaintainerDelete.as_view(), name='maintainer-delete'),
|
||||||
|
|
||||||
url(r'^contact/create/$', whoisdb_views.ContactCreate.as_view(), name='contact-create'),
|
url(r'^contact/create/$', whoisdb_views.ContactCreate.as_view(), name='contact-create'),
|
||||||
url(r'^contact/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDetail.as_view(), name='contact-detail'),
|
url(r'^contact/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDetail.as_view(), name='contact-detail'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect, Http404
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.views.generic import DetailView, CreateView, UpdateView
|
from django.views.generic import DetailView, CreateView, UpdateView
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -10,6 +10,13 @@ from .models import Maintainer, Contact, InetNum, ASBlock, ASNumber
|
||||||
from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm, ASBlockForm, ASNumberForm
|
from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm, ASBlockForm, ASNumberForm
|
||||||
from .generic import DeleteCheckView, MntGenericMixin
|
from .generic import DeleteCheckView, MntGenericMixin
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def createObjectOverview(request):
|
||||||
|
mnts = request.user.maintainer_set.all()
|
||||||
|
netblocks = InetNum.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
|
||||||
|
asblocks = ASBlock.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
|
||||||
|
|
||||||
|
return render(request, "whoisdb/create_overview.html", {"netblocks": netblocks, "asblocks": asblocks})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def dbDashboard(request):
|
def dbDashboard(request):
|
||||||
|
@ -49,6 +56,22 @@ def dbDashboard(request):
|
||||||
return render(request, "whoisdb/overview.html", {"mnts": mnts, "contacts": contacts, "mntForm": mntForm, "contactForm": contactForm, "netblocks": netblocks, "asblocks": asblocks, "asnumbers": asnumbers})
|
return render(request, "whoisdb/overview.html", {"mnts": mnts, "contacts": contacts, "mntForm": mntForm, "contactForm": contactForm, "netblocks": netblocks, "asblocks": asblocks, "asnumbers": asnumbers})
|
||||||
|
|
||||||
|
|
||||||
|
def showHandle(request, handle):
|
||||||
|
# a) find handle
|
||||||
|
models = [Contact, Maintainer, ASBlock, ASNumber, InetNum]
|
||||||
|
obj = None
|
||||||
|
|
||||||
|
for model in models:
|
||||||
|
if handle.endswith(model.handleSuffix):
|
||||||
|
obj = get_object_or_404(model, handle=handle)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not obj:
|
||||||
|
raise Http404("Handle object not found")
|
||||||
|
|
||||||
|
return render(request, "whoisdb/handle_show.html", {"object": obj})
|
||||||
|
|
||||||
|
|
||||||
class MaintainerCreate(LoginRequiredMixin, CreateView):
|
class MaintainerCreate(LoginRequiredMixin, CreateView):
|
||||||
template_name = "whoisdb/obj_create.html"
|
template_name = "whoisdb/obj_create.html"
|
||||||
form_class = MntForm
|
form_class = MntForm
|
||||||
|
@ -100,10 +123,11 @@ class MaintainerDelete(LoginRequiredMixin, DeleteCheckView):
|
||||||
|
|
||||||
|
|
||||||
class MaintainerDetail(LoginRequiredMixin, DetailView):
|
class MaintainerDetail(LoginRequiredMixin, DetailView):
|
||||||
|
template_name = "whoisdb/handle_show.html"
|
||||||
model = Maintainer
|
model = Maintainer
|
||||||
slug_field = "handle"
|
slug_field = "handle"
|
||||||
slug_url_kwarg = "handle"
|
slug_url_kwarg = "handle"
|
||||||
context_object_name = "mnt"
|
#context_object_name = "mnt"
|
||||||
|
|
||||||
|
|
||||||
class ContactDetail(DetailView):
|
class ContactDetail(DetailView):
|
||||||
|
|
Loading…
Reference in New Issue