Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d6254ac
Added required method create_instance_from_form and create_cropped_in…
dzhuang May 18, 2022
a5b11dc
Allow customized crop_url and image_url in image model.
dzhuang May 18, 2022
452d35b
Fix deprecationWarning: find_element_by_* with find_element() in sele…
dzhuang May 18, 2022
119fb13
Allow serialize_extra method to do more serialization of image objects.
dzhuang May 18, 2022
df1fe24
Added demo_custom app
dzhuang May 19, 2022
7a33b30
Format text with f-strings and format.
dzhuang May 19, 2022
12fdb86
Optimize templates code styles.
dzhuang May 19, 2022
8a29273
Minimize fontawesome icons.
dzhuang May 19, 2022
b3a6cda
Fix tests dependencies.
dzhuang May 20, 2022
b7afd5f
Added Selenium tests for demo_custom
dzhuang May 20, 2022
8f785aa
Added GalleryAdminFormMixin in admin of demo app to prepend bootstrap…
dzhuang May 20, 2022
ef4931c
Generate galleryfield-ui.js
dzhuang May 20, 2022
8e0f375
Added jquery, bootsrap locations in DJANGO_GALLERY_FIELD_CONFIG["asse…
dzhuang May 20, 2022
6c502bb
Added bootstrap statics in admin.
dzhuang May 21, 2022
ced9182
Use temp folder as storage in tests.
dzhuang May 21, 2022
b85a314
Added bootstrap version settings and checks. Fixed Bootstrap version …
dzhuang May 21, 2022
e1923af
Fix temp folder in tests: mkdtemp.
dzhuang May 21, 2022
e1288ed
Fix render of previewMaxWidth and previewMaxHeight
dzhuang May 21, 2022
2031ef1
Added tests for bootstrap_version checks.
dzhuang May 21, 2022
1ccc7e7
Update demo_custom and added tests for removing CustomImage instances…
dzhuang May 21, 2022
e4c8cc5
Fix tests in Django 3.1
dzhuang May 21, 2022
731720e
Deprecate glyphicons.
dzhuang May 22, 2022
21cfe4e
Isolate get_cookie method from jquery.fileupload-ui-gallery-widget.js
dzhuang May 22, 2022
bd8b044
Update bundle-source.js and jquery.fileupload-ui-gallery-widget.js
dzhuang May 22, 2022
1c40682
Drop jQuery-UI in favor of SortableJS for sorting images.
dzhuang May 22, 2022
2fb438f
Fix delay on SortableJS options.
dzhuang May 23, 2022
52a7b01
temp icons
dzhuang May 24, 2022
2296f61
Switch to material design icons from fontawesome.
dzhuang May 27, 2022
27b1be6
Use new build script via subset-iconfont.
dzhuang Jun 3, 2022
9181956
Merge pull request #27 from dzhuang/icons
dzhuang Jul 11, 2022
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
1 change: 1 addition & 0 deletions demo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = "demo.apps.DemoConfig"
15 changes: 14 additions & 1 deletion demo/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
from django import forms
from django.contrib import admin

from demo.models import DemoGallery
from galleryfield.mixins import GalleryFormMediaMixin
from galleryfield.models import BuiltInGalleryImage

admin.site.register(DemoGallery)

class DemoGalleryAdminForm(GalleryFormMediaMixin, forms.ModelForm):
class Meta:
model = DemoGallery
exclude = ()


class DemoGalleryAdmin(admin.ModelAdmin):
form = DemoGalleryAdminForm


admin.site.register(DemoGallery, DemoGalleryAdmin)
admin.site.register(BuiltInGalleryImage)
7 changes: 0 additions & 7 deletions demo/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ class Migration(migrations.Migration):
]

