[ADD] website functionalities like...

- Page layout (Pre-header, Post-footer, etc...)
    - products page layout
    - product quick view
    - producta share option
    - product ribbon
    - product discription
    - product limit per page
This commit is contained in:
Kaushal Prajapati 2017-11-30 14:15:04 +05:30 committed by Siddharth Bhalgami
parent 8f01116341
commit 78fdb9318a
20 changed files with 950 additions and 118 deletions

View File

@ -22,7 +22,7 @@
<t t-set="head" t-value="head_website + (head or '')"/>
<div id="wrapwrap" t-att-class="pageName or ''">
<header>
<header class="oe_structure">
<div class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
@ -52,7 +52,7 @@
<main>
<t t-raw="0"/>
</main>
<footer >
<footer class="oe_structure">
<div id="footer">
</div>
</footer>

View File

@ -80,6 +80,15 @@ class Website(Home):
raise request.not_found()
@http.route(['/website/menu/render'], type='json', auth="user", website=True)
def menu_render(self, **kwargs):
menu = request.env['website.menu'].browse(
int(kwargs['menu_id']))
value = {
'menu': menu,
}
return request.env['ir.ui.view'].render_template(kwargs['template'], value)
#------------------------------------------------------
# Login - overwrite of the web login so that regular users are redirected to the backend
# while portal users are redirected to the frontend by default

View File

@ -17,7 +17,8 @@ from flectra.addons.portal.controllers.portal import pager
from flectra.tools import pycompat
from flectra.http import request
from flectra.osv.expression import FALSE_DOMAIN
from flectra.tools.translate import _
from flectra.tools.translate import _, html_translate
from flectra.exceptions import Warning
logger = logging.getLogger(__name__)
@ -670,6 +671,11 @@ class Website(models.Model):
return self.env.ref('website.backend_dashboard').read()[0]
return self.env.ref('website.action_website').read()[0]
@api.multi
def get_website_menus(self, website_id):
menus = request.env['website.menu'].search([('parent_id', '=', False), ('website_id', '=', website_id)])
if menus:
return menus
class SeoMetadata(models.AbstractModel):
@ -902,6 +908,7 @@ class Menu(models.Model):
parent_left = fields.Integer('Parent Left', index=True)
parent_right = fields.Integer('Parent Rigth', index=True)
is_visible = fields.Boolean(compute='_compute_visible', string='Is Visible')
menu_view = fields.Many2one('ir.ui.view', domain=[('type', '=', 'qweb')], string='Menu View')
@api.one
def _compute_visible(self):

View File

@ -5,6 +5,7 @@ var core = require('web.core');
var Dialog = require('web.Dialog');
var widgets = require('web_editor.widget');
var options = require('web_editor.snippets.options');
var ajax = require('web.ajax');
var _t = core._t;
var qweb = core.qweb;
@ -935,4 +936,22 @@ options.registry.gallery_img = options.Class.extend({
});
},
});
options.registry.js_menu = options.Class.extend({
selected_menu: function (type,value){
if(type != true)
return
var menu = eval(value);
var section=this.$target.find('.menu_view');
section.empty();
console.info(section)
ajax.jsonRpc('/website/menu/render', 'call', {'template': menu[1], 'menu_id':menu[0]}
).then(function (result) {
$(result).appendTo(section);
}).fail(function () {
self.$('.o_website_links_code_error').show();
self.$('.o_website_links_code_error').html(
"<div>ServerError</div><p>We are not able to render template.</p>");
}) ;
},
});
});

View File

@ -14,6 +14,16 @@
</section>
</template>
<template id="s_menu" name="Menu Snippets">
<section class="s_menu">
<div class="container">
<div class="row menu_view">
<h2 style="color: rgb(193, 191, 177);text-align: center;">Select Top Menu From Option</h2>
</div>
</div>
</section>
</template>
<template id="s_banner" name="Slider">
<div id="myCarousel" class="carousel slide s_banner" data-interval="10000" style="height: 400px;">
<!-- Indicators -->
@ -683,6 +693,7 @@
<div class="o_panel_body">
<t t-snippet="website.s_title" t-thumbnail="/website/static/src/img/blocks/block_title.png"/>
<t t-snippet="website.s_cover" t-thumbnail="/website/static/src/img/blocks/block_banner.png"/>
<t t-snippet="website.s_menu" t-thumbnail="/website/static/src/img/blocks/block_menu.png"/>
<t t-snippet="website.s_text_image" t-thumbnail="/website/static/src/img/blocks/block_text_image.png"/>
<t t-snippet="website.s_image_text" t-thumbnail="/website/static/src/img/blocks/block_image_text.png"/>
<t t-snippet="website.s_big_message" t-thumbnail="/website/static/src/img/blocks/block_jumbotron.png"/>
@ -752,6 +763,21 @@
<template id="snippet_options">
<t t-call="web_editor.snippet_options"/>
<div data-js='js_menu' data-selector=".s_menu">
<t t-set="top_menu"
t-value="website.get_website_menus(website.id) if website else None" />
<li class="dropdown-submenu">
<a tabindex="-2" href="#"><i class="fa fa-magic"/>Select Top Menu</a>
<ul class="dropdown-menu">
<t t-foreach="top_menu" t-as="menu">
<li t-att-data-selected_menu="[menu.id,menu.menu_view.key]"
t-att-data-view="menu.menu_view.key"><a
href="#"><span t-esc="menu.name"/></a></li>
</t>
</ul>
</li>
</div>
<div data-js='gallery' data-selector=".o_gallery">
<li data-add-images="true" data-no-preview="true"><a href="#"><i class="fa fa-plus-circle"/>Add images</a></li>
<li data-remove-all-images="true" data-no-preview="true"><a href="#"><i class="fa fa-trash"/>Remove all images</a></li>

View File

