From 5a3d3d2e9d2ab67218144f7c2e35d70f47f9ad93 Mon Sep 17 00:00:00 2001
From: luxagraf <sng@luxagraf.net>
Date: Mon, 4 Jun 2018 20:27:23 -0500
Subject: converted jrnl entry to use image for featured image

---
 app/jrnl/admin.py                         | 11 ++++++++-
 app/jrnl/models.py                        | 31 ++++++++++++++++++-------
 app/jrnl/templates/horizontal_select.html | 17 ++++++++++++++
 app/photos/models.py                      |  4 ++--
 app/photos/static/image-preview.js        |  1 +
 app/photos/static/my_styles.css           | 38 +++++++++++++++++++++++++++++++
 app/photos/urls.py                        |  5 ++++
 app/photos/views.py                       | 10 ++++++++
 app/utils/static/image-loader.js          | 30 ++++++++++++++++++++++++
 app/utils/widgets.py                      |  8 +++++++
 design/templates/archives/jrnl.html       |  5 +++-
 11 files changed, 148 insertions(+), 12 deletions(-)
 create mode 100644 app/jrnl/templates/horizontal_select.html
 create mode 100644 app/photos/static/my_styles.css

diff --git a/app/jrnl/admin.py b/app/jrnl/admin.py
index c3b0c81..2d843b9 100644
--- a/app/jrnl/admin.py
+++ b/app/jrnl/admin.py
@@ -6,6 +6,7 @@ from utils.widgets import AdminImageWidget, LGEntryForm
 from .models import Entry, HomepageCurrator
 
 from photos.forms import GalleryForm
+from photos.models import LuxImage
 from utils.util import get_latlon
 
 
@@ -13,6 +14,10 @@ from utils.util import get_latlon
 class EntryAdmin(OSMGeoAdmin):
     form = LGEntryForm
 
+    def render_change_form(self, request, context, *args, **kwargs):
+        context['adminform'].form.fields['featured_image'].queryset = LuxImage.objects.all()[:50]
+        return super(EntryAdmin, self).render_change_form(request, context, *args, **kwargs)
+
     def formfield_for_dbfield(self, db_field, **kwargs):
         if db_field.name == 'thumbnail' or db_field.name == 'image':
             field = forms.FileField(widget=AdminImageWidget)
@@ -46,7 +51,7 @@ class EntryAdmin(OSMGeoAdmin):
             'fields': (
                 'dek',
                 'meta_description',
-                ('image', 'thumbnail'),
+                'image',
                 'template_name',
                 'enable_comments',
             ),
