diff --git a/representative_image/Readme.md b/representative_image/Readme.md
new file mode 100644
index 0000000..a26c5f2
--- /dev/null
+++ b/representative_image/Readme.md
@@ -0,0 +1,8 @@
+# Summary
+
+This plugin extracts a presentative image from the article's summary or article's content. The image can be access at `article.repImage`.
+
+The plugin also remove any images from the summary after extraction to avoid duplication.
+
+It allows the flexibility on where and how to display the representative image of an article together with its summary in a template page. For example, the article metadata can be displayed in thumbnail format, in which there is a short summary and an image. The layout of the summary and the image can be varied for aesthetical purpose. It doesn't have to depend on article's content format.
+
diff --git a/representative_image/__init__.py b/representative_image/__init__.py
new file mode 100644
index 0000000..f52720f
--- /dev/null
+++ b/representative_image/__init__.py
@@ -0,0 +1 @@
+from .representative_image import *
diff --git a/representative_image/representative_image.py b/representative_image/representative_image.py
new file mode 100644
index 0000000..7d5d61c
--- /dev/null
+++ b/representative_image/representative_image.py
@@ -0,0 +1,31 @@
+from pelican import signals
+from pelican.contents import Content, Article
+from bs4 import BeautifulSoup
+
+def images_extraction(instance):
+ representativeImage = None
+ if type(instance) == Article:
+ # Process Summary:
+ # If summary contains images, extract one to be the representativeImage and remove images from summary
+ soup = BeautifulSoup(instance.summary, 'html.parser')
+ images = soup.find_all('img')
+ for i in images:
+ if not representativeImage:
+ representativeImage = i['src']
+ i.extract()
+ if len(images) > 0:
+ # set _summary field which is based on metadata. summary field is only based on article's content and not settable
+ instance._summary = unicode(soup)
+
+ # If there are no image in summary, look for it in the content body
+ if not representativeImage:
+ soup = BeautifulSoup(instance.content, 'html.parser')
+ imageTag = soup.find('img')
+ if imageTag:
+ representativeImage = imageTag['src']
+
+ # Set the attribute to content instance
+ instance.repImage = representativeImage
+
+def register():
+ signals.content_object_init.connect(images_extraction)
diff --git a/representative_image/test_representative_image.py b/representative_image/test_representative_image.py
new file mode 100644
index 0000000..60efa8b
--- /dev/null
+++ b/representative_image/test_representative_image.py
@@ -0,0 +1,54 @@
+#!/bin/sh
+import unittest
+
+from jinja2.utils import generate_lorem_ipsum
+
+# Generate content with image
+TEST_CONTENT_IMAGE_URL = 'https://testimage.com/test.jpg'
+TEST_CONTENT = str(generate_lorem_ipsum(n=3, html=True)) + '
'+ str(generate_lorem_ipsum(n=2,html=True))
+TEST_SUMMARY_IMAGE_URL = 'https://testimage.com/summary.jpg'
+TEST_SUMMARY_WITHOUTIMAGE = str(generate_lorem_ipsum(n=1, html=True))
+TEST_SUMMARY_WITHIMAGE = TEST_SUMMARY_WITHOUTIMAGE + '
'
+
+
+from pelican.contents import Article
+import representative_image
+
+class TestRepresentativeImage(unittest.TestCase):
+
+ def setUp(self):
+ super(TestRepresentativeImage, self).setUp()
+ representative_image.register()
+
+ def test_extract_image_from_content(self):
+ args = {
+ 'content': TEST_CONTENT,
+ 'metadata': {
+ 'summary': TEST_SUMMARY_WITHOUTIMAGE,
+ },
+ }
+
+ article = Article(**args)
+ self.assertEqual(article.repImage, TEST_CONTENT_IMAGE_URL)
+
+ def test_extract_image_from_summary(self):
+ args = {
+ 'content': TEST_CONTENT,
+ 'metadata': {
+ 'summary': TEST_SUMMARY_WITHIMAGE,
+ },
+ }
+
+ article = Article(**args)
+ self.assertEqual(article.repImage, TEST_SUMMARY_IMAGE_URL)
+ self.assertEqual(article.summary, TEST_SUMMARY_WITHOUTIMAGE)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+
+
+
+
+