From 8154c30318127e0491786b65975bee808cd8e27b Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Wed, 10 Mar 2021 21:52:59 +0100
Subject: tinymce: upgrade and disable drag&drop for images


diff --git a/program/urls.py b/program/urls.py
index 776b9c0..78b9af4 100644
--- a/program/urls.py
+++ b/program/urls.py
@@ -4,10 +4,6 @@ from django.views.decorators.cache import cache_page
 
 import views
 
-import os
-
-PROGRAM_SITE_MEDIA = os.path.join(os.path.dirname(__file__), '../site_media')
-
 urlpatterns = patterns('',
                        url(r'^today/?$', views.DayScheduleView.as_view()),
                        url(r'^week/?$', views.WeekScheduleView.as_view()),
@@ -36,4 +32,4 @@ urlpatterns = patterns('',
 if settings.DEBUG:
     urlpatterns += \
         patterns('',
-                 url(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': PROGRAM_SITE_MEDIA}))
+                 url(r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}))
diff --git a/pv/settings.py b/pv/settings.py
index 47b9d2e..41da596 100644
--- a/pv/settings.py
+++ b/pv/settings.py
@@ -90,11 +90,16 @@ INSTALLED_APPS = (
     'tinymce',
 )
 
-TINYMCE_JS_URL = '/static/js/tiny_mce/tiny_mce.js'
 TINYMCE_DEFAULT_CONFIG = {
-    'plugins': 'contextmenu',
+    'plugins': 'advimage,advlink,advlist,autoresize,contextmenu,paste',
     'theme': 'advanced',
     'theme_advanced_toolbar_location': 'top',
+    'theme_advanced_toolbar_align': 'center',
+    'theme_advanced_statusbar_location': 'bottom',
+    'theme_advanced_resizing': 'true',
+    'theme_advanced_path': 'false',
+    'theme_advanced_buttons3_add': 'separator,pastetext,pasteword,selectall',
+    'paste_block_drop': 'true',
 }
 
 CACHE_BACKEND = 'locmem://'
diff --git a/requirements.txt b/requirements.txt
index ce6754c..e4bc92f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,5 +2,5 @@ Django==1.8.14
 MySQL-python==1.2.5
 Pillow==3.3.0
 PyYAML==3.11
-django-tinymce==2.3.0
+django-tinymce==2.6.1
 python-dateutil==2.5.3
-- 
cgit v0.10.2


From dde04a95dcb76b765b8c66775a6ee242d3c316c4 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Mon, 29 Mar 2021 14:22:19 +0200
Subject: add show and note images based on S3 storage


diff --git a/program/admin.py b/program/admin.py
index 16d6973..b186c3e 100644
--- a/program/admin.py
+++ b/program/admin.py
@@ -154,7 +154,7 @@ class ShowAdmin(admin.ModelAdmin):
     prepopulated_fields = {'slug': ('name',)}
     search_fields = ('name', 'short_description', 'description')
     fields = (
-        'predecessor', 'broadcastformat', 'name', 'slug', 'image', 'image_enabled', 'short_description', 'description',
+        'predecessor', 'broadcastformat', 'name', 'slug', 'image', 'short_description', 'description',
         'email', 'website', 'hosts', 'owners', 'showinformation', 'showtopic', 'language',
         'musicfocus',
     )
diff --git a/program/migrations/0013_show_and_note_images.py b/program/migrations/0013_show_and_note_images.py
new file mode 100644
index 0000000..2cf8d1a
--- /dev/null
+++ b/program/migrations/0013_show_and_note_images.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import program.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('program', '0012_add_language'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='show',
+            name='image_enabled',
+        ),
+        migrations.AlterField(
+            model_name='show',
+            name='image',
+            field=models.ImageField(upload_to=program.models.show_image_filename, null=True, verbose_name='Image', blank=True),
+        ),
+        migrations.AddField(
+            model_name='note',
+            name='image',
+            field=models.ImageField(upload_to=program.models.note_image_filename, null=True, verbose_name='Image', blank=True),
+        ),
+    ]
diff --git a/program/models.py b/program/models.py
index b5123cb..048e51b 100644
--- a/program/models.py
+++ b/program/models.py
@@ -1,3 +1,4 @@
+import os
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist, ValidationError, MultipleObjectsReturned
 from django.core.urlresolvers import reverse
@@ -11,7 +12,7 @@ from datetime import date, datetime, time, timedelta
 from dateutil.relativedelta import relativedelta
 from dateutil.rrule import rrule
 
-from utils import get_automation_id_choices
+from utils import get_automation_id_choices, hash_file
 
 
 class BroadcastFormat(models.Model):
@@ -243,6 +244,12 @@ class Host(models.Model):
         return self.shows.filter(programslots__until__gt=datetime.today).distinct()
 
 
+def show_image_filename(instance, filename):
+    instance.image.open()
+    filename_base, filename_ext = os.path.splitext(filename)
+    return "shows/%s/%s%s" % (instance.slug, hash_file(instance.image.file), filename_ext)
+
+
 class Show(models.Model):
     predecessor = models.ForeignKey('self', blank=True, null=True, related_name='successors', verbose_name=_("Predecessor"))
     hosts = models.ManyToManyField(Host, blank=True, related_name='shows', verbose_name=_("Hosts"))
@@ -254,8 +261,7 @@ class Show(models.Model):
     musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus"))
     name = models.CharField(_("Name"), max_length=255)
     slug = models.CharField(_("Slug"), max_length=255, unique=True)
-    image = models.ImageField(_("Image"), blank=True, null=True, upload_to='show_images')
-    image_enabled = models.BooleanField(_("show Image"), default=True)
+    image = models.ImageField(_("Image"), blank=True, null=True, upload_to=show_image_filename)
     short_description = models.CharField(_("Short description"), max_length=64)
     description = tinymce_models.HTMLField(_("Description"), blank=True, null=True)
     email = models.EmailField(_("E-Mail"), blank=True, null=True)
@@ -492,7 +498,14 @@ class TimeSlot(models.Model):
         return reverse('timeslot-detail', args=[str(self.id)])
 
 
+def note_image_filename(instance, filename):
+    instance.image.open()
+    filename_base, filename_ext = os.path.splitext(filename)
+    return "notes/%s/%s%s" % (instance.show.slug, hash_file(instance.image.file), filename_ext)
+
+
 class Note(models.Model):
+
     STATUS_CHOICES = (
         (0, _("Cancellation")),
         (1, _("Recommendation")),
@@ -500,6 +513,7 @@ class Note(models.Model):
     )
     timeslot = models.OneToOneField(TimeSlot, verbose_name=_("Time slot"))
     title = models.CharField(_("Title"), max_length=128)
+    image = models.ImageField(_("Image"), blank=True, null=True, upload_to=note_image_filename)
     content = tinymce_models.HTMLField(_("Content"))
     status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1)
     start = models.DateTimeField(editable=False)