@@ -55,6 +60,7 @@ class EntryAdmin(OSMGeoAdmin):
             'fields': (
                 'field_notes',
                 'books',
+                'featured_image',
             ),
             'classes': (
                 'collapse',
@@ -77,6 +83,9 @@ class EntryAdmin(OSMGeoAdmin):
 
     class Media:
         js = ('image-loader.js', 'next-prev-links.js')
+        css = {
+            "all": ("my_styles.css",)
+        }
 
 
 @admin.register(HomepageCurrator)
diff --git a/app/jrnl/models.py b/app/jrnl/models.py
index bcfd0b8..19f5417 100644
--- a/app/jrnl/models.py
+++ b/app/jrnl/models.py
@@ -1,6 +1,9 @@
 import datetime
 import os
 import re
+from PIL import Image
+from django.db.models.signals import post_save
+from django.dispatch import receiver
 
 from django.contrib.gis.db import models
 from django.utils.html import format_html
@@ -18,7 +21,8 @@ from django.template.defaultfilters import slugify
 import markdown
 from bs4 import BeautifulSoup
 
-from photos.models import PhotoGallery, LuxImage
+from photos.models import PhotoGallery, LuxImage, LuxImageSize
+from photos.utils import resize_image
 from locations.models import Location
 from sketches.models import Sketch
 from books.models import Book
@@ -73,7 +77,7 @@ class Entry(models.Model):
     status = models.IntegerField(choices=PUB_STATUS, default=0)
     photo_gallery = models.ForeignKey(PhotoGallery, on_delete=models.CASCADE, blank=True, null=True, verbose_name='photo set')
     image = models.FileField(upload_to=get_upload_path, null=True, blank=True, help_text="should be 520 by 290")
-    thumbnail = models.FileField(upload_to=get_tn_path, null=True, blank=True, help_text="should be 160 wide")
+    #thumbnail = models.FileField(upload_to=get_tn_path, null=True, blank=True, help_text="should be 160 wide")
     meta_description = models.CharField(max_length=256, null=True, blank=True)
     TEMPLATES = (
         (0, 'single'),
@@ -108,7 +112,7 @@ class Entry(models.Model):
         return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date
 
     def get_thumbnail_url(self):
-        image_dir, img = self.thumbnail.url.split('post-thumbnail/')[1].split('/')
+        image_dir, img = self.image.url.split('post-thumbnail/')[1].split('/')
         return '%spost-thumbnail/%s/%s' % (settings.IMAGES_URL, image_dir, img)
 
     def get_image_url(self):
@@ -175,8 +179,15 @@ class Entry(models.Model):
         except model.DoesNotExist:
             return ''
 
-    def save(self):
-        if self.pk:
+    def get_image_name(self):
+        return self.image.url.split("post-images/")[1][5:-4]
+
+    def get_image_ext(self):
+        return self.image.url[-3:]
+
+    def save(self, *args, **kwargs):
+        created = self.pk is None
+        if not created:
             md = render_images(self.body_markdown)
             self.body_html = markdown_to_html(md)
         self.has_video = parse_video(self.body_html)
@@ -184,9 +195,13 @@ class Entry(models.Model):
             self.location = Location.objects.filter(geometry__contains=self.point).get()
         except Location.DoesNotExist:
             raise forms.ValidationError("There is no location associated with that point, add it:  %sadmin/locations/location/add/" % (settings.BASE_URL))
-
-        super(Entry, self).save()
-
+        old = type(self).objects.get(pk=self.pk) if self.pk else None
+        print("self.image.path: ", self.image.path)
+        if old and old.featured_image != self.featured_image or created:  # Field has changed
+            s = LuxImageSize.objects.get(name="featured_jrnl")
+            self.featured_image.sizes.add(s)
+            self.featured_image.save()
+        super(Entry, self).save(*args, **kwargs)
 
 
 class HomepageCurrator(models.Model):
diff --git a/app/jrnl/templates/horizontal_select.html b/app/jrnl/templates/horizontal_select.html
new file mode 100644
index 0000000..4b0a2a1
--- /dev/null
+++ b/app/jrnl/templates/horizontal_select.html
@@ -0,0 +1,17 @@
+{% with id=widget.attrs.id %}
+    <ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>
+        {% for group, options, index in widget.optgroups %}
+            {% if group %}
+                <li>{{ group }}
+                <ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>
+            {% endif %}
+            {% for option in options %}
+            <li data-imageid="{{option.value}}">{% include option.template_name with widget=option %}</li>
+            {% endfor %}
+            {% if group %}
+                </ul>
+                </li>
+            {% endif %}
+        {% endfor %}
+    </ul>
+{% endwith %}
diff --git a/app/photos/models.py b/app/photos/models.py
index 557b9a0..0e5b0e4 100644
--- a/app/photos/models.py
+++ b/app/photos/models.py
@@ -120,7 +120,7 @@ class LuxImage(models.Model):
                 s = LuxImageSize.objects.get(name=size)
                 if s not in self.sizes.all():
                     print("new size is "+s.name)
-                    self.sizes.add(s) 
+                    self.sizes.add(s)
             return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, size, self.get_image_ext())
 
     def get_image_path_by_size(self, size="original"):
@@ -141,7 +141,7 @@ class LuxImage(models.Model):
     @property
     def longitude(self):
         return self.point.x
-    
+
     @property
     def get_previous_published(self):
         return self.get_previous_by_pub_date()
diff --git a/app/photos/static/image-preview.js b/app/photos/static/image-preview.js
index b46dbd8..b8fead5 100644
--- a/app/photos/static/image-preview.js
+++ b/app/photos/static/image-preview.js
@@ -36,6 +36,7 @@ function build_image_preview () {
         return;
     }
 }
