Commit 1f6307c6 authored by Elger Jonker's avatar Elger Jonker

Bugfixes, map scale and reset zoom


Former-commit-id: 273d5223
parent df4829d3
{% extends 'game/base.html' %}{% load static %}
{% extends 'game/base.html' %}{% load static %}{% load i18n %}
{% block head %}
......@@ -9,12 +9,14 @@
<link rel="stylesheet" type="text/css" href="{% static '/css/overrides.css' %}">
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script type="text/javascript" src="{% static 'js/vendor/vue.js' %}"></script>
<script type="text/javascript" src="{% static '/js/vendor/jquery-3.2.1.js' %}"></script>
<script type="text/javascript" src="{% static '/js/vendor/leaflet-src.js' %}"></script>
<script type="text/javascript" src="{% static '/js/vendor/Leaflet.fullscreen.js' %}"></script>
<script type="text/javascript" src="{% static '/js/vendor/leaflet.markercluster-src.js' %}"></script>
<script type="text/javascript" src="{% static '/js/vendor/raven.min.vue.3.19.1.js' %}"></script>
<script type="text/javascript" src="{% static '/js/failmap.js' %}"></script>
<script type="text/javascript" src="{% static '/js/views.js' %}"></script>
<meta name="country" content="{{ contest.target_country }}">
<meta name="debug" content="{{ debug }}">
......@@ -27,7 +29,8 @@
let mapbox_token = document.head.querySelector("[name=mapbox_token]").getAttribute('content');
let debug = document.head.querySelector("[name=debug]").getAttribute('content');
failmap.initialize(mapbox_token, country, debug);
failmap.initialize(mapbox_token, country, debug, false);
views();
// todo: add timer that repeats this query every 10 seconds or so.
fetch('/game/data/contest/').then(response => response.json()).then(data => {
......@@ -51,4 +54,52 @@
<!-- Stuff that made failmap.js not really portable -->
<div id="fullscreenreport"></div>
{% verbatim %}
<!-- Maybe it's nice to filter by team... -->
<script type="x-template" id="historycontrol_template">
<div id="historycontrol"></div>
</script>
<script type="x-template" id="domainlist_template">
<div id="domainlist">
<div v-if="urls.length > 1" v-cloak>
<table width='100%'>
<thead>
<tr>
<th style='min-width: 20px; width: 20px;'>{% trans "H" %}</th>
<th style='min-width: 20px; width: 20px;'>{% trans "M" %}</th>
<th style='min-width: 20px; width: 20px;'>{% trans "L" %}</th>
<th>{% trans "Url" %}</th>
</tr>
</thead>
{% verbatim %}
<tbody>
<tr v-for="url in urls">
<td><span v-bind:class="colorize(url.high, url.medium, url.low)">{{ url.high }}</span></td>
<td><span v-bind:class="colorize(url.high, url.medium, url.low)">{{ url.medium }}</span></td>
<td><span v-bind:class="colorize(url.high, url.medium, url.low)">{{ url.low }}</span></td>
<td nowrap><span v-bind:class="colorize(url.high, url.medium, url.low)">{{ url.url }}</span></td>
</tr>
</tbody>
{% endverbatim %}
</table>
</div>
</div>
</script>
{% verbatim %}
<script type="x-template" id="map_item_hover">
<div id="map_hover_info_contents">
<div v-if="properties.organization_name" v-cloak>
<h4>{{ properties.organization_name }}</h4>
<div class="progress">
<div class="progress-bar bg-danger" :style="{width:high}"></div>
<div class="progress-bar bg-warning" :style="{width:medium}"></div>
<div class="progress-bar bg-success" :style="{width:low}"></div>
<div class="progress-bar bg-success" :style="{width:perfect}"></div>
</div>
</div>
</div>
</script>
{% endverbatim %}
{% endblock %}
......@@ -457,13 +457,14 @@ def contest_map_data(request):
features.append(get_bare_organization_feature(bare_organization))
# and organizations that have been accepted, but don't have urls yet, thus no scans.
bare_organizations = list(OrganizationSubmission.objects.all().filter(
has_been_accepted=True,
organization_in_system__u_many_o_upgrade__url__isnull=True,
added_by_team__participating_in_contest=contest.pk
))
for bare_organization in bare_organizations:
features.append(get_bare_organization_feature(bare_organization))
# this is included in the fact that there is an url(or not) and no scans.
# bare_organizations = list(OrganizationSubmission.objects.all().filter(
# has_been_accepted=True,
# organization_in_system__u_many_o_upgrade__url__isnull=True,
# added_by_team__participating_in_contest=contest.pk
# ))
# for bare_organization in bare_organizations:
# features.append(get_bare_organization_feature(bare_organization))
# organizations that have been accepted, do have urls, but don't have a scan yet
bare_organizations = list(OrganizationSubmission.objects.all().filter(
......@@ -472,27 +473,24 @@ def contest_map_data(request):
organization_in_system__u_many_o_upgrade__endpoint__endpointgenericscan__isnull=True,
added_by_team__participating_in_contest=contest.pk
))
# todo: should be the real deal, we can also get the real coordinates if the organization has been added
# which might be altered during to contest to match reality.
# todo: this can have the real ID... for nicer map transitions? That's done by name right?
# todo: this can have the real ID, real mapping information.
for bare_organization in bare_organizations:
features.append(get_bare_organization_feature(bare_organization))
# todo: what happens if this area is already in there? should be done with add_or_update_features.
# submitted url is always for a single organization, it's stored like that to reduce complexity.
bare_urls = list(UrlSubmission.objects.all().filter(
has_been_accepted=False,
has_been_rejected=False,
added_by_team__participating_in_contest=contest.pk
).prefetch_related('for_organization__coordinate_set'))
for bare_url in bare_urls:
features.append(get_bare_url_feature(bare_url))
features = add_bare_url_features(features, bare_urls)
# loop over the organizations and calculate the ratings.
# you prefetch all the related objects, which still have to be iterated... damn,
# because now you can't simply iterate over the data.
# you can't sort by the first organization, as the second one might be different entirely.
# let's build the data iteratively based on organizations in the queryset.
for scan in endpoint_scans:
features = add_or_update_features(features, scan)
......@@ -581,6 +579,7 @@ def get_bare_organization_feature(submitted_organization):
"high_urls": 0,
"medium_urls": 0,
"low_urls": 0,
"origin": "get_bare_organization_feature"
},
"geometry":
{
......@@ -592,45 +591,72 @@ def get_bare_organization_feature(submitted_organization):
}
def get_bare_url_feature(submitted_url):
print(submitted_url)
print(submitted_url.for_organization.coordinate_set)
def add_bare_url_features(features, submitted_urls):
if not submitted_url.for_organization.coordinate_set:
geojsontype = "Point"
area = [0, 0]
else:
coordinate = submitted_url.for_organization.coordinate_set.first()
# submitted url is always for a single organization.
for submitted_url in submitted_urls:
# as the submitted urls doesn't have ratings etc, we only check if we need to add the organization
# to the list of features.
# check if the organization is not yet in the list of features.
if organization_in_features(submitted_url.for_organization, features):
continue
# else create a nice dummy feature
# take into account that some contests / urls could not associated with a region. If there is no region
# attached to it, there is also nothing to plot, thus return the existing features
if not submitted_url.for_organization.coordinate_set:
continue
# get the last known coordinate from this set. They are (usually) ordered by date. But it doesn't matter much
# as it's more an indication than that it actually needs to be true/
coordinate = submitted_url.for_organization.coordinate_set.last()
geojsontype = coordinate.geojsontype
area = coordinate.area
return {
"type": "Feature",
"properties":
{
"organization_id": "%s" % submitted_url.for_organization.pk,
"organization_type": submitted_url.for_organization.type.name,
"organization_name": submitted_url.for_organization.name,
"organization_slug": slugify(submitted_url.for_organization.name),
"overall": 0,
"high": 0,
"medium": 0,
"low": 0,
"data_from": 0,
"color": "pending",
"total_urls": 0,
"high_urls": 0,
"medium_urls": 0,
"low_urls": 0,
},
"geometry":
{
"type": geojsontype,
# Sometimes the data is a string, sometimes it's a list. The admin
# interface might influence this.
"coordinates": area
}
}
feature = {
"type": "Feature",
"properties":
{
"organization_id": "%s" % submitted_url.for_organization.pk,
"organization_type": submitted_url.for_organization.type.name,
"organization_name": submitted_url.for_organization.name,
"organization_slug": slugify(submitted_url.for_organization.name),
"overall": 0,
"high": 0,
"medium": 0,
"low": 0,
"data_from": 0,
"color": "pending",
"total_urls": 0,
"high_urls": 0,
"medium_urls": 0,
"low_urls": 0,
"origin": "add_bare_url_features"
},
"geometry":
{
"type": geojsontype,
# Sometimes the data is a string, sometimes it's a list. The admin
# interface might influence this.
"coordinates": area
}
}
features.append(feature)
return features
def organization_in_features(organization, features):
id = str(organization.pk) # stored as string in the feature
for feature in features:
if feature['properties']['organization_id'] == id:
return True
# not in there
return False
def make_new_feature(organization, scan):
......@@ -671,6 +697,7 @@ def make_new_feature(organization, scan):
"high_urls": 0,
"medium_urls": 0,
"low_urls": 0,
"origin": "make_new_feature"
},
"geometry":
{
......
......@@ -42,13 +42,13 @@ const failmap = {
yellowIcon: new L.divIcon({className: 'leaflet-marker-yellow'}),
grayIcon: new L.divIcon({className: 'leaflet-marker-gray'}),
initialize: function (mapbox_token, country_code, debug) {
initialize: function (mapbox_token, country_code, debug, show_filters=true) {
this.mapbox_token = mapbox_token;
// don't name this variable location, because that redirects the browser.
loc = this.initial_location(country_code);
this.map = L.map('map',
{ dragging: !L.Browser.mobile, touchZoom: true, tap: false, }
{ dragging: !L.Browser.mobile, touchZoom: true, tap: false, zoomSnap: 0}
).setView(loc.coordinates, loc.zoomlevel);
this.map.scrollWheelZoom.disable();
......@@ -87,9 +87,11 @@ const failmap = {
let html = "<div id=\"fullscreen\">" +
"<span class='btn btn-success btn-lg btn-block' v-on:click='toggleFullScreen()'>{{fullscreen}}</span>" +
"</div>";
this.add_div(html, "info_nobackground", false);
this.add_div('<div id="historycontrol"></div>', "info table-light", true);
if (show_filters)
this.add_div('<div id="historycontrol"></div>', "info table-light", true);
this.add_div("<input id='searchbar' type='text' onkeyup='failmap.search(this.value)' placeholder=\"" + gettext('Search organization') + "\"/>", "info table-light", true);
this.add_div("<div><div id='infobox'></div><br /><br /><div id='domainlist'></div></div>", "info table-light", true);
......@@ -102,6 +104,28 @@ const failmap = {
this.add_div("<span class='legend_title'>" + gettext('legend_basic_security') + "</span><br />" + labels.join('<br />'), "info legend table-light", false, {position: 'bottomright'});
this.add_div(document.getElementById('fullscreenreport').innerHTML, "fullscreenmap", true);
// scale
L.control.scale().addTo(this.map);
// show whole map:
// https://gist.github.com/stefanocudini/a5cde3c11c9b1f277368
(function() {
var control = new L.Control({position:'topleft'});
control.onAdd = function(map) {
var azoom = L.DomUtil.create('a','resetzoom');
azoom.innerHTML = "<span style='font-size: 1.4em; background-color: white; border: 2px solid rgba(0,0,0,0.35); border-radius: 4px; padding: 6px; height: 34px; position: absolute; width: 34px; text-align: center; line-height: 1.2;'>🗺️</span>";
L.DomEvent
.disableClickPropagation(azoom)
.addListener(azoom, 'click', function() {
map.fitBounds(failmap.polygons.getBounds(),
{paddingTopLeft: [0,0], paddingBottomRight: [0, 0]})
}, azoom);
return azoom;
};
return control;
}())
.addTo(this.map);
this.light_map = new L.tileLayer(this.tile_uri(), {
maxZoom: 18,
attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
......
......@@ -88,7 +88,6 @@ const report_mixin = {
twitter_handle: '',
name: "",
urls: Array,
mailto: document.head.querySelector("[name=mailto]").getAttribute('content'),
selected: null,
loading: false,
visible: false, // fullscreenreport
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment