diff options
-rw-r--r-- | app/jrnl/admin.py | 17 | ||||
-rw-r--r-- | app/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py | 5 | ||||
-rw-r--r-- | app/jrnl/migrations/0036_enjoyitem.py | 23 | ||||
-rw-r--r-- | app/jrnl/migrations/0037_enjoyitem_entry.py | 20 | ||||
-rw-r--r-- | app/jrnl/migrations/0038_remove_enjoyitem_entry.py | 17 | ||||
-rw-r--r-- | app/jrnl/migrations/0039_enjoyitem_order.py | 18 | ||||
-rw-r--r-- | app/jrnl/migrations/0040_auto_20190315_1906.py | 20 | ||||
-rw-r--r-- | app/jrnl/migrations/0041_auto_20190315_2240.py | 28 | ||||
-rw-r--r-- | app/jrnl/models.py | 11 | ||||
-rw-r--r-- | app/jrnl/views.py | 6 | ||||
-rw-r--r-- | app/sightings/models.py | 7 | ||||
-rw-r--r-- | design/sass/_details.scss | 71 | ||||
-rw-r--r-- | design/sass/_footer.scss | 10 | ||||
-rw-r--r-- | design/sass/_mixins.scss | 2 | ||||
-rw-r--r-- | design/sass/_typography.scss | 10 | ||||
-rw-r--r-- | design/sass/screenv9.scss | 3 | ||||
-rw-r--r-- | design/templates/archives/homepage-light.html | 2 | ||||
-rw-r--r-- | design/templates/base.html | 4 | ||||
-rw-r--r-- | design/templates/jrnl/entry_detail.html | 26 | ||||
-rw-r--r-- | design/templates/lib/img_archive.html | 2 |
20 files changed, 291 insertions, 11 deletions
diff --git a/app/jrnl/admin.py b/app/jrnl/admin.py index 3c35272..d800f34 100644 --- a/app/jrnl/admin.py +++ b/app/jrnl/admin.py @@ -1,19 +1,31 @@ from django.contrib import admin from django import forms from django.contrib.gis.admin import OSMGeoAdmin +from django.contrib.contenttypes.admin import GenericStackedInline from utils.widgets import AdminImageWidget, LGEntryForm -from .models import Entry, HomepageCurrator, Home +from .models import Entry, HomepageCurrator, Home, RelatedPost from photos.forms import GalleryForm from photos.models import LuxImage from utils.util import get_latlon +@admin.register(RelatedPost) +class RelatedPostAdmin(admin.ModelAdmin): + pass + + @admin.register(Entry) class EntryAdmin(OSMGeoAdmin): form = LGEntryForm + def get_queryset(self, request): + test_model_qs = super(EntryAdmin, self).get_queryset(request) + test_model_qs = test_model_qs.prefetch_related('related').prefetch_related('books') + + return test_model_qs + def render_change_form(self, request, context, *args, **kwargs): #context['adminform'].form.fields['featured_image'].queryset = LuxImage.objects.all()[:200] return super(EntryAdmin, self).render_change_form(request, context, *args, **kwargs) @@ -32,7 +44,7 @@ class EntryAdmin(OSMGeoAdmin): search_fields = ['title', 'body_markdown'] prepopulated_fields = {"slug": ('title',)} list_filter = ('pub_date', 'enable_comments', 'status', 'location__state__country__lux_region') - filter_horizontal = ('field_notes', 'books') + filter_horizontal = ('field_notes', 'books', 'related') fieldsets = ( ('Entry', { 'fields': ( @@ -63,6 +75,7 @@ class EntryAdmin(OSMGeoAdmin): 'fields': ( 'field_notes', 'books', + 'related', ), 'classes': ( 'collapse', diff --git a/app/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py b/app/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py index 4b1dea3..7b1b247 100644 --- a/app/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py +++ b/app/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py @@ -70,6 +70,11 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='entry', + name='field_notes', + field=models.ManyToManyField(blank=True, to='fieldnotes.FieldNote'), + ), + migrations.AddField( + model_name='entry', name='books', field=models.ManyToManyField(blank=True, to='books.Book'), ), diff --git a/app/jrnl/migrations/0036_enjoyitem.py b/app/jrnl/migrations/0036_enjoyitem.py new file mode 100644 index 0000000..aaa8596 --- /dev/null +++ b/app/jrnl/migrations/0036_enjoyitem.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.7 on 2019-03-14 23:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('jrnl', '0035_auto_20190303_1610'), + ] + + operations = [ + migrations.CreateModel( + name='EnjoyItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + ] diff --git a/app/jrnl/migrations/0037_enjoyitem_entry.py b/app/jrnl/migrations/0037_enjoyitem_entry.py new file mode 100644 index 0000000..733f94c --- /dev/null +++ b/app/jrnl/migrations/0037_enjoyitem_entry.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1.7 on 2019-03-15 00:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0036_enjoyitem'), + ] + + operations = [ + migrations.AddField( + model_name='enjoyitem', + name='entry', + field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, to='jrnl.Entry'), + preserve_default=False, + ), + ] diff --git a/app/jrnl/migrations/0038_remove_enjoyitem_entry.py b/app/jrnl/migrations/0038_remove_enjoyitem_entry.py new file mode 100644 index 0000000..0eec153 --- /dev/null +++ b/app/jrnl/migrations/0038_remove_enjoyitem_entry.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-03-15 00:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0037_enjoyitem_entry'), + ] + + operations = [ + migrations.RemoveField( + model_name='enjoyitem', + name='entry', + ), + ] diff --git a/app/jrnl/migrations/0039_enjoyitem_order.py b/app/jrnl/migrations/0039_enjoyitem_order.py new file mode 100644 index 0000000..41c5324 --- /dev/null +++ b/app/jrnl/migrations/0039_enjoyitem_order.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-15 18:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0038_remove_enjoyitem_entry'), + ] + + operations = [ + migrations.AddField( + model_name='enjoyitem', + name='order', + field=models.IntegerField(default=1), + ), + ] diff --git a/app/jrnl/migrations/0040_auto_20190315_1906.py b/app/jrnl/migrations/0040_auto_20190315_1906.py new file mode 100644 index 0000000..9ce813b --- /dev/null +++ b/app/jrnl/migrations/0040_auto_20190315_1906.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1.7 on 2019-03-15 19:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0039_enjoyitem_order'), + ] + + operations = [ + migrations.RemoveField( + model_name='enjoyitem', + name='content_type', + ), + migrations.DeleteModel( + name='EnjoyItem', + ), + ] diff --git a/app/jrnl/migrations/0041_auto_20190315_2240.py b/app/jrnl/migrations/0041_auto_20190315_2240.py new file mode 100644 index 0000000..06b9697 --- /dev/null +++ b/app/jrnl/migrations/0041_auto_20190315_2240.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.7 on 2019-03-15 22:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('jrnl', '0040_auto_20190315_1906'), + ] + + operations = [ + migrations.CreateModel( + name='RelatedPost', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField()), + ('post_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.ContentType')), + ], + ), + migrations.AddField( + model_name='entry', + name='related', + field=models.ManyToManyField(blank=True, to='jrnl.RelatedPost'), + ), + ] diff --git a/app/jrnl/models.py b/app/jrnl/models.py index 88fd84b..a88e79a 100644 --- a/app/jrnl/models.py +++ b/app/jrnl/models.py @@ -3,6 +3,8 @@ import os from django.dispatch import receiver from django.contrib.gis.db import models +from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey +from django.contrib.contenttypes.models import ContentType from django.urls import reverse from django.utils.functional import cached_property from django.apps import apps @@ -35,6 +37,14 @@ def image_url_replace(s): return s +class RelatedPost(models.Model): + post_model = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL) + slug = models.SlugField() + + def __str__(self): + return self.slug + + class Entry(models.Model): title = models.CharField(max_length=200) subtitle = models.CharField(max_length=200, blank=True) @@ -67,6 +77,7 @@ class Entry(models.Model): has_video = models.BooleanField(blank=True, default=False) field_notes = models.ManyToManyField(FieldNote, blank=True) books = models.ManyToManyField(Book, blank=True) + related = models.ManyToManyField(RelatedPost, blank=True) class Meta: ordering = ('-pub_date',) diff --git a/app/jrnl/views.py b/app/jrnl/views.py index 43ca2d9..7ea3e41 100644 --- a/app/jrnl/views.py +++ b/app/jrnl/views.py @@ -3,6 +3,7 @@ from django.views.generic.detail import DetailView from django.views.generic.dates import DateDetailView from django.views.generic.dates import YearArchiveView, MonthArchiveView from django.contrib.syndication.views import Feed +from django.apps import apps from django.shortcuts import get_object_or_404 from django.conf import settings from django.db.models import Q @@ -96,6 +97,11 @@ class EntryDetailView(DateDetailView): Q(location=self.location) | Q(location__in=Location.objects.filter(parent=self.location)) ).select_related().order_by('ap_id', 'ap__apclass__kind').distinct("ap") + related = [] + for obj in self.object.related.all(): + model = apps.get_model(obj.post_model.app_label, obj.post_model.model) + related.append(model.objects.get(slug=obj.slug)) + context['related'] = related return context diff --git a/app/sightings/models.py b/app/sightings/models.py index 5723776..3852b1c 100644 --- a/app/sightings/models.py +++ b/app/sightings/models.py @@ -12,7 +12,7 @@ from django.conf import settings from bs4 import BeautifulSoup from locations.models import Location -from photos.models import LuxImage +from photos.models import LuxImage, LuxImageSize from utils.next_prev import next_in_order, prev_in_order from utils.util import render_images, extract_main_image, markdown_to_html @@ -75,7 +75,7 @@ class AP(models.Model): return self.common_name def get_image_url(self): - return "%s%s" % (settings.IMAGES_URL, self.image.url.split("media")[1][8:]) + return "%s%s" % (settings.IMAGES_URL, self.image.image.url.split("media")[1][8:]) def get_absolute_url(self): return reverse("sightings:detail", kwargs={"slug": self.slug}) @@ -121,6 +121,9 @@ class AP(models.Model): main_image = extract_main_image(self.body_markdown) if main_image: self.image = main_image + s = LuxImageSize.objects.get(name="featured_jrnl") + self.image.sizes.add(s) + self.image.save() self.slug = slugify(self.common_name[:50]) super(AP, self).save(*args, **kwargs) diff --git a/design/sass/_details.scss b/design/sass/_details.scss index d5681c6..41afd04 100644 --- a/design/sass/_details.scss +++ b/design/sass/_details.scss @@ -246,6 +246,77 @@ h4.post-source { } } +.article-afterward { + @extend %clearfix; + @include constrain_narrow; + padding-bottom: 2rem; + border-bottom: 3px double #efefef; + @include breakpoint(alpha) { + .article-card-list { + display: flex; + justify-content: space-between + } + .article-card-mini { + margin-bottom: 0 + } + } + .hedtinycaps { + display: inline-block; + margin-bottom: 2.5rem; + border-bottom: 2px solid #efefef; + } +} +.article-card-list { + padding: 0 !important; + margin: 0 !important; + list-style-type: none !important; +} +.article-card-mini, .detail .article-card-mini { + margin-left: 0; + margin-right: 0; + margin-bottom: 2rem; + max-width: 100%; + a { text-decoration: none;} + .post-title { + @include fontsize(24); + text-align: center; + line-height: 1; + margin: 0; + } + .post-subtitle { + @include fontsize(18); + text-align: center; + font-family: $fancy_serif; + font-style: italic; + font-weight: normal; + margin: .25rem auto 0; + } + .post-summary, .post-date{ + text-align: center; + display: inline-block; + line-height: 0.6 !important; + color: #b6b6b6; + } + @include breakpoint(omega) { + max-width: 200px; + margin-bottom: 0; + } + @include breakpoint(beta) { + max-width: 235px; + } +} +.post-mini-image { + max-height: 220px; + overflow: hidden; + @include breakpoint(beta) { + margin-bottom: 1rem; + } + img { + margin-top: -20%; + } +} + + .entry-footer { @extend %clearfix; @include constrain_narrow; diff --git a/design/sass/_footer.scss b/design/sass/_footer.scss index ed1635c..08c9942 100644 --- a/design/sass/_footer.scss +++ b/design/sass/_footer.scss @@ -9,7 +9,9 @@ footer { margin-bottom: 1.2em; } } - ul { + .footer-nav { + list-style-type: none !important; + margin-left: 0 !important; border-top: 1px $brown dotted; border-bottom: 1px $brown dotted; padding: .5rem 0; @@ -73,3 +75,9 @@ footer { text-decoration: none; width: 70px; } +#license { + @include fancy_sans; + @include fontsize(12); + text-transform: none; + letter-spacing: normal; +} diff --git a/design/sass/_mixins.scss b/design/sass/_mixins.scss index dd6f257..8c936eb 100644 --- a/design/sass/_mixins.scss +++ b/design/sass/_mixins.scss @@ -14,7 +14,7 @@ $secondary-link-color: #838383; $archive_p_line_height: 1.6; //$light; -$narrow-beta-width: 640px; +$narrow-beta-width: 720px; $narrow-max-width: 750px; $max_width: 1440px; diff --git a/design/sass/_typography.scss b/design/sass/_typography.scss new file mode 100644 index 0000000..b4e3793 --- /dev/null +++ b/design/sass/_typography.scss @@ -0,0 +1,10 @@ +.hedtinycaps { + @include fontsize(16); + @include fancy_sans; + text-transform: uppercase; + letter-spacing: 1px; +} +.article-afterward > .hedtinycaps, p > .hedtinycaps { + text-align: left; +} + diff --git a/design/sass/screenv9.scss b/design/sass/screenv9.scss index d594e28..312018c 100644 --- a/design/sass/screenv9.scss +++ b/design/sass/screenv9.scss @@ -4,7 +4,6 @@ @import "_global.scss"; @import "_pagination.scss"; @import "_header.scss"; -@import "_footer.scss"; @import "_archives.scss"; @import "_details.scss"; @import "_images.scss"; @@ -16,3 +15,5 @@ @import "_photos.scss"; @import "_notes.scss"; @import "_forms.scss"; +@import "_typography.scss"; +@import "_footer.scss"; diff --git a/design/templates/archives/homepage-light.html b/design/templates/archives/homepage-light.html index 1964795..1021885 100644 --- a/design/templates/archives/homepage-light.html +++ b/design/templates/archives/homepage-light.html @@ -2,7 +2,7 @@ {% load typogrify_tags %} {% block sitename %} <head itemscope itemtype="http://schema.org/WebSite"> - <title itemprop='name'>Luxagraf</title> + <title itemprop='name'>Luxagraf: thoughts on ecology, culture, travel, photography, walking and other ephemera</title> <link rel="canonical" href="https://luxagraf.net/" itemprop="url">{%endblock%} {%block extrahead%} diff --git a/design/templates/base.html b/design/templates/base.html index 495a496..bf78846 100644 --- a/design/templates/base.html +++ b/design/templates/base.html @@ -14,7 +14,7 @@ title="Luxagraf RSS feed" href="https://luxagraf.net/rss/"> {%block stylesheet%}<link rel="stylesheet" - href="/media/screenv9.css" + href="/media/screenv9.css?{% now "u" %}" media="screen">{%endblock%} <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> <link rel="manifest" href="/manifest.webmanifest" /> @@ -44,7 +44,7 @@ {% block primary %}{% endblock %} {% block extrabody %}{% endblock %} <footer class="bl"> - <ul> + <ul class="footer-nav"> <li><a href="/blogroll" title="Sites that inspire us">Blogroll</a></li> <li><a href="/contact/" title="contact luxagraf">Contact</a></li> <li>Follow Along: diff --git a/design/templates/jrnl/entry_detail.html b/design/templates/jrnl/entry_detail.html index 7d7e2a3..c19add1 100644 --- a/design/templates/jrnl/entry_detail.html +++ b/design/templates/jrnl/entry_detail.html @@ -121,6 +121,32 @@ class="detail {%if t == 1 or t == 3 or t == 5 %}double{%else%}single{%endif%}{%i </ul> </nav>{%endwith%}{%endwith%} </div> + <div class="article-afterward related"> + <h6 class="hedtinycaps">You might also enjoy</h6> + <ul class="article-card-list">{% for object in related %} + <li class="article-card-mini"><a href="{{object.get_absolute_url}}" title="{{object.title}}"> + <div class="post-image post-mini-image"> + {% if object.featured_image %} + {% include "lib/img_archive.html" with image=object.featured_image nolightbox=True %} + {% elif object.image %} + {% include "lib/img_archive.html" with image=object.image nolightbox=True %} + {% else %} + <img src="{{object.get_image_url}}" alt="{{ object.title }}" class="u-photo post-image" itemprop="image" />{%endif%} + </div> + <h4 class="p-name entry-title post-title" itemprop="headline">{% if object.title %}{{object.title|safe|smartypants|widont}}{% else %}{{object.common_name}}{%endif%}</h4> + {% if object.subtitle%}<h5 class="post-subtitle">{{object.subtitle|safe|smartypants}}</h5>{%endif%} + <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> + <p class="post-summary"> + <span class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + {% if object.location.country_name == "United States" %}{{object.location.state_name}}{%else%}{{object.location.country_name}}{%endif%} + </span> + – + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}"><span>{{object.pub_date|date:" Y"}}</span></time> + </p> + </a> + </li> + {% endfor %}</ul> + </div> {% comment %} <div class="mailing-list--wrapper"> <h5>If you enjoyed this, you should join the mailing list…</h5> diff --git a/design/templates/lib/img_archive.html b/design/templates/lib/img_archive.html index c256ebe..a512930 100644 --- a/design/templates/lib/img_archive.html +++ b/design/templates/lib/img_archive.html @@ -2,4 +2,4 @@ srcset="{{image.get_featured_jrnl}} 520w, {{image.get_picwide_sm}} 720w" src="{{image.get_featured_jrnl}}" alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}" - data-jslghtbx="{{image.get_image_by_size}}" data-jslghtbx-group="group" {% if caption%}data-jslghtbx-caption="{{image.caption}}"{%endif%}> + {% if not nolightbox %}data-jslghtbx="{{image.get_image_by_size}}" data-jslghtbx-group="group" {% if caption%}data-jslghtbx-caption="{{image.caption}}"{%endif%}{%endif%}> |