varia.website/plugins/pelican_comment_system/pelican_comment_system.py

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)