Skip to content
Open
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
*.pyc
*.pyc
build/
dist/
django_articles.*/
8 changes: 1 addition & 7 deletions articles/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
__version__ = '2.4.2'

from articles.directives import *
try:
import listeners
except ImportError:
# this happens when setup.py is grabbing __version__; nothing to worry
# about
pass
from .directives import *
4 changes: 2 additions & 2 deletions articles/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from django.contrib import admin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from forms import ArticleAdminForm
from models import Tag, Article, ArticleStatus, Attachment
from .forms import ArticleAdminForm
from .models import Tag, Article, ArticleStatus, Attachment

log = logging.getLogger('articles.admin')

Expand Down
4 changes: 3 additions & 1 deletion articles/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
:license: BSD, see LICENSE for more details.
"""

from __future__ import unicode_literals

# Options
# ~~~~~~~

Expand Down Expand Up @@ -66,7 +68,7 @@ def pygments_directive(name, arguments, options, content, lineno,
lexer = TextLexer()
# take an arbitrary option if more than one is given
formatter = options and VARIANTS[options.keys()[0]] or DEFAULT
parsed = highlight(u'\n'.join(content), lexer, formatter)
parsed = highlight('\n'.join(content), lexer, formatter)
parsed = '<div class="codeblock">%s</div>' % parsed
return [nodes.raw('', parsed, format='html')]

Expand Down
2 changes: 1 addition & 1 deletion articles/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.core.urlresolvers import reverse
from django.utils.feedgenerator import Atom1Feed

from articles.models import Article, Tag
from .models import Article, Tag

# default to 24 hours for feed caching
FEED_TIMEOUT = getattr(settings, 'ARTICLE_FEED_TIMEOUT', 86400)
Expand Down
2 changes: 1 addition & 1 deletion articles/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django import forms
from django.utils.translation import ugettext_lazy as _
from models import Article, Tag
from .models import Article, Tag

log = logging.getLogger('articles.forms')

Expand Down
4 changes: 2 additions & 2 deletions articles/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from django.db.models import signals, Q

from decorators import logtime
from models import Article, Tag
from .decorators import logtime
from .models import Article, Tag

log = logging.getLogger('articles.listeners')

Expand Down
14 changes: 8 additions & 6 deletions articles/management/commands/check_for_articles_from_email.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import unicode_literals

from base64 import b64decode
from datetime import datetime
from email.parser import FeedParser
Expand Down Expand Up @@ -112,7 +114,7 @@ def connect(self):

M.login(self.username, self.password)
M.select()
except socket.error, err:
except socket.error as err:
raise
else:
return M
Expand Down Expand Up @@ -165,7 +167,7 @@ def connect(self):

M.user(self.username)
M.pass_(self.password)
except socket.error, err:
except socket.error as err:
raise
else:
return M
Expand Down Expand Up @@ -208,7 +210,7 @@ class Command(BaseCommand):

def log(self, message, level=2):
if self.verbosity >= level:
print message
print(message)

def handle(self, *args, **options):
"""Main entry point for the command"""
Expand Down Expand Up @@ -317,7 +319,7 @@ def create_articles(self, emails):
try:
# try to grab the timestamp from the email message
publish_date = datetime.fromtimestamp(time.mktime(parsedate(email['Date'])))
except StandardError, err:
except StandardError as err:
self.log("An error occurred when I tried to convert the email's timestamp into a datetime object: %s" % (err,))
publish_date = datetime.now()

Expand All @@ -334,7 +336,7 @@ def create_articles(self, emails):
try:
article.save()
self.log('Article created.')
except StandardError, err:
except StandardError as err:
# log it and move on to the next message
self.log('Error creating article: %s' % (err,), 0)
continue
Expand All @@ -355,7 +357,7 @@ def create_articles(self, emails):

if ack:
# notify the user when the article is posted
subject = u'%s: %s' % (_("Article Posted"), title)
subject = '%s: %s' % (_("Article Posted"), title)
message = _("""Your email (%(title)s) has been posted as an article on %(site_name)s.