diff --git a/program/templates/v2/show_detail.html b/program/templates/v2/show_detail.html
index 55f7f3b..7b2e07e 100644
--- a/program/templates/v2/show_detail.html
+++ b/program/templates/v2/show_detail.html
@@ -43,8 +43,8 @@
         <div id="description">{{ show.description|safe }}</div>
 {% endif %}
 
-{% if show.image and show.image_enabled %}
-        <div id="image" style="float: right;"><img src="/program/static/{{ show.image }}" width="200" alt="image"></div>
+{% if show.image %}
+        <div id="image"><img src="https://images.helsinki.at/program/{{ show.image }}"></div>
 {% endif %}
 
         <p>
@@ -69,7 +69,7 @@
                 <div class="title">{{ note.title }}</div>
             </li>
 {%   endfor %}
-{%   if show.predecessor and show.predecessor.notes.all %}
+{%   if show.predecessor and show.predecessor.notes.all %}p
 {%     if show.name != show.predecessor.name %}
             <h3>Davor als <a href="/program/shows/{{ show.predecessor.slug }}">{{ show.predecessor.name }}</a></h3>
 {%     endif %}
diff --git a/program/templates/v2/timeslot_detail.html b/program/templates/v2/timeslot_detail.html
index ab3b429..f1e65b4 100644
--- a/program/templates/v2/timeslot_detail.html
+++ b/program/templates/v2/timeslot_detail.html
@@ -32,6 +32,10 @@
 
 {% if timeslot.note %}
         <div class="timeslot-note">{{ timeslot.note.content|safe }}</div>