+
 document.addEventListener("DOMContentLoaded", function(event) { 
     build_image_preview();
 });
diff --git a/app/photos/static/my_styles.css b/app/photos/static/my_styles.css
new file mode 100644
index 0000000..986c8e6
--- /dev/null
+++ b/app/photos/static/my_styles.css
@@ -0,0 +1,38 @@
+
+/*o.v.*/
+
+#id_featured_image {
+    /*style the "box" in its minimzed state*/
+    border:1px solid black; width:230px; overflow:hidden;
+    height:300px; overflow-y:scroll;
+    /*animate collapsing the dropdown from open to closed state (v. fast)*/
+}
+#id_featured_image input {
+    /*hide the nasty default radio buttons. like, completely!*/
+    position:absolute;top:0;left:0;opacity:0;
+}
+
+
+#id_featured_image label {
+    /*style the labels to look like dropdown options, kinda*/
+    color: #000;
+    display:none; 
+    margin: 2px 2px 2px 10px; 
+    height:102px; 
+    opacity:.6; 
+    background-repeat: no-repeat;
+}
+#id_featured_image:hover label{
+    /*this is how labels render in the "expanded" state. we want to see only the selected radio button in the collapsed menu, and all of them when expanded*/
+    display:block;
+}
+#id_featured_image label:hover {
+    opacity:.8;
+}
+#id_featured_image input:checked + label {
+    /*tricky! labels immediately following a checked radio button (with our markup they are semantically related) should be fully opaque regardless of hover, and they should always be visible (i.e. even in the collapsed menu*/
+    opacity:1 !important; display:block;
+}
+
+/*pfft, nothing as cool here, just the value trace*/
+#trace {margin:0 0 20px;}
diff --git a/app/photos/urls.py b/app/photos/urls.py
index 7be732d..1da29a6 100644
--- a/app/photos/urls.py
+++ b/app/photos/urls.py
@@ -15,6 +15,11 @@ urlpatterns = [
         views.photo_preview_json,
         name="admin_image_preview"
     ),
