diff options
Diffstat (limited to 'app/posts')
-rw-r--r-- | app/posts/admin.py | 8 | ||||
-rw-r--r-- | app/posts/migrations/0006_post_short_title.py | 19 | ||||
-rw-r--r-- | app/posts/migrations/0007_auto_20191007_0949.py | 21 | ||||
-rw-r--r-- | app/posts/migrations/0008_post_topics.py | 19 | ||||
-rw-r--r-- | app/posts/models.py | 16 | ||||
-rw-r--r-- | app/posts/templates/posts/guide_detail.html | 182 | ||||
-rw-r--r-- | app/posts/templates/posts/post_list.html | 14 | ||||
-rw-r--r-- | app/posts/views.py | 8 |
8 files changed, 275 insertions, 12 deletions
diff --git a/app/posts/admin.py b/app/posts/admin.py index ad8f509..a8831e7 100644 --- a/app/posts/admin.py +++ b/app/posts/admin.py @@ -35,15 +35,15 @@ class EntryAdmin(OSMGeoAdmin): field = super(EntryAdmin, self).formfield_for_dbfield(db_field, **kwargs) return field - list_display = ('title', 'post_type', 'pub_date', 'template_name', 'status', 'category') + list_display = ('title', 'post_type', 'pub_date', 'template_name', 'status',) search_fields = ['title', 'body_markdown'] prepopulated_fields = {"slug": ('title',)} - list_filter = ('pub_date', 'enable_comments', 'status', 'category') + list_filter = ('pub_date', 'enable_comments', 'status') filter_horizontal = ('related',) fieldsets = ( ('Entry', { 'fields': ( - 'title', + ('title', 'short_title'), 'subtitle', 'body_markdown', ('pub_date', 'status', 'post_type', 'old_id',), @@ -61,7 +61,7 @@ class EntryAdmin(OSMGeoAdmin): 'point', 'dek', 'meta_description', - ('category', 'tags'), + 'topics', 'template_name', 'enable_comments', 'featured_image', diff --git a/app/posts/migrations/0006_post_short_title.py b/app/posts/migrations/0006_post_short_title.py new file mode 100644 index 0000000..a364147 --- /dev/null +++ b/app/posts/migrations/0006_post_short_title.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.7 on 2019-09-27 20:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0005_auto_20190918_1244'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='short_title', + field=models.CharField(default='blurk', max_length=200), + preserve_default=False, + ), + ] diff --git a/app/posts/migrations/0007_auto_20191007_0949.py b/app/posts/migrations/0007_auto_20191007_0949.py new file mode 100644 index 0000000..2d99da4 --- /dev/null +++ b/app/posts/migrations/0007_auto_20191007_0949.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.6 on 2019-10-07 09:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0006_post_short_title'), + ] + + operations = [ + migrations.RemoveField( + model_name='post', + name='category', + ), + migrations.RemoveField( + model_name='post', + name='tags', + ), + ] diff --git a/app/posts/migrations/0008_post_topics.py b/app/posts/migrations/0008_post_topics.py new file mode 100644 index 0000000..cd867d8 --- /dev/null +++ b/app/posts/migrations/0008_post_topics.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2019-10-07 09:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('taxonomy', '0002_auto_20191007_0913'), + ('posts', '0007_auto_20191007_0949'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='topics', + field=models.ManyToManyField(blank=True, to='taxonomy.Category'), + ), + ] diff --git a/app/posts/models.py b/app/posts/models.py index 1e1da1e..1d71ecb 100644 --- a/app/posts/models.py +++ b/app/posts/models.py @@ -36,6 +36,7 @@ from utils.util import render_images, parse_video, markdown_to_html class Post(models.Model): old_id = models.IntegerField(blank=True, null=True) title = models.CharField(max_length=200) + short_title = models.CharField(max_length=200) subtitle = models.CharField(max_length=200, blank=True) slug = models.SlugField(unique_for_date='pub_date') prologue_markdown = models.TextField(blank=True, null=True) @@ -70,9 +71,8 @@ class Post(models.Model): books = models.ManyToManyField(Book, blank=True) field_notes = models.ManyToManyField(FieldNote, blank=True) related = models.ManyToManyField(RelatedPost, blank=True) - tags = TaggableManager(through=TaggedItems, blank=True, help_text='Topics Covered') - category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True) point = models.PointField(null=True, blank=True) + topics = models.ManyToManyField(Category, blank=True) location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) originally_published_by = models.CharField(max_length=400, null=True, blank=True) originally_published_by_url = models.CharField(max_length=400, null=True, blank=True) @@ -198,3 +198,15 @@ def post_save_events(sender, update_fields, created, instance, **kwargs): post_save.disconnect(post_save_events, sender=Post) instance.save() post_save.connect(post_save_events, sender=Post) + + +class PostSitemap(Sitemap): + changefreq = "never" + priority = 1.0 + protocol = "https" + + def items(self): + return Post.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/app/posts/templates/posts/guide_detail.html b/app/posts/templates/posts/guide_detail.html new file mode 100644 index 0000000..12fb7e1 --- /dev/null +++ b/app/posts/templates/posts/guide_detail.html @@ -0,0 +1,182 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} +{%block htmlclass%}class="detail single"{%endblock%} +{% block pagetitle %}{{object.title|title|smartypants|safe}} - by Scott Gilbertson{% endblock %} + +{% block metadescription %}{% autoescape on %}{{object.meta_description|striptags|safe}}{% endautoescape %}{% endblock %} +{%block extrahead%} +{% if object.has_code %} + <link rel="stylesheet" href="/media/src/solarized.css" type="text/css" media="screen"/> +{%endif %} + <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="{% if object.meta_description %}{{object.meta_description}}{%else%}{{object.sub_title}}{%endif%}" /> + <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="{{self.get_featured_image}}" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image"/> + <meta name="twitter:description" content="{% if object.meta_description %}{{object.meta_description}}{%else%}{{object.sub_title}}{%endif%}"/> + <meta name="twitter:title" content="{{object.title|safe}}"/> + <meta name="twitter:site" content="@luxagraf"/> + <meta name="twitter:domain" content="luxagraf"/>{% if object.featured_image %} + <meta name="twitter:image:src" content="{{object.featured_image.get_image_url}}"/>{%endif%} + <meta name="twitter:creator" content="@luxagraf"/> +{%endblock%} + +{%block bodyid %}{% if object.get_post_type_display == 'tools' %}class="src"{% endif %}{%endblock%} + +{% block primary %} + <main> + <ul class="bl" id="breadcrumbs" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> + <li><a href="/" title="luxagraf homepage" itemprop="url"><span itemprop="title">Home</span></a> → </li> + <li><a href="/guides/" title="Advice, Tools, Tips and Tricks for Full Time Van, RV, and School Bus Life." itemprop="url"> <span itemprop="title">Guides</span></a> → </li> + <li itemprop="title">{{object.short_title|smartypants|safe}}</li> + </ul> + <article class="h-entry hentry {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/Article"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post-title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}</h1> + <h2 class="post-subtitle">{{object.sub_title|smartypants|safe}}</h2> + <div class="post-linewrapper"> + {% if object.originally_published_by %}<h4 class="post-source">Originally Published By: <a href="{{object.originally_published_by_url}}" title="View {{object.title}} on {{object.originally_published_by}}">{{object.originally_published_by}}</a></h4>{%endif%} + {% if object.location %}<div class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + <h3 class="h-adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">{% if object.location.country_name == "United States" %}<span class="p-locality locality" itemprop="addressLocality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name|safe}}</a>, <span class="p-country-name" itemprop="addressCountry">U.S.</span>{%else%}<span class="p-region" itemprop="addressRegion">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}"><span itemprop="addressCountry">{{object.location.country_name|safe}}</span></a>{%endif%}</h3> + – <a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a> + </div>{%endif%} + <h3>Filed Under: <a href="/guides/">Guides</a>, {% for tag in object.tags.all %}<a href="/guides/{{tag.slug}}">{{tag}}</a>{%endfor%}</h3> + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, 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 post--body--{% with object.template_name as t %}{%if t == 0 or t == 2 %}single{%endif%}{%if t == 1 or t == 3 %}double{%endif%}{%endwith%} post-guide" itemprop="articleBody"> + {% if object.preamble %}<div class="afterward"> + {{object.preamble_html|smartypants|safe}} + </div>{%endif%} + {{object.body_html|safe|smartypants}} + </div> + {% if object.afterword_html %}<div class="afterward"> + <h4>Afterward</h4> + {{object.afterword_html|smartypants|safe}} + </div>{%endif%} + {%if wildlife or object.field_notes.all or object.books.all %}<div class="entry-footer">{%if wildlife %} + <aside id="wildlife"> + <h3>Fauna and Flora</h3> + {% regroup wildlife by ap.apclass.get_kind_display as wildlife_list %} + <ul> + {% for object_list in wildlife_list %} + <li class="grouper">{{object_list.grouper}}<ul> + {% for object in object_list.list %} + <li>{%if object.ap.body_markdown%}<a href="{% url 'sightings:detail' object.ap.slug %}">{{object}}</a>{%else%}{{object}}{%endif%} </li> + {% endfor %}</ul> + {% endfor %}</ul> + </aside> + {% endif %}{%if object.field_notes.all %} + <aside {% if wildlife %}class="margin-left-none" {%endif%}id="field_notes"> + <h3>Field Notes</h3> + <ul>{% for obj in object.field_notes.all %} + <li><a href="{% url 'fieldnotes:detail' year=obj.pub_date.year month=obj.pub_date|date:"m" slug=obj.slug %}">{{obj}}</a></li> + {% endfor %}</ul> + </aside>{% endif %} + {%if object.books.all %} + <aside id="recommended-reading" {%if object.field_notes.all and wildlife %}class="rr-clear{%endif%}" > + <h3>Recommended Reading</h3> + <ul>{% for obj in object.books.all %} + <li><a href="{% url 'books:detail' slug=obj.slug %}"><img src="{{obj.get_small_image_url}}" /></a></li> + {% endfor %}</ul> + </aside>{% endif %} + </div>{%endif%} + </article> + + {% comment %} <div class="mailing-list--wrapper"> + <h5>If you enjoyed this, you should join the mailing list…</h5> + {% include 'mailing_list.html' %} + </div> {% endcomment %} + </main> + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} +<p class="comments--header">{{comment_count}} Comment{{ comment_count|pluralize }}</p> +{% render_comment_list for object %} +{%endif%} +{% render_comment_form for object %} +{% else %} +<p class="comments--header" style="text-align: center">Sorry, comments have been disabled for this post.</p> +{%endif%} +{% endblock %} +{% block js %} +<script type="text/javascript"> +document.addEventListener("DOMContentLoaded", function(event) { + var leaflet = document.createElement('script'); + leaflet.src = "/media/js/leaflet-master/leaflet-mod.js"; + document.body.appendChild(leaflet); + var lightbox = document.createElement('script'); + lightbox.src = "/media/js/lightbox.js"; + document.body.appendChild(lightbox); + leaflet.onload = function(){ + var detail = document.createElement('script'); + detail.src = "/media/js/detail.min.js"; + document.body.appendChild(detail); + {% with object.get_template_name_display as t %}{%if t == "single" or t == "single-dark" %} + detail.onload = function(){ + createMap(); + var open = false; + } + {%endif%}{%endwith%} + } + + lightbox.onload = function() { + var opts= { + //nextOnClick: false, + captions: true, + onload: function(){ + var im = document.getElementById("jslghtbx-contentwrapper"); + var link = im.appendChild(document.createElement('a')) + link.href = im.firstChild.src; + link.innerHTML= "open "; + link.target = "_blank"; + link.setAttribute('class', 'p-link'); + im.appendChild(link); + } + }; + var lightbox = new Lightbox(); + lightbox.load(opts); + } + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} + //delay loading of gravatar images using noscript data-hash attribute + dataattr = document.getElementsByClassName("datahashloader"); + for(var i=0; i<dataattr.length; i++) { + var c = dataattr[i].parentNode; + var img = document.createElement("img"); + img.src = 'https://images.luxagraf.net/gravcache/' + dataattr[i].getAttribute('data-hash') + '.jpg'; + img.className += "gravatar"; + c.insertBefore(img, c.childNodes[3]); + } +{%endif%} +{%endif%} +{% if object.has_video %} +var tester = document.getElementsByClassName("vidauto"); +var wrapper = document.getElementById('wrapper'); +var dist = 100; + +window.onscroll = function() { + for (var i=0; i<tester.length; i++) { + checkVisible(tester[i]) ? tester[i].play() : tester[i].pause(); + } +}; + +function checkVisible(elm) { + var rect = elm.getBoundingClientRect(); + var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight); + return !(rect.bottom < 0 || rect.top - viewHeight >= 0); +} +{%endif%} + +}); +</script> +{%endblock%} diff --git a/app/posts/templates/posts/post_list.html b/app/posts/templates/posts/post_list.html index a264a11..9ec3e25 100644 --- a/app/posts/templates/posts/post_list.html +++ b/app/posts/templates/posts/post_list.html @@ -2,19 +2,21 @@ {% load typogrify_tags %} {% load html5_datetime %} {% load pagination_tags %} -{% block pagetitle %}Guides for the Perplexed{% endblock %} -{% block metadescription %}Guides for fellow travelers: tools, tips, and tricks to make life on the road easier.{% endblock %} +{% block pagetitle %}Advice, Tools, Tips, and Tricks for Full Time Van or RV Life.{% endblock %} +{% block metadescription %}Guides for fellow travelers: tools, tips, and tricks to make life on the road in an RV or Van easier and more enjoyable.{% endblock %} {% block primary %}<ul class="bl" id="breadcrumbs" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> <li><a href="/" title="luxagraf homepage" itemprop="url"><span itemprop="title">Home</span></a> → </li> - <li>Essays</li> + <li itemprop="title">{{archive_type}}</li> </ul> <main role="main" id="essay-archive" class="essay-archive archive-list"> <div class="essay-intro"> <h2>Guides for fellow travelers</h2> - <p>The less stuff you travel with the better off you will be, up to a point. But where is that point? What's enough? What's too much? That point is what I'm trying to discover here. </p> - <p>What do you really need? What's worth having? What's not?</p> - <p>Topics include {% for topic in topic_list %}{{topic}}, {% endfor %}travel, cooking, photography, writing, simplicity, and once, coffee.</p> + <h3>Advice, Tools, Tips, and Tricks for Full Time Van or RV Life.</h3> + <p>After {{years_on_the_road}} years on the road, here's what I know: all you really need is a van or RV, a way to cook, and way to keep food cool.</p> + <p>After that, it's all luxury. The less stuff you travel with the better off you will be. Well, up to a point. But where is that point? What do you really need? What should you skip? What's enough? What's too much?</p> + <p>Ultimately, these are questions you'll have to answer for yourself, and you won't answer them until you get out there and start living on the road full time. In the mean time, this is me answering those questions for myself, and hoping maybe it will help you find your own answers faster.</p> + <p>Topics include {% for topic in topic_list %}{{topic}}, {% endfor %}camping, cooking, photography, simplicity, and once or twice, coffee.</p> </div> <h1 class="topic-hed">Guides</h1> {% autopaginate object_list 30 %} diff --git a/app/posts/views.py b/app/posts/views.py index 0324d16..fc85fa7 100644 --- a/app/posts/views.py +++ b/app/posts/views.py @@ -26,6 +26,11 @@ class GuidesListView(PostList): def get_queryset(self): queryset = super(GuidesListView, self).get_queryset() return queryset.filter(post_type__in=[0,1]).filter(status__exact=1).order_by('-pub_date').prefetch_related('location').prefetch_related('featured_image') + + def get_context_data(self, **kwargs): + context = super(GuidesListView, self).get_context_data(**kwargs) + context['archive_type'] = 'Guides' + return context class EssayListView(PostList): @@ -53,6 +58,9 @@ class PostDetailView(DetailView): context['related'] = related return context + def get_template_names(self): + obj = self.get_object() + return ["posts/%s_detail.html" % obj.get_post_type_display(), 'posts/post_detail.html'] class PostDetailViewTXT(PostDetailView): template_name = "posts/entry_detail.txt" |