http://%(domain)s%(article_url)s""") % {
Expand Down
20 changes: 11 additions & 9 deletions articles/management/commands/convert_comments_to_disqus.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Command(NoArgsCommand):
def handle_noargs(self, **opts):
if hasattr(settings, 'DISQUS_USER_API_KEY'):
self.forum_id = self.determine_forum()
#print self.get_value_from_api('get_thread_list', {'forum_id': self.forum_id, 'limit': 1000})
#print(self.get_value_from_api('get_thread_list', {'forum_id': self.forum_id, 'limit': 1000}))
self.import_comments(self.forum_id)
else:
sys.exit('Please specify your DISQUS_USER_API_KEY in settings.py')
Expand All @@ -47,8 +47,8 @@ def get_value_from_api(self, url, args={}, method='GET'):
url = 'http://disqus.com/api/%s/%s' % (url, additional)
try:
handle = urllib2.urlopen(url, data)
except urllib2.HTTPError, err:
print 'Failed to %s %s with args %s' % (method, url, args)
except urllib2.HTTPError as err:
print('Failed to %s %s with args %s' % (method, url, args))
return None
else:
json_obj = json.loads(handle.read())['message']
Expand All @@ -68,12 +68,12 @@ def determine_forum(self):
forum_id = None
while forum_id not in possible_ids:
if forum_id is not None:
print 'Invalid forum ID. Please try again.'
print('Invalid forum ID. Please try again.')

print 'You have the following forums on Disqus:\n'
print('You have the following forums on Disqus:\n')

for forum in forums:
print '\t%s. %s' % (forum['id'], forum['name'])
print('\t%s. %s' % (forum['id'], forum['name']))

forum_id = raw_input('\nInto which forum do you want to import your existing comments? ')

Expand All @@ -86,7 +86,7 @@ def forum_api_key(self):
return self._forum_api_key[self.forum_id]

def import_comments(self, forum_id):
print 'Importing into forum %s' % self.forum_id
print('Importing into forum %s' % self.forum_id)

article_ct = ContentType.objects.get_for_model(Article)
for comment in Comment.objects.filter(content_type=article_ct):
Expand All @@ -102,7 +102,7 @@ def import_comments(self, forum_id):
'title': article.title,
'url': 'http://%s%s' % (Site.objects.get_current().domain, article.get_absolute_url()),
}, method='POST')
print 'Created new thread for %s' % article.title
print('Created new thread for %s' % article.title)

# create the comment on disqus
comment_obj = self.get_value_from_api('create_post', {
Expand All @@ -117,7 +117,9 @@ def import_comments(self, forum_id):
'state': self.get_state(comment)
}, method='POST')

print 'Imported comment for %s by %s on %s' % (article, comment.user_name, comment.submit_date)
print('Imported comment for %s by %s on %s' % (
article, comment.user_name, comment.submit_date)
)

def get_state(self, comment):
"""Determines a comment's state on Disqus based on its properties in Django"""
Expand Down
22 changes: 15 additions & 7 deletions articles/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import unicode_literals

from hashlib import sha1
from datetime import datetime
import logging
Expand All @@ -13,10 +15,11 @@
from django.core.cache import cache
from django.conf import settings
from django.template.defaultfilters import slugify, striptags
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.utils.text import truncate_html_words

from decorators import logtime, once_per_instance
from .decorators import logtime, once_per_instance

WORD_LIMIT = getattr(settings, 'ARTICLES_TEASER_LIMIT', 75)
AUTO_TAG = getattr(settings, 'ARTICLES_AUTO_TAG', True)
Expand Down Expand Up @@ -72,11 +75,12 @@ def get_name(user):
return name
User.get_name = get_name

@python_2_unicode_compatible
class Tag(models.Model):
name = models.CharField(max_length=64, unique=True)
slug = models.CharField(max_length=64, unique=True, null=True, blank=True)

def __unicode__(self):
def __str__(self):
return self.name