@ -114,6 +114,16 @@
</t>
<t t-set="x_icon" t-value="'/web/image/website/%s/favicon/' % website.id"/>
</xpath>
<xpath expr="//header" position="before">
<pre-header>
<div id="pre-header" class="oe_structure oe_empty"/>
</pre-header>
</xpath>
<xpath expr="//footer" position="after">
<post-footer>
<div id="post-footer" class="oe_structure oe_empty"/>
</post-footer>
</xpath>
<xpath expr="//t[@t-set='head_website']" position="replace">
<t t-set="head_website">
<meta t-if="main_object and 'website_indexed' in main_object
@ -204,6 +214,39 @@
</xpath>
</template>
<template id="menu_snippets" name="Menu View">
<nav>
<div class="container">
<div class="navbar-header"/>
<ul id="nav_menu" class="nav navbar-nav navbar-right">
<t t-foreach="menu.child_id" t-as="submenu">
<t t-call="website.recursive_menu_snippets"/>
</t>
</ul>
</div>
</nav>
</template>
<template id="recursive_menu_snippets" name="Recursive Menu">
<li t-if="not submenu.child_id">
<a t-att-href="submenu.url" t-ignore="true"
t-att-target="submenu.new_window or '_blank'">
<span t-esc="submenu.name"/>
</a>
</li>
<li t-if="submenu.child_id" >
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<span t-esc="submenu.name"/>
<span class="caret" t-ignore="true"/>
</a>
<ul class="dropdown-menu" role="menu">
<t t-foreach="submenu.child_id" t-as="submenu">
<t t-call="website.submenus"/>
</t>
</ul>
</li>
</template>
<template id="layout_footer_copyright" inherit_id="website.layout" name="Footer Copyright">
<xpath expr="//footer" position="inside">
<div class="container mt16 mb8">

View File

@ -261,5 +261,57 @@
action="website.ir_actions_server_website_dashboard"
parent="website.menu_website_configuration"/>
<record id="website_menu_form" model="ir.ui.view">
<field name="name">website.menu.form</field>
<field name="model">website.menu</field>
<field name="arch" type="xml">
<form string="Website menu">
<sheet>
<group>
<field name="website_id"
options="{'no_create': True}"/>
</group>
<group>
<field name="name"/>
<field name="url"/>
<field name="new_window"/>
<field name="parent_id"/>
<field name="menu_view"
attrs="{'invisible':[('parent_id', '!=', False)]}"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="website.action_website_menu" model="ir.actions.act_window">
<field name="name">Website Menu</field>
<field name="res_model">website.menu</field>
<field name="view_mode">list,form</field>
<field name="context">{'search_default_my_websites':1}</field>
</record>
<record id="website.menu_tree" model="ir.ui.view">
<field name="name">website.menu.tree</field>
<field name="model">website.menu</field>
<field name="field_parent">child_id</field>
<field name="arch" type="xml">
<tree string="Website menu">
<field name="sequence" widget="handle"/>
<field name="website_id" options="{'no_create': True}"/>
<field name="name"/>
<field name="url"/>
<field name="new_window"/>
<field name="parent_id"/>
</tree>
</field>
</record>
<menuitem id="menu_website_menu"
name="Website Menu"
sequence="1"
action="website.action_website_menu"
parent="website.menu_website_configuration"/>
</data>
</flectra>

View File

