├── tests
├── __init__.py
├── logo.png
├── requirements.txt
├── settings.py
└── tests_models.py
├── cmsplugin_gallery
├── settings.py
├── migrations
│ ├── __init__.py
│ ├── 0007_remove_image_src.py
│ ├── 0004_auto_20170118_0753.py
│ ├── 0003_auto_20170104_1122.py
│ ├── 0002_auto_20161223_1005.py
│ ├── 0006_auto_20180411_1046.py
│ ├── 0005_auto_20180411_1046.py
│ └── 0001_initial.py
├── __init__.py
├── locale
│ ├── en
│ │ └── LC_MESSAGES
│ │ │ ├── django.mo
│ │ │ └── django.po
│ ├── pl
│ │ └── LC_MESSAGES
│ │ │ ├── django.mo
│ │ │ └── django.po
│ └── ru
│ │ └── LC_MESSAGES
│ │ ├── django.mo
│ │ └── django.po
├── admin.py
├── templates
│ └── cmsplugin_gallery
│ │ └── gallery.html
├── forms.py
├── cms_plugins.py
├── utils.py
└── models.py
├── setup.cfg
├── MANIFEST.in
├── .gitignore
├── AUTHORS
├── .coveragerc
├── .travis.yml
├── tox.ini
├── .github
└── workflows
│ └── test.yml
├── History.md
├── LICENSE
├── Makefile
├── setup.py
└── README.md
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/settings.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '2.0.3'
2 |
--------------------------------------------------------------------------------
/tests/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piotrkilczuk/cmsplugin_gallery/HEAD/tests/logo.png
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | universal = false
3 |
4 | [options]
5 | install_requires =
6 | Django>=2.2,<4.0
7 | python_requires = >=3.7
8 |
--------------------------------------------------------------------------------
/tests/requirements.txt:
--------------------------------------------------------------------------------
1 | # requirements from setup.py
2 | # other requirements
3 | django-app-helper==3.0.1
4 | tox
5 | coverage
6 | Django==3.2
7 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md
2 | include History.md
3 | recursive-include cmsplugin_gallery/locale *
4 | recursive-include cmsplugin_gallery/templates *
5 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/en/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piotrkilczuk/cmsplugin_gallery/HEAD/cmsplugin_gallery/locale/en/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/pl/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piotrkilczuk/cmsplugin_gallery/HEAD/cmsplugin_gallery/locale/pl/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/ru/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piotrkilczuk/cmsplugin_gallery/HEAD/cmsplugin_gallery/locale/ru/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | build/*
4 | .idea
5 | .project
6 | .pydevproject
7 | .settings
8 | .coverage
9 | *.egg-info
10 | dist
11 | .eggs
12 | tests/_env
13 | tests/_oldenv
14 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Ales Zabala Alava
2 | Dries Desmet
3 | eserge
4 | Lalo Martins
5 | Piotr Kilczuk
6 | Vinit Kumar
7 |
8 | https://github.com/centralniak/cmsplugin_gallery/contributors
9 |
10 | Thanks!
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = True
3 | source = cmsplugin_gallery
4 | omit =
5 | migrations/*
6 | tests/*
7 |
8 | [report]
9 | exclude_lines =
10 | pragma: no cover
11 | def __repr__
12 | if self.debug:
13 | if settings.DEBUG
14 | raise AssertionError
15 | raise NotImplementedError
16 | if 0:
17 | if __name__ == .__main__.:
18 | ignore_errors = True
19 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0007_remove_image_src.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 1.11.12 on 2018-04-11 05:21
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('cmsplugin_gallery', '0006_auto_20180411_1046'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='image',
15 | name='src',
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.4"
5 | - "3.5"
6 | - "3.6"
7 |
8 | sudo: false
9 |
10 | env:
11 | - TOX_ENV=py27-latest
12 | - TOX_ENV=py35-latest
13 | # Django 1.8
14 | - TOX_ENV=py27-dj18-cms34
15 | - TOX_ENV=py27-dj18-cms33
16 | - TOX_ENV=py35-dj18-cms34
17 | # Django 1.9
18 | - TOX_ENV=py27-dj19-cms34
19 | - TOX_ENV=py27-dj19-cms33
20 | - TOX_ENV=py35-dj19-cms34
21 |
22 | install:
23 | - pip install tox coverage
24 |
25 | script:
26 | - tox -e $TOX_ENV
27 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/admin.py:
--------------------------------------------------------------------------------
1 | from inline_ordering.admin import OrderableStackedInline
2 | from . import forms
3 | from . import models
4 |
5 |
6 | class ImageInline(OrderableStackedInline):
7 |
8 | model = models.Image
9 |
10 | def formfield_for_dbfield(self, db_field, **kwargs):
11 | if db_field.name == 'image_src':
12 | kwargs.pop('request', None)
13 | kwargs['widget'] = forms.AdminImageWidget
14 | return db_field.formfield(**kwargs)
15 | return super().\
16 | formfield_for_dbfield(db_field, **kwargs)
17 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist =
3 | py{35,27}-latest
4 | py{35,27}-dj18-cms{34,33}
5 | py{35,27}-dj19-cms{34,33}
6 |
7 | skip_missing_interpreters=True
8 |
9 |
10 | [testenv]
11 | deps =
12 | -r{toxinidir}/tests/requirements.txt
13 | dj18: Django>=1.8,<1.9
14 | dj19: Django>=1.9,<1.10
15 | latest: django-cms
16 | cms33: django-cms>=3.3,<3.4
17 | cms34: django-cms>=3.4,<3.5
18 | commands =
19 | {envpython} --version
20 | {env:COMMAND:coverage} erase
21 | {env:COMMAND:coverage} run setup.py test
22 | {env:COMMAND:coverage} report
23 |
24 | [flake8]
25 | max-line-length = 120
26 | exclude = */docs/*,*/migrations/*
27 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/templates/cmsplugin_gallery/gallery.html:
--------------------------------------------------------------------------------
1 | {% load thumbnail %}
2 |
3 |
4 | {% for image in images %}
5 |
6 |

7 |
8 | {% endfor %}
9 |
10 |
11 | {% for image in images %}
12 |
13 |

14 |
15 | {% endfor %}
16 |
17 |
20 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/forms.py:
--------------------------------------------------------------------------------
1 | from django.contrib.admin.widgets import AdminFileWidget
2 | from django.utils.translation import gettext as _
3 | from django.utils.safestring import mark_safe
4 |
5 |
6 | class AdminImageWidget(AdminFileWidget):
7 | def render(self, name, value, attrs=None):
8 | output = []
9 | if value and getattr(value, "url", None):
10 | image_url = value.url
11 | file_name = str(value)
12 | output.append(' 
%s ' % \
13 | (f'{str(image_url)}', f'{str(image_url)}', f'{str(file_name)}', _('Change:')))
14 | output.append(super(AdminFileWidget, self).render(name, value, attrs))
15 | return mark_safe(''.join(output))
16 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0004_auto_20170118_0753.py:
--------------------------------------------------------------------------------
1 | from django.db import migrations, models
2 |
3 |
4 | class Migration(migrations.Migration):
5 |
6 | dependencies = [
7 | ('cmsplugin_gallery', '0003_auto_20170104_1122'),
8 | ]
9 |
10 | operations = [
11 | migrations.AlterField(
12 | model_name='image',
13 | name='alt',
14 | field=models.CharField(max_length=255, verbose_name='Alt text', blank=True),
15 | ),
16 | migrations.AlterField(
17 | model_name='image',
18 | name='crop',
19 | field=models.CharField(default=b'0%', max_length=10, verbose_name=b'Positionering', choices=[(b'0%', b'0%'), (b'25%', b'25%'), (b'50%', b'50%'), (b'75%', b'75%'), (b'100%', b'100%')]),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/tests/settings.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | HELPER_SETTINGS = {
5 | 'INSTALLED_APPS': [
6 | 'easy_thumbnails',
7 | 'filer',
8 | 'mptt',
9 | ],
10 | 'ALLOWED_HOSTS': ['localhost'],
11 | 'CMS_LANGUAGES': {
12 | 1: [{
13 | 'code': 'en',
14 | 'name': 'English',
15 | }]
16 | },
17 | 'LANGUAGE_CODE': 'en',
18 | 'DEFAULT_AUTO_FIELD': 'django.db.models.AutoField',
19 | 'SECRET_KEY': 'herozyz',
20 | 'GALLERY_PLUGIN_MODULE_NAME': 'UI',
21 | 'CMSPLUGIN_GALLERY_TEMPLATES': [
22 | ('cmsplugin_gallery/gallery.html', 'gallery.html'),
23 | ]
24 | }
25 |
26 | def run():
27 | from djangocms_helper import runner
28 | runner.cms('cmsplugin_gallery')
29 |
30 | if __name__ == '__main__':
31 | run()
32 |
33 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/en/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2010-09-29 07:07-0500\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 |
19 | #: cms_plugins.py:12
20 | msgid "Image gallery"
21 | msgstr ""
22 |
23 | #: models.py:10
24 | #, python-format
25 | msgid "%(count)d image(s) in gallery"
26 | msgstr ""
27 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0003_auto_20170104_1122.py:
--------------------------------------------------------------------------------
1 | from django.db import migrations, models
2 | import cmsplugin_gallery.models
3 |
4 |
5 | class Migration(migrations.Migration):
6 |
7 | dependencies = [
8 | ('cmsplugin_gallery', '0002_auto_20161223_1005'),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name='galleryplugin',
14 | name='overlay_image',
15 | field=models.ImageField(upload_to=cmsplugin_gallery.models.UploadPath(b'GalleryPlugin'), null=True, verbose_name='Overlay Image', blank=True),
16 | ),
17 | migrations.AddField(
18 | model_name='galleryplugin',
19 | name='overlay_position',
20 | field=models.IntegerField(default=3, choices=[(1, b'Top Left'), (2, b'Top right'), (3, b'Bottom Left'), (4, b'Bottom Right')]),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0002_auto_20161223_1005.py:
--------------------------------------------------------------------------------
1 | from django.db import migrations, models
2 |
3 |
4 | class Migration(migrations.Migration):
5 |
6 | dependencies = [
7 | ('cmsplugin_gallery', '0001_initial'),
8 | ]
9 |
10 | operations = [
11 | migrations.AddField(
12 | model_name='image',
13 | name='crop',
14 | field=models.CharField(default=b'0%', max_length=10, choices=[(b'0%', b'0%'), (b'25%', b'25%'), (b'50%', b'50%'), (b'75%', b'75%'), (b'100%', b'100%')]),
15 | ),
16 | migrations.AlterField(
17 | model_name='galleryplugin',
18 | name='cmsplugin_ptr',
19 | field=models.OneToOneField(on_delete=models.CASCADE, parent_link=True, related_name='cmsplugin_gallery_galleryplugin', auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin'),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/pl/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: PACKAGE VERSION\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2010-09-29 07:07-0500\n"
11 | "PO-Revision-Date: 2010-09-29 14:09+0100\n"
12 | "Last-Translator: Piotr Kilczuk \n"
13 | "Language-Team: LANGUAGE \n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18 |
19 | #: cms_plugins.py:12
20 | msgid "Image gallery"
21 | msgstr "Galeria zdjęć"
22 |
23 | #: models.py:10
24 | #, python-format
25 | msgid "%(count)d image(s) in gallery"
26 | msgstr "%(count)d obrazków w galerii"
27 |
28 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | unit-tests:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | fail-fast: false
10 | matrix:
11 | python-version: [ 3.7, 3.8, 3.9, '3.10']
12 | requirements-file: [
13 | requirements.txt,
14 | ]
15 | os: [
16 | ubuntu-20.04,
17 | ]
18 |
19 | steps:
20 | - uses: actions/checkout@v3
21 | - name: Set up Python ${{ matrix.python-version }}
22 | uses: actions/setup-python@v3
23 | with:
24 | python-version: ${{ matrix.python-version }}
25 | cache: 'pip'
26 | - name: dev prerequisites
27 | run: sudo apt-get install python-dev libpq-dev libmagic1 gcc libxml2-dev libxslt1-dev libjpeg62 libopenjp2-7 -y
28 | - name: Install dependencies
29 | run: |
30 | python -m pip install --upgrade pip
31 | pip install -r tests/${{ matrix.requirements-file }}
32 | python setup.py install
33 |
34 | - name: Run Tests
35 | run: python setup.py test
--------------------------------------------------------------------------------
/tests/tests_models.py:
--------------------------------------------------------------------------------
1 |
2 | # -*- coding: utf-8 -*-
3 | from django.test import TestCase
4 | from cmsplugin_gallery.models import GalleryPlugin, Image as GalleryImage
5 | from django.core.files import File
6 | import filer.fields.image
7 |
8 | class GalleryTestCase(TestCase):
9 |
10 | def setUp(self):
11 | from filer.models import Image
12 | self.gallery = GalleryPlugin.objects.create(
13 | template='cmsplugin_gallery/gallery.html')
14 | img_file = File(open(u'tests/logo.png', 'rb'))
15 | image_instance = Image.objects.get_or_create(
16 | file=img_file,
17 | defaults={
18 | 'name': img_file.name
19 | }
20 | )[0]
21 | self.image = GalleryImage.objects.create(image_src=image_instance, gallery=self.gallery)
22 |
23 | def test_gallery_instance(self):
24 | image_instance = GalleryImage.objects.get(id=self.image.id)
25 | self.assertEqual(image_instance.gallery.id, self.gallery.id)
26 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0006_auto_20180411_1046.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 1.11.12 on 2018-04-11 05:16
2 |
3 | from django.db import migrations
4 | import filer.fields.image
5 |
6 |
7 | def migrate_to_filer(apps, schema_editor):
8 | from filer.models import Image
9 |
10 | GalleryImage = apps.get_model('cmsplugin_gallery', 'Image')
11 | images = GalleryImage.objects.all()
12 |
13 | try:
14 | for image in images:
15 | if image.src:
16 | image_src = Image.objects.get_or_create(
17 | file=image.src.file,
18 | defaults={
19 | 'name': image.src.name
20 | }
21 | )[0]
22 |
23 | images.filter(pk=image.pk).update(image_src=image_src)
24 | except Exception as e:
25 | print(e)
26 |
27 | class Migration(migrations.Migration):
28 |
29 | dependencies = [
30 | ('cmsplugin_gallery', '0005_auto_20180411_1046'),
31 | ]
32 |
33 | operations = [
34 | migrations.RunPython(migrate_to_filer)
35 | ]
36 |
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 |
2 | 2.0.3 / 2022-04-20
3 | ==================
4 |
5 | * fix: issue with the history.md file not included in MANIFEST.in file
6 |
7 | 2.0.0 / 2022-04-20
8 | ==================
9 |
10 | * feat: add make file for release automation and generate python3 wheels only
11 | * correct readme
12 | * fix: correct metadata
13 | * feat:add more metadata to the setup.py and add a setup.cfg too
14 | * feat: update ci setup
15 | * feat: set default auto field
16 | * feat: get package building for python 3.10 too
17 | * feat: python3+ only
18 | * fix: upgrade deps and also add more settins
19 | * feat: try updated package for djangocms-helper
20 | * fix: upgrade to a newer version of djangocms-helper
21 | * feat: add github actions
22 | * feat: make python code python3.7+
23 | * fix: make code django 3.2+ compatible
24 | * Merge pull request #50 from centralniak/fix/failing-migration-tofiler
25 | * wrap migration in try except
26 | * bump version
27 | * fix yet another typo
28 | * make it backward compatible
29 | * add module name in settings name
30 | * bump version
31 | * fix typo
32 | * add configurable module name
33 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0005_auto_20180411_1046.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 1.11.12 on 2018-04-11 05:16
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import filer.fields.image
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | migrations.swappable_dependency(settings.FILER_IMAGE_MODEL),
13 | ('cmsplugin_gallery', '0004_auto_20170118_0753'),
14 | ]
15 |
16 | operations = [
17 | migrations.AddField(
18 | model_name='image',
19 | name='image_src',
20 | field=filer.fields.image.FilerImageField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.FILER_IMAGE_MODEL, verbose_name='Image File'),
21 | ),
22 | migrations.AlterField(
23 | model_name='galleryplugin',
24 | name='template',
25 | field=models.CharField(choices=[(b'cmsplugin_gallery/gallery-fancy.html', b'gallery-fancy.html'), (b'cmsplugin_gallery/gallery-slider.html', b'gallery-slider.html'), (b'cmsplugin_gallery/gallery.html', b'gallery.html')], default=b'cmsplugin_gallery/gallery-fancy.html', max_length=255),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/cms_plugins.py:
--------------------------------------------------------------------------------
1 | from cms.plugin_base import CMSPluginBase
2 | from cms.plugin_pool import plugin_pool
3 | from django.utils.translation import gettext_lazy as _
4 | from django.conf import settings
5 |
6 | from . import admin
7 | from . import models
8 |
9 | if hasattr(settings, 'GALLERY_PLUGIN_MODULE_NAME'):
10 | MODULE_NAME = settings.GALLERY_PLUGIN_MODULE_NAME
11 | else:
12 | MODULE_NAME = 'UI'
13 |
14 | class CMSGalleryPlugin(CMSPluginBase):
15 |
16 | model = models.GalleryPlugin
17 | inlines = [admin.ImageInline, ]
18 | name = _('Image gallery Plugin')
19 | module = MODULE_NAME
20 | render_template = 'cmsplugin_gallery/gallery.html'
21 |
22 | def render(self, context, instance, placeholder):
23 | images = instance.image_set.all()
24 | grouped_images = [images[i:i + 4] for i in range(0, len(images.values_list('image_src')), 4)]
25 | context.update({
26 | 'images': instance.image_set.all(),
27 | 'grouped_images': grouped_images,
28 | 'gallery': instance,
29 | })
30 | self.render_template = instance.template
31 | return context
32 |
33 |
34 | plugin_pool.register_plugin(CMSGalleryPlugin)
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2012, Piotr Kilczuk
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
5 | following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
8 | following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10 | following disclaimer in the documentation and/or other materials provided with the distribution.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
13 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
15 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
17 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
18 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/cmsplugin_gallery/locale/ru/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2012-06-16 16:07+0700\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
20 | "10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
21 |
22 | #: cms_plugins.py:13
23 | msgid "Image gallery"
24 | msgstr "Галерея изображений"
25 |
26 | #: forms.py:13
27 | msgid "Change:"
28 | msgstr "Изменить:"
29 |
30 | #: models.py:26
31 | #, python-format
32 | msgid "%(count)d image(s) in gallery"
33 | msgstr "%(count)d изображений в галерее"
34 |
35 | #: models.py:35
36 | msgid "Gallery"
37 | msgstr "Галерея"
38 |
39 | #: models.py:36
40 | msgid "Image file"
41 | msgstr "Файл изображения"
42 |
43 | #: models.py:39 models.py:40
44 | msgid "Image height"
45 | msgstr "Высота изображения"
46 |
47 | #: models.py:41
48 | msgid "Title"
49 | msgstr "Заголовок"
50 |
51 | #: models.py:42
52 | msgid "Alt text"
53 | msgstr "Альтеративный текст"
54 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean clean-test clean-pyc clean-build docs help
2 | .DEFAULT_GOAL := help
3 |
4 | define BROWSER_PYSCRIPT
5 | import os, webbrowser, sys
6 |
7 | try:
8 | from urllib import pathname2url
9 | except:
10 | from urllib.request import pathname2url
11 |
12 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
13 | endef
14 | export BROWSER_PYSCRIPT
15 |
16 | define PRINT_HELP_PYSCRIPT
17 | import re, sys
18 |
19 | for line in sys.stdin:
20 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
21 | if match:
22 | target, help = match.groups()
23 | print("%-20s %s" % (target, help))
24 | endef
25 | export PRINT_HELP_PYSCRIPT
26 |
27 | BROWSER := python -c "$$BROWSER_PYSCRIPT"
28 |
29 | help:
30 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
31 |
32 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
33 |
34 | clean-build: ## remove build artifacts
35 | rm -fr build/
36 | rm -fr dist/
37 | rm -fr .eggs/
38 | find . -name '*.egg-info' -exec rm -fr {} +
39 | find . -name '*.egg' -exec rm -rf {} +
40 |
41 | clean-pyc: ## remove Python file artifacts
42 | find . -name '*.pyc' -exec rm -f {} +
43 | find . -name '*.pyo' -exec rm -f {} +
44 | find . -name '*~' -exec rm -rf {} +
45 | find . -name '__pycache__' -exec rm -fr {} +
46 |
47 | clean-test: ## remove test and coverage artifacts
48 | rm -fr .tox/
49 | rm -f .coverage
50 | rm -fr htmlcov/
51 | rm -fr .pytest_cache
52 |
53 | lint: ## check style with flake8
54 | flake8 cmsplugin_gallery tests
55 |
56 | test: ## run tests quickly with the default Python
57 | python setup.py test
58 |
59 | test-all: ## run tests on every Python version with tox
60 | tox
61 |
62 |
63 | release-to-pypi: dist ## package and upload a release
64 | twine upload dist/*
65 |
66 | dist: clean ## builds source and wheel package
67 | python setup.py sdist
68 | python setup.py bdist_wheel
69 | ls -l dist
70 |
71 | install: clean ## install the package to the active Python's site-packages
72 | python setup.py install
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from setuptools import find_packages, setup
4 |
5 |
6 | CLASSIFIERS = [
7 | 'Development Status :: 5 - Production/Stable',
8 | 'Environment :: Web Environment',
9 | 'Intended Audience :: Developers',
10 | 'License :: OSI Approved :: BSD License',
11 | 'Operating System :: OS Independent',
12 | 'Programming Language :: Python',
13 | 'Programming Language :: Python :: 3',
14 | 'Programming Language :: Python :: 3.7',
15 | 'Programming Language :: Python :: 3.8',
16 | 'Programming Language :: Python :: 3.9',
17 | 'Programming Language :: Python :: 3.10',
18 | 'Framework :: Django',
19 | 'Framework :: Django :: 2.2',
20 | 'Framework :: Django :: 3.1',
21 | 'Framework :: Django :: 3.2',
22 | 'Framework :: Django CMS',
23 | 'Framework :: Django CMS :: 3.9',
24 | 'Framework :: Django CMS :: 3.10',
25 | 'Topic :: Internet :: WWW/HTTP',
26 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
27 | 'Topic :: Software Development',
28 | 'Topic :: Software Development :: Libraries',
29 | 'Topic :: Software Development :: Libraries :: Application Frameworks',
30 | ]
31 |
32 | with open("README.md") as readme_file:
33 | readme = readme_file.read()
34 |
35 | with open("History.md") as history_file:
36 | history = history_file.read()
37 |
38 | from cmsplugin_gallery import __version__
39 |
40 | setup(
41 | name='cmsplugin_gallery',
42 | version=__version__,
43 | author='Piotr Kilczuk',
44 | author_email='piotr@tymaszweb.pl',
45 | url='https://github.com/piotrkilczuk/cmsplugin_gallery',
46 | description = 'DjangoCMS image gallery plugin with drag&drop '
47 | 'reordering in admin, support for thumbnails and '
48 | 'jQueryTOOLS overlay.',
49 | long_description=readme + "\n\n" + history,
50 | long_description_content_type="text/markdown",
51 | packages=find_packages(),
52 | provides=['cmsplugin_gallery', ],
53 | include_package_data=True,
54 | install_requires = ['django-cms>=3.9.0', 'django-inline-ordering==1.0.2',
55 | 'easy-thumbnails', 'django-filer',],
56 | test_suite='tests.settings.run',
57 | )
58 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | from django.db import models, migrations
2 | import cmsplugin_gallery.models
3 |
4 |
5 | class Migration(migrations.Migration):
6 |
7 | dependencies = [
8 | ('cms', '0014_auto_20160404_1908'),
9 | ]
10 |
11 | operations = [
12 | migrations.CreateModel(
13 | name='GalleryPlugin',
14 | fields=[
15 | ('cmsplugin_ptr', models.OneToOneField(on_delete=models.CASCADE, parent_link=True, auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
16 | ('template', models.CharField(default=b'cmsplugin_gallery/gallery-fancy.html', max_length=255, choices=[(b'cmsplugin_gallery/gallery-fancy.html', b'gallery-fancy.html'), (b'cmsplugin_gallery/gallery.html', b'gallery.html')])),
17 | ],
18 | options={
19 | 'abstract': False,
20 | },
21 | bases=('cms.cmsplugin',),
22 | ),
23 | migrations.CreateModel(
24 | name='Image',
25 | fields=[
26 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
27 | ('inline_ordering_position', models.IntegerField(null=True, blank=True)),
28 | ('src', models.ImageField(height_field=b'src_height', upload_to=cmsplugin_gallery.models.UploadPath(b'GalleryPlugin'), width_field=b'src_width', verbose_name='Image file')),
29 | ('src_height', models.PositiveSmallIntegerField(verbose_name='Image height', null=True, editable=False)),
30 | ('src_width', models.PositiveSmallIntegerField(verbose_name='Image height', null=True, editable=False)),
31 | ('title', models.CharField(max_length=255, verbose_name='Title', blank=True)),
32 | ('alt', models.TextField(verbose_name='Alt text', blank=True)),
33 | ('gallery', models.ForeignKey(on_delete=models.CASCADE, verbose_name='Gallery', to='cmsplugin_gallery.GalleryPlugin')),
34 | ],
35 | options={
36 | 'ordering': ('inline_ordering_position',),
37 | 'abstract': False,
38 | },
39 | bases=(models.Model,),
40 | ),
41 | ]
42 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/utils.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import os
3 | import threading
4 |
5 | from django.conf import settings
6 |
7 | localdata = threading.local()
8 | localdata.TEMPLATES = tuple()
9 | TEMPLATES = localdata.TEMPLATES
10 |
11 |
12 | def autodiscover_templates():
13 | '''
14 | Autodiscovers cmsplugin_gallery templates the way
15 | 'django.template.loaders.filesystem.Loader' and
16 | 'django.template.loaders.app_directories.Loader' work.
17 | '''
18 | def sorted_templates(templates):
19 | '''
20 | Sorts templates
21 | '''
22 | TEMPLATES = sorted(templates, key=lambda template: template[1])
23 | return TEMPLATES
24 |
25 | # obviously, cache for better performance
26 | global TEMPLATES
27 | if TEMPLATES:
28 | return TEMPLATES
29 |
30 | #override templates from settings
31 | override_dir = getattr(settings, 'CMSPLUGIN_GALLERY_TEMPLATES', None)
32 | if override_dir:
33 | return sorted_templates(override_dir)
34 |
35 | templates = []
36 | # templates = [
37 | # ('cmsplugin_gallery/gallery.html', 'gallery.html'),
38 | # ]
39 |
40 | dirs_to_scan = []
41 | if 'django.template.loaders.app_directories.Loader' in settings.TEMPLATE_LOADERS:
42 | for app in settings.INSTALLED_APPS:
43 | _ = __import__(app)
44 | dir = os.path.dirname(_.__file__)
45 | if not dir in dirs_to_scan:
46 | #append 'templates' for app directories
47 | dirs_to_scan.append(os.path.join(dir, 'templates'))
48 |
49 | if 'django.template.loaders.filesystem.Loader' in settings.TEMPLATE_LOADERS:
50 | for dir in settings.TEMPLATE_DIRS:
51 | if not dir in dirs_to_scan:
52 | #filesystem loader assumes our templates in 'templates' already
53 | dirs_to_scan.append(dir)
54 |
55 | for dir in dirs_to_scan:
56 | found = glob.glob(os.path.join(dir, 'cmsplugin_gallery/*.html'))
57 | for file in found:
58 | dir, file = os.path.split(file)
59 | key, value = os.path.join(dir.split('/')[-1], file), file
60 | f = False
61 | for _, template in templates:
62 | if template == file:
63 | f = True
64 | if not f:
65 | templates.append((key, value,))
66 | #print os.path.basename(file)
67 |
68 | return sorted_templates(templates)
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cmsplugin Gallery
2 |
3 | [](https://github.com/piotrkilczuk/cmsplugin_gallery/actions/workflows/test.yml)
4 |
5 | `cmsplugin_gallery` is the most versatile gallery plugin for djangoCMS. It supports
6 | Python3.7 and above.
7 |
8 | Features:
9 |
10 | - Latest version of the plugin supports filer.
11 | - Drag & Drop reordering of photos in the plugin admin
12 | - Unlimited, auto-discovered custom templates - you can change template
13 | of given gallery at anytime, use javascript galleries etc.
14 |
15 |
16 | ## Requirements
17 |
18 | Supports Django version 3.2+ and latest Django CMS version.
19 | Follow individual installation instructions before installing **cmsplugin_gallery**.
20 | Please note that cmsplugin_gallery requires:
21 |
22 | - django-inline-ordering http://pypi.python.org/pypi/django-inline-ordering/
23 | - easy-thumbnails http://pypi.python.org/pypi/easy-thumbnails/
24 | - django-filer https://pypi.python.org/pypi/django-filer
25 |
26 | **IMPORTANT**
27 |
28 | If you are using version later than 1.1.4, Please update all your templates to use
29 | `image_src` instead of `src`. `image_src` is the new FilerImageField instead of the old
30 | `src` which was the ImageField. Check this [diff](https://github.com/centralniak/cmsplugin_gallery/commit/364378842885ba2e2e0f2730076658b3c039534c) for the change in sample template.
31 |
32 | ## Installation
33 |
34 | - `pip install cmsplugin_gallery`
35 | - Add `'cmsplugin_gallery'` to `INSTALLED_APPS` (if necessary)
36 | - Run Migrations
37 |
38 | ## Usage
39 |
40 | The easiest approach is to use a nice feature of cmsplugin_gallery -
41 | the template autodiscovery. In order to take advantage of it, add your custom
42 | templates in the cmsplugin_gallery subdirectory of any of template dirs scanned
43 | by Django.
44 |
45 | If you don't want to use the autodiscovery, you can hardcode available templates
46 | in settings.py using following setting:
47 |
48 | ```python
49 | CMSPLUGIN_GALLERY_TEMPLATES = (
50 | ('app/template.html', 'Template #1', ),
51 | ('app/other_template.html', 'Template #2', ),
52 | )
53 | ```
54 | Embed as a typical plugin.
55 |
56 | ## Bugs & Contribution
57 |
58 | Please use GitHub to report bugs, feature requests and submit your code.
59 |
60 | [Report New Issue](https://github.com/piotrkilczuk/cmsplugin_gallery/issues/new/choose)
61 |
62 |
63 | ## Contributors
64 |
65 | [Contributors](https://github.com/piotrkilczuk/cmsplugin_gallery/graphs/contributors)
66 |
--------------------------------------------------------------------------------
/cmsplugin_gallery/models.py:
--------------------------------------------------------------------------------
1 | from datetime import date
2 | import os
3 | import threading
4 |
5 | from cms.models import CMSPlugin
6 |
7 | try:
8 | from cms.utils import get_cms_setting
9 | except ImportError:
10 | from cms.utils.conf import get_cms_setting
11 |
12 | from django.db import models
13 | from django.utils.translation import gettext_lazy as _
14 | from inline_ordering.models import Orderable
15 | from django.utils.deconstruct import deconstructible
16 | from django.db import connection
17 | from filer.fields.image import FilerImageField
18 | from . import utils
19 |
20 | localdata = threading.local()
21 | localdata.TEMPLATE_CHOICES = utils.autodiscover_templates()
22 | TEMPLATE_CHOICES = localdata.TEMPLATE_CHOICES
23 |
24 |
25 | @deconstructible
26 | class UploadPath:
27 |
28 | def __init__(self, sub_path):
29 | self.path = sub_path
30 |
31 | def __call__(self, instance, filename):
32 | return "gallery/%s" % (filename)
33 |
34 | get_upload_path = UploadPath('GalleryPlugin')
35 |
36 | class GalleryPlugin(CMSPlugin):
37 |
38 | TOP_LEFT = 1
39 | TOP_RIGHT = 2
40 | BOTTOM_LEFT = 3
41 | BOTTOM_RIGHT = 4
42 | OVERLAY_POSITION_CHOICES = (
43 | (TOP_LEFT, 'Top Left'),
44 | (TOP_RIGHT, 'Top right'),
45 | (BOTTOM_LEFT, 'Bottom Left'),
46 | (BOTTOM_RIGHT, 'Bottom Right'),
47 | )
48 |
49 | def copy_relations(self, oldinstance):
50 | for img in oldinstance.image_set.all():
51 | new_img = Image()
52 | new_img.gallery=self
53 | new_img.image_src = img.image_src
54 | new_img.src_height = img.src_height
55 | new_img.src_width = img.src_width
56 | new_img.title = img.title
57 | new_img.alt = img.alt
58 | new_img.crop = img.crop
59 | new_img.save()
60 |
61 | overlay_image = models.ImageField(_("Overlay Image"), upload_to=get_upload_path, null=True, blank=True)
62 | overlay_position = models.IntegerField(default=BOTTOM_LEFT,
63 | choices=OVERLAY_POSITION_CHOICES)
64 | template = models.CharField(max_length=255,
65 | choices=TEMPLATE_CHOICES,
66 | default=TEMPLATE_CHOICES[0][0],
67 | editable=len(TEMPLATE_CHOICES) > 1)
68 |
69 | def __unicode__(self):
70 | return _('%(count)d image(s) in gallery') % {'count': self.image_set.count()}
71 |
72 |
73 | class Image(Orderable):
74 | ZERO_PERCENT = "0%"
75 | TWENTY_FIVE_PERCENT = "25%"
76 | FIFTY_PERCENT = "50%"
77 | SEVENTY_FIVE_PERCENT = "75%"
78 | HUNDRED_PERCENT = "100%"
79 | CROP_CHOICES = (
80 | (ZERO_PERCENT, "0%"),
81 | (TWENTY_FIVE_PERCENT, "25%"),
82 | (FIFTY_PERCENT, "50%"),
83 | (SEVENTY_FIVE_PERCENT, "75%"),
84 | (HUNDRED_PERCENT, "100%"),
85 | )
86 |
87 | def get_media_path(self, filename):
88 | pages = self.gallery.placeholder.page_set.all()
89 | if pages.count():
90 | return pages[0].get_media_path(filename)
91 | else:
92 | today = date.today()
93 | return os.path.join(get_cms_setting('PAGE_MEDIA_PATH'),
94 | str(today.year), str(today.month), str(today.day), filename)
95 |
96 | gallery = models.ForeignKey(GalleryPlugin, on_delete=models.CASCADE, verbose_name=_("Gallery"))
97 | image_src = FilerImageField(
98 | verbose_name=_('Image File'),
99 | blank=True,
100 | null=True,
101 | on_delete=models.SET_NULL,
102 | related_name='+',
103 | )
104 | src_height = models.PositiveSmallIntegerField(_("Image height"), editable=False, null=True)
105 | src_width = models.PositiveSmallIntegerField(_("Image height"), editable=False, null=True)
106 | title = models.CharField(_("Title"), max_length=255, blank=True)
107 | alt = models.CharField(_("Alt text"), blank=True, max_length=255)
108 | crop = models.CharField(default=ZERO_PERCENT, choices=CROP_CHOICES, max_length=10, verbose_name="Positionering")
109 |
110 | def __unicode__(self):
111 | return self.title or self.alt or str(self.pk)
112 |
--------------------------------------------------------------------------------