@staticmethod
Expand Down Expand Up @@ -124,6 +128,7 @@ def default(self):
else:
return default[0]

@python_2_unicode_compatible
class ArticleStatus(models.Model):
name = models.CharField(max_length=50)
ordering = models.IntegerField(default=0)
Expand All @@ -135,9 +140,9 @@ class Meta:
ordering = ('ordering', 'name')
verbose_name_plural = _('Article statuses')

def __unicode__(self):
def __str__(self):
if self.is_live:
return u'%s (live)' % self.name
return '%s (live)' % self.name
else:
return self.name

Expand Down Expand Up @@ -174,6 +179,7 @@ def live(self, user=None):
<li><a href="http://thresholdstate.com/articles/4312/the-textile-reference-manual" target="_blank">Textile Guide</a></li>
</ul>""")

@python_2_unicode_compatible
class Article(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique_for_year='publish_date')
Expand Down Expand Up @@ -223,7 +229,7 @@ def __init__(self, *args, **kwargs):
if not self.rendered_content or not len(self.rendered_content.strip()):
self.save()

def __unicode__(self):
def __str__(self):
return self.title

def save(self, *args, **kwargs):
Expand Down Expand Up @@ -494,6 +500,7 @@ class Meta:
ordering = ('-publish_date', 'title')
get_latest_by = 'publish_date'

@python_2_unicode_compatible
class Attachment(models.Model):
upload_to = lambda inst, fn: 'attach/%s/%s/%s' % (datetime.now().year, inst.article.slug, fn)

Expand All @@ -504,8 +511,8 @@ class Attachment(models.Model):
class Meta:
ordering = ('-article', 'id')

def __unicode__(self):
return u'%s: %s' % (self.article, self.caption)
def __str__(self):
return '%s: %s' % (self.article, self.caption)

@property
def filename(self):
Expand All @@ -522,3 +529,4 @@ def content_type_class(self):

return content_type

from . import listeners # last to avoid circular import issue
5 changes: 3 additions & 2 deletions articles/templates/articles/_articles.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{% load i18n %}
{% load url from future %}
{% if forloop.first %}<ol class="article-list">{% endif %}
<li>
<h3><a href="{{ article.get_absolute_url }}" title="{% trans 'Read this article' %}">{{ article.title }}</a></h3>
<div class="quiet">
{% trans 'Posted on' %} {{ article.publish_date|date:"F jS, Y" }}
{% trans 'by' %} <a href="{% url articles_by_author article.author.username %}" title="{% trans 'View articles posted by' %} {{ article.author.get_name }}">{{ article.author.get_name }}</a>
{% trans 'by' %} <a href="{% url 'articles_by_author' article.author.username %}" title="{% trans 'View articles posted by' %} {{ article.author.get_name }}">{{ article.author.get_name }}</a>
</div>
</li>
{% if forloop.last %}</ol>{% endif %}
{% if forloop.last %}</ol>{% endif %}
3 changes: 2 additions & 1 deletion articles/templates/articles/_meta.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{% load i18n humanize %}
{% load url from future %}

<div id="article-meta">
<h4>Meta</h4>

<p><strong>{% trans 'Published' %}</strong>: {{ article.publish_date|naturalday }}</p>

<p><strong>{% trans 'Author' %}</strong>: <a href="{% url articles_by_author article.author.username %}" title="{% trans 'Read other articles by this author' %}">{{ article.author.get_name }}</a></p>
<p><strong>{% trans 'Author' %}</strong>: <a href="{% url 'articles_by_author' article.author.username %}" title="{% trans 'Read other articles by this author' %}">{{ article.author.get_name }}</a></p>

<p><strong>{% trans 'Comments' %}</strong>: <a href="#disqus_thread">&nbsp;</a></p>

Expand Down
5 changes: 3 additions & 2 deletions articles/templates/articles/article_detail.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{% extends 'articles/base.html' %}
{% load i18n %}
{% load url from future %}

{% block title %}{% trans article.title %}{% endblock %}
{% block meta-keywords %}{{ article.keywords|escape }}{% endblock %}
{% block meta-description %}{{ article.description|escape }}{% endblock %}
{% block extra-head %}
{{ block.super }}
{% for tag in article.tags.all %}
<link rel="alternate" type="application/rss+xml" title="Blog Articles Tagged '{{ tag.name }}' RSS Feed" href="{% url articles_rss_feed_tag tag.rss_name %}" />
<link rel="alternate" type="application/atom+xml" title="Blog Articles Tagged '{{ tag.name }}' Atom Feed" href="{% url articles_atom_feed_tag tag.rss_name %}" />{% endfor %}
<link rel="alternate" type="application/rss+xml" title="Blog Articles Tagged '{{ tag.name }}' RSS Feed" href="{% url 'articles_rss_feed_tag' tag.rss_name %}" />
<link rel="alternate" type="application/atom+xml" title="Blog Articles Tagged '{{ tag.name }}' Atom Feed" href="{% url 'articles_atom_feed_tag' tag.rss_name %}" />{% endfor %}
{% endblock %}

{% block content %}
Expand Down
7 changes: 4 additions & 3 deletions articles/templates/articles/base.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{% extends 'base.html' %}
{% load url from future %}
{% load article_tags i18n %}

{% block extra-head %}
{{ block.super }}
<link rel="alternate" type="application/rss+xml" title="Latest Blog Articles RSS Feed" href="{% url articles_rss_feed_latest %}" />
<link rel="alternate" type="application/atom+xml" title="Latest Blog Articles Atom Feed" href="{% url articles_atom_feed_latest %}" />
<link rel="alternate" type="application/rss+xml" title="Latest Blog Articles RSS Feed" href="{% url 'articles_rss_feed_latest' %}" />
<link rel="alternate" type="application/atom+xml" title="Latest Blog Articles Atom Feed" href="{% url 'articles_atom_feed_latest' %}" />
{% endblock %}

{% block content %}
Expand All @@ -17,7 +18,7 @@ <h2 class="title">{% trans 'Article Archives' %}</h2>
<strong>{{ year.0 }}</strong>
<ul class="months">
{% for month in year.1 %}
<li><a href="{% url articles_in_month month.year,month.month %}" title="{% trans 'View articles posted in this month' %}">{{ month|date:"N" }}</a></li>
<li><a href="{% url 'articles_in_month' month.year month.month %}" title="{% trans 'View articles posted in this month' %}">{{ month|date:"N" }}</a></li>
{% endfor %}
</ul>
<div class="clear">&nbsp;</div>
Expand Down
5 changes: 3 additions & 2 deletions articles/templates/articles/display_tag.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{% extends 'articles/base.html' %}
{% load url from future %}
{% load i18n %}

{% block title %}{% trans 'Articles Tagged' %}: {{ tag.name }}{% endblock %}
{% block extra-head %}
{{ block.super }}
<link rel="alternate" type="application/rss+xml" title="Blog Articles Tagged '{{ tag.name }}' RSS Feed" href="{% url articles_rss_feed_tag tag.rss_name %}" />
<link rel="alternate" type="application/atom+xml" title="Blog Articles Tagged '{{ tag.name }}' Atom Feed" href="{% url articles_atom_feed_tag tag.rss_name %}" />
<link rel="alternate" type="application/rss+xml" title="Blog Articles Tagged '{{ tag.name }}' RSS Feed" href="{% url 'articles_rss_feed_tag' tag.rss_name %}" />
<link rel="alternate" type="application/atom+xml" title="Blog Articles Tagged '{{ tag.name }}' Atom Feed" href="{% url 'articles_atom_feed_tag' tag.rss_name %}" />
{% endblock %}

{% block articles-content %}
Expand Down
2 changes: 1 addition & 1 deletion articles/templatetags/article_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def render(self, context):
pub = article.publish_date

# see if we already have an article in this year
if not archives.has_key(pub.year):
if not pub.year in archives:
# if not, initialize a dict for the year
archives[pub.year] = {}

Expand Down
Loading