Skip to content

Commit

Permalink
Merge pull request OCA#50 from LeartS/8.0-website_sale_recently_viewe…
Browse files Browse the repository at this point in the history
…d_products

New module: website_sale_recently_viewed_products
  • Loading branch information
nhomar committed Sep 3, 2015
2 parents df1485f + 2c89865 commit 554fa4e
Show file tree
Hide file tree
Showing 15 changed files with 365 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ cache: pip

addons:
apt:
sources:
- pov-wkhtmltopdf
packages:
- expect-dev # provides unbuffer utility
- python-lxml # because pip installation is slow
- wkhtmltopdf

language: python

python:
- "2.7"

Expand All @@ -19,6 +23,10 @@ env:
virtualenv:
system_site_packages: true

before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"

install:
- git clone https://github.com/OCA/maintainer-quality-tools.git $HOME/maintainer-quality-tools
- export PATH=$HOME/maintainer-quality-tools/travis:$PATH
Expand Down
57 changes: 57 additions & 0 deletions website_sale_recently_viewed_products/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3

========================
Recently Viewed Products
========================

Let the users keep track of the products they saw on the ecommerce.
Uses the session id as a key to store the product history, so it works
for both authenticated and anonymous users

Usage
=====

Open some products pages on the e-commerce, then hover or click on
'Recent Products' in the top menu. You should see a page or a popover (click
vs hover) with your last viewed products.

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

Known Issues / Roadmap
======================

* 'Add to cart' button near viewed products not already in the cart
* Translations
* Tests
* Configurable options like number of shown results
* Counter near the 'My Products' link, like the cart counter
* Save product views for user if logged in so that they remain saved
cross-session.
* Backend view of the records and analytics (probably better to split this
in another module)

Credits
=======

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

* Leonardo Donelli <donelli@webmonks.it>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit http://odoo-community.org.
3 changes: 3 additions & 0 deletions website_sale_recently_viewed_products/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models
38 changes: 38 additions & 0 deletions website_sale_recently_viewed_products/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) Leonardo Donelli @ MONK Software http://www.wearemonk.com
#
# This is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the
# GNU Affero General Public License
# along with web_expand_dialog.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Recently Viewed Products',
'summary': (
'Let the users keep track of the products they saw on the ecommerce'),
'author': "MONK Software,Odoo Community Association (OCA)",
'website': "http://www.wearemonk.com",
'category': 'Website',
'version': '8.0.1.0.0',
'license': 'AGPL-3',
'depends': ['website_sale'],
'data': [
'security/ir.model.access.csv',
'templates/website.xml',
'templates/website_sale.xml',
'templates/website_sale_recently_viewed_products.xml',
],
}
2 changes: 2 additions & 0 deletions website_sale_recently_viewed_products/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import main
35 changes: 35 additions & 0 deletions website_sale_recently_viewed_products/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-

from openerp import fields, http
from openerp.http import request
from openerp.addons.website_sale.controllers.main import website_sale


class WebsiteSale(website_sale):

@http.route()
def product(self, product, category='', search='', **kwargs):
record = request.env['website.sale.product.view'].search([
('sessionid', '=', request.session.sid),
('product_id', '=', product.id)
])
if not record:
record = request.env['website.sale.product.view'].create({
'sessionid': request.session.sid,
'product_id': product.id,
})
else:
record.last_view_datetime = fields.Datetime.now()
return super(WebsiteSale, self).product(product, category,
search, **kwargs)

@http.route(['/shop/recent'], type='http', auth='public', website=True)
def recent(self, **kwargs):
records = request.env['website.sale.product.view'].search(
[('sessionid', '=', request.session.sid)], limit=10)
values = {'history': records}
if kwargs.get('type') == 'popover':
return request.website.render(
'website_sale_recently_viewed_products.popover', values)
return request.website.render(
'website_sale_recently_viewed_products.page', values)
2 changes: 2 additions & 0 deletions website_sale_recently_viewed_products/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_view
from . import website
47 changes: 47 additions & 0 deletions website_sale_recently_viewed_products/models/product_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from openerp import api, fields, models, _


class ProductHistory(models.Model):
_name = 'website.sale.product.view'
_description = 'Ecommerce Product Views'

sessionid = fields.Char('Session ID', index=True)
product_id = fields.Many2one('product.template', 'Product')
last_view_datetime = fields.Datetime(
'Last view datetime', default=fields.Datetime.now)

_sql_constraints = [
('unique_session_product', 'UNIQUE(sessionid, product_id)',
'There is already a record for this product and session')
]
_order = 'last_view_datetime DESC'

@api.multi
def human_readable_datetime_difference(self, now=None):
"""
Return an human readable form of the difference between the supplied
datetime (or the current datetime if not supplied) and the history
record ``last_view_datetime``.
"""
if now is None:
now = fields.Datetime.from_string(fields.Datetime.now())
timedifference = now - fields.Datetime.from_string(
self.last_view_datetime)
minutes = timedifference.seconds // 60
hours = timedifference.seconds // 3600
days = timedifference.days
# string concatenation and explicit singular/plural
# to make life easier for translators
if days > 1:
return str(days) + ' ' + _('days ago')
elif days == 1:
return str(days) + ' ' + _('Yesterday')
elif hours > 1:
return str(hours) + ' ' + _('hours ago')
elif hours == 1:
return str(hours) + ' ' + _('hour ago')
elif minutes > 1:
return str(minutes) + ' ' + _('minutes ago')
elif minutes == 1:
return str(minutes) + ' ' + _('minute ago')
return _('Less than a minute ago')
11 changes: 11 additions & 0 deletions website_sale_recently_viewed_products/models/website.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from openerp import api, models
from openerp.http import request