operations = [
migrations.CreateModel(
name='MyImageModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('photo', models.ImageField(upload_to='')),
],
),
migrations.CreateModel(
name='DemoGallery',
fields=[
Expand Down
3 changes: 3 additions & 0 deletions demo/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ django<=3.3
django-crispy-forms
sorl-thumbnail
pillow

# For serving permissions associated files
django-sendfile2
18 changes: 18 additions & 0 deletions demo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
'galleryfield',
'sorl.thumbnail',
'demo',
'demo_custom'
]

MIDDLEWARE = [
Expand Down Expand Up @@ -131,3 +132,20 @@
# }

LOGIN_URL = "/admin/login/"

SENDFILE_URL = "/protected"

# SENDFILE_BACKEND = "django_sendfile.backends.nginx" # production
SENDFILE_BACKEND = "django_sendfile.backends.development"

SENDFILE_ROOT = BASE_DIR / 'protected'

DJANGO_GALLERY_FIELD_CONFIG = {
# "bootstrap_version": 3
# "assets": {
# "bootstrap_css": "https://cdnjs.cloudflare.com/ajax/libs/"
# "twitter-bootstrap/3.4.1/css/bootstrap.min.css",
# "bootstrap_js": "https://cdnjs.cloudflare.com/ajax/libs/"
# "twitter-bootstrap/3.4.1/css/bootstrap.min.js"
# }
}
9 changes: 8 additions & 1 deletion demo/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
<head>

<title>Django Gallery</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8">

{# todo: Add docs about dependencies of the widget #}

{% block header %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css" integrity="sha512-mG7Xo6XLlQ13JGPQLgLxI7bz8QlErrsE9rYQDRgF+6AlQHm9Tn5bh/vaIKxBmM9mULPC6yizAhEmKyGgNHCIvg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
Expand All @@ -31,6 +33,11 @@
<ul class="nav navbar-nav">
<li><a href="/">{% trans "Create New" %}</a></li>
</ul>

<ul class="nav navbar-nav">
<li><a href="/custom">{% trans "Create New (Customized)" %}</a></li>
</ul>

{% if update_view_url %}
<ul class="nav navbar-nav">
<li><a href="{{ update_view_url }}">{% trans "Update" %}</a></li>
Expand Down
2 changes: 2 additions & 0 deletions demo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@
path('admin/', admin.site.urls),
]

urlpatterns += [path(r"custom/", include("demo_custom.urls"))]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
1 change: 1 addition & 0 deletions demo_custom/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = "demo_custom.apps.DemoCustomConfig"
19 changes: 19 additions & 0 deletions demo_custom/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django import forms
from django.contrib import admin

from demo_custom.models import CustomDemoGallery, CustomImage
from galleryfield.mixins import GalleryFormMediaMixin


class CustomDemoGalleryAdminForm(GalleryFormMediaMixin, forms.ModelForm):
class Meta:
model = CustomDemoGallery
exclude = ()


class CustomDemoGalleryAdmin(admin.ModelAdmin):
form = CustomDemoGalleryAdminForm


admin.site.register(CustomDemoGallery, CustomDemoGalleryAdmin)
admin.site.register(CustomImage)
9 changes: 9 additions & 0 deletions demo_custom/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.apps import AppConfig


class DemoCustomConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'demo_custom'

def ready(self):
import demo_custom.receivers # noqa
25 changes: 25 additions & 0 deletions demo_custom/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms

from demo_custom.models import CustomDemoGallery


class CustomGalleryForm(forms.ModelForm):
class Meta:
model = CustomDemoGallery
fields = ["images"]

# Remove label
labels = {'images': ""}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["images"].widget.download_template = (
"demo_custom/custom_download_template.html")

self.helper = FormHelper(self)
self.helper.layout.append(
Submit("Submit", "submit",
css_class="gallery-widget-submit-button"))
69 changes: 69 additions & 0 deletions demo_custom/image_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from django_sendfile import sendfile

from demo_custom.models import CustomImage
from galleryfield.image_views import (ImageCreateView, ImageCropView,
ImageListView)


class CustomImageCreateView(LoginRequiredMixin, ImageCreateView):
target_model = "demo_custom.CustomImage"
disable_server_side_crop = False

def create_instance_from_form(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()


class CustomImageListView(LoginRequiredMixin, ImageListView):
target_model = "demo_custom.CustomImage"
disable_server_side_crop = False

def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user=self.request.user)


class CustomImageCropView(LoginRequiredMixin, ImageCropView):
target_model = "demo_custom.CustomImage"
disable_server_side_crop = False

def create_cropped_instance_from_form(self, form):
self.object: CustomImage = form.save(commit=False)
self.object.save()


# {{{ sendfile


@login_required
def image_download(request, **kwargs):
user_id = kwargs["user_id"] # noqa
photo_id = kwargs["image_id"]
file_name = kwargs["file_name"] # noqa

from demo_custom.models import CustomImage

download_object = get_object_or_404(CustomImage, pk=photo_id)

privilege = False

if request.user.is_staff:
privilege = True

return _auth_download(request, download_object, privilege)


@login_required
def _auth_download(request, download_object, privilege=False):
if request.user == download_object.user or privilege:
return sendfile(request, download_object.photo.path)

raise PermissionDenied(_("may not view other user's image"))

# }}}
39 changes: 39 additions & 0 deletions demo_custom/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 3.2.13 on 2022-05-21 11:59

import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models

import demo_custom.models
import galleryfield.fields


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='CustomDemoGallery',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('images', galleryfield.fields.GalleryField(blank=True, null=True, target_model='demo_custom.CustomImage', verbose_name='Photos')),
('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Creator')),
],
),
migrations.CreateModel(
name='CustomImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('photo', models.ImageField(storage=demo_custom.models.UserImageStorage(), upload_to=demo_custom.models.user_img_path, verbose_name='Photo')),
('added_at', models.DateTimeField(default=django.utils.timezone.now)),
('gallery', models.ManyToManyField(blank=True, related_name='related_images', to='demo_custom.CustomDemoGallery', verbose_name='Gallery belongs to')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')),
],
),
]
Empty file.
82 changes: 82 additions & 0 deletions demo_custom/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.urls import reverse
from django.utils.deconstruct import deconstructible
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _

