summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2015-11-13 11:09:59 -0500
committerluxagraf <sng@luxagraf.net>2015-11-13 11:09:59 -0500
commit8d91fcde168b141ffa33173380e75739fdf45cd2 (patch)
tree8b9a4e493461df5bec0f60b846d0ce3e48d3f43c
parent92abffcc98404825324341d5ee654642364890bb (diff)
rewrote Links to make them more browse/earacble on live site and in
admin, also ported to CBVs/new build system and added back some templates for viewing. Deleted tumblr integration.
-rw-r--r--app/links/admin.py4
-rw-r--r--app/links/detail_urls.py10
-rw-r--r--app/links/models.py9
-rw-r--r--app/links/retriever.py34
-rw-r--r--app/links/tumblr.py240
-rw-r--r--app/links/urls.py48
-rw-r--r--app/links/views.py33
-rw-r--r--app/utils/widgets.py38
-rw-r--r--config/base_urls.py4
-rw-r--r--design/sass/_inbox.scss11
-rw-r--r--design/templates/archives/links.html23
-rw-r--r--design/templates/details/link.html19
12 files changed, 174 insertions, 299 deletions
diff --git a/app/links/admin.py b/app/links/admin.py
index f80e665..cbfbc52 100644
--- a/app/links/admin.py
+++ b/app/links/admin.py
@@ -1,11 +1,13 @@
from django.contrib import admin
+
+from utils.widgets import TagListFilter
from links.models import Link
class LinkAdmin(admin.ModelAdmin):
list_display = ('title', 'admin_link', 'rating', 'pub_date', 'status')
search_fields = ['title', 'description', 'url']
- list_filter = ['rating', 'status']
+ list_filter = ['rating', 'status', TagListFilter]
fieldsets = (
(None, {
'fields': (
diff --git a/app/links/detail_urls.py b/app/links/detail_urls.py
deleted file mode 100644
index 9c76a7f..0000000
--- a/app/links/detail_urls.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from django.conf.urls import *
-from django.views.generic.base import RedirectView
-from django.views.generic.detail import DetailView
-from links.models import Link
-
-
-urlpatterns = patterns('',
- (r'^$', RedirectView.as_view(url="/links/1/")),
- (r'^(?P<object_id>\d+)/$', DetailView.as_view(model=Link, template_name='details/link.html')),
-)
diff --git a/app/links/models.py b/app/links/models.py
index 11a6798..cdfd786 100644
--- a/app/links/models.py
+++ b/app/links/models.py
@@ -1,10 +1,13 @@
import datetime
+from django.core.urlresolvers import reverse
from django.utils.encoding import force_text
from django.db import models
from django.contrib.sitemaps import Sitemap
from django.contrib.syndication.views import Feed
from taggit.managers import TaggableManager
+import markdown
+
RATINGS = (
('1', "1 Star"),
('2', "2 Stars"),
@@ -36,10 +39,10 @@ class Link(models.Model):
return self.title
def get_absolute_url(self):
- return self.url
+ return reverse("links:detail", kwargs={"slug": self.pk})
- def get_model_name(self):
- return 'link'
+ def render_description(self):
+ return markdown.markdown(self.description, extensions=['extra'], safe_mode=False)
def get_previous_published(self):
return self.get_previous_by_pub_date(status__exact=1)
diff --git a/app/links/retriever.py b/app/links/retriever.py
index 9aec34d..843487a 100644
--- a/app/links/retriever.py
+++ b/app/links/retriever.py
@@ -6,19 +6,18 @@ from django.core.mail import EmailMessage
from django.conf import settings
from links.models import Link
-# https://github.com/mgan59/python-pinboard/
-#import pinboard
import requests
import json
+
def sync_pinboard_links():
PB_URL = "https://api.pinboard.in/v1/posts/all?results=70&format=json"
r = requests.get(PB_URL, auth=((settings.PIN_USER, settings.PIN_PASS)))
links = json.loads(r.text)
for link in links:
try:
- #check to see if link exists
+ # check to see if link exists
row = Link.objects.get(link_id=link['hash'])
print("already have" + row.title)
except ObjectDoesNotExist:
@@ -37,35 +36,6 @@ def sync_pinboard_links():
l.tags.add(t)
email_link(l)
-"""
-def sync_pinboard_links_old():
- sync bookmarks from my pinboard account
- dependancies: python-pinboard https://github.com/mgan59/python-pinboard/
- p = pinboard.open(settings.PIN_USER, settings.PIN_PASS)
- dupe = False
- links = p.posts(count=30)
- for link in links:
- try:
- #check to see if link exists
- row = Link.objects.get(link_id=safestr(link['hash']))
- except ObjectDoesNotExist:
- l, created = Link.objects.get_or_create(
- title=link['description'],
- link_id=safestr(link['hash']),
- url=safestr(link['href']),
- description=safestr(link['extended']),
- rating="3",
- pub_date=datetime.datetime.strptime(link['time'], "%Y-%m-%dT%H:%M:%SZ"),
- status=0
- )
- print l.title
- if created:
- print l.title
- for t in link['tags']:
- l.tags.add(t)
- email_link(l)
-
-"""
def email_link(link):
"""
diff --git a/app/links/tumblr.py b/app/links/tumblr.py
deleted file mode 100644
index 8c0e286..0000000
--- a/app/links/tumblr.py
+++ /dev/null
@@ -1,240 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright 2008 Ryan Cox ( ryan.a.cox@gmail.com ) All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-'''A wrapper library for Tumblr's public web API: http://www.tumblr.com/api'''
-
-__author__ = 'ryan.a.cox@gmail.com'
-__version__ = '0.1'
-
-
-from urllib2 import Request, urlopen, URLError, HTTPError
-from urllib import urlencode, quote
-import base64
-import re
-
-try:
- import simplejson
-except ImportError:
- from django.utils import simplejson
-
-GENERATOR = 'python-tumblr'
-PAGESIZE = 50
-
-
-class TumblrError(Exception):
- ''' General Tumblr error '''
- def __init__(self, msg):
- self.msg = msg
-
- def __str__(self):
- return self.msg
-
-class TumblrAuthError(TumblrError):
- ''' Wraps a 403 result '''
- pass
-
-class TumblrRequestError(TumblrError):
- ''' Wraps a 400 result '''
- pass
-
-class TumblrIterator:
- def __init__(self, name, start, max, type):
- self.name = name
- self.start = start
- self.max = max
- self.type = type
- self.results = None
- self.index = 0
-
- def __iter__(self):
- return self
-
- def next(self):
- if not self.results or (self.index == len(self.results['posts'])):
- self.start += self.index
- self.index = 0
- url = "http://%s.tumblr.com/api/read/json?start=%s&num=%s" % (self.name,self.start, PAGESIZE)
- if self.type:
- url += "&type=" + self.type
- response = urlopen(url)
- page = response.read()
- m = re.match("^.*?({.*}).*$", page,re.DOTALL | re.MULTILINE | re.UNICODE)
- self.results = simplejson.loads(m.group(1))
-
- if (self.index >= self.max) or len(self.results['posts']) == 0:
- raise StopIteration
-
- self.index += 1
- return self.results['posts'][self.index-1]
-
-class Api:
- def __init__(self, name, email=None, password=None ):
- self.name = name
- self.is_authenticated = False
- self.email = email
- self.password = password
-
- def auth_check(self):
- if self.is_authenticated:
- return
- url = 'http://www.tumblr.com/api/write'
- values = {
- 'action': 'authenticate',
- 'generator' : GENERATOR,
- 'email': self.email,
- 'password' : self.password }
-
- data = urlencode(values)
- req = Request(url, data)
- try:
- response = urlopen(req)
- page = response.read()
- self.url = page
- self.is_authenticated = True
- return
- except HTTPError, e:
- if 403 == e.code:
- raise TumblrAuthError(str(e))
- if 400 == e.code:
- raise TumblrRequestError(str(e))
- except Exception, e:
- raise TumblrError(str(e))
-
-
- def write_regular(self, title=None, body=None, **args):
- if title:
- args['title'] = title
- if body:
- args['body'] = body
- args = self._fixnames(args)
- if not 'title' in args and not 'body' in args:
- raise TumblrError("Must supply either body or title argument")
-
- self.auth_check()
- args['type'] = 'regular'
- return self._write(args)
-
- def write_photo(self, source=None, **args):
- if source:
- args['source'] = source
-
- args = self._fixnames(args)
- if 'source' in args and 'data' in args:
- raise TumblrError("Must NOT supply both source and data arguments")
-
- if not 'source' in args and not 'data' in args:
- raise TumblrError("Must supply source or data argument")
-
- self.auth_check()
- args['type'] = 'photo'
- return self._write(args)
-
- def write_quote(self, quote=None, **args):
- if quote:
- args['quote'] = quote
- args = self._fixnames(args)
- if not 'quote' in args:
- raise TumblrError("Must supply quote arguments")
-
- self.auth_check()
- args['type'] = 'quote'
- return self._write(args)
-
- def write_link(self, url=None, **args):
- if url:
- args['url'] = url
- args = self._fixnames(args)
- if not 'url' in args:
- raise TumblrError("Must supply url argument")
-
- self.auth_check()
- args['type'] = 'link'
- return self._write(args)
-
- def write_conversation(self, conversation=None, **args):
- if conversation:
- args['conversation'] = conversation
- args = self._fixnames(args)
- if not 'conversation' in args:
- raise TumblrError("Must supply conversation argument")
-
- self.auth_check()
- args['type'] = 'conversation'
- return self._write(args)
-
- def write_video(self, embed=None, **args):
- if embed:
- args['embed'] = embed
- args = self._fixnames(args)
- if 'embed' in args and 'data' in args:
- raise TumblrError("Must NOT supply both embed and data arguments")
-
- if not 'embed' in args and not 'data' in args:
- raise TumblrError("Must supply embed or data argument")
-
- self.auth_check()
- args['type'] = 'video'
- return self._write(args)
-
- def _fixnames(self, args):
- for key in args:
- if '_' in key:
- value = args[key]
- del args[key]
- args[key.replace('_', '-')] = value
- return args
-
- def _write(self, params, headers=None):
- self.auth_check()
- url = 'http://www.tumblr.com/api/write'
- params['email'] = self.email
- params['password'] = self.password
- params['generator'] = GENERATOR
- data = urlencode(params)
- if headers:
- req = Request(url, data, headers)
- else:
- req = Request(url, data)
- newid = None
- try:
- urlopen(req)
- raise TumblrError("Error writing post")
-
- except HTTPError, e:
- if 201 == e.code:
- newid = e.read()
- return self.read(id=newid)
- raise TumblrError(e.read())
-
- def read(self, id=None, start=0,max=2**31-1,type=None):
- if id:
- url = "http://%s.tumblr.com/api/read/json?id=%s" % (self.name,id)
- print url
- response = urlopen(url)
- page = response.read()
- m = re.match("^.*?({.*}).*$", page,re.DOTALL | re.MULTILINE | re.UNICODE)
- results = simplejson.loads(m.group(1))
- if len(results['posts']) == 0:
- return None
-
- return results['posts'][0]
- else:
- return TumblrIterator(self.name,start,max,type)
-
-if __name__ == "__main__":
- pass
diff --git a/app/links/urls.py b/app/links/urls.py
index fa8e042..5fdaf30 100644
--- a/app/links/urls.py
+++ b/app/links/urls.py
@@ -1,15 +1,37 @@
-from django.conf.urls import *
-from django.views.generic import list_detail
-from django.views.generic.simple import redirect_to
-from links.models import Link
+from django.conf.urls import url
+from django.views.generic.base import RedirectView
-photos_paged = {
- 'queryset': Link.objects.filter(status__exact=1).order_by('-pub_date'),
- 'paginate': True,
- 'page_url': '/links/%d/',
-}
+from . import views
-urlpatterns = patterns('',
- (r'^(?P<page>\d+)/$', list_detail.object_list, dict(photos_paged, template_name='archives/links.html')),
- (r'^$', redirect_to, {'url': '/links/1/'}),
-)
+urlpatterns = [
+ url(
+ regex=r'tag/(?P<slug>[-\w]+)/(?P<page>\d+)/$',
+ view=views.LinkTagListView.as_view(),
+ name='list-tag'
+ ),
+ url(
+ regex=r'(?P<slug>[-\d]+).txt$',
+ view=views.LinkDetailViewTXT.as_view(),
+ name="detail-txt"
+ ),
+ url(
+ regex=r'(?P<slug>[-\d]+)$',
+ view=views.LinkDetailView.as_view(),
+ name='detail'
+ ),
+ url(
+ regex=r'(?P<page>\d+)/$',
+ view=views.LinkListView.as_view(),
+ name='list'
+ ),
+ url(
+ regex=r'^tag/(?P<slug>[-\w]+)/$',
+ view=RedirectView.as_view(url="/links/tag/%(slug)s/1/", permanent=False),
+ name="live-redirect-tag"
+ ),
+ url(
+ regex=r'^$',
+ view=RedirectView.as_view(url="/links/1/", permanent=False),
+ name="live-redirect"
+ ),
+]
diff --git a/app/links/views.py b/app/links/views.py
new file mode 100644
index 0000000..36ccca3
--- /dev/null
+++ b/app/links/views.py
@@ -0,0 +1,33 @@
+from django.views.generic.detail import DetailView
+from utils.views import PaginatedListView
+
+from .models import Link
+from taggit.models import Tag
+
+
+class LinkListView(PaginatedListView):
+ model = Link
+ template_name = 'archives/links.html'
+
+
+class LinkTagListView(PaginatedListView):
+ model = Link
+ template_name = 'archives/links.html'
+
+ def get_queryset(self):
+ return Link.objects.filter(tags__name=self.kwargs['slug'])
+
+ def get_context_data(self, **kwargs):
+ # Call the base implementation first to get a context
+ context = super(LinkTagListView, self).get_context_data(**kwargs)
+ context['tag'] = Tag.objects.get(slug__exact=self.kwargs['slug'])
+ return context
+
+class LinkDetailView(DetailView):
+ model = Link
+ template_name = "details/link.html"
+ slug_field = "id"
+
+
+class LinkDetailViewTXT(LinkDetailView):
+ template_name = "details/entry.txt"
diff --git a/app/utils/widgets.py b/app/utils/widgets.py
index dc0c4e0..944601e 100644
--- a/app/utils/widgets.py
+++ b/app/utils/widgets.py
@@ -1,10 +1,48 @@
import os
from django import forms
+from django.contrib import admin
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext_lazy as _
from django.conf import settings
+class TagListFilter(admin.SimpleListFilter):
+ # Human-readable title which will be displayed in the
+ # right admin sidebar just above the filter options.
+ title = _('tag')
+
+ # Parameter for the filter that will be used in the URL query.
+ parameter_name = 'tag'
+
+ def lookups(self, request, model_admin):
+ """
+ Returns a list of tuples. The first element in each
+ tuple is the coded value for the option that will
+ appear in the URL query. The second element is the
+ human-readable name for the option that will appear
+ in the right sidebar.
+ """
+ tl = []
+ self.model_to_use = model_admin.model
+ for t in self.model_to_use.tags.all().order_by('name'):
+ tl += (t.name, t.name),
+ return tl
+
+ def queryset(self, request, queryset):
+ """
+ Returns the filtered queryset based on the value
+ provided in the query string and retrievable via
+ `self.value()`.
+ """
+ qs = self.model_to_use.objects.all()
+ try:
+ request.GET['tag']
+ return qs.filter(tags__name=self.value())
+ except:
+ return qs
+
+
def thumbnail(image_path):
absolute_url = os.path.join(settings.IMAGES_URL, image_path[7:])
print(absolute_url)
diff --git a/config/base_urls.py b/config/base_urls.py
index 9e2f628..99e9929 100644
--- a/config/base_urls.py
+++ b/config/base_urls.py
@@ -47,6 +47,10 @@ urlpatterns += patterns('',
#(r'^contact/', TemplateView.as_view(template_name='details/contact.html')),
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
url(
+ regex=r'^links/',
+ view=include('links.urls', namespace='links')
+ ),
+ url(
regex=r'^jrnl/',
view=include('jrnl.urls', namespace='jrnl')
),
diff --git a/design/sass/_inbox.scss b/design/sass/_inbox.scss
index e69de29..d58c539 100644
--- a/design/sass/_inbox.scss
+++ b/design/sass/_inbox.scss
@@ -0,0 +1,11 @@
+.links main {
+ @include constrain_narrow;
+ .link-title a {
+ color: $body_font;
+ font-weight: 400;
+ text-decoration: none;
+ }
+ @include breakpoint(beta) {
+ text-align: left;
+ }
+}
diff --git a/design/templates/archives/links.html b/design/templates/archives/links.html
new file mode 100644
index 0000000..646385c
--- /dev/null
+++ b/design/templates/archives/links.html
@@ -0,0 +1,23 @@
+{% extends 'base.html' %}
+{% load typogrify_tags %}
+{% load pagination_tags %}
+{% block pagetitle %} Links | luxagraf {% endblock %}
+{% block metadescription %} {% endblock %}
+{%block bodyid%}class="links" id="links-archive"{%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> &rarr; </li>
+ {% if tag %}<li><a href="/links/" title="all links" itemprop="url"><span itemprop="title">Links</span></a> &rarr; </li><li>{{tag}}</li>{%else%}<li>Links</li>{% endif%}
+ </ul>
+ <main role="main">{% autopaginate object_list 100 %} {% for object in object_list %}
+ <article>
+ <h3 class="link-title"><a href="{{object.get_absolute_url}}">{{object.title|smartypants|safe}}</a> <span><a href="{{object.url}}">&rarr;</a></h3>
+
+ </article>
+ {% endfor %}
+ </main>
+ <nav class="pagination">
+ {% paginate %}
+ </nav>
+{% endblock %}
diff --git a/design/templates/details/link.html b/design/templates/details/link.html
new file mode 100644
index 0000000..6ba0619
--- /dev/null
+++ b/design/templates/details/link.html
@@ -0,0 +1,19 @@
+{% extends 'base.html' %}
+{% load typogrify_tags %}
+{%block bodyid%}class="links"{%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> &rarr; </li>
+ <li><a href="/links/" title="links" itemprop="url"><span itemprop="title">links</span></a> &rarr; </li>
+ </ul>
+ <main>
+ <h2 title="link-title">{{object.title|smartypants|widont|safe}}</h2>
+ <h3>{{object.url|urlize}}</h3>
+ <article class="h-entry hentry post--article book" itemscope itemType="http://schema.org/Article">
+ {{object.render_description|amp|smartypants|safe}}
+ <ul>{% for tag in object.tags.all %}
+ <li><a href="/links/tag/{{tag.slug}}/">{{tag}}</a></li>
+ {%endfor%}</ul>
+ </article>
+</main>
+{% endblock %}
+