diff options
-rw-r--r-- | app/posts/admin.py | 2 | ||||
-rw-r--r-- | app/posts/models.py | 2 | ||||
-rw-r--r-- | app/posts/templates/posts/friends_detail.html | 175 | ||||
-rw-r--r-- | app/posts/templates/posts/friends_list.html | 29 | ||||
-rw-r--r-- | app/posts/urls/friends_urls.py | 20 | ||||
-rw-r--r-- | app/posts/views/friends_views.py | 39 |
6 files changed, 266 insertions, 1 deletions
diff --git a/app/posts/admin.py b/app/posts/admin.py index 7819ac5..661075b 100644 --- a/app/posts/admin.py +++ b/app/posts/admin.py @@ -45,7 +45,7 @@ class PostAdmin(OSMGeoAdmin): ('title', 'short_title'), 'subtitle', 'body_markdown', - ('pub_date', 'status', 'post_type'), + ('pub_date', 'status', 'post_type', 'issue'), ('slug', 'enable_comments',), 'point', 'dek', diff --git a/app/posts/models.py b/app/posts/models.py index efe1904..cc6e1d5 100644 --- a/app/posts/models.py +++ b/app/posts/models.py @@ -47,6 +47,7 @@ class PostType(models.IntegerChoices): JRNL = 4, ('jrnl') FIELD_NOTE = 5, ('field note') GUIDE = 6, ('guide') + FRIENDS = 7, ('friends') class Post(models.Model): @@ -95,6 +96,7 @@ class Post(models.Model): originally_published_by_url = models.CharField(max_length=400, null=True, blank=True) field_notes = models.ManyToManyField('self', blank=True, symmetrical=False, limit_choices_to = {'post_type': PostType.FIELD_NOTE}) books = models.ManyToManyField(Book, blank=True) + issue = models.PositiveIntegerField(null=True) class Meta: ordering = ('-pub_date',) diff --git a/app/posts/templates/posts/friends_detail.html b/app/posts/templates/posts/friends_detail.html new file mode 100644 index 0000000..5a8c92d --- /dev/null +++ b/app/posts/templates/posts/friends_detail.html @@ -0,0 +1,175 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load get_image_by_size %} +{%block htmlclass%}{%endblock%} +{% block sitename %} +<head itemscope itemtype="http://schema.org/WebSite"> + <title itemprop='name'>{{object.title|safe}} by Scott Gilbertson</title> + <link rel="canonical" href="https://luxagraf.net{{object.get_absolute_url}}">{%endblock%} + + {%block extrahead%} + <link rel="canonical" href="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:type" content="article" /> + <meta property="og:title" content="{{object.title|safe}}" /> + <meta property="og:url" content="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:description" content="{{object.meta_description}}" /> + <meta property="article:published_time" content="{{object.pub_date|date:'c'}}" /> + <meta property="article:author" content="Scott Gilbertson" /> + <meta property="og:site_name" content="Luxagraf" /> + <meta property="og:image" content="{{object.get_featured_image}}" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image"/> + <meta name="twitter:description" content="{{object.meta_description}}"/> + <meta name="twitter:title" content="{{object.title|safe}}"/> + <meta name="twitter:site" content="@luxagraf"/> + <meta name="twitter:domain" content="luxagraf"/> + <meta name="twitter:image:src" content="{{object.get_featured_image}}"/> + <meta name="twitter:creator" content="@luxagraf"/> +<script type="application/ld+json"> +{ + "@context": "https://schema.org", + "@type": "Article", + "mainEntityOfPage": { + "@type": "WebPage", + "@id": "https://luxagraf.net{{object.get_absolute_url}}" + }, + "headline": "{{object.title}}", + "datePublished": "{{object.pub_date|date:'c'}}+04:00", + "dateModified": "{{object.pub_date|date:'c'}}+04:00", + "author": { + "@type": "Person", + "name": "Scott Gilbertson" + }, + "publisher": { + "@type": "Organization", + "name": "Luxagraf", + "logo": { + "@type": "ImageObject", + "url": "https://luxagraf.net/media/img/logo-white.jpg" + } + }, + "description": "{{object.meta_description}}" +} +</script> +{%endblock%} +{%block bodyid%}id="home" class="friends"{%endblock%} +{% block breadcrumbs %}<nav class="breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList"> + <span class="nav-item" itemprop="item"> + <a href="/" itemprop="name">Home</a> + <meta itemprop="position" content="1" /> + </span> + <span class="nav-item" itemprop="item"> + <a href="/friends/" itemprop="name">Friends of a Long Year</a> + <meta itemprop="position" content="2" /> + </span> + <span class="nav-item" itemprop="item"> + <span itemprop="name">{{object.get_issue_str}}</span> + <meta itemprop="position" content="3" /> + </span> + </nav> +{% endblock %} +{% block primary %} + <main> + <figure class="large-top-image"> + <a href="{{object.get_absolute_url}}" title="{{object.title}}">{%with image=object.featured_image%} + <img class="u-photo" itemprop="image" sizes="(max-width: 960px) 100vw" + srcset="{{image.get_srcset}}" + src="{{image.get_src}}" + alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}"> + </a>{%endwith%} + </figure> + <article class="h-entry hentry content" itemscope itemType="http://schema.org/BlogPosting"> + <header id="header" class="post-header"> + <h1 class="p-name post-title" itemprop="headline">{{object.title|smartypants|safe}}</h1> + <div class="post-dateline"> + <time class="dt-published published dt-updated post-date lttr-box" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">Transmission {{object.get_issue_str}} – {{object.pub_date|date:"F, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <div id="article" class="e-content entry-content post-body" itemprop="articleBody"> + {{object.body_html|safe|smartypants}} + </div> + {%if object.books.all %}<div class="entry-footer"> + <aside id="recommended-reading" class="" > + <h3>Recommended Reading</h3>{% for obj in object.books.all %} + <div itemprop="mainEntity" itemscope itemtype="http://schema.org/Book"> + <div class="book-cover-wrapper"> + <img src="{{obj.get_image_url}}" alt="{{obj.title}} cover" class="lttr-cover" /> + </div> + <div class="meta-cover"> + <h5 class="post-title book-title" itemprop="name">{{obj.title|smartypants|widont|safe}}</h6> + <h6 class="post-subtitle" itemprop="author" itemscope itemtype="http://schema.org/Person"> + <meta itemprop="name" content="{{obj.author_name}}"/>by {{obj.author_name}}</h5> + <dl class="book-metadata"> + {% if obj.rating %}<dt>Rating</dt><dd class="book-stars"> + {% for i in obj.ratings_range %}{% if i <= obj.get_rating%}★{%else%}☆{%endif%}{%endfor%}</span></dd>{%endif%} + {% if obj.read_in %}<dt>Read</dt> + <dd>{{obj.read_in}}</dd>{%endif%} + {% if obj.pages %}<dt>Pages</dt> + <dd itemprop="numberOfPages">{{obj.pages}}</dd>{%endif%} + {% if obj.publish_date %}<dt>Published</dt> + <dd>{%if obj.publish_place%}{{obj.publish_place}}, {%endif%}{{obj.publish_date}}</dd>{%endif%} + {% if obj.isbn %}<dt>ISBN</dt> + <dd>{{obj.isbn}}</dd>{%endif%} + </dl> + <div class="buy-btn-wrapper"> + {% if obj.isbn %}<a class="buy-btn" href="http://worldcat.org/isbn/{{obj.isbn}}" title="find {{obj.title}} in your local library">Borrow</a>{%endif%} + {% if obj.afflink %}<a class="buy-btn" href="{{obj.afflink}}" title="buy {{obj.title}} at Amazon">Buy</a>{%endif%} + </div> + </div>{%if obj.body_html%} + <div class="thoughts" itemprop="review" itemscope itemtype="http://schema.org/Review"> + <h5>Notes</h5> + <span class="hide" itemprop="reviewRating">{{obj.rating}}</span> + <meta itemprop="author" content="Scott Gilbertson" /> + <meta itemprop="datePublished" content="{{obj.read_date|date:"c"}}"> + <div itemprop="reviewBody">{{obj.body_html|safe|smartypants|widont}}</div> + </div>{%endif%} + </div> + {% endfor %} + </aside>{%endif%} + </article> + + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + <nav class="page-navigation"> + <div>{% if prev%} + <span class="label">Previous:</span> + <a href="{{ prev.get_absolute_url }}" rel="prev" title=" {{prev.title}}">{{prev.title|safe}}</a> + </div>{%endif%}{% if next%} + <div> + <span class="label">Next:</span> + <a href="{{ next.get_absolute_url }}" rel="next" title=" {{next.title}}">{{next.title|safe}}</a> + </div>{%endif%} + </nav>{%endwith%}{%endwith%} + </div> + {% if object.related.all %}<div class="article-afterward related"> + <div class="related-bottom"> + <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> + <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> + </div>{%endif%} + </main> +{% endblock %} + +{% block js %}{% comment %} <script async src="/media/js/hyphenate.min.js" type="text/javascript"></script>{% endcomment%}{% endblock%} diff --git a/app/posts/templates/posts/friends_list.html b/app/posts/templates/posts/friends_list.html new file mode 100644 index 0000000..3318a08 --- /dev/null +++ b/app/posts/templates/posts/friends_list.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} + +{% block pagetitle %}Luxagraf | Friends of a Long Year {% endblock %} +{% block metadescription %}An infrequesnt mailing list about living outdoors, travel, literature, music, vintage vehicles, and other ephemera.{% endblock %} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %} + <main role="main" class="archive-wrapper"> + <div class="archive-intro"> + <h1 class="list-hed">Join the <em>Friends of a Long Year</em>.</h1> + <iframe target='_parent' style="border:none; background:white; width:100%;" title="embedded form for subscribing the the Friends of a Long Year newsletter" src="{% url 'lttr:subscribe' slug='friends' %}"></iframe> + <h2 class="list-subhed">Say what? </h2> + <p><em>Friends of a Long Year</em> is a monthly letter about living outdoors, travel, literature, music, vintage vehicles, and other ephemera. It's written in the spirit of Mary Austin. And Mike’s emails. A more detailed explaination can be found <a href="/jrnl/2020/11/invitation">here</a>.</p> + <p>Unsubscribing is easy. It's all <a href="/src/building-your-own-mailing-list-software">self-hosted</a> and designed to <a href="/privacy" title="My privacy policy">respect your privacy</a>.</p> + <p>There's also the <em><a href="/range/">Range</a></em> newsletter if you'd like a photo in your inbox once a week.</p> + </div> + <h3 class="archive-sans">Letters</h3> + <ul class="archive-list">{% for object in object_list %} + <li class="h-entry hentry archive-list-card" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}" class="u-url"> + {% if object.featured_image %}<div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" class="u-photo" /></div>{%endif%} + <span class="date dt-published card-smcaps">issue {{object.get_issue_str}} – {{object.pub_date|date:"M y"}}</span> + <h2 class="card-hed">{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary card-lede">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + </li> + {%endfor%}</ul> + </main> +{%endblock%} diff --git a/app/posts/urls/friends_urls.py b/app/posts/urls/friends_urls.py new file mode 100644 index 0000000..2e2d220 --- /dev/null +++ b/app/posts/urls/friends_urls.py @@ -0,0 +1,20 @@ +from django.urls import path, re_path, include +from django.views.generic.base import RedirectView + +from ..views import friends_views as views + +app_name = "range" + +urlpatterns = [ + path( + r'<str:slug>', + views.FriendsDetailView.as_view(), + name="friends-detail" + ), + path( + r'', + views.FriendsListView.as_view(), + {'page':1}, + name="friends-base" + ), +] diff --git a/app/posts/views/friends_views.py b/app/posts/views/friends_views.py new file mode 100644 index 0000000..dd4dac2 --- /dev/null +++ b/app/posts/views/friends_views.py @@ -0,0 +1,39 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.views.generic.dates import DateDetailView +from django.urls import reverse +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 + +from utils.views import PaginatedListView, LuxDetailView + +#from ..models import Entry, HomepageCurrator, Home +from ..models import Post, PostType +from locations.models import LuxCheckIn, Country, Region, Location +from sightings.models import Sighting + + +class FriendsDetailView(LuxDetailView): + model = Post + slug_field = "slug" + template_name = "posts/friends_detail.html" + + +class FriendsListView(PaginatedListView): + """ + Return a list of Newsletter posts in reverse chronological order + """ + model = Post + template_name = "posts/friends_list.html" + queryset = Post.objects.filter(post_type=PostType.FRIENDS,status=1).order_by('-pub_date') + + def get_context_data(self, **kwargs): + context = super(FriendsListView, self).get_context_data(**kwargs) + context['breadcrumbs'] = ['friends of a long year',] + return context + + |