Anna Sirota
caae613747
* removes all soft-deletion; * shows a "Delete extension" button on the draft page in case it can be deleted; * shows a "Delete version" button on the version page in case it can be deleted; * a version can be deleted if * its file isn't approved, and it doesn't have any ratings; * an extension can be deleted if * it's not listed, and doesn't have any ratings or abuse reports; * all it's versions can also be deleted; * changes default `File.status` from `APPROVED` to `AWAITING_REVIEW` With version's file status being `APPROVED` by default, a version can never be deleted, even when the extension is still a draft. This change doesn't affect the approval process because * when an extension is approved its latest version becomes approved automatically (no change here); * when a new version is uploaded to an approved extension, it's approved automatically (this is new). This allows authors to delete their drafts, freeing the extension slug and making it possible to re-upload the same file. This also makes it possible to easily fix mistakes during the drafting of a new extension (e.g. delete a version and re-upload it without bumping a version for each typo/mistake in packaging and so on). (see #78 and #63) Reviewed-on: #81
155 lines
4.7 KiB
Python
155 lines
4.7 KiB
Python
import logging
|
|
|
|
from django import forms
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from files.validators import FileMIMETypeValidator
|
|
from constants.base import ALLOWED_PREVIEW_MIMETYPES
|
|
|
|
import extensions.models
|
|
import files.models
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class EditPreviewForm(forms.ModelForm):
|
|
class Meta:
|
|
model = extensions.models.Extension.previews.through
|
|
fields = (
|
|
'caption',
|
|
'position',
|
|
)
|
|
widgets = {
|
|
'position': forms.HiddenInput(attrs={'data-position': ''}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.base_fields['caption'].widget.attrs.update({'placeholder': 'Describe the preview'})
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
EditPreviewFormSet = forms.inlineformset_factory(
|
|
extensions.models.Extension,
|
|
extensions.models.Extension.previews.through,
|
|
form=EditPreviewForm,
|
|
extra=0,
|
|
)
|
|
|
|
|
|
class AddPreviewFileForm(forms.ModelForm):
|
|
msg_unexpected_file_type = _('Choose a JPEG, PNG or WebP image, or an MP4 video')
|
|
|
|
class Meta:
|
|
model = files.models.File
|
|
fields = ('caption', 'source')
|
|
|
|
source = forms.FileField(
|
|
allow_empty_file=False,
|
|
required=True,
|
|
validators=[
|
|
FileMIMETypeValidator(
|
|
allowed_mimetypes=ALLOWED_PREVIEW_MIMETYPES,
|
|
message=msg_unexpected_file_type,
|
|
),
|
|
],
|
|
widget=forms.ClearableFileInput(
|
|
attrs={'accept': ','.join(ALLOWED_PREVIEW_MIMETYPES)},
|
|
),
|
|
)
|
|
caption = forms.CharField(max_length=255, required=False)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.request = kwargs.pop('request')
|
|
self.extension = kwargs.pop('extension')
|
|
self.base_fields['caption'].widget.attrs.update({'placeholder': 'Describe the preview'})
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""Save Preview from the cleaned form data."""
|
|
# If file with this hash was already uploaded by the same user, return it
|
|
hash_ = self.instance.generate_hash(self.instance.source)
|
|
model = self.instance.__class__
|
|
existing_image = model.objects.filter(original_hash=hash_, user=self.request.user).first()
|
|
if (
|
|
existing_image
|
|
and not existing_image.extension_preview.filter(extension_id=self.extension.id).count()
|
|
):
|
|
logger.warning('Found an existing %s pk=%s', model, existing_image.pk)
|
|
self.instance = existing_image
|
|
|
|
# Fill in missing fields from request and the source file
|
|
self.instance.user = self.request.user
|
|
|
|
instance = super().save(*args, **kwargs)
|
|
|
|
# Create extension preview and save caption to it
|
|
instance.extension_preview.create(
|
|
caption=self.cleaned_data['caption'],
|
|
extension=self.extension,
|
|
)
|
|
return instance
|
|
|
|
|
|
class AddPreviewModelFormSet(forms.BaseModelFormSet):
|
|
def __init__(self, *args, **kwargs):
|
|
self.request = kwargs.pop('request')
|
|
self.extension = kwargs.pop('extension')
|
|
super().__init__(*args, **kwargs)
|
|
# Make sure formset doesn't attempt to select existing File records
|
|
self.queryset = files.models.File.objects.none()
|
|
|
|
def get_form_kwargs(self, *args, **kwargs):
|
|
form_kwargs = super().get_form_kwargs(*args, **kwargs)
|
|
form_kwargs['request'] = self.request
|
|
form_kwargs['extension'] = self.extension
|
|
return form_kwargs
|
|
|
|
|
|
AddPreviewFormSet = forms.modelformset_factory(
|
|
files.models.File,
|
|
form=AddPreviewFileForm,
|
|
formset=AddPreviewModelFormSet,
|
|
extra=1,
|
|
)
|
|
|
|
|
|
class ExtensionUpdateForm(forms.ModelForm):
|
|
class Meta:
|
|
model = extensions.models.Extension
|
|
fields = (
|
|
'description',
|
|
'support',
|
|
)
|
|
|
|
|
|
class ExtensionDeleteForm(forms.ModelForm):
|
|
class Meta:
|
|
model = extensions.models.Extension
|
|
fields = []
|
|
|
|
|
|
class VersionForm(forms.ModelForm):
|
|
class Meta:
|
|
model = extensions.models.Version
|
|
fields = {'file', 'release_notes'}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Limit 'file' choices to the initial file value."""
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Mark 'file' field as disabled so that Django form allows using its initial value.
|
|
self.fields['file'].disabled = True
|
|
|
|
def clean_file(self, *args, **kwargs):
|
|
"""Return file that was passed to the form via the initial values.
|
|
|
|
This ensures that it doesn't have to be supplied by the form data.
|
|
"""
|
|
return self.initial['file']
|
|
|
|
|
|
class VersionDeleteForm(forms.ModelForm):
|
|
class Meta:
|
|
model = extensions.models.Version
|
|
fields = []
|