from galleryfield.fields import GalleryField


class CustomDemoGallery(models.Model):
"""
The gallery model used in demo_custom.

"""
images = GalleryField(
verbose_name=_('Photos'), target_model='demo_custom.CustomImage',
blank=True, null=True)
creator = models.ForeignKey(
settings.AUTH_USER_MODEL, null=True,
verbose_name=_('Creator'), on_delete=models.CASCADE)

def get_absolute_url(self):
return reverse('custom-gallery-update', kwargs={'pk': self.pk})


@deconstructible
class UserImageStorage(FileSystemStorage):
def __init__(self):
super().__init__(location=settings.SENDFILE_ROOT)


storage = UserImageStorage()


def user_img_path(instance, filename):
return "user_images/user_{}/{}".format(instance.user_id, filename)


class CustomImage(models.Model):
photo = models.ImageField(
upload_to=user_img_path, storage=storage, verbose_name=_("Photo"))

user = models.ForeignKey(
settings.AUTH_USER_MODEL, null=False, blank=False,
verbose_name=_('Owner'), on_delete=models.CASCADE)

added_at = models.DateTimeField(default=now)

# This is used to determine where the image is an orphan
gallery = models.ManyToManyField(
CustomDemoGallery, blank=True,
verbose_name=_("Gallery belongs to"), related_name="related_images")

def get_image_url(self):
import os
file_name = os.path.basename(self.photo.path)

return reverse(
"image_download", args=[
self.user_id,
self.pk,
file_name
])

def get_crop_url(self):
return reverse(
"customimage-crop", args=[
self.pk
])

def serialize_extra(self, request):
from django.utils import formats

return {
"added_datetime": formats.date_format(
self.added_at, "SHORT_DATETIME_FORMAT")}

@classmethod
def get_image_field(cls):
return cls._meta.get_field("photo")
Loading