Skip to content

Commit 9ab3120

Browse files
authored
Merge pull request #323 from patriknordlen/calendar
View and filter tests/engagements and users in calendar
2 parents 96012f1 + ad3dddd commit 9ab3120

File tree

7 files changed

+107
-20
lines changed

7 files changed

+107
-20
lines changed

dojo/engagement/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
urlpatterns = [
66
# engagements and calendar
7-
url(r'^calendar$', views.calendar, name='calendar'),
7+
url(r'^calendar$', views.engagement_calendar, name='calendar'),
8+
url(r'^calendar/engagements$', views.engagement_calendar, name='engagement_calendar'),
89
url(r'^engagement$', views.engagement, name='engagement'),
910
url(r'^engagement/new$', views.new_engagement, name='new_eng'),
1011
url(r'^engagement/(?P<eid>\d+)$', views.view_engagement,

dojo/engagement/views.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import os
44
from datetime import datetime, timedelta
5+
import operator
56

67
from django.contrib.auth.models import User
78
from django.conf import settings
@@ -23,7 +24,7 @@
2324
from dojo.models import Finding, Product, Engagement, Test, \
2425
Check_List, Test_Type, Notes, \
2526
Risk_Acceptance, Development_Environment, BurpRawRequestResponse, Endpoint, \
26-
JIRA_PKey, JIRA_Conf, JIRA_Issue, Cred_User, Cred_Mapping
27+
JIRA_PKey, JIRA_Conf, JIRA_Issue, Cred_User, Cred_Mapping, Dojo_User
2728
from dojo.tools.factory import import_parser_factory
2829
from dojo.utils import get_page_items, add_breadcrumb, handle_uploaded_threat, \
2930
FileIterWrapper, get_cal_event, message, get_system_setting
@@ -42,11 +43,24 @@
4243

4344
@user_passes_test(lambda u: u.is_staff)
4445
@cache_page(60 * 5) # cache for 5 minutes
45-
def calendar(request):
46-
engagements = Engagement.objects.all()
47-
add_breadcrumb(title="Calendar", top_level=True, request=request)
46+
def engagement_calendar(request):
47+
if not 'lead' in request.GET or '0' in request.GET.getlist('lead'):
48+
engagements = Engagement.objects.all()
49+
else:
50+
filters = []
51+
leads = request.GET.getlist('lead','')
52+
if '-1' in request.GET.getlist('lead'):
53+
leads.remove('-1')
54+
filters.append(Q(lead__isnull=True))
55+
filters.append(Q(lead__in=leads))
56+
engagements = Engagement.objects.filter(reduce(operator.or_, filters))
57+
58+
add_breadcrumb(title="Engagement Calendar", top_level=True, request=request)
4859
return render(request, 'dojo/calendar.html', {
49-
'engagements': engagements})
60+
'caltype': 'engagements',
61+
'leads': request.GET.getlist('lead', ''),
62+
'engagements': engagements,
63+
'users': Dojo_User.objects.all()})
5064

5165

5266
@user_passes_test(lambda u: u.is_staff)

dojo/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ def __init__(self, *args, **kwargs):
568568

569569
class Meta:
570570
model = Test
571-
fields = ['test_type', 'target_start', 'target_end', 'environment', 'percent_complete', 'tags']
571+
fields = ['test_type', 'target_start', 'target_end', 'environment', 'percent_complete', 'tags', 'lead']
572572

573573

574574
class DeleteTestForm(forms.ModelForm):

dojo/templates/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@
240240
{% endif %}
241241
{% if request.user.is_staff %}
242242
<li>
243-
<a href="{% url 'calendar' %}"><i class="fa fa-calendar fa-fw"></i>
243+
<a href="{% url 'engagement_calendar' %}"><i class="fa fa-calendar fa-fw"></i>
244244
<span>Calendar</span></a>
245245
</li>
246246
<li>