class Website(models.Model):
_inherit = 'website'

@api.multi
def recently_viewed_products(self):
return request.env['website.sale.product.view'].search([
('sessionid', '=', request.session.sid)], limit=10)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_website_sale_product_view_public,website_sale_product_view_public,model_website_sale_product_view,base.group_public,1,1,1,0
access_website_sale_product_view_user,website_sale_product_view_user,model_website_sale_product_view,base.group_user,1,1,1,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
div.history-popover {
max-width: 350px;
min-width: 250px;
}

.row.history-line {
border-bottom: 1px #EEE solid;
padding: 5px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
$(document).ready(function() {
var _t = openerp._t;
var product_history_link = $('ul#top_menu li a[href^="/shop/recent"]');
var product_history_link_counter;

product_history_link.popover({
trigger: 'manual',
animation: true,
html: true,
title: function () {
return _t("Recent Products");
},
container: 'body',
placement: 'auto',
template: '<div class="popover history-popover" role="tooltip">' +
'<div class="arrow"></div><h3 class="popover-title"></h3>' +
'<div class="popover-content"></div></div>'
}).on("mouseenter",function () {
var self = this;
clearTimeout(product_history_link_counter);
product_history_link.not(self).popover('hide');
product_history_link_counter = setTimeout(function(){
if($(self).is(':hover') && !$(".history-popover:visible").length)
{
$.get("/shop/recent", {'type': 'popover'})
.then(function (data) {
$(self).data("bs.popover").options.content = data;
$(self).popover("show");
$(".popover").on("mouseleave", function () {
$(self).trigger('mouseleave');
});
});
}
}, 100);
}).on("mouseleave", function () {
var self = this;
setTimeout(function () {
if (!$(".popover:hover").length) {
if(!$(self).is(':hover'))
{
$(self).popover('hide');
}
}
}, 1000);
});

});
14 changes: 14 additions & 0 deletions website_sale_recently_viewed_products/templates/website.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<template id="assets_frontend" inherit_id="website.assets_frontend"
name="Recently Viewed Products Assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/website_sale_recently_viewed_products/static/src/js/website_sale_recently_viewed_products.js"></script>
<link rel='stylesheet' href='/website_sale_recently_viewed_products/static/src/css/website_sale_recently_viewed_products.css'/>
</xpath>
</template>

</data>
</openerp>
18 changes: 18 additions & 0 deletions website_sale_recently_viewed_products/templates/website_sale.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<template id="recently_viewed_products_header" inherit_id="website_sale.header"
name="Recently Viewed Products Header">
<xpath expr="//header//ul[@id='top_menu']/li[a[@href='/shop/cart']]" position="after">
<li t-if="website.recently_viewed_products()">
<a href="/shop/recent">
<i class="fa fa-history"></i>
Recent Products
</a>
</li>
</xpath>
</template>

</data>
</openerp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<template id="page" name="User product history">
<t t-call="website.layout">
<div class="container oe_website_sale">
<h1>Recently viewed products</h1>
<t t-if="history" t-call="website_sale_recently_viewed_products.table"/>
<t t-if="not history">
<strong>Seems like you haven't viewed any of our products yet!</strong>
<br/>Go to our <a href="/shop">shop</a> to see our catalogue.
</t>
</div>
</t>
</template>

<template id="table" name="Viewed products table">
<table class="table" t-if="history">
<thead>
<tr>
<th colspan="2">Product</th>
<th>Last View</th>
</tr>
</thead>
<tbody>
<t t-foreach="history" t-as="r">
<tr>
<td>
<span t-field="r.product_id.image_medium"
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
</td>
<td>
<a t-attf-href="/shop/product/#{ slug(r.product_id) }">
<strong t-esc="r.product_id.with_context(display_default_code=False).display_name"/>
</a>
</td>
<td>
<span t-esc="r.human_readable_datetime_difference()"/>
</td>
</tr>
</t>
</tbody>
</table>
</template>

<template id="popover" name="Viewed products popover">
<t t-if="not history">
<strong>You haven't viewed any products yet!</strong>
</t>
<t t-if="history" t-foreach="history" t-as="r">
<div class="row history-line">
<div class="col-xs-3">
<span t-field="r.product_id.image_small"
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
</div>
<div class="col-xs-9">
<a t-attf-href="/shop/product/#{ slug(r.product_id) }">
<span t-esc="r.product_id.with_context(display_default_code=False).display_name"/>
</a><br/>
<small>
Last viewed:
<span t-esc="r.human_readable_datetime_difference()"/>
</small>
</div>
</div>
</t>
</template>

</data>
</openerp>

0 comments on commit 554fa4e

Please sign in to comment.