Skip to content

Commit

Permalink
[9.0][website_blog_excerpt_img] Migrate, LGPL.
Browse files Browse the repository at this point in the history
Relicensed, migrated, new tests.
  • Loading branch information
yajo committed Dec 22, 2016
1 parent 181802a commit 120a927
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 110 deletions.
16 changes: 6 additions & 10 deletions website_blog_excerpt_img/README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3

=======================
Excerpt + Image in Blog
Expand All @@ -26,7 +26,7 @@ To use this module, you need to:

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/186/8.0
:target: https://runbot.odoo-community.org/runbot/186/9.0

Known issues / Roadmap
======================
Expand All @@ -42,19 +42,15 @@ Bug Tracker
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/website/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed `feedback
<https://github.com/OCA/
website/issues/new?body=module:%20
website_blog_excerpt_img%0Aversion:%20
8.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
help us smashing it by providing a detailed and welcomed feedback.

Credits
=======

Contributors
------------

* Jairo Llopis <yajo.sk8@gmail.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>

Maintainer
----------
Expand Down
5 changes: 5 additions & 0 deletions website_blog_excerpt_img/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from . import models
10 changes: 5 additions & 5 deletions website_blog_excerpt_img/__openerp__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
# © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": "Excerpt + Image in Blog",
"summary": "New layout for blog summary, including an excerpt and image",
"version": "8.0.1.0.0",
"version": "9.0.1.0.0",
"category": "Website",
"website": "https://grupoesoc.es",
"author": "Grupo ESOC Ingeniería de Servicios, "
"Odoo Community Association (OCA)",
"license": "AGPL-3",
"license": "LGPL-3",
"application": False,
"installable": True,
"images": [
Expand All @@ -21,7 +21,7 @@
"html_text",
],
"data": [
"views/assets.xml",
"views/blog.xml",
"templates/assets.xml",
"templates/blog.xml",
],
}
5 changes: 5 additions & 0 deletions website_blog_excerpt_img/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from . import blog_post
38 changes: 38 additions & 0 deletions website_blog_excerpt_img/models/blog_post.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import json
import re
from openerp import models


class BlogPost(models.Model):
_inherit = "blog.post"

def main_image(self):
"""Get blog's main image URL."""
Converter = self.env['ir.fields.converter']
html = self.content
# Get a dictionary of properties, avoiding possible malformed ones
try:
properties = json.loads(self.cover_properties)
except (TypeError, ValueError):
properties = dict()
# Prepend cover image to post content, if there is one
cover = properties.pop("background-image", "none")
if cover and cover != "none":
html = u"<div style={q}background-image:{}{q}/>{}".format(
cover,
html,
q='"' if '"' not in cover else "'",
)
# Return the first found image URL or None
try:
return next(Converter.imgs_from_html(html, 1))
except StopIteration:
return None

def content_excerpt(self, length=80):
"""Get the blog post content excerpt."""
return self.env['ir.fields.converter'].text_from_html(self.content, 80)

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
@charset "utf-8"
/* © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
/* Copyright 2016 Jairo Llopis <[email protected]>
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */

.website_blog
.excerpt-img .img
Expand Down
15 changes: 15 additions & 0 deletions website_blog_excerpt_img/templates/assets.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -->

<odoo>

<template id="assets_frontend" inherit_id="website_blog.assets_frontend">
<xpath expr=".">
<link
rel="stylesheet"
href="/website_blog_excerpt_img/static/src/css/website_blog_excerpt_img.sass"/>
</xpath>
</template>

</odoo>
41 changes: 41 additions & 0 deletions website_blog_excerpt_img/templates/blog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -->

<odoo>

<template id="blog_post_short"
inherit_id="website_blog.blog_post_short">
<!-- Entries are Bootstrap rows -->
<xpath expr="//div[@t-as='blog_post']" position="attributes">
<attribute name="class">mb32 row</attribute>
</xpath>
<xpath expr="//h2/.." position="attributes">
<attribute name="class">col-xs-10</attribute>
</xpath>
<xpath expr="//div[@name='blog_post_data']/.." position="attributes">
<attribute name="class">text-muted col-xs-12 mb16</attribute>
</xpath>

<!-- Excerpt, image, read more button -->
<xpath expr="//div[@name='blog_post_data']/.." position="after">
<t t-set="image_url" t-value="blog_post.main_image()"/>

<div t-attf-class="excerpt-txt col-sm-#{7 if image_url else 12}">
<p t-esc="blog_post.content_excerpt()"/>
<p>
<a t-attf-href="/blog/#{slug(blog_post.blog_id)}/post/#{slug(blog_post)}"
class="btn btn-primary">
Read more
</a>
</p>
</div>
<div t-if="image_url" class="col-sm-5 text-center excerpt-img">
<!-- Use cover image if available -->
<img class="img img-responsive img-thumbnail"
t-att-src="image_url"/>
</div>
</xpath>
</template>

</odoo>
5 changes: 5 additions & 0 deletions website_blog_excerpt_img/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from . import test_html
144 changes: 144 additions & 0 deletions website_blog_excerpt_img/tests/test_html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import json
from lxml import html
from openerp.tests.common import HttpCase


class HTMLCase(HttpCase):
def setUp(self):
super(HTMLCase, self).setUp()
with self.cursor() as cr:
env = self.env(cr)
self.blog_id = env["blog.blog"].create({
"name": "Test blog",
}).id
Post = env["blog.post"]
# Create a post with cover image but no image in content
Post.create({
"name": "Post 1",
"content": "A covered post",
"cover_properties":
json.dumps({"background-image": "url(/post-1)"}),
"website_published": True,
"blog_id": self.blog_id
})
# Create a post like the previous one, but url is double-quoted
Post.create({
"name": "Post 2",
"content": "A covered post",
"cover_properties":
json.dumps({"background-image": 'url("/post-2")'}),
"website_published": True,
"blog_id": self.blog_id
})
# Create a post like the previous one, but url is single-quoted
Post.create({
"name": "Post 3",
"content": "A covered post",
"cover_properties":
json.dumps({"background-image": "url('/post-3')"}),
"website_published": True,
"blog_id": self.blog_id
})
# Create a post with malformed cover_properties and no cover, but
# with background image in content
Post.create({
"name": "Post 4",
"content":
"<div style='background-image:url(/post-4)'>Badly</div>",
"cover_properties": "malformed",
"website_published": True,
"blog_id": self.blog_id
})
# Create a post with default cover_properties and <img> in content
Post.create({
"name": "Post 5",
"content": "Cool post with image <img src='/post-5'/>",
"website_published": True,
"blog_id": self.blog_id
})
# Create a post with no images
Post.create({
"name": "Post 6",
"content": "Really boring",
"website_published": True,
"blog_id": self.blog_id
})
# Create a post with lots of words
Post.create({
"name": "Post 7",
"content": "Lots of words " * 80,
"website_published": True,
"blog_id": self.blog_id
})

# Open the blog index and store its HTML content
self.html = html.document_fromstring(
self.url_open(
"/blog/test-blog-%d" % self.blog_id,
timeout=30).read())

def container(self, post_title):
"""Find the container of a blog post with given title."""
query = u"""
.//div[@id='main_column']/div
[.//h2[contains(text(), "{}")]]
"""
return self.html.xpath(query.format(post_title))[0]

def image(self, container):
"""Find the extracted image URL in a given container."""
query = ".//div[contains(@class, 'excerpt-img')]/img"
return container.xpath(query)[0].attrib["src"]

def text(self, container):
"""Find the text excerpt in a given container."""
query = ".//div[contains(@class, 'excerpt-txt')]/p[1]"
return container.xpath(query)[0].text_content().strip()

def test_cover_bg_unquoted(self):
"""Cover image without quotes."""
container = self.container("Post 1")
self.assertEqual(self.text(container), "A covered post")
self.assertEqual(self.image(container), "/post-1")

def test_cover_bg_double_quoted(self):
"""Cover image with double quotes."""
container = self.container("Post 2")
self.assertEqual(self.text(container), "A covered post")
self.assertEqual(self.image(container), "/post-2")

def test_cover_bg_single_quoted(self):
"""Cover image with single quotes."""
container = self.container("Post 3")
self.assertEqual(self.text(container), "A covered post")
self.assertEqual(self.image(container), "/post-3")

def test_cover_malformed(self):
"""Cover image malformed properties and background in content."""
container = self.container("Post 4")
self.assertEqual(self.text(container), "Badly")
self.assertEqual(self.image(container), "/post-4")

def test_content_img(self):
"""No cover image, <img> element in content."""
container = self.container("Post 5")
self.assertEqual(self.text(container), "Cool post with image")
self.assertEqual(self.image(container), "/post-5")

def test_no_img(self):
"""No image anywhere."""
container = self.container("Post 6")
self.assertEqual(self.text(container), "Really boring")
with self.assertRaises(IndexError):
self.image(container)

def test_text_excerpt(self):
"""Lots of words get truncated."""
container = self.container("Post 7")
text = self.text(container)
self.assertEqual(len(text.split()), 80)
self.assertTrue(text.endswith(u"…"))
17 changes: 0 additions & 17 deletions website_blog_excerpt_img/views/assets.xml

This file was deleted.

Loading

0 comments on commit 120a927

Please sign in to comment.