dojo/templates/dojo/calendar.html

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,46 @@
11
{% extends 'base.html' %}
2+
{% load static from staticfiles %}
3+
{% block add_css %}
4+
<link rel="stylesheet" href="{% static "chosen-bootstrap/chosen.bootstrap.min.css" %}">
5+
{% endblock %}
26
{% block content %}
7+
<form method="GET" id="calfilter" action="/calendar">
8+
<div class="container-fluid chosen-container side-by-side">
9+
<div class="row">
10+
<div class="col-lg-6" style="width:200px;">
11+
<select data-placeholder="Calendar type" id="caltype" class="chosen-select">
12+
<option value="engagements">Engagements</option>
13+
<option value="tests">Tests</option>
14+
</select>
15+
</div>
16+
<div class="col-lg-6" style="width:400px;">
17+
<select data-placeholder="All users" multiple id="lead" name="lead" class="chosen-select">
18+
<option value="0">All users</option>
19+
<option value="-1">Unassigned</option>
20+
{% for u in users %}
21+
<option value="{{ u.id }}">{{ u.username }}</option>
22+
{% endfor %}
23+
</select>
24+
</div>
25+
<div class="col-lg-6">
26+
<button class="btn btn-primary" type="submit">Apply</button>
27+
</div>
28+
</div>
29+
</div>
30+
</form>
31+
<br/><br/>
332
<div id="calendar"></div>
433
<br/><br/>
534
{% endblock %}
635
{% block postscript %}
36+
<script type="application/javascript" src="{% static "chosen/chosen.jquery.min.js" %}"></script>
737
<script>
8-
$(function () {
38+
$(function () {
39+
$('#caltype').change(function() { $('#calfilter').attr('action', '/calendar/' + $(this).val()); });
40+
$(".chosen-select").chosen({disable_search_threshold: 10});
41+
$('#caltype').val('{{ caltype }}'); $('#caltype').trigger('change');
42+
$('#lead').val([{% for lead in leads %} '{{ lead }}', {% endfor %}]);
43+
$('.chosen-select').trigger('chosen:updated');
944
$('#calendar').fullCalendar({
1045
header: {
1146
left: 'prev,next today',
@@ -15,16 +50,29 @@
1550
editable: false,
1651
eventLimit: true, // allow "more" link when too many events
1752
events: [
18-
{% for e in engagements %}
19-
{
20-
title: '{{e.product.name}}: {{ e.name|default:"Unknown" }}',
21-
start: '{{e.target_start|date:"c"}}',
22-
end: '{{e.target_end|date:"c"}}',
23-
url: '{% url 'view_engagement' e.id %}',
24-
color: {% if e.active %}'#337ab7'{% else %}'#b9b9b9'{% endif %},
25-
overlap: true
26-
},
27-
{% endfor %}
53+
{% if caltype == 'tests' %}
54+
{% for t in tests %}
55+
{
56+
title: '{{t.engagement.product.name}}: {{ t.engagement.name|default:"Unknown" }} - {{ t.test_type }} ({{ t.lead|default:"Unassigned" }})',
57+
start: '{{t.target_start|date:"c"}}',
58+
end: '{{t.target_end|date:"c"}}',
59+
url: '{% url 'view_test' t.id %}',
60+
color: {% if t.engagement.active %}'#337ab7'{% else %}'#b9b9b9'{% endif %},
61+
overlap: true
62+
},
63+
{% endfor %}
64+
{% else %}
65+
{% for e in engagements %}
66+
{
67+
title: '{{e.product.name}}: {{ e.name|default:"Unknown" }} ({{ e.lead|default:"Unassigned" }})',
68+
start: '{{e.target_start|date:"c"}}',
69+
end: '{{e.target_end|date:"c"}}',
70+
url: '{% url 'view_engagement' e.id %}',
71+
color: {% if e.active %}'#337ab7'{% else %}'#b9b9b9'{% endif %},
72+
overlap: true
73+
},
74+
{% endfor %}
75+
{% endif %}
2876
]
2977
});
3078

dojo/test/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
urlpatterns = [
66
# tests
7+
url(r'^calendar/tests$', views.test_calendar, name='test_calendar'),
78
url(r'^test/(?P<tid>\d+)$', views.view_test,
89
name='view_test'),
910
url(r'^test/(?P<tid>\d+)/ics$', views.test_ics,

dojo/test/views.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22

33
import logging
44
import sys
5+
import operator
56
from datetime import datetime
67

78
from django.conf import settings
89
from django.contrib import messages
910
from django.contrib.auth.decorators import user_passes_test
1011
from django.core.urlresolvers import reverse
12+
from django.db.models import Q
1113
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404, HttpResponse
1214
from django.shortcuts import render, get_object_or_404
15+
from django.views.decorators.cache import cache_page
1316
from pytz import timezone
1417

1518
from dojo.filters import TemplateFindingFilter
1619
from dojo.forms import NoteForm, TestForm, FindingForm, \
1720
DeleteTestForm, AddFindingForm, \
1821
ImportScanForm, ReImportScanForm, FindingBulkUpdateForm, JIRAFindingForm
1922
from dojo.models import Finding, Test, Notes, \
20-
BurpRawRequestResponse, Endpoint, Stub_Finding, Finding_Template, JIRA_PKey, Cred_User, Cred_Mapping
23+
BurpRawRequestResponse, Endpoint, Stub_Finding, Finding_Template, JIRA_PKey, Cred_User, Cred_Mapping, Dojo_User
2124
from dojo.tools.factory import import_parser_factory
2225
from dojo.utils import get_page_items, add_breadcrumb, get_cal_event, message, process_notifications, get_system_setting
2326
from dojo.tasks import add_issue_task
@@ -157,6 +160,26 @@ def delete_test_note(request, tid, nid):
157160
return HttpResponseForbidden()
158161

159162

163+
@user_passes_test(lambda u: u.is_staff)
164+
@cache_page(60 * 5) # cache for 5 minutes
165+
def test_calendar(request):
166+
if not 'lead' in request.GET or '0' in request.GET.getlist('lead'):
167+
tests = Test.objects.all()
168+
else:
169+
filters = []
170+
leads = request.GET.getlist('lead','')
171+
if '-1' in request.GET.getlist('lead'):
172+
leads.remove('-1')
173+
filters.append(Q(lead__isnull=True))
174+
filters.append(Q(lead__in=leads))
175+
tests = Test.objects.filter(reduce(operator.or_, filters))
176+
add_breadcrumb(title="Test Calendar", top_level=True, request=request)
177+
return render(request, 'dojo/calendar.html', {
178+
'caltype': 'tests',
179+
'leads': request.GET.getlist('lead', ''),
180+
'tests': tests,
181+
'users': Dojo_User.objects.all()})
182+
160183
@user_passes_test(lambda u: u.is_staff)
161184
def test_ics(request, tid):
162185
test = get_object_or_404(Test, id=tid)

0 commit comments

Comments
 (0)