[MERGE] with lp:~openerp-dev/openobject-addons/trunk-website-al.

bzr revid: dka@tinyerp.com-20140113130212-4f17b0inc4u3s5k3
bzr revid: dka@tinyerp.com-20140115052550-15ulprts5fo6s30r
This commit is contained in:
Darshan Kalola (OpenERP)
2014-01-15 10:55:50 +05:30
51 changed files with 870 additions and 576 deletions
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_decimal_precision_config,decimal.precision configuration,model_decimal_precision,base.group_system,1,1,1,1
access_decimal_precision_all,decimal.precision,model_decimal_precision,,1,0,0,0
access_decimal_precision_test_all,decimal.precision.test,model_decimal_precision_test,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_decimal_precision_config decimal.precision configuration model_decimal_precision base.group_system 1 1 1 1
3 access_decimal_precision_all decimal.precision model_decimal_precision 1 0 0 0
4 access_decimal_precision_test_all decimal.precision.test model_decimal_precision_test 1 1 1 1
+1 -1
View File
@@ -237,7 +237,7 @@ class test_message_compose(TestMail):
email_template.send_mail(cr, uid, email_template_id, self.group_pigs_id, force_send=True, context=context)
sent_emails = self._build_email_kwargs_list
email_to_lst = [
['b@b.b', 'c@c.c'], ['"Followers of Pigs" <admin@example.com>'],
['b@b.b', 'c@c.c'], ['"Followers of Pigs" <admin@yourcompany.example.com>'],
['"Followers of Pigs" <raoul@raoul.fr>'], ['"Followers of Pigs" <bert@bert.fr>']]
self.assertEqual(len(sent_emails), 4, 'email_template: send_mail: 3 valid email recipients + email_to -> should send 4 emails')
for email in sent_emails:
-15
View File
@@ -272,21 +272,6 @@ class event_event(osv.osv):
}
return {'value': dic}
def on_change_address_id(self, cr, uid, ids, address_id, context=None):
values = {}
if not address_id:
return values
address = self.pool.get('res.partner').browse(cr, uid, address_id, context=context)
values.update({
'street' : address.street,
'street2' : address.street2,
'city' : address.city,
'country_id' : address.country_id and address.country_id.id or False,
'state_id' : address.state_id and address.state_id.id or False,
'zip' : address.zip,
})
return {'value' : values}
def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
res = {'value':{}}
if date_end:
+1 -1
View File
@@ -74,7 +74,7 @@
</div>
<group name="main_field_group">
<group>
<field name="address_id" on_change="on_change_address_id(address_id)" context="{'show_address': 1}" options='{"always_reload": True}'/>
<field name="address_id" context="{'show_address': 1}" options='{"always_reload": True}'/>
<field name="organizer_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
-6
View File
@@ -74,8 +74,6 @@
<field name="deadline" eval="(DateTime.today() + timedelta(60)).strftime('%Y-%m-%d')"/>
<field name="price">1500.0</field>
<field name="register_max">10</field>
<field name="description_sale">Cathering for breakfast and lunch
VIP Parking access</field>
</record>
@@ -105,8 +103,6 @@ VIP Parking access</field>
<field name="deadline" eval="(DateTime.today() + timedelta(60)).strftime('%Y-%m-%d')"/>
<field name="price">1500.0</field>
<field name="register_max">5</field>
<field name="description_sale">Cathering for breakfast and lunch
VIP Parking access</field>
</record>
<record id="event_3_ticket_1" model="event.event.ticket">
@@ -125,8 +121,6 @@ VIP Parking access</field>
<field name="deadline" eval="(DateTime.today() + timedelta(60)).strftime('%Y-%m-%d')"/>
<field name="price">1500.0</field>
<field name="register_max">5</field>
<field name="description_sale">Cathering for breakfast and lunch
VIP Parking access</field>
</record>
</data>
@@ -94,87 +94,6 @@
Resource: product.product
-->
<record id="product.product_product_2" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_1" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_3" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_4" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_5" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_6" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_7" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_6" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_9" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_10" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_11" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_12" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_13" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_14" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_15" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_16" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_17" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_18" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_19" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_27" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_20" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_21" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_22" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_23" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_24" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_25" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="product.product_product_26" model="product.product">
<field name="company_id" ref="res_company_oerp_editor"/>
</record>
<record id="Odoo1" model="product.category">
<field name="parent_id" ref="product.product_category_all"/>
<field name="name">Odoo Offers</field>
@@ -20,8 +20,9 @@ class AdyenController(http.Controller):
@website.route([
'/payment/adyen/return/',
], type='http', auth='public')
def adyen_return(self, **post):
""" Paypal IPN. """
def adyen_return(self, pspReference, **post):
""" Paypal IPN."""
post["pspReference"] = pspReference
_logger.info('Beginning Adyen form_feedback with post data %s', pprint.pformat(post)) # debug
request.registry['payment.transaction'].form_feedback(request.cr, request.uid, post, 'adyen', context=request.context)
return_url = post.pop('return_url', '')
@@ -8,7 +8,6 @@
<field name="env">test</field>
<field name="message"><![CDATA[
<p>You will be redirected to the Paypal website after cliking on the payment button.</p>]]></field>
<field name="paypal_tx_url">https://www.sandbox.paypal.com/cgi-bin/webscr</field>
<field name="paypal_email_id">dummy</field>
<field name="paypal_seller_id">dummy</field>
<field name="paypal_api_username">dummy</field>
+8 -1
View File
@@ -24,7 +24,9 @@ import re
from _common import rounding
from lxml import etree
from openerp.osv.orm import setup_modifiers
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
@@ -828,8 +830,13 @@ class product_product(osv.osv):
partner_id = context.get('partner_id', False)
# all user don't have access to seller and partner
# check access and use superuser
self.check_access_rights(cr, user, "read")
self.check_access_rule(cr, user, ids, "read", context=context)
result = []
for product in self.browse(cr, user, ids, context=context):
for product in self.browse(cr, SUPERUSER_ID, ids, context=context):
sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids)
if sellers:
for s in sellers:
+3 -3
View File
@@ -431,7 +431,7 @@
<field name="name">product.public.category.form</field>
<field name="model">product.public.category</field>
<field name="arch" type="xml">
<form string="Product Categories" version="7.0">
<form string="Pos/Public Categories" version="7.0">
<sheet>
<field name="image_medium" widget='image' class="oe_avatar oe_right"/>
<div class="oe_left">
@@ -457,7 +457,7 @@
</field>
</record>
<record id="product_public_category_action" model="ir.actions.act_window">
<field name="name">Public Categories</field>
<field name="name">Pos/Public Product Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.public.category</field>
<field name="view_type">form</field>
@@ -476,7 +476,7 @@
</p>
</field>
</record>
<menuitem action="product_public_category_action" id="menu_product_public_category" parent="base.menu_product" sequence="10" />
<menuitem action="product_public_category_action" id="menu_product_public_category" parent="prod_config_main" sequence="10" />
<!-- END -->
<!-- Unit of Measure -->
+102 -2
View File
@@ -183,7 +183,7 @@ class Text(orm.AbstractModel):
_inherit = ['website.qweb.field', 'ir.qweb.field.text']
def from_html(self, cr, uid, model, column, element, context=None):
return element.text_content()
return html_to_text(element)
class Selection(orm.AbstractModel):
_name = 'website.qweb.field.selection'
@@ -211,7 +211,8 @@ class ManyToOne(orm.AbstractModel):
M2O = self.pool[column._obj]
field = element.get('data-oe-field')
id = int(element.get('data-oe-id'))
value = element.text_content().strip()
# FIXME: weird things are going to happen for char-type _rec_name
value = html_to_text(element)
# if anything blows up, just ignore it and bail
try:
@@ -419,3 +420,102 @@ class Contact(orm.AbstractModel):
}, engine='website.qweb', context=context)
return ir_qweb.HTMLSafe(html)
def html_to_text(element):
""" Converts HTML content with HTML-specified line breaks (br, p, div, ...)
in roughly equivalent textual content.
Used to replace and fixup the roundtripping of text and m2o: when using
libxml 2.8.0 (but not 2.9.1) and parsing HTML with lxml.html.fromstring
whitespace text nodes (text nodes composed *solely* of whitespace) are
stripped out with no recourse, and fundamentally relying on newlines
being in the text (e.g. inserted during user edition) is probably poor form
anyway.
-> this utility function collapses whitespace sequences and replaces
nodes by roughly corresponding linebreaks
* p are pre-and post-fixed by 2 newlines
* br are replaced by a single newline
* block-level elements not already mentioned are pre- and post-fixed by
a single newline
ought be somewhat similar (but much less high-tech) to aaronsw's html2text.
the latter produces full-blown markdown, our text -> html converter only
replaces newlines by <br> elements at this point so we're reverting that,
and a few more newline-ish elements in case the user tried to add
newlines/paragraphs into the text field
:param element: lxml.html content
:returns: corresponding pure-text output
"""
# output is a list of str | int. Integers are padding requests (in minimum
# number of newlines). When multiple padding requests, fold them into the
# biggest one
output = []
_wrap(element, output)
# remove any leading or tailing whitespace, replace sequences of
# (whitespace)\n(whitespace) by a single newline, where (whitespace) is a
# non-newline whitespace in this case
return re.sub(
r'[ \t\r\f]*\n[ \t\r\f]*',
'\n',
''.join(_realize_padding(output)).strip())
_PADDED_BLOCK = set('p h1 h2 h3 h4 h5 h6'.split())
# https://developer.mozilla.org/en-US/docs/HTML/Block-level_elements minus p
_MISC_BLOCK = set((
'address article aside audio blockquote canvas dd dl div figcaption figure'
' footer form header hgroup hr ol output pre section tfoot ul video'
).split())
def _collapse_whitespace(text):
""" Collapses sequences of whitespace characters in ``text`` to a single
space
"""
return re.sub('\s+', ' ', text)
def _realize_padding(it):
""" Fold and convert padding requests: integers in the output sequence are
requests for at least n newlines of padding. Runs thereof can be collapsed
into the largest requests and converted to newlines.
"""
padding = None
for item in it:
if isinstance(item, int):
padding = max(padding, item)
continue
if padding:
yield '\n' * padding
padding = None
yield item
# leftover padding irrelevant as the output will be stripped
def _wrap(element, output, wrapper=u''):
""" Recursively extracts text from ``element`` (via _element_to_text), and
wraps it all in ``wrapper``. Extracted text is added to ``output``
:type wrapper: basestring | int
"""
output.append(wrapper)
if element.text:
output.append(_collapse_whitespace(element.text))
for child in element:
_element_to_text(child, output)
output.append(wrapper)
def _element_to_text(e, output):
if e.tag == 'br':
output.append(u'\n')
elif e.tag in _PADDED_BLOCK:
_wrap(e, output, 2)
elif e.tag in _MISC_BLOCK:
_wrap(e, output, 1)
else:
# inline
_wrap(e, output)
if e.tail:
output.append(_collapse_whitespace(e.tail))
+1 -1
View File
@@ -624,7 +624,7 @@ class res_company(osv.osv):
partner = self.browse(cr, openerp.SUPERUSER_ID, ids[0], context=context).partner_id
return partner and partner.google_map_link(zoom, context=context) or None
class base_language_install(osv.osv):
class base_language_install(osv.osv_memory):
_inherit = "base.language.install"
_columns = {
'website_ids': fields.many2many('website', string='Websites to translate'),
+30 -2
View File
@@ -81,6 +81,10 @@
outline: none !important;
}
.css_editable_display {
display: block !important;
}
.css_non_editable_mode_hidden {
display: none;
}
@@ -103,6 +107,14 @@
top: 2px;
}
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
background-image: url("/website/static/src/img/edit_here.png") !important;
}
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before {
content: "Press The Top-Left Edit Button" !important;
}
/* ---- EDITOR BAR ---- {{{ */
table.editorbar-panel {
cursor: pointer;
@@ -432,6 +444,10 @@ ul.oe_menu_editor .disclose {
}
/* ---- SNIPPET MANIPULATOR ---- {{{ */
.resize_editor_busy {
background-color: rgba(0, 0, 0, 0.3);
}
.oe_overlay {
display: none;
position: absolute;
@@ -514,16 +530,28 @@ ul.oe_menu_editor .disclose {
.oe_overlay .oe_handle.e:before {
content: "\F061";
}
.oe_overlay .oe_handle.s {
.oe_overlay .oe_handle.s, .oe_overlay .oe_handle.size {
top: auto;
left: 2px;
width: 100%;
bottom: -4px;
cursor: n-resize;
}
.oe_overlay .oe_handle.s:before {
.oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.size:before {
content: "\F063";
}
.oe_overlay .oe_handle.size {
border-style: dashed;
border-width: 0 0 1px 0;
border-color: rgba(0, 0, 0, 0.5);
}
.oe_overlay .oe_handle.size:before {
content: "Resize";
width: 64px;
text-align: center;
margin-left: -32px;
margin-top: -4px;
}
.oe_overlay .oe_handle.w {
top: 2px;
height: 100%;
+22 -1
View File
@@ -67,6 +67,9 @@
.oe_editable:focus
outline: none !important
.css_editable_display
display: block !important
.css_non_editable_mode_hidden
display: none
@@ -85,6 +88,12 @@
position: relative !important
top: +2px
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
background-image: url('/website/static/src/img/edit_here.png') !important
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
content: 'Press The Top-Left Edit Button' !important
// }}}
/* ---- EDITOR BAR ---- {{{ */
@@ -349,6 +358,8 @@ ul.oe_menu_editor
/* ---- SNIPPET MANIPULATOR ---- {{{ */
.resize_editor_busy
background-color: rgba(0,0,0,0.3)
.oe_overlay
display: none
position: absolute
@@ -403,7 +414,7 @@ ul.oe_menu_editor
cursor: w-resize
&:before
content: "\F061"
.oe_handle.s
.oe_handle.s, .oe_handle.size
top: auto
left: 2px
width: 100%
@@ -411,6 +422,16 @@ ul.oe_menu_editor
cursor: n-resize
&:before
content: "\F063"
.oe_handle.size
border-style: dashed
border-width: 0 0 1px 0
border-color: rgba(0, 0, 0, 0.5)
&:before
content: "Resize"
width: 64px
text-align: center
margin-left: -32px
margin-top: -4px
.oe_handle.w
top: 2px
height: 100%
+67 -147
View File
@@ -78,95 +78,6 @@
margin-bottom: 0px !important;
}
.pt128 {
padding-top: 128px !important;
}
.pt92 {
padding-top: 92px !important;
}
.pt64 {
padding-top: 64px !important;
}
.pt48 {
padding-top: 48px !important;
}
.pt32 {
padding-top: 32px !important;
}
.pt16 {
padding-top: 16px !important;
}
.pt8 {
padding-top: 8px !important;
}
.pt4 {
padding-top: 4px !important;
}
.pt0 {
padding-top: 0px !important;
}
.pb128 {
padding-bottom: 128px !important;
}
.pb92 {
padding-bottom: 92px !important;
}
.pb64 {
padding-bottom: 64px !important;
}
.pb48 {
padding-bottom: 48px !important;
}
.pb32 {
padding-bottom: 32px !important;
}
.pb16 {
padding-bottom: 16px !important;
}
.pb8 {
padding-bottom: 8px !important;
}
.pb4 {
padding-bottom: 4px !important;
}
.pb0 {
padding-bottom: 0px !important;
}
/* Grid of unequally tall elements */
.grid > [class*="col-md"] {
display: inline-block;
float: none;
vertical-align: top;
box-sizing: border-box;
}
.grid > [class*="col-md"].grid-align-top > [class*="col-md"] {
vertical-align: top;
}
.grid > [class*="col-md"].grid-align-middle > [class*="col-md"] {
vertical-align: middle;
}
.grid > [class*="col-md"].grid-align-bottom > [class*="col-md"] {
vertical-align: bottom;
}
/* Extra Styles */
img.shadow {
-webkit-border-radius: 3px;
@@ -185,9 +96,14 @@ h1.text-muted, h2.text-muted, h3.text-muted {
}
/* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */
.navbar.navbar-default.navbar-static-top {
.navbar.navbar-static-top {
margin-bottom: 0;
}
.navbar.navbar-static-top ul.nav > li.divider {
margin-top: 15px;
padding-top: 20px;
border-right: 1px solid grey;
}
/* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */
html, body, #wrapwrap {
@@ -209,7 +125,7 @@ header, main, footer {
footer {
height: 100%;
background: #eff8f8;
background: rgba(100, 200, 200, 0.1);
background: rgba(200, 200, 200, 0.1);
}
#footer_container {
@@ -233,7 +149,7 @@ footer {
}
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
background-image: url("/website/static/src/img/edit_here.png");
background-image: url("/website/static/src/img/under_construction.png");
background-repeat: no-repeat;
background-position: center;
height: 220px !important;
@@ -244,7 +160,7 @@ footer {
}
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before {
content: "Press The Top-Left Edit Button";
content: "This page is under construction";
text-align: center;
display: block;
padding-top: 160px;
@@ -257,6 +173,10 @@ footer {
content: "Drag Building Blocks Here";
}
.css_editable_display {
display: none;
}
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.navbar .nav > li > p {
margin-bottom: 0px;
@@ -334,20 +254,23 @@ ul.nav-stacked > li > a {
}
/* ---- SNIPPETS --- */
[data-snippet-id], .colmd, .hr, .blockquote {
overflow: hidden;
}
@media (max-width: 400px) {
[data-snippet-id] {
height: auto !important;
}
}
.carousel-inner {
height: 100%;
}
.carousel-inner .item {
height: 100%;
background-size: 100%;
}
.carousel.oe_small .carousel-inner {
height: 200px;
}
.carousel.oe_medium .carousel-inner {
height: 300px;
}
.carousel.oe_big .carousel-inner {
height: 450px;
}
.carousel .carousel-caption {
left: auto;
right: auto;
@@ -434,7 +357,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active {
/* Background */
.oe_dark {
background: #eff8f8;
background: rgba(100, 200, 200, 0.14);
background: rgba(200, 200, 200, 0.14);
-webkit-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
-moz-box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
box-shadow: 0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset;
@@ -442,16 +365,55 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active {
.oe_black {
background-color: rgba(0, 0, 0, 0.9);
color: white;
}
.oe_green {
background-color: rgba(0, 128, 0, 0.8);
background-color: #51d466;
color: white;
}
.oe_green .text-muted {
color: #dddddd;
}
.oe_blue_light {
background-color: #4791d2;
color: white;
}
.oe_blue_light .text-muted {
color: #dddddd;
}
.oe_blue {
background-color: #34495e;
color: white;
}
.oe_orange {
background-color: #e67e22;
color: white;
}
.oe_orange .text-muted {
color: #dddddd;
}
.oe_purple {
background-color: #b163a3;
color: white;
}
.oe_purple .text-muted {
color: #dddddd;
}
.oe_red {
background-color: rgba(255, 0, 0, 0.8);
background-color: #f75353;
color: white;
}
.oe_red .text-muted {
color: #dddddd;
}
/* Misc */
.oe_img_bg {
background-size: 100%;
}
@@ -511,45 +473,3 @@ span[data-oe-type="monetary"] {
-o-column-count: 3;
column-count: 3;
}
.bs-callout {
margin: 20px 0;
padding: 20px;
border-left: 3px solid #eeeeee;
}
.bs-callout h4 {
margin-top: 0;
margin-bottom: 5px;
}
.bs-callout p:last-child {
margin-bottom: 0;
}
.bs-callout-danger {
background-color: #fdf7f7;
border-color: #eed3d7;
}
.bs-callout-danger h4 {
color: #b94a48;
}
.bs-callout-warning {
background-color: #faf8f0;
border-color: #faebcc;
}
.bs-callout-warning h4 {
color: #8a6d3b;
}
.bs-callout-info {
background-color: #f4f8fa;
border-color: #bce8f1;
}
.bs-callout-info h4 {
color: #34789a;
}
+61 -95
View File
@@ -51,57 +51,6 @@
.mb0
margin-bottom: 0px !important
.pt128
padding-top: 128px !important
.pt92
padding-top: 92px !important
.pt64
padding-top: 64px !important
.pt48
padding-top: 48px !important
.pt32
padding-top: 32px !important
.pt16
padding-top: 16px !important
.pt8
padding-top: 8px !important
.pt4
padding-top: 4px !important
.pt0
padding-top: 0px !important
.pb128
padding-bottom: 128px !important
.pb92
padding-bottom: 92px !important
.pb64
padding-bottom: 64px !important
.pb48
padding-bottom: 48px !important
.pb32
padding-bottom: 32px !important
.pb16
padding-bottom: 16px !important
.pb8
padding-bottom: 8px !important
.pb4
padding-bottom: 4px !important
.pb0
padding-bottom: 0px !important
/* Grid of unequally tall elements */
.grid > [class*="col-md"]
display: inline-block
float: none
vertical-align: top
box-sizing: border-box
&.grid-align-top > [class*="col-md"]
vertical-align: top
&.grid-align-middle > [class*="col-md"]
vertical-align: middle
&.grid-align-bottom > [class*="col-md"]
vertical-align: bottom
/* Extra Styles */
img.shadow
@@ -114,8 +63,13 @@ h1.text-muted, h2.text-muted, h3.text-muted
/* ----- BOOTSTRAP HACK FOR HEADER NAV BAR ----- */
.navbar.navbar-default.navbar-static-top
.navbar.navbar-static-top
margin-bottom: 0
ul.nav
> li.divider
margin-top: 15px
padding-top: 20px
border-right: 1px solid grey
/* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */
@@ -133,7 +87,7 @@ header, main, footer
footer
height: 100%
background: rgb(239, 248, 248)
background: rgba(100, 200, 200, 0.1)
background: rgba(200, 200, 200, 0.1)
#footer_container
padding-top: 24px
@@ -154,7 +108,7 @@ footer
background-image: url('/website/static/src/img/drag_here.png')
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
background-image: url('/website/static/src/img/edit_here.png')
background-image: url('/website/static/src/img/under_construction.png')
background-repeat: no-repeat
background-position: center
height: 220px !important
@@ -163,7 +117,7 @@ footer
position: static
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
content: 'Press The Top-Left Edit Button'
content: 'This page is under construction'
text-align: center
display: block
padding-top: 160px
@@ -174,6 +128,9 @@ footer
.oe_structure.oe_editable.oe_empty:empty:before, .oe_editable[data-oe-type=html]:empty:before, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
content: 'Drag Building Blocks Here'
.css_editable_display
display: none
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.navbar .nav > li > p
@@ -242,18 +199,19 @@ ul.nav-stacked > li > a
color: white
/* ---- SNIPPETS --- */
[data-snippet-id],.colmd,.hr,.blockquote
overflow: hidden
.carousel-inner .item
@media (max-width: 400px)
[data-snippet-id]
height: auto !important
.carousel-inner
height: 100%
background-size: 100%
.item
height: 100%
background-size: 100%
.carousel
&.oe_small .carousel-inner
height: 200px
&.oe_medium .carousel-inner
height: 300px
&.oe_big .carousel-inner
height: 450px
.carousel-caption
left: auto
right: auto
@@ -326,17 +284,52 @@ div.carousel[data-snippet-id="slider"]
padding: 32px 0
/* Background */
.oe_dark
background: #eff8f8
background: rgba(100, 200, 200, 0.14)
background: rgba(200, 200, 200, 0.14)
+box-shadow(0px 5px 9px -7px rgba(0, 0, 255, 0.5) inset, 0px -3px 9px -7px rgba(0, 0, 255, 0.5) inset)
.oe_black
background-color: rgba(0, 0, 0, 0.9)
color: white
.oe_green
background-color: rgba(0, 128, 0, 0.8)
background-color: #51d466
color: white
.text-muted
color: #ddd
.oe_blue_light
background-color: #4791d2
color: white
.text-muted
color: #ddd
.oe_blue
background-color: #34495e
color: white
.oe_orange
background-color: #e67e22
color: white
.text-muted
color: #ddd
.oe_purple
background-color: #b163a3
color: white
.text-muted
color: #ddd
.oe_red
background-color: rgba(255, 0, 0, 0.8)
background-color: #f75353
color: white
.text-muted
color: #ddd
/* Misc */
.oe_img_bg
background-size: 100%
@@ -382,30 +375,3 @@ span[data-oe-type="monetary"]
.oe_template_fallback
@include column-count(3)
// ---- Callouts from bootstrap doc ---- {{{
.bs-callout
margin: 20px 0
padding: 20px
border-left: 3px solid #eee
.bs-callout h4
margin-top: 0
margin-bottom: 5px
.bs-callout p:last-child
margin-bottom: 0
.bs-callout-danger
background-color: #fdf7f7
border-color: #eed3d7
.bs-callout-danger h4
color: #b94a48
.bs-callout-warning
background-color: #faf8f0
border-color: #faebcc
.bs-callout-warning h4
color: #8a6d3b
.bs-callout-info
background-color: #f4f8fa
border-color: #bce8f1
.bs-callout-info h4
color: #34789a
// }}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

@@ -94,6 +94,11 @@
}).then(function (views) {
self.loadViews.call(self, views);
self.open.call(self);
// cf. fp's direct request
if (views.length >= 2) {
var mainTemplate = views[1];
self.$('#ace-view-list').val(mainTemplate.id).trigger('change');
}
});
var $editor = self.$('.ace_editor');
+1 -1
View File
@@ -92,7 +92,7 @@
}
});
}
$.getJSON("http://seo.eu01.aws.af.cm/suggest/"+encodeURIComponent(this.root + " "), addSuggestions);
$.getJSON("http://suggest.hp.af.cm/suggest/"+encodeURIComponent(this.root + " "), addSuggestions);
},
});
@@ -126,6 +126,9 @@
self.make_active(false);
self.$el.toggleClass("hidden");
});
$("#wrapwrap").click(function () {
self.$el.addClass("hidden");
});
this.fetch_snippet_templates();
@@ -181,6 +184,7 @@
'top': pos.top - mt,
'left': pos.left
});
$el.find(".oe_handle.size").css("bottom", mb+'px');
},
show: function () {
this.$el.removeClass("hidden");
@@ -678,17 +682,6 @@
});
website.snippet.styleRegistry.size = website.snippet.StyleEditor.extend({
select: function(event, np) {
this._super(event, np);
this.parent.parent.cover_target(this.$overlay, this.$target);
},
preview: function (event, np) {
this._super(event, np);
this.parent.parent.cover_target(this.$overlay, this.$target);
}
});
website.snippet.styleRegistry.background = website.snippet.StyleEditor.extend({
_get_bg: function () {
return this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, "");
@@ -879,7 +872,7 @@
}
var Editor = website.snippet.styleRegistry[val['snippet-style-id']] || website.snippet.StyleEditor;
var editor = new Editor(self, self.$target, val['snippet-style-id']);
$ul.prepend(editor.$el);
$ul.prepend(editor.$el.addClass("snippet-style-" + val['snippet-style-id']));
});
if ($ul.find("li").length) {
@@ -973,96 +966,121 @@
if (!resize_values.s) $box.find(".oe_handle.s").remove();
if (!resize_values.e) $box.find(".oe_handle.e").remove();
if (!resize_values.w) $box.find(".oe_handle.w").remove();
if (!resize_values.size) $box.find(".oe_handle.size").remove();
this.$overlay.append($box.find(".oe_handles").html());
this.$overlay.find(".oe_handle").on('mousedown', function (event){
event.preventDefault();
event.preventDefault();
var $handle = $(this);
var $handle = $(this);
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
else if ($handle.hasClass('size')) {
compass = 'size';
XY = 'Y';
}
var resize = resize_values[compass];
if (!resize) return;
var resize = resize_values[compass];
if (!resize) return;
if (compass === 'size') {
var grid = resize[0];
var offset = self.$target.offset().top;
if (self.$target.css("background").match(/rgba\(0, 0, 0, 0\)/)) {
self.$target.addClass("resize_editor_busy");
}
} else {
var xy = event['page'+XY];
var current = resize[2] || 0;
_.each(resize[0], function (val, key) {
if (self.$target.hasClass(val)) {
current = key;
}
});
self.parent.editor_busy = true;
var xy = event['page'+XY];
var begin = current;
var beginClass = self.$target.attr("class");
var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[-]*[0-9]+'), 'g');
}
var cursor = $handle.css("cursor")+'-important';
var $body = $(document.body);
$body.addClass(cursor);
self.parent.editor_busy = true;
var body_mousemove = function (event){
event.preventDefault();
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
var cursor = $handle.css("cursor")+'-important';
var $body = $(document.body);
$body.addClass(cursor);
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev != current && dd < (2*resize[1][prev] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
}
var body_mousemove = function (event){
event.preventDefault();
if (compass === 'size') {
var dy = event.pageY-offset;
dy = dy - dy%resize[0];
if (dy <= 0) dy = resize[0];
self.$target.css("height", dy+"px");
self.on_resize(compass, beginClass, current);
self.parent.cover_target(self.$overlay, self.$target);
return;
}
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
if (change) {
self.on_resize(compass, beginClass, current);
self.parent.cover_target(self.$overlay, self.$target);
}
};
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev != current && dd < (2*resize[1][prev] + resize[1][current])/3) {
self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
}
var body_mouseup = function(){
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
$body.removeClass(cursor);
self.parent.editor_busy = false;
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
if (change) {
self.on_resize(compass, beginClass, current);
self.parent.cover_target(self.$overlay, self.$target);
}
};
var body_mouseup = function(){
$body.unbind('mousemove', body_mousemove);
$body.unbind('mouseup', body_mouseup);
$body.removeClass(cursor);
self.parent.editor_busy = false;
self.$target.removeClass("resize_editor_busy");
};
$body.mousemove(body_mousemove);
$body.mouseup(body_mouseup);
});
},
getSize: function () {
var grid = [0,4,8,16,32,48,64,92,128];
this.grid = {
// list of class (Array), grid (Array), default value (INT)
n: [_.map(grid, function (v) {return 'mt'+v;}), grid],
s: [_.map(grid, function (v) {return 'mb'+v;}), grid]
s: [_.map(grid, function (v) {return 'mb'+v;}), grid],
size: [8]
};
return this.grid;
},
@@ -1089,6 +1107,8 @@
var grid = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11];
this.grid.w = [_.map(grid, function (v) {return 'col-md-offset-'+v;}), _.map(grid, function (v) {return width/12*v;}), 12];
this.grid.s = null;
return this.grid;
},
_drag_and_drop_after_insert_dropzone: function(){
@@ -1277,6 +1297,10 @@
this.$target.css("background-image", "");
this.$target.removeClass(this._class);
},
load_style_options : function () {
this._super();
$(".snippet-style-size li[data-class='']").remove();
},
start : function () {
var self = this;
this._super();
+20 -22
View File
@@ -291,7 +291,7 @@
var self = this;
var testId = 'test_'+tour.id+'_tour';
this.tours.push(tour);
var stepDelay = 500; //ms
var defaultDelay = 500; //ms
var test = {
id: tour.id,
run: function (force) {
@@ -299,31 +299,33 @@
this.reset();
}
var actionSteps = _.filter(tour.steps, function (step) {
return step.trigger;
return step.trigger || step.sampleText;
});
function executeStep (step) {
window.localStorage.setItem(testId, step.stepId);
step.triggers(function () {
function next () {
var nextStep = actionSteps.shift();
if (nextStep) {
// Ensure the previous step has been fully propagated
setTimeout(function () {
setTimeout(function () {
executeStep(nextStep);
}, stepDelay);
}, 0);
executeStep(nextStep);
} else {
window.localStorage.removeItem(testId);
}
});
var $element = $(step.element);
if (step.snippet && step.trigger === 'drag') {
website.TestConsole.dragAndDropSnippet(step.snippet);
} else if (step.trigger.id === 'change') {
$element.trigger($.Event("change", { srcElement: $element }));
} else {
$element.trigger($.Event("click", { srcElement: $element }));
}
setTimeout(function () {
if (step.triggers) step.triggers(next);
var $element = $(step.element);
if (step.snippet && step.trigger === 'drag') {
website.TestConsole.dragAndDropSnippet(step.snippet);
} else if (step.trigger && step.trigger.id === 'change') {
$element.trigger($.Event("change", { srcElement: $element }));
} else if (step.sampleText) {
$element.val(step.sampleText);
$element.trigger($.Event("change", { srcElement: $element }));
} else if ($element.is(":visible")) { // Click by default
$element.trigger($.Event("click", { srcElement: $element }));
}
if (!step.triggers) next();
}, step.delay || defaultDelay);
}
var url = new website.UrlParser(window.location.href);
if (tour.path && url.pathname !== tour.path) {
@@ -342,11 +344,7 @@
executeStep(currentStep);
});
} else {
setTimeout(function () {
setTimeout(function () {
executeStep(currentStep);
}, stepDelay);
}, 0);
executeStep(currentStep);
}
}
},
@@ -86,6 +86,7 @@
<div class='oe_handle n'></div>
<div class='oe_handle e'></div>
<div class='oe_handle w'></div>
<div class='oe_handle size'></div>
<div class='oe_handle s'></div>
</div>
<div class='oe_snippet_thumbnail'>Margin resize</div>
+108 -4
View File
@@ -1,15 +1,120 @@
# -*- coding: utf-8 -*-
from functools import partial
import textwrap
import unittest2
from xml.dom.minidom import getDOMImplementation
from lxml import html
from lxml.builder import E
from openerp.tests import common
from openerp.addons.base.ir import ir_qweb
from openerp.addons.website.models.ir_qweb import html_to_text
impl = getDOMImplementation()
document = impl.createDocument(None, None, None)
class TestHTMLToText(unittest2.TestCase):
def test_rawstring(self):
self.assertEqual(
"foobar",
html_to_text(E.div("foobar")))
def test_br(self):
self.assertEqual(
"foo\nbar",
html_to_text(E.div("foo", E.br(), "bar")))
self.assertEqual(
"foo\n\nbar\nbaz",
html_to_text(E.div(
"foo", E.br(), E.br(),
"bar", E.br(),
"baz")))
def test_p(self):
self.assertEqual(
"foo\n\nbar\n\nbaz",
html_to_text(E.div(
"foo",
E.p("bar"),
"baz")))
self.assertEqual(
"foo",
html_to_text(E.div(E.p("foo"))))
self.assertEqual(
"foo\n\nbar",
html_to_text(E.div("foo", E.p("bar"))))
self.assertEqual(
"foo\n\nbar",
html_to_text(E.div(E.p("foo"), "bar")))
self.assertEqual(
"foo\n\nbar\n\nbaz",
html_to_text(E.div(
E.p("foo"),
E.p("bar"),
E.p("baz"),
)))
def test_div(self):
self.assertEqual(
"foo\nbar\nbaz",
html_to_text(E.div(
"foo",
E.div("bar"),
"baz"
)))
self.assertEqual(
"foo",
html_to_text(E.div(E.div("foo"))))
self.assertEqual(
"foo\nbar",
html_to_text(E.div("foo", E.div("bar"))))
self.assertEqual(
"foo\nbar",
html_to_text(E.div(E.div("foo"), "bar")))
self.assertEqual(
"foo\nbar\nbaz",
html_to_text(E.div(
"foo",
E.div("bar"),
E.div("baz")
)))
def test_other_block(self):
self.assertEqual(
"foo\nbar\nbaz",
html_to_text(E.div(
"foo",
E.section("bar"),
"baz"
)))
def test_inline(self):
self.assertEqual(
"foobarbaz",
html_to_text(E.div("foo", E.span("bar"), "baz")))
def test_whitespace(self):
self.assertEqual(
"foo bar\nbaz",
html_to_text(E.div(
"foo\nbar",
E.br(),
"baz")
))
self.assertEqual(
"foo bar\nbaz",
html_to_text(E.div(
E.div(E.span("foo"), " bar"),
"baz")))
class TestConvertBack(common.TransactionCase):
def setUp(self):
super(TestConvertBack, self).setUp()
@@ -70,7 +175,7 @@ class TestConvertBack(common.TransactionCase):
self.field_roundtrip('selection_str', 'B')
def test_text(self):
self.field_roundtrip('text', """
self.field_roundtrip('text', textwrap.dedent("""\
You must obey the dance commander
Givin' out the order for fun
You must obey the dance commander
@@ -88,8 +193,7 @@ class TestConvertBack(common.TransactionCase):
Let's start the show
Because you never know
You never know
You never know until you go
""")
You never know until you go"""))
def test_m2o(self):
""" the M2O field conversion (from html) is markedly different from
+8 -7
View File
@@ -4,21 +4,20 @@ import os
import select
import time
import json
from openerp import tools
from openerp import sql_db, tools
# avoid "ValueError: too many values to unpack"
def _exc_info_to_string(err, test):
return err
# TODO according to al this should be one line of Python
class LineReader:
class Stream:
def __init__(self, file_descriptor):
self._file_descriptor = file_descriptor
self._buffer = ''
def fileno(self):
return self._file_descriptor
# TODO Rewrite & fix
def readlines(self):
data = os.read(self._file_descriptor, 4096)
if not data: # EOF
@@ -50,6 +49,9 @@ class WebsiteUiSuite(unittest.TestSuite):
return iter([self])
def run(self, result):
# clean slate
if sql_db._Pool is not None:
sql_db._Pool.close_all(sql_db.dsn(tools.config['db_name']))
# check for PhantomJS...
try:
subprocess.call([ 'phantomjs', '-v' ], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
@@ -74,7 +76,6 @@ class WebsiteUiSuite(unittest.TestSuite):
self._options['timeout'] = self._timeout
self._options['port'] = tools.config.get('xmlrpc_port', 80)
self._options['db'] = tools.config.get('db_name', '')
# TODO use correct key from tools if exists (I could not find it --ddm)
self._options['user'] = 'admin'
self._options['password'] = tools.config.get('admin_passwd', 'admin')
@@ -84,7 +85,7 @@ class WebsiteUiSuite(unittest.TestSuite):
json.dumps(self._options)
], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
readable = [LineReader(phantom.stdout.fileno())]
readable = [Stream(phantom.stdout.fileno())]
try:
output = []
@@ -138,7 +139,7 @@ def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(WebsiteUiSuite(full_path('dummy_test.js'), {}))
base.addTest(WebsiteUiSuite(full_path('dummy_test.js'), {}, 5.0))
base.addTest(WebsiteUiSuite(full_path('simple_dom_test.js'), { 'action': 'website.action_website_homepage' }, 60.0))
base.addTest(WebsiteUiSuite(full_path('homepage_test.js'), { 'action': 'website.action_website_homepage' }, 60.0))
return base
@@ -43,7 +43,7 @@ function run (test) {
var page = require('webpage').create();
page.viewportSize = { width: 1920, height: 1080 };
page.viewportSize = { width: 1440, height: 900 };
page.onError = function(message, trace) {
console.log('{ "event": "error", "message": "'+message+'"}');
@@ -64,13 +64,17 @@ function run (test) {
test(page, timeout);
}
};
var maxRetries = 10;
var retryDelay = 1000; // ms
var tries = 0;
page.open(url, function openPage (status) {
if (status !== 'success') {
tries++;
if (tries < 5) {
page.open(url, openPage);
if (tries < maxRetries) {
setTimeout(function () {
page.open(url, openPage);
}, retryDelay);
} else {
console.log('{ "event": "error", "message": "'+url+' failed to load '+tries+' times ('+status+')"}');
phantom.exit(1);
+127 -23
View File
@@ -25,7 +25,7 @@
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_banner.png"/>
<span class="oe_snippet_thumbnail_title">Banner</span>
</div>
<div id="myCarousel" class="oe_snippet_body carousel slide oe_medium mb32">
<div id="myCarousel" class="oe_snippet_body carousel slide mb32" style="height: 320px;">
<!-- Indicators -->
<ol class="carousel-indicators hidden">
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
@@ -68,8 +68,8 @@
and give it to them.
</p>
</div>
<div class="col-md-6 mt16 mb16">
<img class="img img-responsive shadow" src="/website/static/src/img/text_image.png"/>
<div class="col-md-6 mt16">
<img class="img img-responsive shadow mb16" src="/website/static/src/img/text_image.png"/>
</div>
</div>
</div>
@@ -85,8 +85,8 @@
<section class="oe_snippet_body">
<div class="container">
<div class="row">
<div class="col-md-6 mt16 mb16">
<img class="img img-responsive shadow" src="/website/static/src/img/image_text.jpg"/>
<div class="col-md-6 mt16">
<img class="img img-responsive shadow mb16" src="/website/static/src/img/image_text.jpg"/>
</div>
<div class="col-md-6 mt16">
<h3>A Section Subtitle</h3>
@@ -277,7 +277,7 @@
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_quotes_slider.png"/>
<span class="oe_snippet_thumbnail_title">Quotes Slider</span>
</div>
<div id="myQuoteCarousel" class="oe_snippet_body carousel slide oe_small mb0">
<div id="myQuoteCarousel" class="oe_snippet_body carousel slide mb0" style="height: 240px;">
<!-- Indicators -->
<ol class="carousel-indicators mb0">
<li data-target="#myQuoteCarousel" data-slide-to="0" class="active"></li>
@@ -676,6 +676,118 @@
</section>
</div>
<div data-snippet-id="features" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_features.png"/>
<span class="oe_snippet_thumbnail_title">Features</span>
</div>
<section class="oe_snippet_body mb16">
<div class="container">
<div class="row">
<div class="col-md-12 text-center mb16">
<h2>List of Features</h2>
<h4 class="text-muted">Add a great slogan</h4>
</div>
<div class="col-md-3 text-center">
<span class="fa fa-suitcase fa-5x"></span>
<h3 class="mt8 mb0">First Feature</h3>
<p class="text-muted">Tell what's the value for the customer for this feature.</p>
<p><a class="btn btn-primary">Learn More</a></p>
</div>
<div class="col-md-3 text-center">
<span class="fa fa-lock fa-5x"></span>
<h3 class="mt8 mb0">Second Feature</h3>
<p class="text-muted">Talk about what the customer would like to know, not what you want to show.</p>
<p><a class="btn btn-success">Take Tour</a></p>
</div>
<div class="col-md-3 text-center">
<span class="fa fa-heart fa-5x"></span>
<h3 class="mt8 mb0">Third Feature</h3>
<p class="text-muted">A small explanation of this great feature, in clear words.</p>
<p><a class="btn btn-info">Buy Now</a></p>
</div>
<div class="col-md-3 text-center">
<span class="fa fa-credit-card fa-5x"></span>
<h3 class="mt8 mb0">Fourth Feature</h3>
<p class="text-muted">A small explanation of this great feature on two lines.</p>
<p><a class="btn btn-danger">Buy Now</a></p>
</div>
</div>
</div>
</section>
</div>
<div data-snippet-id="features-list" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_features.png"/>
<span class="oe_snippet_thumbnail_title">Feature Grid</span>
</div>
<section class="oe_snippet_body mb16">
<div class="container">
<div class="row">
<div class="col-md-5 text-center mb16">
<h2>List of Features</h2>
<h4 class="text-muted">Add a great slogan</h4>
</div>
<div class="col-md-5 text-center mb16 col-md-offset-2">
<h2>Second List</h2>
<h4 class="text-muted">Add a great slogan</h4>
</div>
</div><div class="row">
<div class="col-md-5">
<ul class="list-unstyled">
<li>
<span style="min-width: 45px" class="fa fa-3x fa-comment pull-left mb16"></span>
<h3 class="mb0">Blog</h3>
<p>OpenERP includes a full features enterprise blog to promote your messages.</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-columns pull-left mb16"></span>
<h3 class="mb0">Pricing Tables</h3>
<p>Use building blocks to use pricing tables to display your products or services.</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-user pull-left mb16"></span>
<h3 class="mb0">Log In &amp; Signup</h3>
<p>Clean &amp; simple Log In, Signup &amp; Forgot Your Password forms.</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-envelope pull-left mb16"></span>
<h3 class="mb0">Contact</h3>
<p>User-friendly contact form with Google Map integration.</p>
</li>
</ul>
</div>
<div class="col-md-5 col-md-offset-2">
<ul class="list-unstyled">
<li>
<span style="min-width: 45px" class="fa fa-3x fa-magic pull-left mb16"></span>
<h3 class="mb0">Bootstrap</h3>
<p>Uses Twitter Bootstrap's sleek, intuitive, and powerful front-end framework.</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-font pull-left mb16"></span>
<h3 class="mb0">Google Web Fonts</h3>
<p>Royalty free, open source fonts for the people, by the people!</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-bolt pull-left mb16"></span>
<h3 class="mb0">Powerful &amp; slick jQuery</h3>
<p>Fantastic jQuery plugins such as Isotope and Bootstrap's inbuilt features.</p>
</li>
<li>
<span style="min-width: 45px" class="fa fa-3x fa-picture-o pull-left mb16"></span>
<h3 class="mb0">Sample images</h3>
<p>Fantastic sample images fully licensed for use.</p>
</li>
</ul>
</div>
</div>
</div>
</section>
</div>
</div>
<div id="snippet_effect" class="tab-pane fade">
@@ -696,8 +808,8 @@
<li data-value="1.7"><a>Very Fast</a></li>
</ul>
</li>
<section class="oe_snippet_body parallax oe_medium"
style="background-image: url('/website/static/src/img/banner/mountains.jpg')"
<section class="oe_snippet_body parallax"
style="height: 320px; background-image: url('/website/static/src/img/banner/mountains.jpg')"
data-scroll-background-ratio="0.3">
<div><div class="oe_structure"/></div>
</section>
@@ -708,12 +820,12 @@
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_quotes_slider.png"/>
<span class="oe_snippet_thumbnail_title">Parallax Slider</span>
</div>
<section class="oe_snippet_body parallax oe_medium" data-snippet-id="parallax"
style="background-image: url('/website/static/src/img/parallax/quote.png')"
<section class="oe_snippet_body parallax" data-snippet-id="parallax"
style="height: 320px; background-image: url('/website/static/src/img/parallax/quote.png')"
data-scroll-background-ratio="0.3">
<div>
<div class="oe_structure">
<div id="myQuoteCarousel" class="carousel slide oe_small mb0" data-snippet-id="slider">
<div id="myQuoteCarousel" class="carousel slide mb0" style="height: 240px;" data-snippet-id="slider">
<!-- Indicators -->
<ol class="carousel-indicators mb0">
<li data-target="#myQuoteCarousel" data-slide-to="0" class="active"></li>
@@ -786,18 +898,6 @@
</li>
</div>
<div data-snippet-style-id='size' data-selector="section[data-snippet-id='parallax'], div[data-snippet-id='carousel'], div[data-snippet-id='slider']">
<li class="dropdown-submenu" data-required="true">
<a tabindex="-1" href="#">Size</a>
<ul class="dropdown-menu">
<li data-class='oe_small'><a>Small</a></li>
<li data-class='oe_medium'><a>Medium</a></li>
<li data-class='oe_big'><a>Big</a></li>
<li data-class=''><a>Auto</a></li>
</ul>
</li>
</div>
<div data-snippet-style-id='background' data-selector="section, section[data-snippet-id='parallax'], div[data-snippet-id='carousel']">
<li class="dropdown-submenu">
<a tabindex="-1" href="#">Background</a>
@@ -808,6 +908,10 @@
<li data-class='oe_dark'><a>Darken</a></li>
<li data-class='oe_green'><a>Green</a></li>
<li data-class='oe_red'><a>Red</a></li>
<li data-class='oe_blue_light'><a>Light Blue</a></li>
<li data-class='oe_blue'><a>Dark Blue</a></li>
<li data-class='oe_orange'><a>Orange</a></li>
<li data-class='oe_purple'><a>Purple</a></li>
<li data-class='oe_black'><a>Black</a></li>
</ul>
</li>
+5 -4
View File
@@ -96,12 +96,13 @@
<t t-foreach="website.menu_id.child_id" t-as="submenu">
<t t-call="website.submenu"/>
</t>
<li class="divider"/>
<li class="dropdown" t-ignore="true" t-if="user_id.id != website.public_user.id">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="badge">
<b>
<span t-esc="user_id.name"/>
<span class="caret"></span>
</span>
</b>
</a>
<ul class="dropdown-menu js_usermenu" role="menu">
<li><a href="/web" role="menuitem">Administration</a></li>
@@ -235,7 +236,7 @@
<xpath expr="//ul[@id='top_menu']" position="inside">
<li t-if="user_id.id == website.public_user.id">
<a t-attf-href="/web#redirect=#{ quote_plus(url_for('', keep_query='*')) }">
<span class="badge">Sign in</span>
<b>Sign in</b>
</a>
</li>
</xpath>
@@ -533,7 +534,7 @@
</div>
<div class="container" t-if="views">
<div class="bs-callout bs-callout-danger" t-if="qweb_template and editable">
<div class="alert alert-danger" t-if="qweb_template and editable">
<h4>Template fallback</h4>
<p>An error occured while rendering the template <code t-esc="qweb_template"/>.</p>
<p>If this error is caused by a change of yours in the templates, you have the possibility to reset one or more templates to their <strong>factory settings</strong>.</p>
+16 -8
View File
@@ -23,6 +23,7 @@ from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website.models import website
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
import werkzeug
@@ -218,16 +219,23 @@ class WebsiteBlog(http.Controller):
}
return request.website.render("website_blog.blog_post_complete", values)
@website.route(['/blogpost/<int:blog_post_id>/comment'], type='http', auth="public")
def blog_post_comment(self, blog_post_id, **post):
@website.route(['/blogpost/comment'], type='http', auth="public", methods=['POST'])
def blog_post_comment(self, blog_post_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context
if post.get('comment'):
request.registry['blog.post'].message_post(
cr, uid, blog_post_id,
body=post.get('comment'),
type='comment',
subtype='mt_comment',
context=dict(context, mail_create_nosubcribe=True))
user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
group_ids = user.groups_id
group_id = request.registry["ir.model.data"].get_object_reference(cr, uid, 'website_mail', 'group_comment')[1]
if group_id in [group.id for group in group_ids]:
blog_post = request.registry['blog.post']
blog_post.check_access_rights(cr, uid, 'read')
blog_post.message_post(
cr, SUPERUSER_ID, int(blog_post_id),
body=post.get('comment'),
type='comment',
subtype='mt_comment',
author_id=user.partner_id.id,
context=dict(context, mail_create_nosubcribe=True))
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
@website.route('/blogpost/new', type='http', auth="public", multilang=True)
@@ -148,6 +148,7 @@
title: "Publish Your Post",
content: "Your blog post is not yet published. You can update this draft version and publish it once you are ready.",
trigger: 'click',
delay: 5000,
},
{
stepId: 'end-tutorial',
+1 -1
View File
@@ -6,5 +6,5 @@ def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(full_path('post_test.js'), { 'action': 'website.action_website_homepage' }, 120.0))
base.addTest(test_ui.WebsiteUiSuite(full_path('post_test.js'), { 'action': 'website.action_website_homepage' }, 60.0))
return base
@@ -2,7 +2,7 @@ var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js');
var waitFor = testRunner.waitFor;
testRunner.run(function homepageTest (page, timeout) {
testRunner.run(function blogTest (page, timeout) {
page.evaluate(function () { localStorage.clear(); });
waitFor(function clientReady () {
return page.evaluate(function () {
@@ -22,9 +22,6 @@ testRunner.run(function homepageTest (page, timeout) {
};
});
var result = after && (after.image === 1) && (after.text === 1);
if (!result && window.location.href.indexOf('/blogpost/') > 0) {
window.location.reload();
}
return result;
}, function finish () {
console.log('{ "event": "success" }');
@@ -133,7 +133,7 @@
<!-- Options: Blog Post Summary: hide author -->
<template id="opt_blog_post_short_author" name="Author"
inherit_option_id="website_blog.blog_post_short">
<xpath expr="//span[@class*='oe_date']" position="after">
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
<span class="fa fa-user"> By <span t-field="blog_post.create_uid"/> &amp;nbsp;</span>
</xpath>
</template>
@@ -182,7 +182,7 @@
<h1 class="text-center" t-field="blog_post.name"/>
<p class="post-meta text-muted text-center" name="blog_post_data">
<span class="fa fa-calendar"> <span t-field="blog_post.create_date"/> &amp;nbsp;</span>
<span class="fa fa-calendar oe_date"> <span t-field="blog_post.create_date"/> &amp;nbsp;</span>
<span t-if="len(blog_post.message_ids) &gt; 0" class="fa fa-comment-o"> With
<a t-attf-href="#comments">
<t t-if="len(blog_post.message_ids) &lt;= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
@@ -218,10 +218,10 @@
<template id="opt_blog_post_complete_comment" name="Allow Comments"
inherit_option_id="website_blog.blog_post_complete" inherit_id="website_blog.blog_post_complete"
groups="website_mail.group_comment">
<xpath expr="//ul[@id='comments-list']" position="after">
<xpath expr="//ul[@id='comments-list']" position="before">
<section class="mb32 css_editable_mode_hidden">
<form id="comment" t-attf-action="/blogpost/#{blog_post.id}/comment"
method="POST">
<form id="comment" t-attf-action="/blogpost/comment" method="POST">
<input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/>
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
@@ -236,7 +236,7 @@
<!-- Options: Blog Post: hide author -->
<template id="opt_blog_post_complete_author" name="Authors"
inherit_option_id="website_blog.blog_post_complete">
<xpath expr="//span[@class*='oe_date']" position="after">
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
<span class="fa fa-user"> By <span t-field="blog_post.create_uid"/> &amp;nbsp;</span>
</xpath>
</template>
@@ -244,7 +244,7 @@
<!-- Options: Blog Post: show category -->
<template id="opt_blog_post_complete_category" name="Blog Category"
inherit_option_id="website_blog.blog_post_complete">
<xpath expr="//span[@class*='oe_date']" position="after">
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
<span class="fa fa-folder-open"> In <span t-field="blog_post.category_id"/> &amp;nbsp;</span>
</xpath>
</template>
@@ -49,10 +49,19 @@
{
stepId: 'choose-name',
element: '.modal input',
sampleText: 'Advanced Technical Training',
placement: 'right',
title: "Create an Event Name",
content: "Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training",
},
{
stepId: 'continue-name',
element: '.modal button.btn-primary',
placement: 'right',
title: "Create Event",
content: "Click <em>Continue</em> to create the event.",
trigger: 'click',
},
{
stepId: 'event-page',
title: "New Event Created",
@@ -137,6 +146,7 @@
title: "Publish your event",
content: "Click to publish your event.",
trigger: 'click',
delay: 5000,
},
{
stepId: 'customize-event',
+2
View File
@@ -21,6 +21,8 @@
from openerp.addons.website_blog.tests import test_controllers
import test_ui
checks = [
test_controllers,
]
+10
View File
@@ -0,0 +1,10 @@
import os
import openerp.addons.website.tests.test_ui as test_ui
def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(full_path('event_test.js'), { 'action': 'website.action_website_homepage' }, 60.0))
return base
@@ -0,0 +1,32 @@
var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js');
var waitFor = testRunner.waitFor;
testRunner.run(function eventTest (page, timeout) {
page.evaluate(function () { localStorage.clear(); });
waitFor(function clientReady () {
return page.evaluate(function () {
return window.$ && window.openerp && window.openerp.website
&& window.openerp.website.TestConsole
&& window.openerp.website.TestConsole.test('event');
});
}, function executeTest () {
page.evaluate(function () {
window.openerp.website.TestConsole.test('event').run(true);
});
waitFor(function testExecuted () {
var after = page.evaluate(function () {
return window.$ && $('button[data-action=edit]').is(":visible") && {
banner: $('#wrap [data-snippet-id=carousel]').length,
text: $('#wrap [data-snippet-id=text-block]').length,
image: $('#wrap [data-snippet-id=three-columns]').length,
};
});
var result = after && (after.banner === 1) && (after.text === 1) && (after.image === 1);
return result;
}, function finish () {
console.log('{ "event": "success" }');
phantom.exit();
}, 4*timeout/5);
}, timeout/5);
});
+3 -3
View File
@@ -136,7 +136,7 @@
<ul class="nav nav-pills nav-stacked">
<t t-foreach="dates" t-as="date">
<li t-att-class="searches.get('date') == date[0] and 'active' or ''" t-if="date[3] or (date[0] in ('old','all'))">
<a t-href="/event/#{ search_path }&amp;date=#{ date[0] }"><t t-esc="date[1]"/>
<a t-href="/event/?date=#{ date[0] }" t-keep-query="country,type"><t t-esc="date[1]"/>
<span t-if="date[3]" class="badge pull-right"><t t-esc="date[3]"/></span>
</a>
</li>
@@ -151,7 +151,7 @@
<ul class="nav nav-pills nav-stacked mt32">
<t t-foreach="types">
<li t-if="type" t-att-class="searches.get('type') == str(type and type[0]) and 'active' or ''">
<a t-href="/event/#{ search_path }&amp;type=#{ type[0] }"><t t-esc="type[1]"/>
<a t-href="/event/?type=#{ type[0] }" t-keep-query="country,date"><t t-esc="type[1]"/>
<span class="badge pull-right"><t t-esc="type_count"/></span>
</a>
</li>
@@ -164,7 +164,7 @@
<ul class="nav nav-pills nav-stacked mt32">
<t t-foreach="countries">
<li t-if="country_id" t-att-class="searches.get('country') == str(country_id and country_id[0]) and 'active' or ''">
<a t-href="/event/#{ search_path }&amp;country=#{ country_id[0] }"><t t-esc="country_id[1]"/>
<a t-href="/event/?country=#{ country_id[0] }" t-keep-query="type,date"><t t-esc="country_id[1]"/>
<span class="badge pull-right"><t t-esc="country_id_count"/></span>
</a>
</li>
+3 -2
View File
@@ -35,7 +35,7 @@ class WebsiteMail(http.Controller):
if email and email != u'false': # post contains stringified booleans
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", email)], context=request.context)
if not partner_ids:
partner_ids = [partner_obj.name_create(request.cr, SUPERUSER_ID, email, request.context)]
partner_ids = [partner_obj.name_create(request.cr, SUPERUSER_ID, email, request.context)[0]]
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
return partner_ids
@@ -54,5 +54,6 @@ class WebsiteMail(http.Controller):
_object.check_access_rule(request.cr, request.uid, [_id], 'read', request.context)
_object.message_subscribe(request.cr, SUPERUSER_ID, [_id], partner_ids, context=request.context)
obj = _object.browse(request.cr, request.uid, _id)
follower_ids = [p.id for p in obj.message_follower_ids]
return obj.message_is_follower and 1 or 0
return partner_ids[0] in follower_ids and 1 or 0
@@ -0,0 +1,4 @@
.js_follow[data-follow='on'] .js_follow_btn ,
.js_follow[data-follow='off'] .js_unfollow_btn {
display: none;
}
@@ -1,12 +1,12 @@
$(document).ready(function () {
$(document).on('click', '.js_follow_btn', function (ev) {
$(document).on('click', '.js_follow_btn, .js_unfollow_btn', function (ev) {
ev.preventDefault();
var self = this;
var self = this;
var $data = $(this).parents("div.js_follow");
var $email = $(".js_follow_email", $data);
var message_is_follower = $data.attr("data-follow");
var $email = $data.find(".js_follow_email");
var message_is_follower = $data.attr("data-follow") || "off";
$data.attr("data-follow", message_is_follower == 'off' ? 'on' : 'off');
openerp.jsonRpc('/website_mail/follow', 'call', {
@@ -15,12 +15,10 @@ $(document).ready(function () {
'message_is_follower': message_is_follower,
'email': $email && $email.val() || false,
}).then(function (result) {
if (result) {
$data.html('<div class="alert alert-success mb0">Thanks for your subscription!</div>')
} else {
$data.attr("data-follow", + result ? 'on' : 'off');
$(self).text('Subscribe')
}
if (result) {
$data.find(" > *").toggleClass("hidden");
}
$data.attr("data-follow", result ? 'on' : 'off');
});
});
+4 -5
View File
@@ -12,18 +12,17 @@
t-att-value="email or ''"
placeholder="your email..."/>
<span class="input-group-btn">
<button href="#"
t-attf-class="btn btn-#{object.message_is_follower and 'default' or 'primary'} js_follow_btn">
<t t-if="object.message_is_follower">Unsubscribe</t>
<t t-if="not object.message_is_follower">Subscribe</t>
</button>
<button href="#" t-attf-class="btn btn-default js_unfollow_btn">Unsubscribe</button>
<button href="#" t-attf-class="btn btn-primary js_follow_btn">Subscribe</button>
</span>
<div class="alert alert-success hidden">Thanks for your subscription!</div>
</div>
</template>
<template id="head" inherit_id="website.layout" name="Mail customization">
<xpath expr="//head" position="inside">
<script type="text/javascript" src="/website_mail/static/src/js/website_mail.js"></script>
<link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/>
</xpath>
</template>
+1 -1
View File
@@ -9,7 +9,7 @@ OpenERP E-Commerce
""",
'author': 'OpenERP SA',
'depends': ['website', 'sale', 'product', 'payment_acquirer'],
'depends': ['website', 'sale', 'payment_acquirer'],
'data': [
'data/website_sale_data.xml',
'views/website_sale.xml',
+17 -13
View File
@@ -199,10 +199,10 @@ class Ecommerce(http.Controller):
@website.route([
'/shop/',
'/shop/page/<int:page>/',
'/shop/category/<int:category>/',
'/shop/category/<int:category>/page/<int:page>/'
'/shop/category/<model("product.public.category"):category>/',
'/shop/category/<model("product.public.category"):category>/page/<int:page>/'
], type='http', auth="public", multilang=True)
def shop(self, category=0, page=0, filters='', search='', **post):
def shop(self, category=None, page=0, filters='', search='', **post):
cr, uid, context = request.cr, request.uid, request.context
product_obj = request.registry.get('product.template')
domain = request.registry.get('website').ecommerce_get_product_domain()
@@ -211,7 +211,7 @@ class Ecommerce(http.Controller):
('name', 'ilike', "%%%s%%" % search),
('description', 'ilike', "%%%s%%" % search)]
if category:
domain.append(('product_variant_ids.public_categ_id', 'child_of', category))
domain.append(('product_variant_ids.public_categ_id', 'child_of', category.id))
if filters:
filters = simplejson.loads(filters)
if filters:
@@ -286,7 +286,7 @@ class Ecommerce(http.Controller):
}
return request.website.render("website_sale.product", values)
@website.route(['/shop/product/<int:product_template_id>/comment'], type='http', auth="public")
@website.route(['/shop/product/comment'], type='http', auth="public", methods=['POST'])
def product_comment(self, product_template_id, **post):
cr, uid, context = request.cr, request.uid, request.context
if post.get('comment'):
@@ -298,11 +298,11 @@ class Ecommerce(http.Controller):
context=dict(context, mail_create_nosubcribe=True))
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
@website.route(['/shop/add_product/', '/shop/category/<int:cat_id>/add_product/'], type='http', auth="user", multilang=True, methods=['POST'])
def add_product(self, name="New Product", cat_id=0, **post):
@website.route(['/shop/add_product/'], type='http', auth="user", multilang=True, methods=['POST'])
def add_product(self, name="New Product", category=0, **post):
Product = request.registry.get('product.product')
product_id = Product.create(request.cr, request.uid, {
'name': name, 'public_categ_id': cat_id
'name': name, 'public_categ_id': category
}, context=request.context)
product = Product.browse(request.cr, request.uid, product_id, context=request.context)
@@ -612,12 +612,12 @@ class Ecommerce(http.Controller):
# if no sale order at this stage: back to checkout beginning
order = self.get_order()
if not order or not order.state == 'draft' or not order.order_line:
if not order or order.state != 'draft' or not order.order_line:
request.registry['website'].ecommerce_reset(cr, uid, context=context)
return request.redirect("/shop/")
# alread a transaction: forward to confirmation
tx = context.get('website_sale_transaction')
if tx and not tx.state == 'draft':
if tx and tx.state != 'draft':
return request.redirect('/shop/confirmation/%s' % order.id)
shipping_partner_id = False
@@ -780,12 +780,16 @@ class Ecommerce(http.Controller):
return request.website.render("website_sale.confirmation", {'order': order})
@website.route(['/shop/change_sequence/'], type='json', auth="public")
def change_sequence(self, id, top):
def change_sequence(self, id, sequence):
product_obj = request.registry.get('product.template')
if top:
if sequence == "top":
product_obj.set_sequence_top(request.cr, request.uid, [id], context=request.context)
else:
elif sequence == "bottom":
product_obj.set_sequence_bottom(request.cr, request.uid, [id], context=request.context)
elif sequence == "up":
product_obj.set_sequence_up(request.cr, request.uid, [id], context=request.context)
elif sequence == "down":
product_obj.set_sequence_down(request.cr, request.uid, [id], context=request.context)
@website.route(['/shop/change_styles/'], type='json', auth="public")
def change_styles(self, id, style_id):
@@ -536,6 +536,7 @@ Weight: 1.1 ounces</field>
</div>
</section>
</field>
<field name="suggested_product_ids" eval="[(6, 0, [ref('product.product_template_7')])]"/>
</record>
<record id="item1" model="product.pricelist.item">
+32 -2
View File
@@ -61,10 +61,16 @@ class product_template(osv.Model):
'website_sequence': fields.integer('Sequence', help="Determine the display order in the Website E-commerce"),
'website_url': fields.function(_website_url, string="Website url", type="char"),
}
def __defaults_website_sequence(self, cr, uid, *kwargs):
cr.execute('SELECT MAX(website_sequence) FROM product_template')
max_sequence = cr.fetchone()[0] or 0
return max_sequence + 1
_defaults = {
'website_size_x': 1,
'website_size_y': 1,
'website_sequence': 1,
'website_sequence': __defaults_website_sequence,
'website_published': False,
}
@@ -74,7 +80,31 @@ class product_template(osv.Model):
return self.write(cr, uid, ids, {'website_sequence': max_sequence + 1}, context=context)
def set_sequence_bottom(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'website_sequence': 0}, context=context)
cr.execute('SELECT MIN(website_sequence) FROM product_template')
min_sequence = cr.fetchone()[0] or 0
return self.write(cr, uid, ids, {'website_sequence': min_sequence -1}, context=context)
def set_sequence_up(self, cr, uid, ids, context=None):
product = self.browse(cr, uid, ids[0], context=context)
cr.execute(""" SELECT id, website_sequence FROM product_template
WHERE website_sequence > %s AND website_published = %s ORDER BY website_sequence ASC LIMIT 1""" % (product.website_sequence, product.website_published))
prev = cr.fetchone()
if prev:
self.write(cr, uid, [prev[0]], {'website_sequence': product.website_sequence}, context=context)
return self.write(cr, uid, [ids[0]], {'website_sequence': prev[1]}, context=context)
else:
return self.set_sequence_top(cr, uid, ids, context=context)
def set_sequence_down(self, cr, uid, ids, context=None):
product = self.browse(cr, uid, ids[0], context=context)
cr.execute(""" SELECT id, website_sequence FROM product_template
WHERE website_sequence < %s AND website_published = %s ORDER BY website_sequence DESC LIMIT 1""" % (product.website_sequence, product.website_published))
next = cr.fetchone()
if next:
self.write(cr, uid, [next[0]], {'website_sequence': product.website_sequence}, context=context)
return self.write(cr, uid, [ids[0]], {'website_sequence': next[1]}, context=context)
else:
return self.set_sequence_bottom(cr, uid, ids, context=context)
def recommended_products(self, cr, uid, ids, context=None):
id = ids[0]
@@ -11,4 +11,4 @@ access_product_attribute,product.attribute.public,website_sale.model_product_att
access_product_attribute_value,product.attribute.value.public,website_sale.model_product_attribute_value,,1,0,0,0
access_product_attribute_product,product.attribute.product.public,website_sale.model_product_attribute_product,,1,0,0,0
access_website_product_style,website.product.style.public,website_sale.model_website_product_style,,1,0,0,0
access_product_supplierinfo,product.supplierinfo.public,product.model_product_supplierinfo,,1,0,0,0
access_product_supplierinfo,product.supplierinfo.public,product.model_product_supplierinfo,base.group_public,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_product_attribute_value product.attribute.value.public website_sale.model_product_attribute_value 1 0 0 0
12 access_product_attribute_product product.attribute.product.public website_sale.model_product_attribute_product 1 0 0 0
13 access_website_product_style website.product.style.public website_sale.model_website_product_style 1 0 0 0
14 access_product_supplierinfo product.supplierinfo.public product.model_product_supplierinfo base.group_public 1 0 0 0
@@ -25,10 +25,17 @@
location.href = location.href.replace(/(\?|#|$).*/, search + location.hash);
}
$(document).on('click', '.js_options .js_go_to_top,.js_options .js_go_to_bottom', function (event) {
$(document).on('click', '.js_options .js_go_to_top,.js_options .js_go_to_bottom,.js_options .js_go_up,.js_options .js_go_down', function (event) {
var $a = $(event.currentTarget);
var $data = $a.parents(".js_options:first");
openerp.jsonRpc('/shop/change_sequence/', 'call', {'id': $data.data('id'), 'top': $a.hasClass('js_go_to_top')})
var sequence = "top";
if ($a.hasClass('js_go_to_bottom'))
sequence = "bottom";
else if ($a.hasClass('js_go_up'))
sequence = "up";
else if ($a.hasClass('js_go_down'))
sequence = "down";
openerp.jsonRpc('/shop/change_sequence/', 'call', {'id': $data.data('id'), 'sequence': sequence})
.then(reload_enable_editor);
});
+20 -14
View File
@@ -51,7 +51,7 @@
<template id="products_cart" name="Shopping cart">
<div class="ribbon-wrapper">
<div class="ribbon btn btn-danger">Promo</div>
<div class="ribbon btn btn-danger">Sale</div>
</div>
<div class="oe_product_image">
<a t-href="/shop/product/#{ slug(product) }/" t-keep-query="category,search,filters">
@@ -166,6 +166,9 @@
<a tabindex="-1" href="#">Promote</a>
<ul class="dropdown-menu" name="sequence">
<li><a href="#" class="js_go_to_top">Push to top</a></li>
<li><a href="#" class="js_go_up">Push up</a>
</li>
<li><a href="#" class="js_go_down">Push down</a></li>
<li><a href="#" class="js_go_to_bottom">Push to bottom</a></li>
</ul>
</li>
@@ -185,7 +188,8 @@
</table>
<t t-if="not bins">
<div class="text-center text-muted">
<h3>No product found</h3>
<h3 class="css_editable_display">No product found</h3>
<h3 class="css_non_editable_mode_hidden">There isn't available products right know, click <a t-href="/page/website.contactus">here</a> to contact us</h3>
<t groups="base.group_website_publisher">
<p>Click on "Content" to define a new product or "Help" for more informations.</p>
<img src="/website/static/src/img/content_here.png"/>
@@ -675,7 +679,7 @@
}'/>
</td>
<td class="text-center">
<a t-href="./add_cart/#{ product.id }/"><strong>Add to Cart</strong></a>
<a t-href="/shop/add_cart/#{ product.product_variant_ids[0].id }/"><strong>Add to Cart</strong></a>
</td>
</tr>
</tbody>
@@ -1027,17 +1031,19 @@
<div class="oe_structure"/>
</div>
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
<h4>Bill To:</h4>
<div t-field="order.partner_invoice_id"/>
<h4 class="mt32">Ship To:</h4>
<div t-field="order.partner_shipping_id"/>
<h4 class="mt32">Amount:</h4>
<!--<div t-field="order.amount_total" t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/> -->
<h4>Bill To:</h4>
<div t-field="order.partner_invoice_id" t-field-options='{
"widget": "contact",
"fields": ["address", "name", "phone", "email"]
}'/>
<h4 class="mt32">Ship To:</h4>
<t t-if="order.partner_shipping_id and order.partner_shipping_id.id != order.partner_invoice_id.id">
<div t-field="order.partner_shipping_id" t-field-options='{
"widget": "contact",
"fields": ["address", "name", "phone"]
}'/>
</t>
<address t-if="order.partner_shipping_id.id == order.partner_invoice_id.id">Ship to the same address</address>
</div>
</div>
@@ -2,6 +2,7 @@
from openerp.addons.website_sale.controllers.main import Ecommerce
from openerp.addons.web.http import request
from openerp.addons.website.models import website
from openerp import SUPERUSER_ID
class Ecommerce(Ecommerce):
@@ -15,8 +16,8 @@ class Ecommerce(Ecommerce):
if order and carrier_id:
# recompute delivery costs
SaleOrder = request.registry['sale.order']
SaleOrder.write(cr, uid, [order.id], {'carrier_id': carrier_id}, context=context)
SaleOrder.delivery_set(cr, uid, [order.id], context=context)
SaleOrder.write(cr, SUPERUSER_ID, [order.id], {'carrier_id': carrier_id}, context=context)
SaleOrder.delivery_set(cr, SUPERUSER_ID, [order.id], context=context)
res = super(Ecommerce, self).payment(**post)
return res
@@ -25,7 +25,7 @@
<tr class="text-muted" id="order_delivery">
<td><abbr title="Delivery will be updated after choosing a new delivery method">Delivery:</abbr></td>
<td class="text-right">
<span t-field="website_sale_order.amount_delivery" t-field-options='{
<span t-field="order.amount_delivery" t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/>
@@ -41,7 +41,7 @@
<li t-foreach="deliveries" t-as="delivery">
<label>
<input t-att-value="delivery.id" type="radio" name="delivery_type"
t-att-checked="website_sale_order.carrier_id and website_sale_order.carrier_id.id == delivery.id and 'checked' or False"/>
t-att-checked="order.carrier_id and order.carrier_id.id == delivery.id and 'checked' or False"/>
<span t-field="delivery.name"/>
<span class="badge" t-field="delivery.price"
t-field-options='{