+
+{%   if timeslot.note.image %}
+        <div id="timeslot-note-image"><img src="https://images.helsinki.at/notes/{{ timeslot.note.image }}"></div>
+{%   endif %}
 {% endif %}
     </div>
 
diff --git a/program/utils.py b/program/utils.py
index 1a81766..3bfecc4 100644
--- a/program/utils.py
+++ b/program/utils.py
@@ -3,8 +3,10 @@ from django.conf import settings
 import json
 import urllib
 import bisect
+import hashlib
 from os.path import join
 from datetime import datetime, date, timedelta
+from functools import partial
 
 
 def get_automation_id_choices():
@@ -56,3 +58,11 @@ def tofirstdayinisoweek(year, week):
     if date(year, 1, 4).isoweekday() > 4:
         ret -= timedelta(days=7)
     return ret
+
+
+def hash_file(file, block_size=65536):
+    hasher = hashlib.sha256()
+    for buf in iter(partial(file.read, block_size), b''):
+        hasher.update(buf)
+
+    return hasher.hexdigest()
diff --git a/pv/settings.py b/pv/settings.py
index 41da596..18a1e5e 100644
--- a/pv/settings.py
+++ b/pv/settings.py
@@ -104,6 +104,14 @@ TINYMCE_DEFAULT_CONFIG = {
 
 CACHE_BACKEND = 'locmem://'
 
+
+DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
+# AWS_ACCESS_KEY_ID = 'django'
+# AWS_SECRET_ACCESS_KEY = 'changeme'
+# AWS_S3_ENDPOINT_URL = 'http://172.17.0.1:9000'
+# AWS_STORAGE_BUCKET_NAME = 'program'
+
+
 MUSIKPROG_IDS = (
     1,    # unmodieriertes musikprogramm
 )
diff --git a/requirements.txt b/requirements.txt
index e4bc92f..7c8535c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,5 @@ Pillow==3.3.0
 PyYAML==3.11
 django-tinymce==2.6.1
 python-dateutil==2.5.3
+django-storages=1.6.5
+boto3=1.4.8
-- 
cgit v0.10.2


From 02093ba844d0f2b303c519c8c49316bd45d11229 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Fri, 28 May 2021 22:34:15 +0200
Subject: fix note image urls


diff --git a/program/templates/v2/timeslot_detail.html b/program/templates/v2/timeslot_detail.html
index f1e65b4..81c40c7 100644
--- a/program/templates/v2/timeslot_detail.html
+++ b/program/templates/v2/timeslot_detail.html
@@ -34,7 +34,7 @@
         <div class="timeslot-note">{{ timeslot.note.content|safe }}</div>
 
 {%   if timeslot.note.image %}
-        <div id="timeslot-note-image"><img src="https://images.helsinki.at/notes/{{ timeslot.note.image }}"></div>
+        <div id="timeslot-note-image"><img src="https://images.helsinki.at/program/{{ timeslot.note.image }}"></div>
 {%   endif %}
 {% endif %}
     </div>
-- 
cgit v0.10.2


From ed13a183a0011b3cf648a1d0533b83c86ec79467 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Wed, 2 Jun 2021 12:04:45 +0200
Subject: fix typo


diff --git a/program/templates/v2/show_detail.html b/program/templates/v2/show_detail.html
index 7b2e07e..73ec5b1 100644
--- a/program/templates/v2/show_detail.html
+++ b/program/templates/v2/show_detail.html
@@ -69,7 +69,7 @@
                 <div class="title">{{ note.title }}</div>
             </li>
 {%   endfor %}
-{%   if show.predecessor and show.predecessor.notes.all %}p
+{%   if show.predecessor and show.predecessor.notes.all %}
 {%     if show.name != show.predecessor.name %}
             <h3>Davor als <a href="/program/shows/{{ show.predecessor.slug }}">{{ show.predecessor.name }}</a></h3>
 {%     endif %}
-- 
cgit v0.10.2


From fda29b207fe3627445319386fd75b8da8860cee6 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Wed, 2 Jun 2021 17:58:43 +0200
Subject: fix size of imagefiel ind show and note model


diff --git a/program/migrations/0013_show_and_note_images.py b/program/migrations/0013_show_and_note_images.py
index 2cf8d1a..91a08b0 100644
--- a/program/migrations/0013_show_and_note_images.py
+++ b/program/migrations/0013_show_and_note_images.py
@@ -19,11 +19,11 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='show',
             name='image',
-            field=models.ImageField(upload_to=program.models.show_image_filename, null=True, verbose_name='Image', blank=True),
+            field=models.ImageField(max_length=350, upload_to=program.models.show_image_filename, null=True, verbose_name='Image', blank=True),
         ),
         migrations.AddField(
             model_name='note',
             name='image',
-            field=models.ImageField(upload_to=program.models.note_image_filename, null=True, verbose_name='Image', blank=True),
+            field=models.ImageField(max_length=350, upload_to=program.models.note_image_filename, null=True, verbose_name='Image', blank=True),
         ),
     ]
