Thumbnails for images and videos #87
@ -1,5 +1,4 @@
|
||||
from typing import Set, Tuple, Mapping, Any
|
||||
import copy
|
||||
import logging
|
||||
|
||||
from django.contrib.admin.models import DELETION
|
||||
@ -21,6 +20,13 @@ See TrackChangesMixin.pre_save_record().
|
||||
"""
|
||||
|
||||
|
||||
def _get_object_state(obj: object, fields=None, include_pk=False) -> dict:
|
||||
data = serializers.serialize('python', [obj], fields=fields)[0]
|
||||
if include_pk:
|
||||
data['fields']['pk'] = data['pk']
|
||||
return data['fields']
|
||||
|
||||
|
||||
class CreatedModifiedMixin(models.Model):
|
||||
"""Add standard date fields to a model."""
|
||||
|
||||
@ -48,11 +54,6 @@ class CreatedModifiedMixin(models.Model):
|
||||
|
||||
|
||||
class RecordDeletionMixin:
|
||||
def serialise(self) -> dict:
|
||||
data = serializers.serialize('python', [self])[0]
|
||||
data['fields']['pk'] = data['pk']
|
||||
return data['fields']
|
||||
|
||||
def record_deletion(self):
|
||||
"""Create a LogEntry describing a deletion of this object."""
|
||||
msg_args = {'type': type(self), 'pk': self.pk}
|
||||
@ -63,7 +64,7 @@ class RecordDeletionMixin:
|
||||
# This shouldn't happen: prior validation steps should have taken care of this.
|
||||
msg_args['reasons'] = cannot_be_deleted_reasons
|
||||
logger.error("%(type)s pk=%(pk)s is being deleted but it %(reasons)s", msg_args)
|
||||
state = self.serialise()
|
||||
state = _get_object_state(self, include_pk=True)
|
||||
message = [
|
||||
{
|
||||
'deleted': {
|
||||
@ -123,9 +124,7 @@ class TrackChangesMixin(RecordDeletionMixin, models.Model):
|
||||
|
||||
update_fields = kwargs.get('update_fields')
|
||||
was_modified = self._was_modified(db_instance, update_fields=update_fields)
|
||||
old_instance_data = {
|
||||
attr: copy.deepcopy(getattr(db_instance, attr)) for attr in self.track_changes_to_fields
|
||||
}
|
||||
old_instance_data = _get_object_state(db_instance, fields=self.track_changes_to_fields)
|
||||
return was_modified, old_instance_data
|
||||
|
||||
def record_status_change(self, was_changed, old_state, **kwargs):
|
||||
@ -151,8 +150,9 @@ class TrackChangesMixin(RecordDeletionMixin, models.Model):
|
||||
if not was_changed or not self.pk:
|
||||
return
|
||||
|
||||
new_state = _get_object_state(self, fields=self.track_changes_to_fields)
|
||||
changed_fields = {
|
||||
field for field in old_state.keys() if getattr(self, field) != old_state[field]
|
||||
field for field in old_state.keys() if new_state.get(field) != old_state.get(field)
|
||||
}
|
||||
message = [
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from semantic_version.django_fields import VersionField as SemanticVersionField
|
||||
from semantic_version import Version
|
||||
import json
|
||||
@ -11,7 +12,10 @@ class VersionStringField(SemanticVersionField):
|
||||
return value
|
||||
if value is None:
|
||||
return value
|
||||
try:
|
||||
return str(Version(value))
|
||||
except Exception as e:
|
||||
raise ValidationError(e)
|
||||
|
||||
def from_db_value(self, value, expression, connection):
|
||||
return self.to_python(value)
|
||||
|
@ -3,13 +3,26 @@
|
||||
<a
|
||||
href="https://www.blender.org/download/releases/{{ version.blender_version_min|version_without_patch|replace:".,-" }}/"
|
||||
title="{{ version.blender_version_min }}">Blender {{ version.blender_version_min|version_without_patch }}</a>
|
||||
{% if version.blender_version_max %}
|
||||
{% if version.blender_version_max|version_without_patch != version.blender_version_min|version_without_patch %}
|
||||
{% if is_editable %}
|
||||
—
|
||||
<input name="blender_version_max" class="form-control-sm"
|
||||
value="{{version.blender_version_max|default_if_none:''}}"
|
||||
placeholder="{% trans 'maximum Blender version' %}"
|
||||
pattern="^([0-9]+\.[0-9]+\.[0-9]+)?$"
|
||||
title="{% trans 'Blender version, e.g. 4.1.0' %}"
|
||||
/>
|
||||
{% for error in form.errors.blender_version_max %}
|
||||
<div class="error">{{ error }}</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if version.blender_version_max %}
|
||||
{% if version.blender_version_max|version_without_patch != version.blender_version_min|version_without_patch %}
|
||||
—
|
||||
<a
|
||||
href="https://www.blender.org/download/releases/{{ version.blender_version_max|version_without_patch|replace:".,-" }}/"
|
||||
title="{{ version.blender_version_max }}">{{ version.blender_version_max|version_without_patch }}</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% else %}
|
||||
{% trans 'and newer' %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -54,7 +54,7 @@
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
<dt>{% trans 'Tagline' %}</dt>
|
||||
<dd title="{{ latest.tagline }}">{{ latest.tagline }}</dd>
|
||||
<dd title="{{ version.tagline }}">{{ version.tagline }}</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -63,20 +63,20 @@
|
||||
<dt>{% trans 'Version' %}</dt>
|
||||
<dd>
|
||||
<a href="{{ extension.get_versions_url }}">
|
||||
{{ latest.version }}
|
||||
{{ version.version }}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="dl-col">
|
||||
<dt>{% trans 'Size' %}</dt>
|
||||
<dd>{{ latest.file.size_bytes|filesizeformat }}</dd>
|
||||
<dd>{{ version.file.size_bytes|filesizeformat }}</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
<dt>{% trans 'Compatibility' %}</dt>
|
||||
<dd>{% include "extensions/components/blender_version.html" with version=latest %}</dd>
|
||||
<dd>{% include "extensions/components/blender_version.html" with version=version is_editable=is_editable form=form %}</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -91,8 +91,8 @@
|
||||
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
<dt>License{{ latest.licenses.count|pluralize }}</dt>
|
||||
{% for license in latest.licenses.all %}
|
||||
<dt>License{{ version.licenses.count|pluralize }}</dt>
|
||||
{% for license in version.licenses.all %}
|
||||
<dd>
|
||||
{% include "common/components/external_link.html" with url=license.url title=license %}
|
||||
</dd>
|
||||
@ -102,14 +102,14 @@
|
||||
|
||||
<div class="dl-row">
|
||||
<div class="dl-col">
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with version=latest %}
|
||||
{% include "extensions/components/detail_card_version_permissions.html" with version=version %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dl-row">
|
||||
<dd>
|
||||
{% if latest.tags.count %}
|
||||
{% include "extensions/components/tags.html" with small=True version=latest %}
|
||||
{% if version.tags.count %}
|
||||
{% include "extensions/components/tags.html" with small=True version=version %}
|
||||
{% else %}
|
||||
No tags.
|
||||
{% endif %}
|
||||
|
@ -78,7 +78,7 @@
|
||||
<div class="is-sticky py-3">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=form.instance.extension latest=form.instance is_initial=True %}
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=form.instance.extension version=form.instance is_initial=True %}
|
||||
|
||||
<section class="card p-3 mt-3">
|
||||
<div class="btn-col">
|
||||
|
@ -4,7 +4,7 @@
|
||||
{% block page_title %}{{ extension.name }}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
{% with latest=extension.latest_version author=extension.latest_version.file.user form=form|add_form_classes %}
|
||||
{% with author=extension.latest_version.file.user form=form|add_form_classes %}
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h2>{{ extension.get_type_display }} {% trans 'details' %}</h2>
|
||||
@ -93,7 +93,7 @@
|
||||
<div class="is-sticky py-3">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=extension latest=latest %}
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=extension version=extension.latest_version %}
|
||||
|
||||
<section class="card p-3 mt-3">
|
||||
<div class="btn-col">
|
||||
|
@ -44,8 +44,7 @@
|
||||
<div class="is-sticky">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=form.instance.extension latest=form.instance %}
|
||||
|
||||
{% include "extensions/components/extension_edit_detail_card.html" with extension=form.instance.extension version=form.instance is_editable=True form=form %}
|
||||
<section class="card p-3 mt-3">
|
||||
<div class="btn-col">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
|
@ -289,3 +289,36 @@ class UpdateVersionViewTest(_BaseTestCase):
|
||||
self.client.force_login(random_user)
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_blender_max_version(self):
|
||||
extension = _create_extension()
|
||||
extension_owner = extension.latest_version.file.user
|
||||
extension.authors.add(extension_owner)
|
||||
self.client.force_login(extension_owner)
|
||||
url = reverse(
|
||||
'extensions:version-update',
|
||||
kwargs={
|
||||
'type_slug': extension.type_slug,
|
||||
'slug': extension.slug,
|
||||
'pk': extension.latest_version.pk,
|
||||
},
|
||||
)
|
||||
version = extension.latest_version
|
||||
|
||||
response = self.client.post(
|
||||
url,
|
||||
{'release_notes': 'text', 'blender_version_max': 'invalid'},
|
||||
)
|
||||
# error page, no redirect
|
||||
self.assertEqual(response.status_code, 200)
|
||||
version.refresh_from_db()
|
||||
self.assertIsNone(version.blender_version_max)
|
||||
|
||||
response2 = self.client.post(
|
||||
url,
|
||||
{'release_notes': 'text', 'blender_version_max': '4.2.0'},
|
||||
)
|
||||
# success, redirect
|
||||
self.assertEqual(response2.status_code, 302)
|
||||
version.refresh_from_db()
|
||||
self.assertEqual(version.blender_version_max, '4.2.0')
|
||||
|
@ -327,7 +327,7 @@ class UpdateVersionView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
|
||||
|
||||
template_name = 'extensions/new_version_finalise.html'
|
||||
model = Version
|
||||
fields = ['release_notes']
|
||||
fields = ['blender_version_max', 'release_notes']
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(
|
||||
|
@ -7,6 +7,7 @@ from django.contrib.admin.utils import NestedObjects
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models, DEFAULT_DB_ALIAS, transaction
|
||||
from django.templatetags.static import static
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
from common.model_mixins import TrackChangesMixin
|
||||
from files.utils import get_sha256_from_value
|
||||
@ -89,7 +90,7 @@ class User(TrackChangesMixin, AbstractUser):
|
||||
date_deletion_requested,
|
||||
)
|
||||
self.is_active = False
|
||||
self.date_deletion_requested = date_deletion_requested
|
||||
self.date_deletion_requested = parse_datetime(date_deletion_requested)
|
||||
self.save(update_fields=['is_active', 'date_deletion_requested'])
|
||||
|
||||
@transaction.atomic
|
||||
|
Loading…
Reference in New Issue
Block a user