added folders

This commit is contained in:
Alexander Karpov 2023-04-24 01:07:11 +03:00
parent 24ef17bde3
commit 83ea171886
14 changed files with 628 additions and 85 deletions

View File

@ -0,0 +1,223 @@
# Generated by Django 4.2 on 2023-04-22 09:07
import akarpov.files.services.files
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
class Migration(migrations.Migration):
replaces = [
("files", "0001_initial"),
("files", "0002_alter_basefile_options_alter_folder_options_and_more"),
("files", "0003_basefile_short_link_folder_short_link"),
("files", "0004_alter_basefile_short_link_alter_folder_short_link"),
("files", "0005_alter_basefile_description_alter_basefile_folder_and_more"),
("files", "0006_alter_basefile_slug"),
("files", "0007_file_alter_folder_parent_delete_basefile_file_folder_and_more"),
("files", "0008_file_completed_on_file_created_on_file_filename_and_more"),
("files", "0009_remove_file_completed_on_remove_file_created_on_and_more"),
("files", "0010_fileintrash"),
("files", "0011_file_file_type_alter_file_description_and_more"),
("files", "0012_alter_file_options_alter_file_file"),
("files", "0013_alter_file_options"),
("files", "0014_alter_fileintrash_file"),
("files", "0015_fileintrash_name"),
("files", "0016_alter_file_file_type_alter_file_name"),
]
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("shortener", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="Folder",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
("name", models.CharField(max_length=100)),
("slug", models.SlugField(blank=True, max_length=20)),
(
"parent",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="files.folder",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="files_folders",
to=settings.AUTH_USER_MODEL,
),
),
(
"short_link",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="FileInTrash",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"file",
models.FileField(
upload_to=akarpov.files.services.files.trash_file_upload
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="trash_files",
to=settings.AUTH_USER_MODEL,
),
),
("name", models.CharField(blank=True, max_length=200)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="File",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("slug", models.SlugField(blank=True, max_length=20, unique=True)),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
("name", models.CharField(blank=True, max_length=255, null=True)),
("description", models.TextField(blank=True, null=True)),
("private", models.BooleanField(default=True)),
("preview", models.FileField(blank=True, upload_to="file/previews/")),
(
"file",
models.FileField(
upload_to=akarpov.files.services.files.user_unique_file_upload
),
),
(
"folder",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="files",
to="files.folder",
),
),
(
"short_link",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="files",
to=settings.AUTH_USER_MODEL,
),
),
("file_type", models.CharField(blank=True, max_length=255, null=True)),
],
options={
"abstract": False,
"ordering": ["-modified"],
},
),
]

View File

@ -0,0 +1,106 @@
# Generated by Django 4.2 on 2023-04-22 09:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("files", "0001_squashed_0016_alter_file_file_type_alter_file_name"),
]
operations = [
migrations.AlterModelOptions(
name="folder",
options={"base_manager_name": "objects"},
),
migrations.RenameField(
model_name="file",
old_name="file",
new_name="file_obj",
),
migrations.RemoveField(
model_name="file",
name="folder",
),
migrations.RemoveField(
model_name="file",
name="id",
),
migrations.RemoveField(
model_name="folder",
name="id",
),
migrations.RemoveField(
model_name="folder",
name="parent",
),
migrations.CreateModel(
name="BaseFileItem",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"parent",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="files.basefileitem",
),
),
(
"polymorphic_ctype",
models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="polymorphic_%(app_label)s.%(class)s_set+",
to="contenttypes.contenttype",
),
),
],
options={
"abstract": False,
"base_manager_name": "objects",
},
),
migrations.AddField(
model_name="file",
name="basefileitem_ptr",
field=models.OneToOneField(
auto_created=True,
default=1,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="files.basefileitem",
),
preserve_default=False,
),
migrations.AddField(
model_name="folder",
name="basefileitem_ptr",
field=models.OneToOneField(
auto_created=True,
default=2,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="files.basefileitem",
),
preserve_default=False,
),
]

View File

