Browse Source

Merge pull request #236 from clokep/rst-comments

pelican_comment_system: Allow restructedtext comments.
Alexis Metaireau 10 years ago
parent
commit
62e437025c

+ 1 - 1
pelican_comment_system/Readme.md

@@ -1,6 +1,6 @@
 # Pelican comment system
 The pelican comment system allows you to add static comments to your articles.
-The comments are stored in Markdown files. Each comment in it own file.
+The comments are stored in files which can be processed by Pelican (e.g.: Markdown, reStructuredText, ...). Each comment in its own file.
 
 #### Features
  - Static comments for each article

+ 22 - 6
pelican_comment_system/comment.py

@@ -3,29 +3,45 @@
 Author: Bernhard Scheirle
 """
 from __future__ import unicode_literals
+import os
+
 from pelican import contents
 from pelican.contents import Content
+from pelican.utils import slugify
+
+from . import avatars
 
 class Comment(Content):
 	mandatory_properties = ('author', 'date')
 	default_template = 'None'
 
-	def __init__(self, id, avatar, content, metadata, settings, source_path, context):
+	def __init__(self, content, metadata, settings, source_path, context):
+		# Strip the path off the full filename.
+		name = os.path.split(source_path)[1]
+
+		if not hasattr(self, 'slug'):
+			#compute the slug before initializing the base Content object, so it doesn't get set there
+			#This is required because we need a slug containing the file extension.
+			self.slug = slugify( name, settings.get('SLUG_SUBSTITUTIONS', ()))
+
 		super(Comment,self).__init__( content, metadata, settings, source_path, context )
-		self.id = id
+
 		self.replies = []
-		self.avatar = avatar
+
+		# Strip the extension from the filename.
+		name = os.path.splitext(name)[0]
+		self.avatar = avatars.getAvatarPath(name, metadata)
 		self.title = "Posted by:  {}".format(metadata['author'])
 
 	def addReply(self, comment):
 		self.replies.append(comment)
 
-	def getReply(self, id):
+	def getReply(self, slug):
 		for reply in self.replies:
-			if reply.id == id:
+			if reply.slug == slug:
 				return reply
 			else:
-				deepReply = reply.getReply(id)
+				deepReply = reply.getReply( slug )
 				if deepReply != None:
 					return deepReply
 		return None

+ 2 - 2
pelican_comment_system/doc/feed.md

@@ -4,14 +4,14 @@ Be sure that the id of the html tag containing the comment matches `COMMENT_URL`
 
 ##### pelicanconf.py
 ```python
-COMMENT_URL = "#my_own_comment_id_{path}"
+COMMENT_URL = "#my_own_comment_id_{slug}"
 ```
 
 ##### Theme
 ```html
 {% for comment in article.comments recursive %}
 	...
-	<article id="my_own_comment_id_{{comment.id}}">{{ comment.content }}</article>
+	<article id="my_own_comment_id_{{comment.slug}}">{{ comment.content }}</article>
 	...
 {% endfor %}
 ```

+ 1 - 1
pelican_comment_system/doc/form.md

@@ -8,7 +8,7 @@ The resulting email contains a valid markdown block. Now you only have to copy t
 Add this in the "comment for loop" in your article theme, so your visitors can reply to a comment.
 
 ```html
-<button onclick="reply('{{comment.id | urlencode}}');">Reply</button>
+<button onclick="reply('{{comment.slug | urlencode}}');">Reply</button>
 ```
 
 #### Form

+ 9 - 9
pelican_comment_system/doc/installation.md

@@ -1,7 +1,7 @@
 # Installation
 Activate the plugin by adding it to your `pelicanconf.py`
 
-	PLUGIN_PATH = '/path/to/pelican-plugins'
+	PLUGIN_PATH = ['/path/to/pelican-plugins']
 	PLUGINS = ['pelican_comment_system']
 	PELICAN_COMMENT_SYSTEM = True
 
@@ -11,13 +11,13 @@ And modify your `article.html` theme (see below).
 Name                                           | Type      | Default                    | Description
 -----------------------------------------------|-----------|----------------------------|-------
 `PELICAN_COMMENT_SYSTEM`                       | `boolean` | `False`                    | Activates or deactivates the comment system
-`PELICAN_COMMENT_SYSTEM_DIR`                   | `string`  | `comments`                 | Folder where the comments are stored
+`PELICAN_COMMENT_SYSTEM_DIR`                   | `string`  | `comments`                 | Folder where the comments are stored, relative to `PATH`
 `PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH` | `string`  | `images/identicon`         | Relative URL to the output folder where the identicons are stored
 `PELICAN_COMMENT_SYSTEM_IDENTICON_DATA`        | `tuple`   | `()`                       | Contains all Metadata tags, which in combination identifies a comment author (like `('author', 'email')`)
 `PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE`        | `int`     | `72`                       | Width and height of the identicons. Has to be a multiple of 3.
 `PELICAN_COMMENT_SYSTEM_AUTHORS`               | `dict`    | `{}`                       | Comment authors, which should have a specific avatar. More info [here](avatars.md)
 `PELICAN_COMMENT_SYSTEM_FEED`                  | `string`  |`feeds/comment.%s.atom.xml` | Relative URL to output the Atom feed for each article.`%s` gets replaced with the slug of the article. More info [here](http://docs.getpelican.com/en/latest/settings.html#feed-settings)
-`COMMENT_URL`                                  | `string`  | `#comment-{path}`          | `{path}` gets replaced with the id of the comment. More info [here](feed.md)
+`COMMENT_URL`                                  | `string`  | `#comment-{slug}`          | `{slug}` gets replaced with the slug of the comment. More info [here](feed.md)
 
 ## Folder structure
 Every comment file has to be stored in a sub folder of `PELICAN_COMMENT_SYSTEM_DIR`.
@@ -46,7 +46,8 @@ Tag           | Required  | Description
 --------------|-----------|----------------
 `date`        | yes       | Date when the comment was posted
 `author`      | yes       | Name of the comment author
-`replyto`     | no        | Identifier of the parent comment. Identifier = Filename (**with** extension)
+`slug`        | no        | Slug of the comment. If not present it will be computed from the file name (including the extension)
+`replyto`     | no        | Slug of the parent comment
 
 Every other (custom) tag gets parsed as well and will be available through the theme.
 
@@ -55,7 +56,7 @@ Every other (custom) tag gets parsed as well and will be available through the t
 	date: 2014-3-21 15:02
 	author: Author of the comment
 	website: http://authors.website.com
-	replyto: 7
+	replyto: 1md
 	anothermetatag: some random tag
 
 	Content of the comment.
@@ -69,13 +70,12 @@ Variables                | Description
 `article.comments`       | Array containing the top level comments for this article (no replies to comments)
 
 ### Comment object
-The comment object is a [content](https://github.com/getpelican/pelican/blob/master/pelican/contents.py#L34) object, so all common attributes are available (like author, content, date, local_date, metadata, ...).
+The comment object is a [content](https://github.com/getpelican/pelican/blob/master/pelican/contents.py#L34) object, so all common attributes are available (like author, content, date, local_date, slug, metadata, ...).
 
 Additional following attributes are added:
 
 Attribute  | Description
 -----------|--------------------------
-`id`       | Identifier of this comment
 `replies`  | Array containing the top level replies for this comment
 `avatar`   | Path to the avatar or identicon of the comment author
 
@@ -89,8 +89,8 @@ Attribute  | Description
 		{% else %}
 			{% set marginLeft = 50 %}
 		{% endif %}
-			<article id="comment-{{comment.id}}" style="border: 1px solid #DDDDDD; padding: 5px 0px 0px 5px; margin: 0px -1px 5px {{marginLeft}}px;">
-				<a href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.id}}" rel="bookmark" title="Permalink to this comment">Permalink</a>
+			<article id="comment-{{comment.slug}}" style="border: 1px solid #DDDDDD; padding: 5px 0px 0px 5px; margin: 0px -1px 5px {{marginLeft}}px;">
+				<a href="{{ SITEURL }}/{{ article.url }}#comment-{{comment.slug}}" rel="bookmark" title="Permalink to this comment">Permalink</a>
 				<h4>{{ comment.author }}</h4>
 				<p>Posted on <abbr class="published" title="{{ comment.date.isoformat() }}">{{ comment.locale_date }}</abbr></p>
 				{{ comment.metadata['my_custom_metadata'] }}

+ 34 - 14
pelican_comment_system/pelican_comment_system.py

@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
 
 from itertools import chain
 from pelican import signals
-from pelican.readers import MarkdownReader
+from pelican.readers import Readers
 from pelican.writers import Writer
 
 from . comment import Comment
@@ -26,13 +26,15 @@ from . import avatars
 def pelican_initialized(pelican):
 	from pelican.settings import DEFAULT_CONFIG
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM', False)
-	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_DIR' 'comments')
+	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_DIR', 'comments')
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH' 'images/identicon')
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ())
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72)
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_AUTHORS', {})
 	DEFAULT_CONFIG.setdefault('PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml'))
-	DEFAULT_CONFIG.setdefault('COMMENT_URL', '#comment-{path}')
+	DEFAULT_CONFIG.setdefault('COMMENT_URL', '#comment-{slug}')
+	DEFAULT_CONFIG['PAGE_EXCLUDES'].append(DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
+	DEFAULT_CONFIG['ARTICLE_EXCLUDES'].append(DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
 	if pelican:
 		pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM', False)
 		pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_DIR', 'comments')
@@ -41,8 +43,10 @@ def pelican_initialized(pelican):
 		pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72)
 		pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_AUTHORS', {})
 		pelican.settings.setdefault('PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml'))
-		pelican.settings.setdefault('COMMENT_URL', '#comment-{path}')
+		pelican.settings.setdefault('COMMENT_URL', '#comment-{slug}')
 
+		pelican.settings['PAGE_EXCLUDES'].append(pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
+		pelican.settings['ARTICLE_EXCLUDES'].append(pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
 
 def initialize(article_generator):
 	avatars.init(
@@ -53,6 +57,22 @@ def initialize(article_generator):
 		article_generator.settings['PELICAN_COMMENT_SYSTEM_AUTHORS'],
 		)
 
+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(gen, items, context, slug):
 	if gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] == None:
 		return
@@ -75,37 +95,37 @@ def add_static_comments(gen, content):
 	context['SITENAME'] += " - Comments: " + content.title
 	context['SITESUBTITLE'] = ""
 
-	folder = os.path.join(gen.settings['PELICAN_COMMENT_SYSTEM_DIR'], content.slug)
+	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: " + content.slug)
 		write_feed(gen, [], context, content.slug)
 		return
 
-	reader = MarkdownReader(gen.settings)
+	reader = Readers(gen.settings)
 	comments = []
 	replies = []
 
 	for file in os.listdir(folder):
 		name, extension = os.path.splitext(file)
-		if extension[1:].lower() in reader.file_extensions:
-			com_content, meta = reader.read(os.path.join(folder, file))
-			
-			avatar_path = avatars.getAvatarPath(name, meta)
+		if extension[1:].lower() in reader.extensions:
+			com = reader.read_file(
+				base_path=folder, path=file,
+				content_class=Comment, context=context)
 
-			com = Comment(file, avatar_path, com_content, meta, gen.settings, file, context)
-
-			if 'replyto' in meta:
+			if hasattr(com, 'replyto'):
 				replies.append( com )
 			else:
 				comments.append( com )
 
+	warn_on_slug_collision( comments + replies )
+
 	write_feed(gen, comments + replies, context, content.slug)
 
 	#TODO: Fix this O(n²) loop
 	for reply in replies:
 		for comment in chain(comments, replies):
-			if comment.id == reply.metadata['replyto']:
+			if comment.slug == reply.replyto:
 				comment.addReply(reply)
 
 	count = 0