diff options
Diffstat (limited to 'app')
54 files changed, 1115 insertions, 23 deletions
diff --git a/app/links/admin.py b/app/links/admin.py index f967dde..136a851 100644 --- a/app/links/admin.py +++ b/app/links/admin.py @@ -26,7 +26,8 @@ class LinkAdmin(admin.ModelAdmin): ('Details', { 'fields': ( 'pub_date', - ('tags', 'image'), + ('oldtags', 'image'), + 'topics', 'status' ), 'classes': 'collapse' 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" diff --git a/app/src/admin.py b/app/src/admin.py index 815267c..f354b15 100644 --- a/app/src/admin.py +++ b/app/src/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Topic, Post, Book +from .models import Topic, SrcPost, Book from utils.widgets import LGEntryForm @@ -33,7 +33,7 @@ class BookAdmin(admin.ModelAdmin): ) -@admin.register(Post) +@admin.register(SrcPost) class PostAdmin(admin.ModelAdmin): form = LGEntryForm list_display = ('title', 'pub_date', 'enable_comments', 'status') diff --git a/app/src/migrations/0004_auto_20191007_0905.py b/app/src/migrations/0004_auto_20191007_0905.py new file mode 100644 index 0000000..6c223a0 --- /dev/null +++ b/app/src/migrations/0004_auto_20191007_0905.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-10-07 09:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('src', '0003_auto_20180707_0958'), + ] + + operations = [ + migrations.RenameModel( + old_name='Post', + new_name='SrcPost', + ), + ] diff --git a/app/src/models.py b/app/src/models.py index 90d58e4..2c9707e 100644 --- a/app/src/models.py +++ b/app/src/models.py @@ -30,7 +30,7 @@ class Topic(models.Model): return datetime.datetime.now() -class Post(models.Model): +class SrcPost(models.Model): title = models.CharField(max_length=200) slug = models.SlugField(unique_for_date='pub_date') body_html = models.TextField(blank=True) diff --git a/app/src/views.py b/app/src/views.py index f501637..f929949 100644 --- a/app/src/views.py +++ b/app/src/views.py @@ -6,7 +6,7 @@ from django.conf import settings #from paypal.standard.forms import PayPalPaymentsForm -from .models import Post, Topic, Book +from .models import SrcPost, Topic, Book class BookListView(ListView): @@ -47,7 +47,7 @@ class BookDetailView(DetailView): class SrcListView(ListView): def get_queryset(self): - return Post.objects.filter(status__exact=1) + return SrcPost.objects.filter(status__exact=1) def get_context_data(self, **kwargs): # Call the base implementation first to get a context @@ -57,7 +57,7 @@ class SrcListView(ListView): class EntryDetailView(DetailView): - model = Post + model = SrcPost slug_field = "slug" @@ -69,7 +69,7 @@ class TopicListView(ListView): template_name = 'src/topic_list.html' def get_queryset(self): - return Post.objects.filter(topics__slug=self.kwargs['slug']) + return SrcPost.objects.filter(topics__slug=self.kwargs['slug']) def get_context_data(self, **kwargs): # Call the base implementation first to get a context @@ -85,4 +85,4 @@ class SrcRSSFeedView(Feed): description_template = 'feeds/blog_description.html' def items(self): - return Post.objects.filter(status__exact=1).order_by('-pub_date')[:10] + return SrcPost.objects.filter(status__exact=1).order_by('-pub_date')[:10] diff --git a/app/taxonomy/migrations/0002_auto_20191007_0913.py b/app/taxonomy/migrations/0002_auto_20191007_0913.py new file mode 100644 index 0000000..fb53007 --- /dev/null +++ b/app/taxonomy/migrations/0002_auto_20191007_0913.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-10-07 09:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('taxonomy', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='category', + options={'verbose_name': 'Category', 'verbose_name_plural': 'Categories'}, + ), + ] diff --git a/app/taxonomy/models.py b/app/taxonomy/models.py index 18d7ae3..9479a7f 100644 --- a/app/taxonomy/models.py +++ b/app/taxonomy/models.py @@ -16,7 +16,7 @@ class LuxTag(TagBase): @cached_property def get_absolute_url(self): - return reverse("taxonomy:tags", kwargs={"slug": self.slug}) + return reverse("taxonomy:cat-detail", kwargs={"slug": self.slug}) class TaggedItems(GenericTaggedItemBase): @@ -40,4 +40,4 @@ class Category(models.Model): verbose_name_plural = _("Categories") def get_absolute_url(self): - return reverse("taxonomy:categories", kwargs={"slug": self.slug}) + return reverse("taxonomy:cat-detail", kwargs={"slug": self.slug}) diff --git a/app/taxonomy/urls.py b/app/taxonomy/urls.py new file mode 100644 index 0000000..882bd52 --- /dev/null +++ b/app/taxonomy/urls.py @@ -0,0 +1,13 @@ +from django.urls import path, re_path + +from . import views + +app_name = "taxonomy" + +urlpatterns = [ + path( + r'<slug>', + views.CategoryDetailView.as_view(), + name="cat-detail" + ), +] diff --git a/app/taxonomy/views.py b/app/taxonomy/views.py new file mode 100644 index 0000000..2d749ab --- /dev/null +++ b/app/taxonomy/views.py @@ -0,0 +1,14 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.contrib.syndication.views import Feed +from django.urls import reverse +from django.conf import settings + +#from paypal.standard.forms import PayPalPaymentsForm + +from .models import Category + + +class CategoryDetailView(DetailView): + model = Category + slug_field = "slug" diff --git a/app/essays/__init__.py b/app/unused_apps/essays/__init__.py index e69de29..e69de29 100644 --- a/app/essays/__init__.py +++ b/app/unused_apps/essays/__init__.py diff --git a/app/essays/admin.py b/app/unused_apps/essays/admin.py index ed39ca3..ed39ca3 100644 --- a/app/essays/admin.py +++ b/app/unused_apps/essays/admin.py diff --git a/app/essays/build.py b/app/unused_apps/essays/build.py index 392e991..392e991 100644 --- a/app/essays/build.py +++ b/app/unused_apps/essays/build.py diff --git a/app/essays/migrations/0001_initial.py b/app/unused_apps/essays/migrations/0001_initial.py index 7b7ea62..7b7ea62 100644 --- a/app/essays/migrations/0001_initial.py +++ b/app/unused_apps/essays/migrations/0001_initial.py diff --git a/app/essays/migrations/0002_auto_20190204_1541.py b/app/unused_apps/essays/migrations/0002_auto_20190204_1541.py index f4e6744..f4e6744 100644 --- a/app/essays/migrations/0002_auto_20190204_1541.py +++ b/app/unused_apps/essays/migrations/0002_auto_20190204_1541.py diff --git a/app/essays/migrations/0003_essay_afterword_html.py b/app/unused_apps/essays/migrations/0003_essay_afterword_html.py index 5f8301b..5f8301b 100644 --- a/app/essays/migrations/0003_essay_afterword_html.py +++ b/app/unused_apps/essays/migrations/0003_essay_afterword_html.py diff --git a/app/essays/migrations/0004_auto_20190205_0830.py b/app/unused_apps/essays/migrations/0004_auto_20190205_0830.py index 65e2e5d..65e2e5d 100644 --- a/app/essays/migrations/0004_auto_20190205_0830.py +++ b/app/unused_apps/essays/migrations/0004_auto_20190205_0830.py diff --git a/app/essays/migrations/0005_auto_20190208_0946.py b/app/unused_apps/essays/migrations/0005_auto_20190208_0946.py index 5b68bb4..5b68bb4 100644 --- a/app/essays/migrations/0005_auto_20190208_0946.py +++ b/app/unused_apps/essays/migrations/0005_auto_20190208_0946.py diff --git a/app/essays/migrations/0006_auto_20190303_1625.py b/app/unused_apps/essays/migrations/0006_auto_20190303_1625.py index dde70fd..dde70fd 100644 --- a/app/essays/migrations/0006_auto_20190303_1625.py +++ b/app/unused_apps/essays/migrations/0006_auto_20190303_1625.py diff --git a/app/essays/migrations/0006_remove_essay_has_video.py b/app/unused_apps/essays/migrations/0006_remove_essay_has_video.py index 0842d8b..0842d8b 100644 --- a/app/essays/migrations/0006_remove_essay_has_video.py +++ b/app/unused_apps/essays/migrations/0006_remove_essay_has_video.py diff --git a/app/essays/migrations/0007_auto_20190414_1455.py b/app/unused_apps/essays/migrations/0007_auto_20190414_1455.py index a5242cb..a5242cb 100644 --- a/app/essays/migrations/0007_auto_20190414_1455.py +++ b/app/unused_apps/essays/migrations/0007_auto_20190414_1455.py diff --git a/app/essays/migrations/0007_essay_has_video.py b/app/unused_apps/essays/migrations/0007_essay_has_video.py index 0057e95..0057e95 100644 --- a/app/essays/migrations/0007_essay_has_video.py +++ b/app/unused_apps/essays/migrations/0007_essay_has_video.py diff --git a/app/essays/migrations/0008_merge_20190303_1638.py b/app/unused_apps/essays/migrations/0008_merge_20190303_1638.py index 7c155d8..7c155d8 100644 --- a/app/essays/migrations/0008_merge_20190303_1638.py +++ b/app/unused_apps/essays/migrations/0008_merge_20190303_1638.py diff --git a/app/essays/migrations/0009_merge_20190414_1500.py b/app/unused_apps/essays/migrations/0009_merge_20190414_1500.py index 83a8323..83a8323 100644 --- a/app/essays/migrations/0009_merge_20190414_1500.py +++ b/app/unused_apps/essays/migrations/0009_merge_20190414_1500.py diff --git a/app/essays/migrations/0010_essay_field_notes.py b/app/unused_apps/essays/migrations/0010_essay_field_notes.py index ca15b38..ca15b38 100644 --- a/app/essays/migrations/0010_essay_field_notes.py +++ b/app/unused_apps/essays/migrations/0010_essay_field_notes.py diff --git a/app/essays/migrations/__init__.py b/app/unused_apps/essays/migrations/__init__.py index e69de29..e69de29 100644 --- a/app/essays/migrations/__init__.py +++ b/app/unused_apps/essays/migrations/__init__.py diff --git a/app/essays/models.py b/app/unused_apps/essays/models.py index c75f72d..c75f72d 100644 --- a/app/essays/models.py +++ b/app/unused_apps/essays/models.py diff --git a/app/essays/urls.py b/app/unused_apps/essays/urls.py index 8216f06..8216f06 100644 --- a/app/essays/urls.py +++ b/app/unused_apps/essays/urls.py diff --git a/app/essays/views.py b/app/unused_apps/essays/views.py index f8c68c7..f8c68c7 100644 --- a/app/essays/views.py +++ b/app/unused_apps/essays/views.py diff --git a/app/unused_apps/guides/__init__.py b/app/unused_apps/guides/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/unused_apps/guides/__init__.py diff --git a/app/unused_apps/guides/admin.py b/app/unused_apps/guides/admin.py new file mode 100644 index 0000000..7895aba --- /dev/null +++ b/app/unused_apps/guides/admin.py @@ -0,0 +1,67 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin + +from utils.widgets import LGEntryForm +from utils.util import get_latlon + +from .models import Guide + + +@admin.register(Guide) +class GuideAdmin(OSMGeoAdmin): + form = LGEntryForm + list_display = ('title', 'pub_date', 'enable_comments', 'status', 'post_type') + list_filter = ('pub_date', 'enable_comments', 'status') + prepopulated_fields = {"slug": ('title',)} + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'sub_title', + 'body_markdown', + ('pub_date', 'status', 'post_type', 'disclaimer'), + 'meta_description', + 'featured_image', + 'dek', + 'tags', + 'prologue_markdown', + 'epilogue_markdown', + 'has_video', + ('slug', 'enable_comments'), + 'point', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ('meta', { + 'fields': ( + ('field_notes', 'books','jrnl'), + ), + 'classes': ( + 'hide', + 'extrapretty', + 'wide' + ) + }), + ) + + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } diff --git a/app/unused_apps/guides/build.py b/app/unused_apps/guides/build.py new file mode 100644 index 0000000..392e991 --- /dev/null +++ b/app/unused_apps/guides/build.py @@ -0,0 +1,22 @@ +import os +from builder.base import BuildNew +from django.urls import reverse +from . import models + + +class BuildEssays(BuildNew): + + def build(self): + self.build_list_view() + self.build_detail_view() + # These are the unique classes for this model: + #self.build_feed("src:feed") + + def build_list_view(self): + response = self.client.get('/essays/') + self.write_file('essays/', response.content) + + +def essaybuilder(): + j = BuildEssays("essays", "essay") + j.build() diff --git a/app/unused_apps/guides/guide_urls.py b/app/unused_apps/guides/guide_urls.py new file mode 100644 index 0000000..ad67061 --- /dev/null +++ b/app/unused_apps/guides/guide_urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path + +from . import views + +app_name = "guide" + +urlpatterns = [ + path( + r'<str:slug>', + views.GuideDetailView.as_view(), + name="guide-detail" + ), + path( + r'<str:slug>.txt', + views.GuideDetailViewTXT.as_view(), + name="guide-detail-txt" + ), +] diff --git a/app/unused_apps/guides/migrations/0001_initial.py b/app/unused_apps/guides/migrations/0001_initial.py new file mode 100644 index 0000000..833dfbe --- /dev/null +++ b/app/unused_apps/guides/migrations/0001_initial.py @@ -0,0 +1,60 @@ +# Generated by Django 2.1.7 on 2019-07-04 09:03 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('jrnl', '0043_auto_20190704_0903'), + ('books', '0009_book_afflink'), + ('photos', '0019_auto_20190704_0903'), + ('fieldnotes', '0002_auto_20190303_1222'), + ('locations', '0018_auto_20190414_2124'), + ('essays', '0010_essay_field_notes'), + ('taxonomy', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Guide', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('sub_title', models.CharField(blank=True, max_length=200)), + ('dek', models.TextField(blank=True)), + ('preamble_markdown', models.TextField(blank=True)), + ('preamble_html', models.TextField(blank=True)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('last_updated', models.DateTimeField(auto_now=True)), + ('enable_comments', models.BooleanField(default=False)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('has_video', models.BooleanField(blank=True, default=False)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('afterword', models.TextField(blank=True)), + ('afterword_html', models.TextField(blank=True)), + ('books', models.ManyToManyField(blank=True, to='books.Book')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='taxonomy.Category')), + ('essays', models.ManyToManyField(blank=True, to='essays.Essay')), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage')), + ('field_notes', models.ManyToManyField(blank=True, to='fieldnotes.FieldNote')), + ('jrnl', models.ManyToManyField(blank=True, to='jrnl.Entry')), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='Topics Covered', through='taxonomy.TaggedItems', to='taxonomy.LuxTag', verbose_name='Tags')), + ], + options={ + 'verbose_name_plural': 'Guides', + 'ordering': ('-pub_date',), + 'get_latest_by': 'pub_date', + }, + ), + ] diff --git a/app/unused_apps/guides/migrations/0002_remove_guide_category.py b/app/unused_apps/guides/migrations/0002_remove_guide_category.py new file mode 100644 index 0000000..76eefa6 --- /dev/null +++ b/app/unused_apps/guides/migrations/0002_remove_guide_category.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-07-04 12:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='guide', + name='category', + ), + ] diff --git a/app/unused_apps/guides/migrations/0003_guide_category.py b/app/unused_apps/guides/migrations/0003_guide_category.py new file mode 100644 index 0000000..82d03ac --- /dev/null +++ b/app/unused_apps/guides/migrations/0003_guide_category.py @@ -0,0 +1,21 @@ +# Generated by Django 2.1.7 on 2019-09-08 17:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('taxonomy', '0001_initial'), + ('guides', '0002_remove_guide_category'), + ] + + operations = [ + migrations.AddField( + model_name='guide', + name='category', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='taxonomy.Category'), + preserve_default=False, + ), + ] diff --git a/app/unused_apps/guides/migrations/0004_auto_20190914_0646.py b/app/unused_apps/guides/migrations/0004_auto_20190914_0646.py new file mode 100644 index 0000000..ccd79a5 --- /dev/null +++ b/app/unused_apps/guides/migrations/0004_auto_20190914_0646.py @@ -0,0 +1,47 @@ +# Generated by Django 2.1.7 on 2019-09-14 06:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0003_guide_category'), + ] + + operations = [ + migrations.RenameField( + model_name='guide', + old_name='afterword', + new_name='epilogue_html', + ), + migrations.RenameField( + model_name='guide', + old_name='afterword_html', + new_name='epilogue_markdown', + ), + migrations.RenameField( + model_name='guide', + old_name='preamble_html', + new_name='prologue_html', + ), + migrations.RenameField( + model_name='guide', + old_name='preamble_markdown', + new_name='prologue_markdown', + ), + migrations.RemoveField( + model_name='guide', + name='essays', + ), + migrations.AddField( + model_name='guide', + name='disclaimer', + field=models.BooleanField(blank=True, default=False), + ), + migrations.AddField( + model_name='guide', + name='post_type', + field=models.IntegerField(choices=[(0, 'guide'), (1, 'review')], default=0), + ), + ] diff --git a/app/unused_apps/guides/migrations/0005_remove_guide_category.py b/app/unused_apps/guides/migrations/0005_remove_guide_category.py new file mode 100644 index 0000000..71d0280 --- /dev/null +++ b/app/unused_apps/guides/migrations/0005_remove_guide_category.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-09-14 07:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0004_auto_20190914_0646'), + ] + + operations = [ + migrations.RemoveField( + model_name='guide', + name='category', + ), + ] diff --git a/app/unused_apps/guides/migrations/__init__.py b/app/unused_apps/guides/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/unused_apps/guides/migrations/__init__.py diff --git a/app/unused_apps/guides/models.py b/app/unused_apps/guides/models.py new file mode 100644 index 0000000..8704887 --- /dev/null +++ b/app/unused_apps/guides/models.py @@ -0,0 +1,136 @@ +from django.contrib.gis.db import models +from django.urls import reverse +from django.contrib.sitemaps import Sitemap +import datetime +from itertools import chain + +from taggit.managers import TaggableManager + +from taxonomy.models import TaggedItems, Category +from utils.util import render_images, markdown_to_html +from fieldnotes.models import FieldNote +from books.models import Book +from locations.models import Location +from photos.models import LuxImage +from essays.models import Essay +from jrnl.models import Entry + + +class Guide(models.Model): + title = models.CharField(max_length=200) + sub_title = models.CharField(max_length=200, blank=True) + dek = models.TextField(blank=True) + prologue_markdown = models.TextField(blank=True) + prologue_html = models.TextField(blank=True) + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + epilogue_markdown = models.TextField(blank=True) + epilogue_html = models.TextField(blank=True) + slug = models.SlugField(unique_for_date='pub_date') + pub_date = models.DateTimeField('Date published') + last_updated = models.DateTimeField(auto_now=True) + enable_comments = models.BooleanField(default=False) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + POST_TYPE = ( + (0, 'guide'), + (1, 'review'), + ) + post_type = models.IntegerField(choices=POST_TYPE, default=0) + meta_description = models.CharField(max_length=256, null=True, blank=True) + tags = TaggableManager(through=TaggedItems, blank=True, help_text='Topics Covered') + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) + has_video = models.BooleanField(blank=True, default=False) + disclaimer = models.BooleanField(blank=True, default=False) + field_notes = models.ManyToManyField(FieldNote, blank=True) + books = models.ManyToManyField(Book, blank=True) + jrnl = models.ManyToManyField(Entry, blank=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'Guides' + + def __str__(self): + return self.title + + def get_absolute_url(self): + if self.post_type == 0: + return reverse('guide:guide-detail', kwargs={"slug": self.slug}) + if self.post_type == 1: + return reverse('review:review-detail', kwargs={"slug": self.slug}) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + @property + def longitude(self): + '''Get the site's longitude.''' + return self.point.x + + @property + def latitude(self): + '''Get the site's latitude.''' + return self.point.y + + 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.prologue_html = markdown_to_html(self.prologue_markdown) + self.epilogue_html = markdown_to_html(self.epilogue_markdown) + self.has_video = parse_video(self.body_html) + if self.point: + try: + 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)) + if created and not self.featured_image: + self.featured_image = LuxImage.objects.latest() + old = type(self).objects.get(pk=self.pk) if self.pk else None + if old and old.featured_image != self.featured_image: # Field has changed + s = LuxImageSize.objects.get(name="featured_jrnl") + ss = LuxImageSize.objects.get(name="picwide-med") + self.featured_image.sizes.add(s) + self.featured_image.sizes.add(ss) + self.featured_image.save() + super(Guide, self).save(*args, **kwargs) + + +class GuideSitemap(Sitemap): + changefreq = "never" + priority = 1.0 + protocol = "https" + + def items(self): + return Guide.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/app/unused_apps/guides/review_urls.py b/app/unused_apps/guides/review_urls.py new file mode 100644 index 0000000..7048065 --- /dev/null +++ b/app/unused_apps/guides/review_urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path + +from . import views + +app_name = "review" + +urlpatterns = [ + path( + r'<str:slug>', + views.GuideDetailView.as_view(), + name="review-detail" + ), + path( + r'<str:slug>.txt', + views.GuideDetailViewTXT.as_view(), + name="review-detail-txt" + ), +] diff --git a/app/unused_apps/guides/templates/guides/guide_detail.html b/app/unused_apps/guides/templates/guides/guide_detail.html new file mode 100644 index 0000000..1ef602a --- /dev/null +++ b/app/unused_apps/guides/templates/guides/guide_detail.html @@ -0,0 +1,176 @@ +{% 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> + <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%} + <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-essay" 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/unused_apps/guides/templates/guides/guide_detail.txt b/app/unused_apps/guides/templates/guides/guide_detail.txt new file mode 100644 index 0000000..547ce79 --- /dev/null +++ b/app/unused_apps/guides/templates/guides/guide_detail.txt @@ -0,0 +1,8 @@ +{{object.title|safe}} +{% for letter in object.title %}={%endfor%} + + by Scott Gilbertson + <{{SITE_URL}}{{object.get_absolute_url}}> + {{object.pub_date|date:"l, d F Y"}} + +{{object.body_markdown|safe}} diff --git a/app/unused_apps/guides/templates/guides/guide_list.html b/app/unused_apps/guides/templates/guides/guide_list.html new file mode 100644 index 0000000..a264a11 --- /dev/null +++ b/app/unused_apps/guides/templates/guides/guide_list.html @@ -0,0 +1,40 @@ +{% extends 'base.html' %} +{% 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 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> + </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> + </div> + <h1 class="topic-hed">Guides</h1> + {% autopaginate object_list 30 %} + <ul class="fancy-archive-list">{% for object in object_list %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}" class="u-url"> + <div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" class="u-photo" /></div> + <span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li> + {%endfor%}</ul> + </main> +{%endblock%} diff --git a/app/unused_apps/guides/urls.py b/app/unused_apps/guides/urls.py new file mode 100644 index 0000000..c91a5b5 --- /dev/null +++ b/app/unused_apps/guides/urls.py @@ -0,0 +1,34 @@ +from django.urls import path, re_path + +from . import views + +app_name = "guides" + +urlpatterns = [ + path( + r'', + views.GuideListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.GuideListView.as_view(), + name="list" + ), + path( + r'<str:category>/<str:slug>', + views.GuideDetailView.as_view(), + name="detail" + ), + path( + r'<str:category>/<str:slug>', + views.GuideDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<str:category>', + views.GuideCatListView.as_view(), + name="list-cat" + ), +] diff --git a/app/unused_apps/guides/views.py b/app/unused_apps/guides/views.py new file mode 100644 index 0000000..01dc974 --- /dev/null +++ b/app/unused_apps/guides/views.py @@ -0,0 +1,62 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.contrib.syndication.views import Feed + +from utils.views import PaginatedListView + +from .models import Guide + + +class GuideListView(PaginatedListView): + model = Guide + + def get_queryset(self, **kwargs): + qs = Guide.objects.filter(status=1) + return qs + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(GuideListView, self).get_context_data(**kwargs) + context['topic_list'] = Guide.tags.all() + return context + + +class GuideCatListView(GuideListView): + model = Guide + + def get_queryset(self, **kwargs): + cat = Category.objects.get(slug=self.kwargs['slug']) + qs = Guide.objects.filter(status=1, tags=cat) + return qs + +class GuideDetailView(DetailView): + model = Guide + + +class GuideDetailViewTXT(GuideDetailView): + template_name = "essays/entry_detail.txt" + + +''' +class TopicListView(ListView): + template_name = 'archives/src_home.html' + + def queryset(self): + return Post.objects.filter(topics__slug=self.kwargs['slug']) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(TopicListView, self).get_context_data(**kwargs) + context['topic'] = Topic.objects.get(slug__exact=self.kwargs['slug']) + return context + + +class SrcRSSFeedView(Feed): + title = "luxagraf:src Code and Technology" + link = "/src/" + description = "Latest postings to luxagraf.net/src" + description_template = 'feeds/blog_description.html' + + def items(self): + return Post.objects.filter(status__exact=1).order_by('-pub_date')[:10] +''' diff --git a/app/unused_apps/sketches/migrations/0003_auto_20190303_1057.py b/app/unused_apps/sketches/migrations/0003_auto_20190303_1057.py new file mode 100644 index 0000000..87cfbf1 --- /dev/null +++ b/app/unused_apps/sketches/migrations/0003_auto_20190303_1057.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.7 on 2019-03-03 10:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sketches', '0002_auto_20180208_0743'), + ] + + operations = [ + migrations.AddField( + model_name='sketch', + name='subtitle', + field=models.CharField(blank=True, max_length=250), + ), + migrations.AlterField( + model_name='sketch', + name='title', + field=models.CharField(blank=True, default='', max_length=250), + preserve_default=False, + ), + ] |