@ -0,0 +1,49 @@
# Generated by Django 4.2 on 2023-04-22 09:21
from django.db import migrations
import django.utils.timezone
import model_utils.fields
class Migration(migrations.Migration):
dependencies = [
("files", "0017_alter_folder_options_rename_file_file_file_obj_and_more"),
]
operations = [
migrations.RemoveField(
model_name="file",
name="created",
),
migrations.RemoveField(
model_name="file",
name="modified",
),
migrations.RemoveField(
model_name="folder",
name="created",
),
migrations.RemoveField(
model_name="folder",
name="modified",
),
migrations.AddField(
model_name="basefileitem",
name="created",
field=model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
migrations.AddField(
model_name="basefileitem",
name="modified",
field=model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="updated",
),
),
]

View File

@ -0,0 +1,54 @@
# Generated by Django 4.2 on 2023-04-22 09:24
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("files", "0018_remove_file_created_remove_file_modified_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="basefileitem",
options={"ordering": ["-modified"]},
),
migrations.AlterModelOptions(
name="file",
options={"base_manager_name": "objects"},
),
migrations.RemoveField(
model_name="file",
name="user",
),
migrations.RemoveField(
model_name="folder",
name="user",
),
migrations.AddField(
model_name="basefileitem",
name="user",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
related_name="files",
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
migrations.AlterField(
model_name="basefileitem",
name="parent",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="files.folder",
),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2023-04-22 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("files", "0019_alter_basefileitem_options_alter_file_options_and_more"),
]
operations = [
migrations.AddField(
model_name="folder",
name="size",
field=models.IntegerField(default=0),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2 on 2023-04-22 09:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("files", "0020_folder_size"),
]
operations = [
migrations.AddField(
model_name="folder",
name="amount",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="folder",
name="private",
field=models.BooleanField(default=True),
),
]

View File

@ -7,28 +7,60 @@
CharField, CharField,
FileField, FileField,
ForeignKey, ForeignKey,
IntegerField,
SlugField, SlugField,
TextField, TextField,
) )
from django.urls import reverse from django.urls import reverse
from model_utils.fields import AutoCreatedField, AutoLastModifiedField
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from polymorphic.models import PolymorphicModel
from akarpov.files.services.files import trash_file_upload, user_unique_file_upload from akarpov.files.services.files import trash_file_upload, user_unique_file_upload
from akarpov.tools.shortener.models import ShortLink from akarpov.tools.shortener.models import ShortLink
class File(TimeStampedModel, ShortLink): class BaseFileItem(PolymorphicModel):
parent = ForeignKey(
to="files.Folder",
null=True,
blank=True,
on_delete=CASCADE,
related_name="children",
)
user = ForeignKey("users.User", related_name="files", on_delete=CASCADE)
created = AutoCreatedField("created")
modified = AutoLastModifiedField("updated")
class Meta:
ordering = ["-modified"]
@property
def is_file(self):
return type(self) is File
def get_folder_chain(self):
folders = [self]
obj = self
while obj.parent:
folders.append(obj.parent)
obj = obj.parent
return folders
def save(self, *args, **kwargs):
update_fields = kwargs.get("update_fields", None)
if update_fields:
kwargs["update_fields"] = set(update_fields).union({"modified"})
super().save(*args, **kwargs)
class File(BaseFileItem, TimeStampedModel, ShortLink):
"""model to store user's files""" """model to store user's files"""
private = BooleanField(default=True) private = BooleanField(default=True)
user = ForeignKey("users.User", related_name="files", on_delete=CASCADE)
folder = ForeignKey(
"files.Folder", related_name="files", blank=True, null=True, on_delete=CASCADE
)
preview = FileField(blank=True, upload_to="file/previews/") preview = FileField(blank=True, upload_to="file/previews/")
file = FileField(blank=False, upload_to=user_unique_file_upload) file_obj = FileField(blank=False, upload_to=user_unique_file_upload)
# meta # meta
name = CharField(max_length=255, null=True, blank=True) name = CharField(max_length=255, null=True, blank=True)
@ -39,6 +71,10 @@ class File(TimeStampedModel, ShortLink):
def file_name(self): def file_name(self):
return self.file.path.split("/")[-1] return self.file.path.split("/")[-1]
@property
def file(self):
return self.file_obj
@property @property
def file_image_url(self): def file_image_url(self):
if self.preview: if self.preview:
@ -61,27 +97,24 @@ def get_absolute_url(self):
def __str__(self): def __str__(self):
return f"file: {self.name}" return f"file: {self.name}"
class Meta:
ordering = ["-modified"] class Folder(BaseFileItem, ShortLink):
name = CharField(max_length=100)
slug = SlugField(max_length=20, blank=True)
private = BooleanField(default=True)
# meta
size = IntegerField(default=0)
amount = IntegerField(default=0)
def get_absolute_url(self):
return reverse("files:folder", kwargs={"slug": self.slug})
def __str__(self):
return f"folder: {self.name}"
class FileInTrash(TimeStampedModel): class FileInTrash(TimeStampedModel):
name = CharField(max_length=200, blank=True) name = CharField(max_length=200, blank=True)
user = ForeignKey("users.User", related_name="trash_files", on_delete=CASCADE) user = ForeignKey("users.User", related_name="trash_files", on_delete=CASCADE)
file = FileField(blank=False, upload_to=trash_file_upload) file = FileField(blank=False, upload_to=trash_file_upload)
class Folder(TimeStampedModel, ShortLink):
name = CharField(max_length=100)
slug = SlugField(max_length=20, blank=True)
user = ForeignKey("users.User", related_name="files_folders", on_delete=CASCADE)
parent = ForeignKey(
"self", null=True, blank=True, related_name="children", on_delete=CASCADE
)
def get_absolute_url(self):
return reverse("files:folder", kwargs={"slug": self.slug})
def __str__(self):
return f"file: {self.name}"