diff --git a/program/models.py b/program/models.py
index 048e51b..ecebda8 100644
--- a/program/models.py
+++ b/program/models.py
@@ -261,7 +261,7 @@ class Show(models.Model):
     musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus"))
     name = models.CharField(_("Name"), max_length=255)
     slug = models.CharField(_("Slug"), max_length=255, unique=True)
-    image = models.ImageField(_("Image"), blank=True, null=True, upload_to=show_image_filename)
+    image = models.ImageField(_("Image"), max_length=350, blank=True, null=True, upload_to=show_image_filename)
     short_description = models.CharField(_("Short description"), max_length=64)
     description = tinymce_models.HTMLField(_("Description"), blank=True, null=True)
     email = models.EmailField(_("E-Mail"), blank=True, null=True)
@@ -513,7 +513,7 @@ class Note(models.Model):
     )
     timeslot = models.OneToOneField(TimeSlot, verbose_name=_("Time slot"))
     title = models.CharField(_("Title"), max_length=128)
-    image = models.ImageField(_("Image"), blank=True, null=True, upload_to=note_image_filename)
+    image = models.ImageField(_("Image"), max_length=350, blank=True, null=True, upload_to=note_image_filename)
     content = tinymce_models.HTMLField(_("Content"))
     status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1)
     start = models.DateTimeField(editable=False)
-- 
cgit v0.10.2


From 88fb9a1d5bc7dc5f22d96fcee970f4ec9f88b289 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Wed, 9 Jun 2021 20:02:18 +0200
Subject: no more S3


diff --git a/pv/settings.py b/pv/settings.py
index 18a1e5e..5e251df 100644
--- a/pv/settings.py
+++ b/pv/settings.py
@@ -105,13 +105,6 @@ TINYMCE_DEFAULT_CONFIG = {
 CACHE_BACKEND = 'locmem://'
 
 
-DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
-# AWS_ACCESS_KEY_ID = 'django'
-# AWS_SECRET_ACCESS_KEY = 'changeme'
-# AWS_S3_ENDPOINT_URL = 'http://172.17.0.1:9000'
-# AWS_STORAGE_BUCKET_NAME = 'program'
-
-
 MUSIKPROG_IDS = (
     1,    # unmodieriertes musikprogramm
 )
diff --git a/requirements.txt b/requirements.txt
index 7c8535c..e4bc92f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,5 +4,3 @@ Pillow==3.3.0
 PyYAML==3.11
 django-tinymce==2.6.1
 python-dateutil==2.5.3
-django-storages=1.6.5
-boto3=1.4.8
-- 
cgit v0.10.2