pelican_comment_system.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. # -*- coding: utf-8 -*-
  2. """
  3. Pelican Comment System
  4. ======================
  5. A Pelican plugin, which allows you to add comments to your articles.
  6. Author: Bernhard Scheirle
  7. """
  8. from __future__ import unicode_literals
  9. import logging
  10. import os
  11. import copy
  12. logger = logging.getLogger(__name__)
  13. from itertools import chain
  14. from pelican import signals
  15. from pelican.readers import Readers
  16. from pelican.writers import Writer
  17. from . comment import Comment
  18. from . import avatars
  19. def pelican_initialized(pelican):
  20. from pelican.settings import DEFAULT_CONFIG
  21. DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM', False)
  22. DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_DIR', 'comments')
  23. DEFAULT_CONFIG.setdefault(
  24. 'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH' 'images/identicon')
  25. DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ())
  26. DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72)
  27. DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_AUTHORS', {})
  28. DEFAULT_CONFIG.setdefault(
  29. 'PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml'))
  30. DEFAULT_CONFIG.setdefault('COMMENT_URL', '#comment-{slug}')
  31. DEFAULT_CONFIG['PAGE_EXCLUDES'].append(
  32. DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
  33. DEFAULT_CONFIG['ARTICLE_EXCLUDES'].append(
  34. DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
  35. if pelican:
  36. pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM', False)
  37. pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_DIR', 'comments')
  38. pelican.settings.setdefault(
  39. 'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH', 'images/identicon')
  40. pelican.settings.setdefault(
  41. 'PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ())
  42. pelican.settings.setdefault(
  43. 'PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72)
  44. pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_AUTHORS', {})
  45. pelican.settings.setdefault(
  46. 'PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml'))
  47. pelican.settings.setdefault('COMMENT_URL', '#comment-{slug}')
  48. pelican.settings['PAGE_EXCLUDES'].append(
  49. pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
  50. pelican.settings['ARTICLE_EXCLUDES'].append(
  51. pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
  52. def initialize(article_generator):
  53. avatars.init(
  54. article_generator.settings['OUTPUT_PATH'],
  55. article_generator.settings[
  56. 'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH'],
  57. article_generator.settings['PELICAN_COMMENT_SYSTEM_IDENTICON_DATA'],
  58. article_generator.settings[
  59. 'PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE'] / 3,
  60. article_generator.settings['PELICAN_COMMENT_SYSTEM_AUTHORS'],
  61. )
  62. def warn_on_slug_collision(items):
  63. slugs = {}
  64. for comment in items:
  65. if not comment.slug in slugs:
  66. slugs[comment.slug] = [comment]
  67. else:
  68. slugs[comment.slug].append(comment)
  69. for slug, itemList in slugs.items():
  70. len_ = len(itemList)
  71. if len_ > 1:
  72. logger.warning('There are %s comments with the same slug: %s' %
  73. (len_, slug))
  74. for x in itemList:
  75. logger.warning(' %s' % x.source_path)
  76. def write_feed(gen, items, context, slug):
  77. if gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] is None:
  78. return
  79. path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] % slug
  80. writer = Writer(gen.output_path, settings=gen.settings)
  81. writer.write_feed(items, context, path)
  82. def add_static_comments(gen, content):
  83. if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
  84. return
  85. content.comments_count = 0
  86. content.comments = []
  87. # Modify the local context, so we get proper values for the feed
  88. context = copy.copy(gen.context)
  89. context['SITEURL'] += "/" + content.url
  90. context['SITENAME'] += " - Comments: " + content.title
  91. context['SITESUBTITLE'] = ""
  92. folder = os.path.join(
  93. gen.settings['PATH'],
  94. gen.settings['PELICAN_COMMENT_SYSTEM_DIR'],
  95. content.slug
  96. )
  97. if not os.path.isdir(folder):
  98. logger.debug("No comments found for: " + content.slug)
  99. write_feed(gen, [], context, content.slug)
  100. return
  101. reader = Readers(gen.settings)
  102. comments = []
  103. replies = []
  104. for file in os.listdir(folder):
  105. name, extension = os.path.splitext(file)
  106. if extension[1:].lower() in reader.extensions:
  107. com = reader.read_file(
  108. base_path=folder, path=file,
  109. content_class=Comment, context=context)
  110. if hasattr(com, 'replyto'):
  111. replies.append(com)
  112. else:
  113. comments.append(com)
  114. warn_on_slug_collision(comments + replies)
  115. write_feed(gen, comments + replies, context, content.slug)
  116. # TODO: Fix this O(n²) loop
  117. for reply in replies:
  118. for comment in chain(comments, replies):
  119. if comment.slug == reply.replyto:
  120. comment.addReply(reply)
  121. count = 0
  122. for comment in comments:
  123. comment.sortReplies()
  124. count += comment.countReplies()
  125. comments = sorted(comments)
  126. content.comments_count = len(comments) + count
  127. content.comments = comments
  128. def writeIdenticonsToDisk(gen, writer):
  129. avatars.generateAndSaveMissingAvatars()
  130. def register():
  131. signals.initialized.connect(pelican_initialized)
  132. signals.article_generator_init.connect(initialize)
  133. signals.article_generator_write_article.connect(add_static_comments)
  134. signals.article_writer_finalized.connect(writeIdenticonsToDisk)