View File

@ -65,18 +65,18 @@ def view(file: File) -> (str, str):
def meta(file: File): def meta(file: File):
descr = "" description = ""
i = 0 i = 0
with file.file.open("r") as f: with file.file.open("r") as f:
lines = f.readlines() lines = f.readlines()
for line in lines: for line in lines:
if i == 0: if i == 0:
descr += line + "\n" description += line + "\n"
else: else:
descr += line + " " description += line + " "
i += 1 i += 1
if i > 20: if i > 20:
descr += "..." description += "..."
break break
url = file.get_absolute_url() url = file.get_absolute_url()
section = "" section = ""
@ -88,7 +88,7 @@ def meta(file: File):
<meta property="og:title" content="{file.name}"> <meta property="og:title" content="{file.name}">
<meta property="og:url" content="{url}"> <meta property="og:url" content="{url}">
<meta property="og:image" content=""> <meta property="og:image" content="">
<meta property="og:description" content="{html.escape(descr)}"> <meta property="og:description" content="{html.escape(description)}">
<meta property="article:author" content="{file.user.username}"> <meta property="article:author" content="{file.user.username}">
<meta property="article:section" content="{section}"> <meta property="article:section" content="{section}">
<meta property="article:published_time" content="{file.created}"> <meta property="article:published_time" content="{file.created}">

View File

@ -47,36 +47,3 @@ def view(file: File) -> (str, str):
""" """
return static, content return static, content
def meta(file: File):
descr = ""
i = 0
with file.file.open("r") as f:
lines = f.readlines()
for line in lines:
if i == 0:
descr += line + "\n"
else:
descr += line + " "
i += 1
if i > 20:
descr += "..."
break
url = file.get_absolute_url()
section = ""
if file.file_type:
section = file.file_type.split("/")[0]
meat_f = f"""
<meta property="og:type" content="article">
<meta property="og:title" content="{file.name}">
<meta property="og:url" content="{url}">
<meta property="og:image" content="">
<meta property="og:description" content="{html.escape(descr)}">
<meta property="article:author" content="{file.user.username}">
<meta property="article:section" content="{section}">
<meta property="article:published_time" content="{file.created}">
<meta property="article:modified_time" content="{file.modified}">
"""
return meat_f

View File

