Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion serveradmin/common/static/js/serveradmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ $(document).ready(function() {
form = document.getElementById(formId);
}
else {
this.closest('form');
form = this.closest('form');
}

if (form) {
Expand Down
2 changes: 1 addition & 1 deletion serveradmin/serverdb/query_committer.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def _validate(attribute_lookup, changed, changed_objects):

newer = _validate_commit(changed, changed_objects)
if newer:
raise CommitNewerData('Newer data available', newer)
raise CommitNewerData(f'Newer data available for attribute {newer}', newer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is absolutely unrelated to polishing of the changes page no ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, a side change I applied during testing



def _delete_attributes(attribute_lookup, changed, changed_servers, deleted):
Expand Down
107 changes: 103 additions & 4 deletions serveradmin/serverdb/templates/serverdb/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@

{% block title %}Changes{% endblock %}

{% block additional_styles %}
<style>
.change-toggle,
#toggle-all-changes {
cursor: pointer;
text-decoration: none;
display: inline-block;
margin-right: 4px;
}
.change-toggle .toggle-icon,
#toggle-all-changes .toggle-icon {
display: inline-block;
transition: transform 0.1s ease;
font-size: 0.7em;
color: #6c757d;
}
.change-toggle[aria-expanded="true"] .toggle-icon,
#toggle-all-changes.expanded .toggle-icon {
transform: rotate(90deg);
}
.change-details {
transition: none;
}
.change-details.collapsing {
transition: none;
}
.change-details ul {
list-style-type: none;
padding-left: 1em;
}
</style>
{% endblock %}

{% block content %}
<form id="changes-form" method="get" action="{% url 'serverdb_changes' %}" onsubmit="spinner.enable();">
<div class="row">
Expand Down Expand Up @@ -57,6 +90,13 @@ <h3>
</div>
</div>
</div>
<div class="form-group row input-controls">
<label for="attribute" class="col-sm-1 col-form-label">Attribute:</label>
<div class="col-md-4">
<input name="attribute" id="attribute" type="text" value="{% if attribute %}{{ attribute }}{% endif %}" class="form-control form-control-sm" placeholder="responsible_admin" />
<small class="form-text text-muted">Requires hostname, object ID, or user/app filter</small>
</div>
</div>
<div class="form-group row input-controls buttons">
<input type="hidden" id="page" name="page" value="{{ commits.number }}" />
<button class="btn btn-success" type="submit">Filter</button>
Expand All @@ -75,7 +115,12 @@ <h3>
<th>App</th>
<th>Owner</th>
<th>Created</th>
<th>Changed</th>
<th>
<a id="toggle-all-changes" href="#" title="Show/hide all attribute changes">
<span class="toggle-icon">&#9654;</span>
</a>
Changed
</th>
<th>Deleted</th>
</tr>
</thead>
Expand All @@ -85,8 +130,18 @@ <h3>
<td>{{ commit.id }}</td>
<td>{{ commit.change_on|date:"r" }}</td>
<td>{{ commit.change_on|timesince }}</td>
<td>{{ commit.app|default:"Servershell" }}</td>
<td>{{ commit.user }}</td>
<td>
{% if commit.app %}
<a href="{% url 'serverdb_changes' %}?user_or_app={{ commit.app.name }}">{{ commit.app }}</a>
{% else %}
Servershell
{% endif %}
</td>
<td>
{% if commit.user %}
<a href="{% url 'serverdb_changes' %}?user_or_app={{ commit.user.username }}">{{ commit.user }}</a>
{% endif %}
</td>
<td>
<ul>
{% for change in commit.change_set.get_queryset %}
Expand All @@ -100,7 +155,27 @@ <h3>
<ul>
{% for change in commit.change_set.get_queryset %}
{% if change.change_type == 'change' %}
<li><a href="{% url 'serverdb_history' %}?commit_id={{ commit.id }}&object_id={{ change.object_id }}">History for {{ change.hostname }}</a></li>
<li>
{% with attr_changes=change|get_attribute_changes %}
{% if attr_changes %}
<a class="change-toggle" data-toggle="collapse" href="#change-{{ change.id }}" role="button" aria-expanded="false" aria-controls="change-{{ change.id }}" title="Show/hide attribute changes">
<span class="toggle-icon">&#9654;</span>
</a>
{% endif %}
{% endwith %}
<a href="{% url 'serverdb_history' %}?commit_id={{ commit.id }}&object_id={{ change.object_id }}">History for {{ change.hostname }}</a>
{% with attr_changes=change|get_attribute_changes %}
{% if attr_changes %}
<div class="collapse change-details" id="change-{{ change.id }}">
<ul class="mb-0 mt-1">
{% for attr_change in attr_changes %}
<li><small class="text-muted">{{ attr_change }}</small></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
</li>
{% endif %}
{% endfor %}
</ul>
Expand All @@ -127,3 +202,27 @@ <h3>
</div>
</form>
{% endblock content %}

