Ability to clone a map and overall UI improvements
This commit is contained in:
parent
5d69d3c22f
commit
8a6e992b9c
6 changed files with 144 additions and 34 deletions
|
@ -144,16 +144,19 @@ body.login header {
|
|||
}
|
||||
h2.section {
|
||||
text-transform: uppercase;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
color: #263B58;
|
||||
padding-top: 28px;
|
||||
}
|
||||
h2.tabs a {
|
||||
color: #666;
|
||||
color: #263B58;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 3px;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
h2.tabs a:not(.selected) {
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
color: #263B58;
|
||||
text-decoration: none;
|
||||
}
|
||||
h2.tabs a:hover {
|
||||
text-decoration: underline;
|
||||
|
@ -310,14 +313,81 @@ ul.umap-autocomplete {
|
|||
/* **************************** */
|
||||
/* Dashboard */
|
||||
/* **************************** */
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.table-header form {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.table-header form input {
|
||||
border: 2px solid #263B58;
|
||||
border-radius: 0;
|
||||
padding: .5rem 1rem;
|
||||
margin-bottom: 0;
|
||||
line-height: inherit;
|
||||
height: 2.5rem;
|
||||
}
|
||||
.table-header form input[type="search"] {
|
||||
width: 30ch;
|
||||
}
|
||||
.table-header form input[type="submit"] {
|
||||
background-color: #263B58;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.table-header .button-download {
|
||||
width: inherit;
|
||||
display: inline;
|
||||
padding: .5rem 1rem;
|
||||
border: 2px solid #263B58;
|
||||
color: #263B58;
|
||||
font-weight: bold;
|
||||
background-color: initial;
|
||||
margin-bottom: 0;
|
||||
line-height: inherit;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
table.maps {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.maps .map_fragment {
|
||||
display: block;
|
||||
height: 80vh;
|
||||
width: 100%;
|
||||
}
|
||||
table.maps a,
|
||||
table.maps thead {
|
||||
color: #263B58;
|
||||
}
|
||||
table.maps a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
table.maps form {
|
||||
display: inline;
|
||||
}
|
||||
table.maps input[type="submit"] {
|
||||
display: inline;
|
||||
background-color: transparent;
|
||||
color: #263B58;
|
||||
padding: 0;
|
||||
width: inherit;
|
||||
height: 1rem;
|
||||
margin: 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
table.maps tbody tr {
|
||||
border-bottom: 1px solid #BDC7D4;
|
||||
}
|
||||
table.maps tbody tr td {
|
||||
padding: 5px 4px;
|
||||
}
|
||||
table.maps tbody tr:nth-child(odd) {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
@ -357,12 +427,14 @@ dialog::backdrop {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
margin: 1rem;
|
||||
border-top: 1px solid gray;
|
||||
}
|
||||
.pagination > * {
|
||||
padding: 1rem;
|
||||
}
|
||||
.pagination a {
|
||||
color: #263B58;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************* */
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{% extends "umap/content.html" %}
|
||||
{% load i18n %}
|
||||
{% block maincontent %}
|
||||
<div class="col wide">
|
||||
<div class="row">
|
||||
<h2 class="section tabs">
|
||||
<a href="{% url "user_dashboard" %}">{% trans "My Maps" %}</a> | <a class="selected" href="{% url 'user_profile' %}">{% trans "My Profile" %}</a>
|
||||
<a href="{% url "user_dashboard" %}">{% trans "My Maps" %}</a>
|
||||
<a class="selected" href="{% url 'user_profile' %}">{% trans "My Profile" %}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<tr>
|
||||
<th>{% blocktrans %}Name{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Preview{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Who can see / edit{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Who can see{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Who can edit{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Last save{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Owner{% endblocktrans %}</th>
|
||||
<th>{% blocktrans %}Actions{% endblocktrans %}</th>
|
||||
|
@ -13,12 +14,12 @@
|
|||
<tbody>
|
||||
{% for map_inst in maps %}
|
||||
{% with unique_id="map_"|addstr:map_inst.pk %}
|
||||
{{ map_inst.preview_settings|json_script:unique_id }}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ map_inst.get_absolute_url }}">{{ map_inst.name }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ map_inst.preview_settings|json_script:unique_id }}
|
||||
<button class="button map-opener neutral" data-map-id="{{ unique_id }}">{% blocktranslate %}Open preview{% endblocktranslate %}</button>
|
||||
<dialog>
|
||||
<form method="dialog">
|
||||
|
@ -29,7 +30,8 @@
|
|||
</form>
|
||||
</dialog>
|
||||
</td>
|
||||
<td>{{ map_inst.get_share_status_display }} / {{ map_inst.get_edit_status_display }}</td>
|
||||
<td>{{ map_inst.get_share_status_display }}</td>
|
||||
<td>{{ map_inst.get_edit_status_display }}</td>
|
||||
<td>{{ map_inst.modified_at }}</td>
|
||||
<td>
|
||||
<a href="{{ map_inst.owner.get_url }}">{{ map_inst.owner }}</a>
|
||||
|
@ -38,28 +40,16 @@
|
|||
<a href="{{ map_inst.get_absolute_url }}?share">{% translate "Share" %}</a> |
|
||||
<a href="{{ map_inst.get_absolute_url }}?edit">{% translate "Edit" %}</a> |
|
||||
<a href="{% url 'map_download' map_inst.pk %}">{% translate "Download" %}</a> |
|
||||
<form action="{% url 'map_clone' map_inst.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans "Clone" %}" />
|
||||
</form> |
|
||||
<a href="{% url 'map_delete' map_inst.pk %}">{% translate "Delete" %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5"></td>
|
||||
<td>
|
||||
<a href="{% url 'user_download' %}?{% spaceless %}
|
||||
{% for map_inst in maps %}map_id={{ map_inst.pk }}{% if not forloop.last %}&{% endif %}{% endfor %}
|
||||
{% endspaceless %}" class="button"
|
||||
>{% blocktranslate count counter=maps.object_list|length %}
|
||||
Download this map
|
||||
{% plural %}
|
||||
Download these {{ counter }} maps
|
||||
{% endblocktranslate %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
{% if maps.has_other_pages %}
|
||||
<div class="pagination">
|
||||
|
@ -88,6 +78,16 @@
|
|||
<span></span>
|
||||
{# djlint:on #}
|
||||
{% endif %}
|
||||
<span>
|
||||
{% blocktranslate with per_page=maps.paginator.per_page %}
|
||||
Lines per page: {{ per_page }}
|
||||
{% endblocktranslate %}
|
||||
</span>
|
||||
<span>
|
||||
{% blocktranslate with count=maps.paginator.count %}
|
||||
{{ count }} maps
|
||||
{% endblocktranslate %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -5,17 +5,36 @@
|
|||
{% endblock head_title %}
|
||||
{% block maincontent %}
|
||||
{% trans "Search my maps" as placeholder %}
|
||||
<div class="col wide">
|
||||
<div class="row">
|
||||
<h2 class="section tabs">
|
||||
<a class="selected" href="{% url 'user_dashboard' %}"
|
||||
>{% blocktranslate with count=maps.paginator.count %}My Maps ({{ count }}){% endblocktranslate %}
|
||||
</a> | <a href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
|
||||
</a>
|
||||
<a href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
|
||||
</h2>
|
||||
{% include "umap/search_bar.html" with action=request.get_full_path placeholder=placeholder %}
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div class="row">
|
||||
{% if maps %}
|
||||
<div class="table-header">
|
||||
<form action="{{ request.get_full_path }}" method="get">
|
||||
<span>
|
||||
<label for="q">{% blocktranslate %}Map’s title{% endblocktranslate %}</label>
|
||||
<input id="q" name="q" type="search"
|
||||
value="{{ request.GET.q|default:"" }}" />
|
||||
</span>
|
||||
<input type="submit" value="{% trans "Search" %}" />
|
||||
</form>
|
||||
{% if maps.object_list|length > 1 %}
|
||||
<a href="{% url 'user_download' %}?{% spaceless %}
|
||||
{% for map_inst in maps %}map_id={{ map_inst.pk }}{% if not forloop.last %}&{% endif %}{% endfor %}
|
||||
{% endspaceless %}" class="button button-download"
|
||||
>{% blocktranslate with count=maps.object_list|length %}
|
||||
Download {{ count }} maps
|
||||
{% endblocktranslate %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if maps or request.GET.q %}
|
||||
{% include "umap/map_table.html" %}
|
||||
{% else %}
|
||||
<div>
|
||||
|
|
|
@ -158,9 +158,23 @@ def test_clone_map_should_create_a_new_instance(client, map):
|
|||
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url)
|
||||
assert response.status_code == 302
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest("pk")
|
||||
assert response["Location"] == clone.get_absolute_url()
|
||||
assert clone.pk != map.pk
|
||||
assert clone.name == "Clone of " + map.name
|
||||
|
||||
|
||||
def test_clone_map_should_be_possible_via_ajax(client, map):
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url, headers={"X-Requested-With": "XMLHttpRequest"})
|
||||
assert response.status_code == 200
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest("pk")
|
||||
assert response.json() == {"redirect": clone.get_absolute_url()}
|
||||
assert clone.pk != map.pk
|
||||
assert clone.name == "Clone of " + map.name
|
||||
|
||||
|
@ -191,7 +205,7 @@ def test_clone_should_set_cloner_as_owner(client, map, user):
|
|||
map.save()
|
||||
client.login(username=user.username, password="123123")
|
||||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == 302
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest("pk")
|
||||
assert clone.pk != map.pk
|
||||
|
@ -442,9 +456,10 @@ def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonym
|
|||
anonymap.edit_status = anonymap.ANONYMOUS
|
||||
anonymap.save()
|
||||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == 302
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest("pk")
|
||||
assert response["Location"] == clone.get_absolute_url()
|
||||
assert clone.pk != anonymap.pk
|
||||
assert clone.name == "Clone of " + anonymap.name
|
||||
assert clone.owner is None
|
||||
|
|
|
@ -860,7 +860,10 @@ class MapClone(PermissionsMixin, View):
|
|||
return HttpResponseForbidden()
|
||||
owner = self.request.user if self.request.user.is_authenticated else None
|
||||
self.object = kwargs["map_inst"].clone(owner=owner)
|
||||
if is_ajax(self.request):
|
||||
response = simple_json_response(redirect=self.object.get_absolute_url())
|
||||
else:
|
||||
response = HttpResponseRedirect(self.object.get_absolute_url())
|
||||
if not self.request.user.is_authenticated:
|
||||
key, value = self.object.signed_cookie_elements
|
||||
response.set_signed_cookie(
|
||||
|
|
Loading…
Reference in a new issue