+    url(
+        r'data/admin/tn/(?P<pk>\d+)/$',
+        views.thumb_preview_json,
+        name="admin_thumb_preview"
+    ),
     url(
         r'galleries/private/(?P<slug>[-\w]+)$',
         views.PrivateGallery.as_view(),
diff --git a/app/photos/views.py b/app/photos/views.py
index d2ecd39..0d51c8e 100644
--- a/app/photos/views.py
+++ b/app/photos/views.py
@@ -79,6 +79,16 @@ def photo_preview_json(request, pk):
     return HttpResponse(data)
 
 
+def thumb_preview_json(request, pk):
+    p = LuxImage.objects.get(pk=pk)
+    thumb = p.get_image_by_size("tn")
+    data = {}
+    data['url'] = thumb[22:]
+    
+    data = json.dumps(data)
+    return HttpResponse(data)
+
+
 def gallery_list_by_area(request, slug, page):
     """Grabs entries by region or country"""
     request.page_url = '/photos/' + slug + '/%d/'
diff --git a/app/utils/static/image-loader.js b/app/utils/static/image-loader.js
index 90054de..2b0a281 100644
--- a/app/utils/static/image-loader.js
+++ b/app/utils/static/image-loader.js
@@ -2,6 +2,36 @@ function add_images(){
     var el = document.getElementById("id_body_markdown");
     var iframe = '<iframe frameborder="0" style="border: #dddddd 1px solid;margin-left: 20px;width:330px; height:720px;" src="/luximages/insert/?textarea='+el.id+'"></iframe>';
     el.insertAdjacentHTML('afterend', iframe);
+
+    var featured_image = document.getElementById("id_featured_image") 
+
+    if (featured_image) {
+        featured_image.querySelectorAll('li').forEach(function(element) {
+            var cur = element.dataset.imageid
+            if (cur != "") {
+                var request = new XMLHttpRequest();
+                request.open('GET', '/photos/luximage/data/admin/tn/'+cur+'/', true);
+                request.onload = function() {
+                    if (request.status >= 200 && request.status < 400) {
+                        var data = JSON.parse(request.responseText);
+                        var el = element.getElementsByTagName('label')[0];
+                        url = "url('"+data['url']+"');";
+                        //console.log(url);
+                        el.style.backgroundImage = 'url('+data["url"]+')';
+                            
+                        //console.log(el.style);
+                    } else {
+                        console.log("server error", request.statusText);
+                    }
+                };
+                request.onerror = function() {
+                    console.log("error on request");
+                };
+                request.send();
+            }
+        });
+    }
+
 }
 document.addEventListener("DOMContentLoaded", function(event) { 
     add_images();
diff --git a/app/utils/widgets.py b/app/utils/widgets.py
index eac4631..2b76f6b 100644
--- a/app/utils/widgets.py
+++ b/app/utils/widgets.py
@@ -99,6 +99,11 @@ def thumbnail(image_path):
     return '<img style="max-width: 400px" src="%s" alt="%s" />' % (absolute_url, image_path)
 
 
+class ImageRadioSelect(forms.RadioSelect):
+    template_name = 'horizontal_select.html'
+
+
+
 class AdminImageWidget(AdminFileWidget):
     """
     A FileField Widget that displays an image instead of a file path
@@ -124,6 +129,7 @@ class LGEntryForm(forms.ModelForm):
     class Meta:
         widgets = {
             'body_markdown': forms.Textarea(attrs={'rows': 40, 'cols': 100}),
+            'featured_image': ImageRadioSelect,
         }
 
 
@@ -132,6 +138,8 @@ class LGEntryFormSmall(forms.ModelForm):
         widgets = {
             'body_markdown': forms.Textarea(attrs={'rows': 12, 'cols': 100}),
         }
+
+
 class OLAdminBase(OSMGeoAdmin):
     default_lon = -9285175
     default_lat = 4025046
diff --git a/design/templates/archives/jrnl.html b/design/templates/archives/jrnl.html
index 94d1dd6..08c1584 100644
--- a/design/templates/archives/jrnl.html
+++ b/design/templates/archives/jrnl.html
@@ -1,5 +1,6 @@
 {% extends 'base.html' %}
 {% load typogrify_tags %}
+{% load get_image_by_size %}
 {% load pagination_tags %}
 
 {% block pagetitle %}Luxagraf | {% if region %}Travel Writing from {{region.name|title|smartypants|safe}}{%else%}Travel Writing from Around the World {%endif%}{% if page != "1" %} -- Page {{page}}{%endif%}{% endblock %}
@@ -17,7 +18,9 @@
         <h1 class="hide">{% if region %}Journal entries from {%if region.name == 'United States'%}the United States{%else%}{{region.name|title|smartypants|safe}}{%endif%}{%else%}Journal {%endif%}</h1>{% autopaginate object_list 24 %} {% for object in object_list %}
         <article class="h-entry hentry {% cycle 'odd' 'even' %} {% cycle 'first' 'second' 'third' %}" itemscope itemType="http://schema.org/Article">
             <div class="post--image">
-                <a href="{{object.get_absolute_url}}" title="{{object.title}}"><img src="{{object.get_image_url}}" alt="{{ object.title }}" class="u-photo post-image" itemprop="image" /></a>
+                <a href="{{object.get_absolute_url}}" title="{{object.title}}">{% if object.featured_image %}
+                    <img {% for size in object.featured_image.sizes.all%}src="{% if size.name == 'featured_jrnl'%}{% get_image_by_size object.featured_image size.name %}{%endif%}"{%endfor%} alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}">{%else%}
+                    <a href="{{object.get_absolute_url}}" title="{{object.title}}"><img src="{{object.get_image_url}}" alt="{{ object.title }}" class="u-photo post-image" itemprop="image" />{%endif%}</a>
             </div>
             <h2 class="p-name entry-title post--title" itemprop="headline"><a href="{{object.get_absolute_url}}" class="u-url" title="{%if object.title_keywords%}{{object.title_keywords}}{%else%}{{object.title}}{%endif%}">{{object.title|safe|smartypants|widont}}</a></h2>
             <p class="p-author author hide" itemprop="author"><span class="byline-author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Scott Gilbertson</span></span></p>
-- 
cgit v1.2.3-70-g09d2