@ -1,7 +1,6 @@
from django.urls import path from django.urls import path
from akarpov.files.views import ( from akarpov.files.views import (
ChunkedUploadDemo,
MyChunkedUploadCompleteView, MyChunkedUploadCompleteView,
MyChunkedUploadView, MyChunkedUploadView,
TopFolderView, TopFolderView,
@ -14,12 +13,16 @@
app_name = "files" app_name = "files"
urlpatterns = [ urlpatterns = [
path("", TopFolderView.as_view(), name="main"), path("", TopFolderView.as_view(), name="main"),
path("upload", ChunkedUploadDemo.as_view(), name="chunked_upload"),
path( path(
"api/chunked_upload_complete/", "api/chunked_upload_complete/",
MyChunkedUploadCompleteView.as_view(), MyChunkedUploadCompleteView.as_view(),
name="api_chunked_upload_complete", name="api_chunked_upload_complete",
), ),
path(
"api/chunked_upload_complete/<str:slug>",
MyChunkedUploadCompleteView.as_view(),
name="api_chunked_upload_complete_folder",
),
path( path(
"api/chunked_upload/", MyChunkedUploadView.as_view(), name="api_chunked_upload" "api/chunked_upload/", MyChunkedUploadView.as_view(), name="api_chunked_upload"
), ),

View File

@ -6,6 +6,7 @@
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now
from django.views.generic import DetailView, ListView, RedirectView, UpdateView from django.views.generic import DetailView, ListView, RedirectView, UpdateView
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
@ -16,7 +17,7 @@
ChunkedUploadView, ChunkedUploadView,
) )
from akarpov.files.forms import FileForm from akarpov.files.forms import FileForm
from akarpov.files.models import File, Folder from akarpov.files.models import BaseFileItem, File, Folder
from akarpov.files.previews import extensions, meta, meta_extensions, previews from akarpov.files.previews import extensions, meta, meta_extensions, previews
from akarpov.files.services.preview import get_base_meta from akarpov.files.services.preview import get_base_meta
@ -26,17 +27,43 @@
class TopFolderView(LoginRequiredMixin, ListView): class TopFolderView(LoginRequiredMixin, ListView):
template_name = "files/list.html" template_name = "files/list.html"
paginate_by = 19 paginate_by = 19
model = File model = BaseFileItem
def get_queryset(self):
return File.objects.filter(user=self.request.user, folder__isnull=True)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
contex = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
contex["folders"] = Folder.objects.filter( context["folder_slug"] = None
user=self.request.user, parent__isnull=True return context
)
return contex def get_queryset(self):
return BaseFileItem.objects.filter(user=self.request.user, parent__isnull=True)
class FileFolderView(ListView):
template_name = "files/folder.html"
model = BaseFileItem
paginate_by = 39
slug_field = "slug"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.object = None
def get_context_data(self, **kwargs):
folder = self.get_object()
context = super().get_context_data(**kwargs)
context["folder_slug"] = folder.slug
return context
def get_object(self, *args):
obj = get_object_or_404(Folder, slug=self.kwargs["slug"])
if self.object:
return self.object
self.object = obj
return obj
def get_queryset(self):
folder = self.get_object()
return BaseFileItem.objects.filter(parent=folder)
class FileUpdateView(LoginRequiredMixin, UpdateView): class FileUpdateView(LoginRequiredMixin, UpdateView):
@ -131,12 +158,6 @@ def get_redirect_url(self, *args, **kwargs):
delete_file_view = DeleteFileView.as_view() delete_file_view = DeleteFileView.as_view()
class FileFolderView(DetailView):
template_name = "files/folder.html"
model = Folder
slug_field = "slug"
folder_view = FileFolderView.as_view() folder_view = FileFolderView.as_view()
@ -169,17 +190,42 @@ def check_permissions(self, request):
) )
def on_completion(self, uploaded_file, request): def on_completion(self, uploaded_file, request):
if uploaded_file.size <= request.user.left_file_upload: folder = None
prepared = True
if "slug" in self.kwargs and self.kwargs["slug"]:
try:
folder = Folder.objects.get(slug=self.kwargs["slug"])
if folder.user != self.request.user:
self.message = {
"message": "You can't upload to this folder",
"status": False,
}
prepared = False
except Folder.DoesNotExist:
self.message = {
"message": "Folder doesn't exist",
"status": False,
}
prepared = False
if prepared and uploaded_file.size <= request.user.left_file_upload:
f = File.objects.create( f = File.objects.create(
user=request.user, file=uploaded_file, name=uploaded_file.name user=request.user,
file_obj=uploaded_file,
name=uploaded_file.name,
parent=folder,
) )
if folder:
folder.modified = now()
folder.size += uploaded_file.size
folder.amount += 1
folder.save()
request.user.left_file_upload -= uploaded_file.size request.user.left_file_upload -= uploaded_file.size
request.user.save() request.user.save()
self.message = { self.message = {
"message": f"File {f.file.name.split('/')[-1]} successfully uploaded", "message": f"File {f.file.name.split('/')[-1]} successfully uploaded",
"status": True, "status": True,
} }
else: elif prepared:
self.message = { self.message = {
"message": "File is too large, please increase disk space", "message": "File is too large, please increase disk space",
"status": False, "status": False,

View File

@ -0,0 +1 @@
{% extends 'files/list.html' %}

View File

@ -37,6 +37,7 @@
{{ folder.name }} {{ folder.name }}
{% endfor %} {% endfor %}
<div class="row justify-content-center"> <div class="row justify-content-center">
{% if request.user.is_authenticated %}
<div class="col-lg-2 col-xxl-2 col-md-4 col-sm-6 col-xs-12 mb-3 m-3 d-flex align-items-stretch card"> <div class="col-lg-2 col-xxl-2 col-md-4 col-sm-6 col-xs-12 mb-3 m-3 d-flex align-items-stretch card">
<div class="card-body d-flex flex-column justify-content-center align-items-center"> <div class="card-body d-flex flex-column justify-content-center align-items-center">
{% csrf_token %} {% csrf_token %}
@ -64,8 +65,10 @@
<div id="messages"></div> <div id="messages"></div>
</div> </div>
</div> </div>
{% for file in file_list %} {% endif %}
{% for file in basefileitem_list %}
<div class="col-lg-2 col-xxl-2 col-md-4 col-sm-6 col-xs-12 mb-3 m-3 d-flex align-items-stretch card"> <div class="col-lg-2 col-xxl-2 col-md-4 col-sm-6 col-xs-12 mb-3 m-3 d-flex align-items-stretch card">
{% if file.is_file %}
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<h5 class="card-title">{{ file.name }}</h5> <h5 class="card-title">{{ file.name }}</h5>
<p class="card-text mb-4"><small class="text-muted">{{ file.file_size | filesizeformat }}</small></p> <p class="card-text mb-4"><small class="text-muted">{{ file.file_size | filesizeformat }}</small></p>
@ -76,6 +79,19 @@
<a href="{% url 'files:view' file.slug %}" class="stretched-link"></a> <a href="{% url 'files:view' file.slug %}" class="stretched-link"></a>
<p class="card-text mb-4 mt-2 ms-3"><small class="text-muted">{{ file.modified | naturaltime }}</small></p> <p class="card-text mb-4 mt-2 ms-3"><small class="text-muted">{{ file.modified | naturaltime }}</small></p>
</div> </div>
{% else %}
<div class="card-body d-flex flex-column">
<h5 class="card-title">{{ file.name }}</h5>
<p class="card-text mb-4"><small class="text-muted">{{ file.size | filesizeformat }}, {{ file.amount }} {% if file.amount == 1 %} item {% else %} items {% endif %}</small></p>
<div class="align-self-stretch align-items-center justify-content-center d-flex flex-column fill-height controlsdiv">
<i class="bi bi-folder"></i>
</div>
<a href="{% url 'files:folder' file.slug %}" class="stretched-link"></a>
<p class="card-text mb-4 mt-2 ms-3"><small class="text-muted">{{ file.modified | naturaltime }}</small></p>
</div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -207,7 +223,11 @@
sel.text(progress + "%"); sel.text(progress + "%");
$.ajax({ $.ajax({
type: "POST", type: "POST",
{% if folder_slug %}
url: "{% url 'files:api_chunked_upload_complete_folder' slug=folder_slug %}",
{% else %}
url: "{% url 'files:api_chunked_upload_complete' %}", url: "{% url 'files:api_chunked_upload_complete' %}",
{% endif %}
data: { data: {
csrfmiddlewaretoken: csrf, csrfmiddlewaretoken: csrf,
upload_id: data.result.upload_id, upload_id: data.result.upload_id,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB