diff options
-rw-r--r-- | app/lttr/templates/lttr/range_detail.html | 175 | ||||
-rw-r--r-- | app/lttr/templates/lttr/range_list.html | 32 | ||||
-rw-r--r-- | app/lttr/urls.py | 5 | ||||
-rw-r--r-- | app/lttr/views.py | 17 |
4 files changed, 229 insertions, 0 deletions
diff --git a/app/lttr/templates/lttr/range_detail.html b/app/lttr/templates/lttr/range_detail.html new file mode 100644 index 0000000..b672a06 --- /dev/null +++ b/app/lttr/templates/lttr/range_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="/newsletter/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">Image {{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/lttr/templates/lttr/range_list.html b/app/lttr/templates/lttr/range_list.html new file mode 100644 index 0000000..9810c3a --- /dev/null +++ b/app/lttr/templates/lttr/range_list.html @@ -0,0 +1,32 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% block pagetitle %}Luxagraf | Range {% endblock %} +{% block metadescription %}A weekly photo, developed.{% 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">Range</h1> + <h2 class="list-subhed">A weekly photo, developed.</h2> + <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='range' %}"></iframe> + <p><em>Range</em> is a weekly mailing of a single photograph. </p> + <p>If you're interested there is also a link to a video of the RAW image processing in <a href="https://www.darktable.org/">darktable</a>, and sometimes a few words about the process. But the primary purpose is to deliver a single photo to your inbox. Simple and fun.</p> + <p>Yes, I know about Instagram. This is an attempt to reclaim that space, sharing photos with friends, but without all the distractions of the corporate social web, without the endless scroll of photos, likes, stories, comments, whatever. This is just an image delivered once a week to your inbox. I've been trying to think of a way to make it reciprocal, so you can send a picture to my inbox. If you have ideas, <a href="mailto:comments@luxagraf.net">email me</a>.</p> + <p>Unsubscribing is easy. It's <a href="/src/building-your-own-mailing-list-software">self-hosted</a> and <a href="/privacy" title="My privacy policy">respects your privacy</a>. If you don't want an email, there's also <a href="/newsletter/range/feed.xml">an RSS feed</a>, and it's all archived below.</p> + </div> + <h3 class="archive-sans">Images</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%} + + <p>If you're not familiar, darktable is open source raw image developer. It's free, you can download a copy for Linux, macOS, or Windows. The <a href="https://www.darktable.org/usermanual/en/">darktable user manual</a> is very helpful if you're brand new. I also recommend <a href="https://www.youtube.com/user/audio2u">Bruce Williams' darktable videos</a>, and <a href="https://www.youtube.com/user/s7habo/videos">Boris Hajdukovic's videos</a>, which were the inspiration for what you see here.</p> + <p>I'm no expert either, so feel free to hit reply and let me know if I get something wrong.</p> diff --git a/app/lttr/urls.py b/app/lttr/urls.py index cd91a16..9186ed1 100644 --- a/app/lttr/urls.py +++ b/app/lttr/urls.py @@ -6,6 +6,11 @@ app_name = "lttr" urlpatterns = [ path( + r'<str:slug>/feed.xml', + views.NewsletterRSSFeedView(), + name="feed" + ), + path( '<str:slug>/unsubscribe/<str:activation_code>', views.UnsubscribeRequestView.as_view(), name='newsletter_unsubscribe' diff --git a/app/lttr/views.py b/app/lttr/views.py index 6b8c60a..9b739a1 100644 --- a/app/lttr/views.py +++ b/app/lttr/views.py @@ -55,6 +55,7 @@ class NewsletterListView(ListView): context['breadcrumbs'] = [self.kwargs['slug'],] return context + class NewsletterOptionsView(ListView): model = Newsletter @@ -114,6 +115,7 @@ class NewsletterSubscribeView(CreateView): return super(NewsletterSubscribeView, self).form_invalid(form) return super(NewsletterSubscribeView, self).form_valid(form) + class UnsubscribeRequestView(DetailView): model = Subscriber template_name = "lttr/unsubscribe.html" @@ -131,3 +133,18 @@ class UnsubscribeRequestView(DetailView): context['newsletter'] = self.kwargs['slug'] return context +class NewsletterRSSFeedView(Feed): + title = "Luxagraf: Range, A Weekly Photo" + link = "/newsletter/range/" + description = "Latest Range Newsletter" + description_template = 'feeds/blog_description.html' + + def items(self): + return NewsletterMailing.objects.filter(newsletter__slug='ranger',status__exact=1) + + def item_pubdate(self, item): + """ + Takes an item, as returned by items(), and returns the item's + pubdate. + """ + |