{% block additional_scripts %}
<script>
$(document).ready(function() {
$('#toggle-all-changes').click(function(e) {
e.preventDefault();
var $this = $(this);
var isExpanded = $this.hasClass('expanded');

if (isExpanded) {
// Collapse all
$('.change-details.show').collapse('hide');
$('.change-toggle').attr('aria-expanded', 'false');
$this.removeClass('expanded');
} else {
// Expand all
$('.change-details:not(.show)').collapse('show');
$('.change-toggle').attr('aria-expanded', 'true');
$this.addClass('expanded');
}
});
});
</script>
{% endblock %}
49 changes: 46 additions & 3 deletions serveradmin/serverdb/templatetags/changes.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,62 @@
"""Serveradmin

Copyright (c) 2020 InnoGames GmbH
Copyright (c) 2025 InnoGames GmbH
"""

from typing import Any, Union

from django import template
from django.core.exceptions import ObjectDoesNotExist
from django.utils.html import escape
from django.utils.safestring import mark_safe

from serveradmin.serverdb.models import Server
from serveradmin.serverdb.models import Change, Server

register = template.Library()


@register.filter
def hostname(object_id):
def hostname(object_id: int) -> Union[str, int]:
try:
return Server.objects.get(server_id=object_id).hostname
except ObjectDoesNotExist:
return object_id


@register.filter
def get_attribute_changes(change: Change) -> list:
"""Extract attribute changes from a Change object's change_json.

Returns a list of HTML-safe formatted strings.
Only works for change_type='change'.
"""
if change.change_type != 'change':
return []

changes_list = []

for attribute_id, attr_change in change.change_json.items():
if attribute_id == 'object_id' or not isinstance(attr_change, dict):
continue

prefix = f'<strong>{attribute_id}:</strong>'
action = attr_change.get('action')
old_val = escape(attr_change.get('old'))
new_val = escape(attr_change.get('new'))

if action == 'update':
if attr_change.get('new') in (None, ''):
changes_list.append(mark_safe(f'{prefix} <del>{old_val}</del>'))
else:
changes_list.append(mark_safe(f'{prefix} <del>{old_val}</del> &rarr; {new_val}'))
elif action == 'new':
changes_list.append(mark_safe(f'{prefix} + {new_val}'))
elif action == 'delete':
changes_list.append(mark_safe(f'{prefix} <del>{old_val}</del>'))
elif action == 'multi':
for val in attr_change.get('remove', []):
changes_list.append(mark_safe(f'{prefix} <del>{escape(str(val))}</del>'))
for val in attr_change.get('add', []):
changes_list.append(mark_safe(f'{prefix} + {escape(str(val))}'))

return changes_list
9 changes: 9 additions & 0 deletions serveradmin/serverdb/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def changes(request):
commits = commits.filter(
Q(app__name=f_user_or_app) | Q(user__username=f_user_or_app))

f_attribute = request.GET.get('attribute')
if f_attribute:
# Only allow attribute filter if another filter is set (no index)
if f_hostname or f_object_id or f_user_or_app:
commits = commits.filter(change__change_json__has_key=f_attribute)
else:
f_attribute = None

commits = commits.select_related('app', 'user')

# This complex statement is just here to be able to prefetch the changes'
Expand Down Expand Up @@ -108,6 +116,7 @@ def count(self):
'object_id': f_object_id,
'commit_id': f_commit,
'user_or_app': f_user_or_app,
'attribute': f_attribute,
})


Expand Down
2 changes: 2 additions & 0 deletions serveradmin/servershell/templates/servershell/inspect.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ <h3>
<div class="col-md-8">
<a class="btn btn-success" data-edit-link href="{% url 'servershell_edit' %}?object_id={{ object_id }}">Edit</a>
<a href="{% url 'servershell_index' %}" class="btn btn-success">Goto Servershell</a>
<a href="/serverdb/changes?object_id={{ object_id }}" class="btn btn-success">History</a>
</div>
</div>
<div class="row mb-3">
Expand Down Expand Up @@ -72,6 +73,7 @@ <h3>
<div class="col-md-8">
<a class="btn btn-success" data-edit-link href="{% url 'servershell_edit' %}?object_id={{ object_id }}">Edit</a>
<a href="{% url 'servershell_index' %}" class="btn btn-success">Goto Servershell</a>
<a href="/serverdb/changes?object_id={{ object_id }}" class="btn btn-success">History</a>
</div>
</div>
</div>
Expand Down
Loading