241 lines
7.1 KiB
Python
241 lines
7.1 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
Pelican Comment System
|
||
|
======================
|
||
|
|
||
|
A Pelican plugin, which allows you to add comments to your articles.
|
||
|
|
||
|
Author: Bernhard Scheirle
|
||
|
"""
|
||
|
from __future__ import unicode_literals
|
||
|
import logging
|
||
|
import os
|
||
|
import copy
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
from itertools import chain
|
||
|
from pelican import signals
|
||
|
from pelican.readers import Readers
|
||
|
from pelican.writers import Writer
|
||
|
|
||
|
from . comment import Comment
|
||
|
from . import avatars
|
||
|
|
||
|
|
||
|
__version__ = "1.3.0"
|
||
|
|
||
|
|
||
|
_all_comments = []
|
||
|
_pelican_writer = None
|
||
|
_pelican_obj = None
|
||
|
|
||
|
def setdefault(pelican, settings):
|
||
|
from pelican.settings import DEFAULT_CONFIG
|
||
|
for key, value in settings:
|
||
|
DEFAULT_CONFIG.setdefault(key, value)
|
||
|
|
||
|
if not pelican:
|
||
|
return
|
||
|
|
||
|
for key, value in settings:
|
||
|
pelican.settings.setdefault(key, value)
|
||
|
|
||
|
|
||
|
def pelican_initialized(pelican):
|
||
|
from pelican.settings import DEFAULT_CONFIG
|
||
|
settings = [
|
||
|
('PELICAN_COMMENT_SYSTEM', False),
|
||
|
('PELICAN_COMMENT_SYSTEM_DIR', 'comments'),
|
||
|
('PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH', 'images/identicon'),
|
||
|
('PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ()),
|
||
|
('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72),
|
||
|
('PELICAN_COMMENT_SYSTEM_AUTHORS', {}),
|
||
|
('PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml')),
|
||
|
('PELICAN_COMMENT_SYSTEM_FEED_ALL', os.path.join('feeds', 'comments.all.atom.xml')),
|
||
|
('COMMENT_URL', '#comment-{slug}')
|
||
|
]
|
||
|
|
||
|
setdefault(pelican, settings)
|
||
|
|
||
|
DEFAULT_CONFIG['PAGE_EXCLUDES'].append(
|
||
|
DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
|
||
|
DEFAULT_CONFIG['ARTICLE_EXCLUDES'].append(
|
||
|
DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
|
||
|
pelican.settings['PAGE_EXCLUDES'].append(
|
||
|
pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
|
||
|
pelican.settings['ARTICLE_EXCLUDES'].append(
|
||
|
pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
|
||
|
|
||
|
global _pelican_obj
|
||
|
_pelican_obj = pelican
|
||
|
|
||
|
|
||
|
def initialize(article_generator):
|
||
|
avatars.init(
|
||
|
article_generator.settings['OUTPUT_PATH'],
|
||
|
article_generator.settings[
|
||
|
'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH'],
|
||
|
article_generator.settings['PELICAN_COMMENT_SYSTEM_IDENTICON_DATA'],
|
||
|
article_generator.settings[
|
||
|
'PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE'] / 3,
|
||
|
article_generator.settings['PELICAN_COMMENT_SYSTEM_AUTHORS'],
|
||
|
)
|
||
|
|
||
|
# Reset old states (autoreload mode)
|
||
|
global _all_comments
|
||
|
global _pelican_writer
|
||
|
_pelican_writer = _pelican_obj.get_writer()
|
||
|
_all_comments = []
|
||
|
|
||
|
def warn_on_slug_collision(items):
|
||
|
slugs = {}
|
||
|
for comment in items:
|
||
|
if not comment.slug in slugs:
|
||
|
slugs[comment.slug] = [comment]
|
||
|
else:
|
||
|
slugs[comment.slug].append(comment)
|
||
|
|
||
|
for slug, itemList in slugs.items():
|
||
|
len_ = len(itemList)
|
||
|
if len_ > 1:
|
||
|
logger.warning('There are %s comments with the same slug: %s', len_, slug)
|
||
|
for x in itemList:
|
||
|
logger.warning(' %s', x.source_path)
|
||
|
|
||
|
|
||
|
def write_feed_all(gen, writer):
|
||
|
if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
|
||
|
return
|
||
|
if gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL'] is None:
|
||
|
return
|
||
|
|
||
|
context = copy.copy(gen.context)
|
||
|
context['SITENAME'] += " - All Comments"
|
||
|
context['SITESUBTITLE'] = ""
|
||
|
path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL']
|
||
|
|
||
|
global _all_comments
|
||
|
_all_comments = sorted(_all_comments)
|
||
|
_all_comments.reverse()
|
||
|
|
||
|
for com in _all_comments:
|
||
|
com.title = com.article.title + " - " + com.title
|
||
|
com.override_url = com.article.url + com.url
|
||
|
|
||
|
writer.write_feed(_all_comments, context, path)
|
||
|
|
||
|
|
||
|
def write_feed(gen, items, context, slug):
|
||
|
if gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] is None:
|
||
|
return
|
||
|
|
||
|
path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] % slug
|
||
|
_pelican_writer.write_feed(items, context, path)
|
||
|
|
||
|
|
||
|
def process_comments(article_generator):
|
||
|
for article in article_generator.articles:
|
||
|
add_static_comments(article_generator, article)
|
||
|
|
||
|
def mirror_to_translations(article):
|
||
|
for translation in article.translations:
|
||
|
translation.comments_count = article.comments_count
|
||
|
translation.comments = article.comments
|
||
|
|
||
|
def add_static_comments(gen, content):
|
||
|
if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
|
||
|
return
|
||
|
|
||
|
global _all_comments
|
||
|
|
||
|
content.comments_count = 0
|
||
|
content.comments = []
|
||
|
mirror_to_translations(content)
|
||
|
|
||
|
# Modify the local context, so we get proper values for the feed
|
||
|
context = copy.copy(gen.context)
|
||
|
context['SITEURL'] += "/" + content.url
|
||
|
context['SITENAME'] += " - Comments: " + content.title
|
||
|
context['SITESUBTITLE'] = ""
|
||
|
|
||
|
folder = os.path.join(
|
||
|
gen.settings['PATH'],
|
||
|
gen.settings['PELICAN_COMMENT_SYSTEM_DIR'],
|
||
|
content.slug
|
||
|
)
|
||
|
|
||
|
if not os.path.isdir(folder):
|
||
|
logger.debug("No comments found for: %s", content.slug)
|
||
|
write_feed(gen, [], context, content.slug)
|
||
|
return
|
||
|
|
||
|
reader = Readers(gen.settings)
|
||
|
comments = []
|
||
|
replies = []
|
||
|
|
||
|
for file in os.listdir(folder):
|
||
|
name, extension = os.path.splitext(file)
|
||
|
if extension[1:].lower() in reader.extensions:
|
||
|
com = reader.read_file(
|
||
|
base_path=folder, path=file,
|
||
|
content_class=Comment, context=context)
|
||
|
|
||
|
com.article = content
|
||
|
_all_comments.append(com)
|
||
|
|
||
|
if hasattr(com, 'replyto'):
|
||
|
replies.append(com)
|
||
|
else:
|
||
|
comments.append(com)
|
||
|
|
||
|
feed_items = sorted(comments + replies)
|
||
|
feed_items.reverse()
|
||
|
warn_on_slug_collision(feed_items)
|
||
|
|
||
|
write_feed(gen, feed_items, context, content.slug)
|
||
|
|
||
|
# TODO: Fix this O(n²) loop
|
||
|
for reply in replies:
|
||
|
found_parent = False
|
||
|
for comment in chain(comments, replies):
|
||
|
if comment.slug == reply.replyto:
|
||
|
comment.addReply(reply)
|
||
|
found_parent = True
|
||
|
break
|
||
|
if not found_parent:
|
||
|
logger.warning('Comment "%s/%s" is a reply to non-existent comment "%s". '
|
||
|
'Make sure the replyto attribute is set correctly.',
|
||
|
content.slug, reply.slug, reply.replyto)
|
||
|
|
||
|
count = 0
|
||
|
for comment in comments:
|
||
|
comment.sortReplies()
|
||
|
count += comment.countReplies()
|
||
|
|
||
|
comments = sorted(comments)
|
||
|
|
||
|
content.comments_count = len(comments) + count
|
||
|
content.comments = comments
|
||
|
mirror_to_translations(content)
|
||
|
|
||
|
|
||
|
def writeIdenticonsToDisk(gen, writer):
|
||
|
avatars.generateAndSaveMissingAvatars()
|
||
|
|
||
|
|
||
|
def pelican_finalized(pelican):
|
||
|
if pelican.settings['PELICAN_COMMENT_SYSTEM'] is not True:
|
||
|
return
|
||
|
global _all_comments
|
||
|
print('Processed %s comment(s)' % len(_all_comments))
|
||
|
|
||
|
|
||
|
def register():
|
||
|
signals.initialized.connect(pelican_initialized)
|
||
|
signals.article_generator_init.connect(initialize)
|
||
|
signals.article_generator_finalized.connect(process_comments)
|
||
|
signals.article_writer_finalized.connect(writeIdenticonsToDisk)
|
||
|
signals.article_writer_finalized.connect(write_feed_all)
|
||
|
signals.finalized.connect(pelican_finalized)
|