diff --git a/addons/decimal_precision/security/ir.model.access.csv b/addons/decimal_precision/security/ir.model.access.csv index aa203eae141..d55da432c57 100644 --- a/addons/decimal_precision/security/ir.model.access.csv +++ b/addons/decimal_precision/security/ir.model.access.csv @@ -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 diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py index a43bbe79713..19b2d6e0801 100644 --- a/addons/email_template/tests/test_mail.py +++ b/addons/email_template/tests/test_mail.py @@ -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" '], + ['b@b.b', 'c@c.c'], ['"Followers of Pigs" '], ['"Followers of Pigs" '], ['"Followers of Pigs" ']] 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: diff --git a/addons/event/event.py b/addons/event/event.py index 122432a9c77..ad3e3624500 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -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: diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index e8d8dae4c45..36f7eee032c 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -74,7 +74,7 @@ - + diff --git a/addons/event_sale/event_demo.xml b/addons/event_sale/event_demo.xml index fabaad18ee4..3ca61d3feb4 100644 --- a/addons/event_sale/event_demo.xml +++ b/addons/event_sale/event_demo.xml @@ -74,8 +74,6 @@ 1500.0 10 - Cathering for breakfast and lunch -VIP Parking access @@ -105,8 +103,6 @@ VIP Parking access 1500.0 5 - Cathering for breakfast and lunch -VIP Parking access @@ -125,8 +121,6 @@ VIP Parking access 1500.0 5 - Cathering for breakfast and lunch -VIP Parking access diff --git a/addons/multi_company/multi_company_demo.xml b/addons/multi_company/multi_company_demo.xml index 0c7b200a867..44a021c1434 100644 --- a/addons/multi_company/multi_company_demo.xml +++ b/addons/multi_company/multi_company_demo.xml @@ -94,87 +94,6 @@ Resource: product.product --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Odoo Offers diff --git a/addons/payment_acquirer_adyen/controllers/main.py b/addons/payment_acquirer_adyen/controllers/main.py index 2ea9fb00c51..f1f15bddf8d 100644 --- a/addons/payment_acquirer_adyen/controllers/main.py +++ b/addons/payment_acquirer_adyen/controllers/main.py @@ -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', '') diff --git a/addons/payment_acquirer_paypal/data/paypal.xml b/addons/payment_acquirer_paypal/data/paypal.xml index 55245804975..d764c20edbd 100644 --- a/addons/payment_acquirer_paypal/data/paypal.xml +++ b/addons/payment_acquirer_paypal/data/paypal.xml @@ -8,7 +8,6 @@ test You will be redirected to the Paypal website after cliking on the payment button.

]]>
- https://www.sandbox.paypal.com/cgi-bin/webscr dummy dummy dummy diff --git a/addons/product/product.py b/addons/product/product.py index 25a5a4332b3..c4049fb4b8d 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -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: diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 2985d8eaa27..c2d6e49c014 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -431,7 +431,7 @@ product.public.category.form product.public.category -
+
@@ -457,7 +457,7 @@ - Public Categories + Pos/Public Product Categories ir.actions.act_window product.public.category form @@ -476,7 +476,7 @@

- + diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index aa7200c79b3..a5f4013a7df 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -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
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)) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 611b582d9ce..ccdfe449617 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -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'), diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index f552465c8bc..d786830c8f5 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -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%; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 9def6bc7127..66665252379 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -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% diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index bf261cf905a..65b322bd9e9 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -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; -} diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index ccd11d3396f..7422a9008ba 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -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 -// }}} diff --git a/addons/website/static/src/img/blocks/block_features.png b/addons/website/static/src/img/blocks/block_features.png new file mode 100644 index 00000000000..b56b1f7ba98 Binary files /dev/null and b/addons/website/static/src/img/blocks/block_features.png differ diff --git a/addons/website/static/src/img/under_construction.png b/addons/website/static/src/img/under_construction.png new file mode 100644 index 00000000000..2d5ee85de9b Binary files /dev/null and b/addons/website/static/src/img/under_construction.png differ diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js index f3b8a5c5885..4ff5aa44146 100644 --- a/addons/website/static/src/js/website.ace.js +++ b/addons/website/static/src/js/website.ace.js @@ -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'); diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index ecfc3d77b1c..563f850486e 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -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); }, }); diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 50975a7263d..6c0a062125d 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -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(); diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 54b5dcfb02b..3e1763c0e20 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -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); } } }, diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml index ecc3bb8cc38..ca5cf01705f 100644 --- a/addons/website/static/src/xml/website.snippets.xml +++ b/addons/website/static/src/xml/website.snippets.xml @@ -86,6 +86,7 @@
+
Margin resize
diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py index 4b24e8f0b9f..d31df8e6956 100644 --- a/addons/website/tests/test_converter.py +++ b/addons/website/tests/test_converter.py @@ -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 diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py index cf380b8e456..5096d0c8b03 100644 --- a/addons/website/tests/test_ui.py +++ b/addons/website/tests/test_ui.py @@ -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 diff --git a/addons/website/tests/ui_suite/ui_test_runner.js b/addons/website/tests/ui_suite/ui_test_runner.js index 15a60c9406d..ea8e03b36d3 100644 --- a/addons/website/tests/ui_suite/ui_test_runner.js +++ b/addons/website/tests/ui_suite/ui_test_runner.js @@ -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); diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index 99bba707863..aa9a136ecdb 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -25,7 +25,7 @@ Banner - @@ -85,8 +85,8 @@
-
- +
+

A Section Subtitle

@@ -277,7 +277,7 @@ Quotes Slider
-
+
+
+ + Features +
+
+
+
+
+

List of Features

+

Add a great slogan

+
+
+ +

First Feature

+

Tell what's the value for the customer for this feature.

+

Learn More

+
+
+ +

Second Feature

+

Talk about what the customer would like to know, not what you want to show.

+

Take Tour

+
+
+ +

Third Feature

+

A small explanation of this great feature, in clear words.

+

Buy Now

+
+
+ +

Fourth Feature

+

A small explanation of this great feature on two lines.

+

Buy Now

+
+
+
+
+
+ +
+
+ + Feature Grid +
+
+
+
+
+

List of Features

+

Add a great slogan

+
+
+

Second List

+

Add a great slogan

+
+
+
+
    +
  • + +

    Blog

    +

    OpenERP includes a full features enterprise blog to promote your messages.

    +
  • +
  • + +

    Pricing Tables

    +

    Use building blocks to use pricing tables to display your products or services.

    +
  • +
  • + +

    Log In & Signup

    +

    Clean & simple Log In, Signup & Forgot Your Password forms.

    +
  • +
  • + +

    Contact

    +

    User-friendly contact form with Google Map integration.

    +
  • +
+ +
+
+
    +
  • + +

    Bootstrap

    +

    Uses Twitter Bootstrap's sleek, intuitive, and powerful front-end framework.

    +
  • +
  • + +

    Google Web Fonts

    +

    Royalty free, open source fonts for the people, by the people!

    +
  • +
  • + +

    Powerful & slick jQuery

    +

    Fantastic jQuery plugins such as Isotope and Bootstrap's inbuilt features.

    +
  • +
  • + +

    Sample images

    +

    Fantastic sample images fully licensed for use.

    +
  • +
+
+
+
+
+
+
@@ -696,8 +808,8 @@
  • Very Fast
  • -
    @@ -708,12 +820,12 @@ Parallax Slider
    -
    -
    + diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index 03798fb105f..28f20346260 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -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] diff --git a/addons/website_sale/security/ir.model.access.csv b/addons/website_sale/security/ir.model.access.csv index a64fc2cde73..8b3318af094 100644 --- a/addons/website_sale/security/ir.model.access.csv +++ b/addons/website_sale/security/ir.model.access.csv @@ -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 diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index c5bd958258d..d6103cba372 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -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); }); diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 3907d564ccd..78c360bc296 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -51,7 +51,7 @@