@ -67,10 +67,6 @@ class TableCompute(object):
for y2 in range(y):
for x2 in range(x):
self.table[(pos // PPR) + y2][(pos % PPR) + x2] = False
self.table[pos // PPR][pos % PPR] = {
'product': p, 'x': x, 'y': y,
'class': " ".join(x.html_class for x in p.website_style_ids if x.html_class)
}
if index <= ppg:
maxy = max(maxy, y + (pos // PPR))
index += 1
@ -86,6 +82,15 @@ class TableCompute(object):
return rows
class WebsiteProductLimit(http.Controller):
@http.route(['/shop/product_limit'], type='json', auth="public")
def change_limit(self, value):
global PPG
PPG = int(value)
return True
class WebsiteSaleForm(WebsiteForm):
@http.route('/website_form/shop.sale.order', type='http', auth="public", methods=['POST'], website=True)
@ -159,7 +164,7 @@ class WebsiteSale(http.Controller):
# id is added to be sure that order is a unique sort key
return 'website_published desc,%s , id desc' % post.get('order', 'website_sequence desc')
def _get_search_domain(self, search, category, attrib_values):
def _get_search_domain(self, search, category, attrib_values, tag_values, brand_values):
domain = request.website.sale_product_domain()
if search:
for srch in search.split(" "):
@ -188,6 +193,12 @@ class WebsiteSale(http.Controller):
if not request.env.user.has_group('website.group_website_publisher'):
domain += [('website_ids', 'in', request.website.id)]
if tag_values:
domain += [('tag_ids', 'in', tag_values)]
if brand_values:
domain += [('brand_id', 'in', brand_values)]
return domain
@http.route([
@ -211,7 +222,17 @@ class WebsiteSale(http.Controller):
attributes_ids = {v[0] for v in attrib_values}
attrib_set = {v[1] for v in attrib_values}
domain = self._get_search_domain(search, category, attrib_values)
# For Tags
tag_list = request.httprequest.args.getlist('tags')
tag_values = [list(map(str, v)) for v in tag_list if v]
tag_set = set([int(v[0]) for v in tag_values])
# For Brands
brand_list = request.httprequest.args.getlist('brands')
brand_values = [list(map(str, v)) for v in brand_list if v]
brand_set = set([int(v[0]) for v in brand_values])
domain = self._get_search_domain(search, category, attrib_values, list(tag_set), list(brand_set))
keep = QueryURL('/shop', category=category and int(category), search=search, attrib=attrib_list, order=post.get('order'))
@ -249,12 +270,26 @@ class WebsiteSale(http.Controller):
products = Product.search(domain, limit=ppg, offset=pager['offset'], order=self._get_search_order(post))
ProductAttribute = request.env['product.attribute']
ProductBrand = request.env['product.brand']
ProductTag = request.env['product.tags']
if products:
# get all products without limit
selected_products = Product.search(domain, limit=False)
attributes = ProductAttribute.search([('attribute_line_ids.product_tmpl_id', 'in', selected_products.ids)])
attributes = ProductAttribute.search([('attribute_line_ids.product_tmpl_id', 'in', products.ids)])
prod_brands = []
prod_tags = []
for product in products:
if product.brand_id:
prod_brands.append(product.brand_id.id)
if product.tag_ids:
for tag_id in product.tag_ids.ids:
prod_tags.append(tag_id)
brands = ProductBrand.browse(list(set(prod_brands)))
tags = ProductTag.browse(list(set(prod_tags)))
else:
attributes = ProductAttribute.browse(attributes_ids)
brands = ProductBrand.browse(brand_set)
tags = ProductTag.browse(tag_set)
limits = request.env['product.view.limit'].search([])
values = {
'search': search,
@ -264,6 +299,8 @@ class WebsiteSale(http.Controller):
'pager': pager,
'pricelist': pricelist,
'products': products,
'tag_set': tag_set,
'brand_set': brand_set,
'search_count': product_count, # common for all searchbox
'bins': TableCompute().process(products, ppg),
'rows': PPR,
@ -271,7 +308,11 @@ class WebsiteSale(http.Controller):
'attributes': attributes,
'compute_currency': compute_currency,
'keep': keep,
'limits': limits,
'parent_category_ids': parent_category_ids,
'get_attribute_value_ids': self.get_attribute_value_ids,
'tags': tags,
'brands': brands,
}
if category:
values['main_object'] = category
@ -305,6 +346,7 @@ class WebsiteSale(http.Controller):
product_context['pricelist'] = pricelist.id
product = product.with_context(product_context)
values = {
'search': search,
'category': category,
@ -976,28 +1018,6 @@ class WebsiteSale(http.Controller):
})
return "/shop/product/%s?enable_editor=1" % slug(product.product_tmpl_id)
@http.route(['/shop/change_styles'], type='json', auth="public")
def change_styles(self, id, style_id):
product = request.env['product.template'].browse(id)
remove = []
active = False
style_id = int(style_id)
for style in product.website_style_ids:
if style.id == style_id:
remove.append(style.id)
active = True
break
style = request.env['product.style'].browse(style_id)
if remove:
product.write({'website_style_ids': [(3, rid) for rid in remove]})
if not active:
product.write({'website_style_ids': [(4, style.id)]})
return not active
@http.route(['/shop/change_sequence'], type='json', auth="public")
def change_sequence(self, id, sequence):
product_tmpl = request.env['product.template'].browse(id)

View File

@ -18,15 +18,6 @@
<field name="state">open</field>
</record>
<record id="website_sale.image_promo" model="product.style">
<field name="name">Sale Ribbon</field>
<field name="html_class">oe_ribbon_promo</field>
</record>
<record id="website_sale.image_full" model="product.style">
<field name="name">Image Full</field>
<field name="html_class">oe_image_full</field>
</record>
<record model="crm.team" id="sales_team.salesteam_website_sales">
<field name="active" eval="True"/>
<field name="dashboard_graph_model">sale.report</field>
@ -64,4 +55,15 @@
]"/>
</function>
</data>
<data noupdate="0">
<record id="product_limit_20" model="product.view.limit">
<field name="name">20</field>
</record>
<record id="product_limit_40" model="product.view.limit">
<field name="name">40</field>
</record>
<record id="product_limit_60" model="product.view.limit">
<field name="name">60</field>
</record>
</data>
</flectra>

View File

@ -11,7 +11,6 @@
<field name="website_size_x">2</field>
<field name="website_size_y">2</field>
<field name="website_sequence">3</field>
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
<field name="website_description" type="html">
<section class="mt16 mb16 oe_dark">
<div class="container">
@ -59,7 +58,6 @@
<record id="product.product_product_5b" model="product.product">
<field name="website_published" eval="True"/>
<field name="website_size_x">2</field>
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_promo')])]"/>
<field name="website_description" type="html">
<section class="mt16 mb16">
<div class="container">
@ -165,7 +163,6 @@
<record id="product.product_product_6" model="product.product">
<field name="website_published" eval="True"/>
<field name="website_sequence">4</field>
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
<field name="description_sale">Color: White
Capacity: 16GB
Connectivity: Wifi
@ -273,7 +270,6 @@ iOS7
</section>
</field>
<field name="website_sequence">4</field>
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
</record>
<record id="product.product_product_7" model="product.product">

View File

@ -8,6 +8,42 @@ from flectra.tools.translate import html_translate
from flectra.tools import float_is_zero
class ProductTags(models.Model):
_name = 'product.tags'
_order = 'sequence'
sequence = fields.Integer(help="Gives the sequence order when "
"displaying a list of rules.")
name = fields.Char(string='Name', required=True, translate=True)
_sql_constraints = [('name_uniq', 'unique (name)',
"Tag name already exists !")]
class ProductBrand(models.Model):
_name = 'product.brand'
_description = 'Product Brands'
_order = 'sequence'
sequence = fields.Integer(help="Gives the sequence order when displaying "
"a list of rules.")
name = fields.Char(string='Name', required=True, translate=True)
brand_image = fields.Binary(string='Brand Image')
_sql_constraints = [('name_uniq', 'unique (name)',
'Brand name already exists !')]
class ProductRibbon(models.Model):
_name = 'product.ribbon'
_description = 'Product Brand'
_order = 'name'
name = fields.Char(string='Name', size=20, required=True, translate=True)
ribbon_color_back = fields.Char(string='Background Color', required=True)
ribbon_color_text = fields.Char(string='Font Color', required=True)
class ProductStyle(models.Model):
_name = "product.style"
@ -59,6 +95,18 @@ class ProductPricelist(models.Model):
return res
class WebsiteProductLimit(models.Model):
_name = 'product.view.limit'
_order = 'sequence'
sequence = fields.Integer(help="Gives the sequence order when "
"displaying a list of rules.")
name = fields.Integer(string='Limit', required=True)
_sql_constraints = [('name', 'unique(name)', 'This must be unique!')]
class ProductPublicCategory(models.Model):
_name = "product.public.category"
_inherit = ["website.seo.metadata"]
@ -152,7 +200,6 @@ class ProductTemplate(models.Model):
'An algorithm figures out a list of accessories based on all the products added to cart.')
website_size_x = fields.Integer('Size X', default=1)
website_size_y = fields.Integer('Size Y', default=1)
website_style_ids = fields.Many2many('product.style', string='Styles')
website_sequence = fields.Integer('Website Sequence', help="Determine the display order in the Website E-commerce",
default=lambda self: self._default_website_sequence())
public_categ_ids = fields.Many2many('product.public.category', string='Website Product Category',
@ -169,6 +216,9 @@ class ProductTemplate(models.Model):
string='Websites', copy=False,
help='List of websites in which '
'Product is published.')
ribbon_id = fields.Many2one('product.ribbon', string='Product Ribbon')
brand_id = fields.Many2one('product.brand', string="Product's Brand")
tag_ids = fields.Many2many('product.tags', string='Product Tags')
def _website_price(self):
# First filter out the ones that have no variant:

View File

@ -6,7 +6,6 @@ access_product_category_pos_manager,product.public.category manager,model_produc
access_product_public_category_public,product.category.public,model_product_public_category,,1,0,0,0
access_product_pricelist_public,product.pricelist.public,product.model_product_pricelist,,1,0,0,0
access_product_pricelist_item_public,product.pricelist.item.public,product.model_product_pricelist_item,,1,0,0,0
access_product_style,product.style.public,website_sale.model_product_style,,1,0,0,0
access_product_supplierinfo,product.supplierinfo.public,product.model_product_supplierinfo,,1,0,0,0
access_product_attribute_public,product.attribute public,product.model_product_attribute,,1,0,0,0
access_product_attribute_value_public,product.attribute value public,product.model_product_attribute_value,,1,0,0,0
@ -20,3 +19,7 @@ access_product_image_public,product.image public,model_product_image,,1,0,0,0
access_product_image_publisher,product.image wbesite publisher,model_product_image,website.group_website_publisher,1,1,1,1
access_product_image_sale,product.image sale,model_product_image,sales_team.group_sale_manager,1,1,1,1
access_website_product_pricelist,access_website_product_pricelist,model_website_product_pricelist,,1,0,0,0
access_product_view_limit,access_product_view_limit,model_product_view_limit,,1,0,0,0
access_product_ribbon,product_ribbon,model_product_ribbon,,1,0,0,0
access_product_tags,access_product_tags,model_product_tags,,1,0,0,0
access_product_brand,access_product_brand,model_product_brand,,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
6 access_product_public_category_public product.category.public model_product_public_category 1 0 0 0
7 access_product_pricelist_public product.pricelist.public product.model_product_pricelist 1 0 0 0
8 access_product_pricelist_item_public product.pricelist.item.public product.model_product_pricelist_item 1 0 0 0
access_product_style product.style.public website_sale.model_product_style 1 0 0 0
9 access_product_supplierinfo product.supplierinfo.public product.model_product_supplierinfo 1 0 0 0
10 access_product_attribute_public product.attribute public product.model_product_attribute 1 0 0 0
11 access_product_attribute_value_public product.attribute value public product.model_product_attribute_value 1 0 0 0
19 access_product_image_publisher product.image wbesite publisher model_product_image website.group_website_publisher 1 1 1 1
20 access_product_image_sale product.image sale model_product_image sales_team.group_sale_manager 1 1 1 1
21 access_website_product_pricelist access_website_product_pricelist model_website_product_pricelist 1 0 0 0
22 access_product_view_limit access_product_view_limit model_product_view_limit 1 0 0 0
23 access_product_ribbon product_ribbon model_product_ribbon 1 0 0 0
24 access_product_tags access_product_tags model_product_tags 1 0 0 0
25 access_product_brand access_product_brand model_product_brand 1 0 0 0

View File

@ -0,0 +1,31 @@
/*
Part of Odoo Module Developed by 73lines
See LICENSE file for full copyright and licensing details.
*/
flectra.define('website_product_misc_options_73lines.products_view_limit', function (require) {
"use strict";
var website = require('website.website');
var ajax = require('web.ajax');
$(function(){
var previous_limit = localStorage['active_limit'];
if (previous_limit) {
for (var i = 0; i < $('.product_limit_link').length; i++) {
if (previous_limit == $('.product_limit_link')[i].getAttribute('value')) {
$('.product_limit_link')[i].classList.add('active');
}
}
$('.product_limit_link.active').parent().addClass('active')
}
$('.product_limit_link').click(function(type){
if(type['type'] !== "click") return;
ajax.jsonRpc('/shop/product_limit', 'call', {'value': $(this)[0].getAttribute('value')});
location.reload();
$(this).parent().siblings().removeClass('active');
$(this).parent().addClass('active');
localStorage['active_limit'] = $(this)[0].getAttribute('value');
});
});
});

View File

@ -0,0 +1,43 @@
/*
Part of Odoo Module Developed by 73lines
See LICENSE file for full copyright and licensing details.
*/
flectra.define('website_sale.products_view_switcher', function (require) {
"use strict";
var ajax = require('web.ajax');
var Widget = require('web.Widget');
var website = require('website.website');
var base = require('web_editor.base');
$(function(){
$('.grid_view').attr('disabled', 'disabled');
var previous_view_type = localStorage['active_view'];
if (previous_view_type == 'grid_view') {
$('div#grid_list').removeClass("oe_list").addClass('oe_grid oe-height-4');
$('.grid_view').attr('disabled', 'disabled');
$('.list_view').removeAttr('disabled');
}
if (previous_view_type == 'list_view') {
$('div#grid_list').removeClass("oe_grid oe-height-4").addClass('oe_list');
$('.list_view').attr('disabled', 'disabled');
$('.grid_view').removeAttr('disabled');
}
$('.grid_view').click(function(type){
if(type['type'] !== "click") return;
$('div#grid_list').removeClass("oe_list").addClass('oe_grid oe-height-4');
$('.grid_view').attr('disabled', 'disabled');
$('.list_view').removeAttr('disabled');
localStorage['active_view'] = 'grid_view';
});
$('.list_view').click(function(type){
if(type['type'] !== "click") return;
$('div#grid_list').removeClass("oe_grid oe-height-4").addClass('oe_list');
$('.list_view').attr('disabled', 'disabled');
$('.grid_view').removeAttr('disabled');
localStorage['active_view'] = 'list_view';
});
});
});

View File

@ -80,19 +80,6 @@ options.registry.website_sale = options.Class.extend({
if (size_y >= 4) $select = $select.add($size.find('tr:eq(3) td:lt('+size_x+')'));
$select.addClass("selected");
this._rpc({
model: 'product.style',
method: 'search_read',
}).then(function (data) {
var $ul = self.$el.find('ul[name="style"]');
for (var k in data) {
$ul.append(
$('<li data-style="'+data[k]['id']+'" data-toggle-class="'+data[k]['html_class']+'" data-no-preview="true"/>')
.append( $('<a/>').text(data[k]['name']) ));
}
self._setActive();
});
this.bind_resize();
},
reload: function () {
@ -141,13 +128,7 @@ options.registry.website_sale = options.Class.extend({
});
},
style: function (previewMode, value, $li) {
this._rpc({
route: '/shop/change_styles',
params: {
id: this.product_tmpl_id,
style_id: value,
},
});
},
go_to: function (previewMode, value) {
this._rpc({

View File

@ -0,0 +1,130 @@
/* ribbon */
.oe_product .ribbon-wrap {
width: 85px;
height: 88px;
z-index: 100;
overflow: hidden;
position: absolute;
top: 0;
right: 0;
}
/* quick view start */
.quick-view .fancybox {
-webkit-transition: all 0.6s ease;
transition: all 0.6s ease;
}
.quick-view .fancybox {
opacity: 0.7;
font-size: 11px;
font-weight: normal;
background: #EEEEEE;
border-radius: 0;
left: 0;
margin: 0;
padding: 7px;
float: none;
color: #000000;
overflow: hidden;
text-align: center;
white-space: nowrap;
display: block;
width: 32px;
text-transform: uppercase;
line-height: 1.42857;
}
.quick-view .fancybox .icon {
margin-right: 10px;
margin-left: 4px;
font-size: 12px;
}
.quick-view .fancybox:hover {
width: 100%;
}
.quick {
top: 0;
height: auto;
position: absolute;
left: 0;
width: 100%;
z-index: 6;
opacity: 1;
transition: 0.3s all ease 0s;
-webkit-transition: 0.3s all ease 0s;
}
.quick .quick-view-bgr {
width: 100%;
text-align: center;
}
.quick .quick-view-bgr a {
display: block;
cursor: pointer;
border: none;
position: absolute;
}
.product-single:hover .quick {
opacity: 1;
z-index: 20;
}
.oe_grid:hover .quick {
opacity: 1;
z-index: 20;
}
.oe_list:hover .quick {
opacity: 1;
z-index: 20;
}
.quick-view-product{
position: relative !important;
}
.modal-lg .product_price {
position: relative !important;
}
.quick-modal .modal-content {
border-radius: 0px;
}
.quick-modal .modal-header {
border-bottom: 0px;
}
.quick-modal .modal-footer {
border-top: 0px;
}
/* quick view end */
/* share Option start */
.share_buttons {
padding-left: 10px;
padding-right: 10px;
&:hover {
text-decoration: none;
}
&:focus {
text-decoration: none;
}
}
.oe_share_facebook, .oe_share_facebook:hover {
color: #3b5998 !important;
}
.oe_share_twitter, .oe_share_twitter:hover {
color: #326ada !important;
}
.oe_share_google, .oe_share_google:hover {
color: #dd4b39 !important;
}
.oe_share_mail, .oe_share_mail:hover {
color: #666666 !important;
}
/* share Option end */

View File

@ -58,4 +58,4 @@
.o_top_margin {
margin-top: 20px;
}
}
}

View File

@ -72,7 +72,9 @@
<field name="public_categ_ids" widget="many2many_tags" string="eCommerce Categories"/>
<field name="alternative_product_ids" widget="many2many_tags"/>
<field name="accessory_product_ids" widget="many2many_tags"/>
<field name="website_style_ids" widget="many2many_tags" groups="base.group_no_one"/>
<field name="ribbon_id"/>
<field name="tag_ids" widget="many2many_tags"/>
<field name="brand_id" options="{'no_create': True}"/>
</group>
<field name="website_ids"/>
</group>
@ -222,4 +224,155 @@
</field>
</record>
<!-- Product Limit Tree View -->
<record id="portfolio_category_tree_view" model="ir.ui.view">
<field name="name">product.view.limit.per.page.tree</field>
<field name="model">product.view.limit</field>
<field name="field_parent" eval="False" />
<field name="arch" type="xml">
<tree string="Product Veiw Limit per Page" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
</tree>
</field>
</record>
<!-- Product Limit Action -->
<record id="website_product_limit_action" model="ir.actions.act_window">
<field name="name">Website Products View Limit</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.view.limit</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new Product View Limit per Page in Shop.
</p>
</field>
</record>
<!-- Product Ribbon Form View -->
<record id="product_ribbon_form_view" model="ir.ui.view">
<field name="name">Product Ribbon Form View</field>
<field name="model">product.ribbon</field>
<field name="arch" type="xml">
<form string="Product Ribbon">
<sheet>
<group>
<field name="name" />
<field name="ribbon_color_back"/>
<field name="ribbon_color_text"/>
</group>
<div class="alert alert-danger mt16" style="font-size:15px;">
You can add Color Name | Hex Code in Ribbon's Background | Font color field.
</div>
</sheet>
</form>
</field>
</record>
<!-- Product Tags Tree View -->
<record id="product_tag_tree_view" model="ir.ui.view">
<field name="name">Product Tags Tree</field>
<field name="model">product.tags</field>
<field name="field_parent" eval="False" />
<field name="arch" type="xml">
<tree string="Product Tags" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
</tree>
</field>
</record>
<!-- Product Brand From View -->
<record id="product_by_brand_form_view" model="ir.ui.view">
<field name="name">product.brand.form</field>
<field name="model">product.brand</field>
<field name="arch" type="xml">
<form string="Product Brands">
<sheet>
<field name="brand_image" widget="image" class="oe_avatar"/>
<div class="oe_title">
<label class="oe_edit_only" for="name" string="Brand Name"/>
<h1><field name="name" placeholder="Brand Name"/></h1>
</div>
</sheet>
</form>
</field>
</record>
<!-- Product Brand Tree View -->
<record id="product_by_brand_tree_view" model="ir.ui.view">
<field name="name">product.brand.tree</field>
<field name="model">product.brand</field>
<field name="field_parent" eval="False" />
<field name="arch" type="xml">
<tree string="Product Brands">
<field name="sequence" widget="handle"/>
<field name="name" />
</tree>
</field>
</record>
<record id="product_brand_kanban_view" model="ir.ui.view">
<field name="name">Product By Brand</field>
<field name="model">product.brand</field>
<field name="arch" type="xml">
<kanban>
<field name="brand_image"/>
<field name="name"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="o_kanban_image">
<img t-att-src="kanban_image('product.brand', 'brand_image')"/>
</div>
<div class="oe_kanban_details">
<strong><field name="name"/></strong>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- Product Brand Action -->
<record id="product_by_brand_action" model="ir.actions.act_window">
<field name="name">Brands</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.brand</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click here to define a new product brand.
</p>
</field>
</record>
<!-- Product Tags Action -->
<record id="product_tags_action" model="ir.actions.act_window">
<field name="name">Tags</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.tags</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click here to define a new product tag.
</p>
</field>
</record>
<!-- Product Brands Menu -->
<menuitem action="product_by_brand_action" id="menu_product_by_brand"
parent="website_sale.menu_catalog"/>
<!-- Product Tags Menu -->
<menuitem action="product_tags_action" id="menu_product_tags" name="Tags"
parent="website_sale.menu_catalog"/>
</flectra>

View File

@ -131,6 +131,10 @@
action="base.action_partner_customer_form"
parent="menu_orders" sequence="4"/>
<!-- Product Limit Menu -->
<menuitem action="website_product_limit_action" id="menu_view_limit"
name="Website Product View Limit" parent="website_sale.menu_ecommerce_settings"/>
<!-- <menuitem id="menu_orders_invoices" name="Invoices" parent="menu_orders" action="action_invoices_ecommerce" sequence="4"/> -->

View File

@ -12,12 +12,15 @@
<xpath expr="." position="inside">
<link rel="stylesheet" href="/website_sale/static/src/css/website_sale.css" />
<link rel="stylesheet" href="/website_sale/static/src/css/website_mail.css" />
<link rel="stylesheet" href="/website_sale/static/src/less/website_sale.less"/>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_utils.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_payment.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_validate.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_tour_buy.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_tracking.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/products_view_switcher.js"></script>
<script type="text/javascript" src="/website_sale/static/src/js/products_view_limit.js"></script>
</xpath>
</template>
@ -78,17 +81,55 @@
</t>
</template>
<!-- View Switcher Icons Template -->
<template id="product_view_switcher" name="Product View Switcher">
<div class="btn-group switch-grp">
<button class="btn btn-default view-switcher grid_view"
data-toggle="tooltip" title="Grid View">
<span class="fa fa-th-large" />
</button>
<button class="btn btn-default view-switcher list_view"
data-toggle="tooltip" title="List View">
<span class="fa fa-bars" />
</button>
</div>
</template>
<!-- View Limit Template -->
<template id="product_limit" name="Product View Limit">
<div t-if="limits" class="product_limit dropdown btn-group">
<button class="btn btn-default dropdown-toggle active_limit"
type="button" data-toggle="dropdown">
<b>
<t t-esc="PPG" />
</b>
<span class="perPage"> Per Page </span>
<span class="caret"/>
</button>
<ul class="dropdown-menu limit_list">
<li t-foreach="limits" t-as="limit">
<a class="product_limit_link" t-att-value="limit['name']">
<t t-esc="limit['name']" />
</a>
</li>
</ul>
</div>
</template>
<!-- Product item used by /shop and /shop/cart -->
<template id="products_item" name="Products item">
<form action="/shop/cart/update" method="post">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
<div itemscope="itemscope" itemtype="http://schema.org/Product">
<div class="ribbon-wrapper">
<div class="ribbon btn btn-danger">Sale</div>
<div class="ribbon-wrap">
<div class="ribbon"
t-attf-style="background-color:#{product.ribbon_id.ribbon_color_back or product.ribbon_id.ribbon_color_back};color:#{product.ribbon_id.ribbon_color_text or product.ribbon_id.ribbon_color_text};">
<span t-esc="product.ribbon_id.name"></span>
</div>
</div>
<div class="oe_product_image">
<a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']&gt;1 else None))">
<span itemprop="image" t-attf-content="{{request.httprequest.url_root}}web/image/product.template/{{product.id}}/image" t-field="product.image" t-options="{'widget': 'image', 'resize': None if product_image_big else '300x300', 'zoom': 'image'}" t-att-alt="product.name" />
<img class="img img-responsive image-center thumb-hover" itemprop="image" t-att-alt="product.name" t-att-prod-id="product.id" t-att-title="product.name" t-attf-src="#{base_url}#{ website.image_url(product, 'image', '300x300')}"/>
</a>
</div>
<t t-if="show_publish">
@ -178,32 +219,21 @@
</t>
<t t-call="website_sale.pricelist_list" />
<t t-call="website.pager" />
<t t-call="website_sale.product_view_switcher" />
<t t-call="website_sale.product_limit" />
</div>
</div>
<div class="row">
<div class="hidden" id="products_grid_before"></div>
<div class="col-md-12" id="products_grid">
<table width="100%">
<tbody>
<tr t-ignore="true">
<td t-foreach="range(0,rows)" t-as="row" t-attf-width="#{100/rows}%"></td>
</tr>
<tr t-foreach="bins" t-as="tr_product">
<t t-foreach="tr_product" t-as="td_product">
<t t-if="td_product">
<t t-set="product" t-value="td_product['product']" />
<td t-att-colspan="td_product['x'] != 1 and td_product['x']" t-att-rowspan="td_product['y'] != 1 and td_product['y']" t-attf-class="oe_product oe_grid oe-height-#{td_product['y']*2} #{ td_product['class'] }">
<div class="oe_product_cart" t-att-data-publish="website in product.website_ids and 'on' or 'off'">
<t t-set="product_image_big" t-value="td_product['x']+td_product['y'] &gt; 2" />
<t t-call="website_sale.products_item" />
</div>
</td>
</t>
<td t-if="not td_product" class="oe-height-2" />
</t>
</tr>
</tbody>
</table>
<t t-foreach="products" t-as="product">
<div id="grid_list"
class="col-md-4 oe_product oe_grid oe_product_cart oe-height-4"
t-att-data-publish="website in product.website_ids and 'on' or 'off'">
<t t-call="website_sale.products_item">
</t>
</div>
</t>
<t t-if="not bins">
<div class="text-center text-muted oe_product">
<h3 class="css_editable_display">No product defined.</h3>
@ -362,15 +392,14 @@
</xpath>
</template>
<template id="products_list_view" inherit_id="website_sale.products" active="False" customize_show="True" name="List View">
<xpath expr="//div[@id='products_grid']//table" position="replace">
<t t-foreach="products" t-as="product">
<div class="oe_product oe_list oe_product_cart" t-att-data-publish="website in product.website_ids and 'on' or 'off'">
<t t-call="website_sale.products_item">
<t t-set="show_publish" t-value="True" />
</t>
</div>
</t>
<template id="products_list_view" inherit_id="website_sale.products" name="List View" active="False">
<xpath expr="//div[@id='products_grid']/t/div[@id='grid_list']" position="replace">
<div class="oe_product oe_list oe_product_cart"
t-att-data-publish="website in product.website_ids and 'on' or 'off'">
<t t-call="website_sale.products_item">
<t t-set="show_publish" t-value="True" />
</t>
</div>
</xpath>
</template>
@ -482,8 +511,60 @@
</p>
</div>
</div>
<div id="product_description_reviews" class="mt8">
<ul class="nav nav-tabs">
<li class="active" id="li-full-desc">
<a href="#full-desc" data-toggle="tab">
<strong>Description</strong>
</a>
</li>
<t t-if="product.attribute_line_ids">
<li role="presentation" class="" id="product_specification_tab">
<a href="#product-additional-info" aria-controls="full" role="tab"
data-toggle="tab" aria-expanded="true">
<strong>Specifications</strong>
</a>
</li>
</t>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="full-desc">
<div itemprop="description" t-field="product.website_description"
class="oe_structure mt16" id="product_full_description" />
</div>
<div class="tab-pane fade" id="product-additional-info">
<div t-if="product.attribute_line_ids">
<div id="product_attribute_details" class="mt32 mb32">
<table class="table table-striped">
<tbody>
<tr t-foreach="product.attribute_line_ids" t-as="pfl">
<td class="col-md-4">
<strong>
<p t-esc="pfl.attribute_id.name" style="margin: 0 0 0px;" />
</strong>
</td>
<td>
<p style="margin: 0 0 0px;">
<t t-set="i" t-value="0" />
<t t-foreach="pfl.value_ids" t-as="pfv">
<t t-esc="pfv.name" />
<span t-if="i != len(pfl.value_ids)-1">
,
</span>
<t t-set="i" t-value="i+1" />
</t>
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<div itemprop="description" t-field="product.website_description" class="oe_structure mt16" id="product_full_description" />
<t t-set="head">
<!-- Facebook and linkedin sharing data -->
<meta property="og:type" content="website" />
@ -543,19 +624,19 @@
<!-- Product options: OpenChatter -->
<template id="product_comment" inherit_id="website_sale.product" active="False" customize_show="True" name="Discussion and Rating">
<xpath expr="//div[@t-field='product.website_description']" position="after">
<div class="o_shop_discussion_rating">
<section class="container mt16 mb16">
<hr/>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<t t-call="portal.message_thread">
<t t-set="object" t-value="product"/>
<t t-set="display_rating" t-value="True"/>
</t>
</div>
</div>
</section>
<xpath expr="//li[@id='li-full-desc']" position="after">
<li id="li-comment">
<a href="#full-comment" data-toggle="tab">
<strong>Discussion and Rating</strong>
</a>
</li>
</xpath>
<xpath expr="//div[@id='full-desc']" position="after">
<div class="tab-pane fade" id="full-comment">
<t t-call="portal.message_thread">
<t t-set="object" t-value="product"/>
<t t-set="display_rating" t-value="True"/>
</t>
</div>
</xpath>
</template>
@ -1654,4 +1735,186 @@
</t>
</template>
<!-- Give Space besides Add to Cart button -->
<template id="product_side_block" inherit_id="website_sale.product"
name="Product Side Block" active="False" customize_show="True">
<xpath expr="//div[hasclass('col-sm-7')]" position="attributes">
<attribute name="class">col-sm-5</attribute>
</xpath>
<xpath expr="//div[@id='product_details']" position="attributes">
<attribute name="class">col-sm-5 col-lg-3 col-lg-offset-1
</attribute>
</xpath>
<xpath expr="//section[@id='product_detail']/div[2]" position="inside">
<div class="col-sm-2 col-lg-3 oe_structure">
<h4> Put Your Content Here Like Images,Carousel,Ecommerce Terms
etc...
</h4>
</div>
</xpath>
</template>
<template id="quick_view_products_item" inherit_id="website_sale.products_item"
active="True" customize_show="True" name="Product Quick View">
<xpath expr="//div[hasclass('oe_product_image')]/a[1]" position="after">
<div t-attf-class="quick" t-if="product.website_published and website in product.website_ids">
<div class="quick-view-bgr">
<a class="quick-view btn btn-primary" t-attf-href="##{ product.id }"
role="button" data-toggle="modal" title="Quick view">
<i class="fa fa-eye"></i>
</a>
</div>
</div>
<div t-att-id="product.id" class="modal fade in quick-modal" aria-hidden="true"
role="dialog" tabindex="-1" t-if="product.website_published">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">X
</button>
</div>
<div class="modal-body col-md-12">
<div class="col-md-5 col-md-offset-1">
<a itemprop="url"
t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']&gt;1 else None))">
<img itemprop="image" class="img img-responsive quick-view-product"
t-att-src="website.image_url(product, 'image')" t-att-alt="product.name" />
</a>
</div>
<div class="col-md-5 col-md-offset-1">
<div class="col-md-12 text-left">
<h2 class="mt16 mb16">
<strong t-field="product.display_name" />
</h2>
<div class="text-muted">
<div t-field="product.description_sale" class="mt16 mb16" />
<div class="js_attributes" />
</div>
</div>
<div class="col-md-12 mt16 mb16">
<div class="css_quantity input-group oe_website_spinner"
contenteditable="false">
<a t-attf-href="#" class="mb8 input-group-addon js_add_cart_json">
<i class="fa fa-minus"></i>
</a>
<input type="text" class="form-control" data-min="1"
name="add_qty" value="1" />
<a t-attf-href="#"
class="mb8 input-group-addon float_left js_add_cart_json">
<i class="fa fa-plus"></i>
</a>
</div>
</div>
<div class="col-md-12 text-left">
<t t-set="attribute_value_ids" t-value="get_attribute_value_ids(product)" />
<form action="/shop/cart/update" class="js_add_cart_variants"
t-att-data-attribute_value_ids="product.product_variant_ids.ids"
method="POST">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
<div class="js_product" t-if="product.product_variant_ids">
<t t-placeholder="select">
<input type="hidden" class="product_id" name="product_id"
t-att-value="product.product_variant_id.id if len(product.product_variant_ids) == 1 else '0'" />
<t t-call="website_sale.variants">
<t t-set="ul_class" t-value="'nav-stacked'" />
</t>
</t>
<h1><t t-call="website_sale.product_price" /></h1>
<p t-if="len(product.product_variant_ids) &gt; 1" class="css_not_available_msg bg-danger"
style="padding: 15px;">This combination does not exist.</p>
<a id="add_to_cart"
class="btn btn-primary btn-md mt8 js_check_product a-submit"
href="#">Add to Cart</a>
<a itemprop="url" class="btn btn-success btn-md mt8"
t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']&gt;1 else None))">
More Info
</a>
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
</xpath>
</template>
<!-- Enable Share Options in Product Page -->
<template id="product_share_button" inherit_id="website_sale.product"
name="Product Share Options" customize_show="True" active="False">
<xpath expr="//form[hasclass('js_add_cart_variants')]" position="after">
<hr/>
<div class="oe_share s_share">
<h2>
<a target="_Blank" class="oe_share_google share_buttons" data-toggle="tooltip" title="Google"
href="https://plus.google.com/share?url={url}">
<i class="fa fa-google-plus-square"/>
</a>
<a target="_Blank" class="oe_share_facebook share_buttons" data-toggle="tooltip" title="Facebook"
href="https://www.facebook.com/sharer/sharer.php?u={url}">
<i class="fa fa-facebook-square"/>
</a>
<a target="_Blank" class="oe_share_twitter share_buttons" data-toggle="tooltip" title="Twitter"
href="https://twitter.com/intent/tweet?text={title}&amp;url={url}">
<i class="fa fa-twitter"/>
</a>
<a href="mailto:?body={url}&amp;subject={title}" class="oe_share_mail share_buttons"
data-toggle="tooltip" title="Mail">
<i class="fa fa-envelope-o"/>
</a>
</h2>
</div>
</xpath>
</template>
<!-- Product Tags Filter -->
<template id="website_product_tags" inherit_id="website_sale.products_attributes"
active="False" customize_show="True" name="Product Tags's Filter">
<xpath expr="//form[hasclass('js_attributes')]" position="inside">
<t t-if="tags">
<div id="tags_div">
<div class="filter_title">
<strong>Tags</strong>
</div>
<t t-foreach="tags" t-as="tag">
<label class="product-tag btn btn-primary btn-xs mb8">
<input type="checkbox" name="tags" class="hidden product_tags"
t-att-title="tag.name" t-att-value="'%s' % (tag.id)"
t-att-checked="'checked' if tag.id in tag_set else None" />
<span style="font-weight: normal" t-field="tag.name" />
</label>
</t>
</div>
</t>
</xpath>
</template>
<!-- Product Brands Filter -->
<template id="website_product_brands" inherit_id="website_sale.products_attributes"
active="True" customize_show="True" name="Product Brand's Filter">
<xpath expr="//form[hasclass('js_attributes')]" position="inside">
<t t-if="brands">
<div id="brands_div">
<div class="filter_title">
<strong>Brands</strong>
</div>
<ul class="nav nav-pills nav-stacked">
<t t-foreach="brands" t-as="brand">
<li class="brand_list">
<label style="margin: 0 40px;">
<input type="checkbox" name="brands"
t-att-value="brand.id" t-att-checked="'checked' if brand.id in brand_set else None" />
<span style="font-weight: normal" t-field="brand.name" />
</label>
</li>
</t>
</ul>
</div>
</t>
</xpath>
</template>
</flectra>