[MERGE] Sync with trunk-website-al
bzr revid: rim@openerp.com-20140131075821-qx8cc0ybtxqr7219
This commit is contained in:
commit
7b70388235
|
@ -68,8 +68,6 @@ class OAuthLogin(openerp.addons.web.controllers.main.Home):
|
|||
|
||||
@http.route()
|
||||
def web_login(self, *args, **kw):
|
||||
http.ensure_db(with_registry=True)
|
||||
request.disable_db = False
|
||||
providers = self.list_providers()
|
||||
|
||||
response = super(OAuthLogin, self).web_login(*args, **kw)
|
||||
|
|
|
@ -23,7 +23,6 @@ import logging
|
|||
import os
|
||||
import tempfile
|
||||
import getpass
|
||||
import urllib
|
||||
|
||||
import werkzeug.urls
|
||||
import werkzeug.exceptions
|
||||
|
@ -79,7 +78,7 @@ class GoogleAppsAwareConsumer(consumer.GenericConsumer):
|
|||
# update fields
|
||||
for attr in ['claimed_id', 'identity']:
|
||||
value = message.getArg(consumer.OPENID2_NS, attr, '')
|
||||
value = 'https://www.google.com/accounts/o8/user-xrds?uri=%s' % urllib.quote_plus(value)
|
||||
value = 'https://www.google.com/accounts/o8/user-xrds?uri=%s' % werkzeug.url_quote_plus(value)
|
||||
message.setArg(consumer.OPENID2_NS, attr, value)
|
||||
|
||||
# now, resign the message
|
||||
|
|
|
@ -34,8 +34,6 @@ class Home(openerp.addons.web.controllers.main.Home):
|
|||
|
||||
@http.route()
|
||||
def web_login(self, *args, **kw):
|
||||
http.ensure_db(with_registry=True)
|
||||
|
||||
mode = request.params.get('mode')
|
||||
qcontext = request.params.copy()
|
||||
response = webmain.render_bootstrap_template(request.session.db, 'auth_signup.signup', qcontext, lazy=True)
|
||||
|
|
|
@ -110,11 +110,11 @@ class delivery_carrier(osv.osv):
|
|||
|
||||
# not using advanced pricing per destination: override grid
|
||||
grid_id = grid_pool.search(cr, uid, [('carrier_id', '=', record.id)], context=context)
|
||||
|
||||
if grid_id and not (record.normal_price or record.free_if_more_than):
|
||||
grid_pool.unlink(cr, uid, grid_id, context=context)
|
||||
|
||||
if not (record.normal_price or record.free_if_more_than):
|
||||
# Check that float, else 0.0 is False
|
||||
if not (isinstance(record.normal_price,float) or record.free_if_more_than):
|
||||
continue
|
||||
|
||||
if not grid_id:
|
||||
|
@ -141,7 +141,7 @@ class delivery_carrier(osv.osv):
|
|||
'list_price': 0.0,
|
||||
}
|
||||
grid_line_pool.create(cr, uid, line_data, context=context)
|
||||
if record.normal_price:
|
||||
if isinstance(record.normal_price,float):
|
||||
line_data = {
|
||||
'grid_id': grid_id and grid_id[0],
|
||||
'name': _('Default price'),
|
||||
|
@ -192,7 +192,7 @@ class delivery_grid(osv.osv):
|
|||
weight = 0
|
||||
volume = 0
|
||||
for line in order.order_line:
|
||||
if not line.product_id:
|
||||
if not line.product_id or line.is_delivery:
|
||||
continue
|
||||
total += line.price_subtotal or 0.0
|
||||
weight += (line.product_id.weight or 0.0) * line.product_uom_qty
|
||||
|
@ -205,9 +205,8 @@ class delivery_grid(osv.osv):
|
|||
grid = self.browse(cr, uid, id, context=context)
|
||||
price = 0.0
|
||||
ok = False
|
||||
|
||||
price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight}
|
||||
for line in grid.line_ids:
|
||||
price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight}
|
||||
test = eval(line.type+line.operator+str(line.max_value), price_dict)
|
||||
if test:
|
||||
if line.price_type=='variable':
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
<field name="normal_price">20</field>
|
||||
<field name="partner_id" ref="res_partner_23"/>
|
||||
<field name="product_id" ref="product_product_delivery"/>
|
||||
<field name="use_detailed_pricelist" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="free_delivery_carrier" model="delivery.carrier">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import simplejson
|
||||
import urllib
|
||||
import werkzeug.urls
|
||||
|
||||
import openerp
|
||||
import openerp.addons.web.controllers.main as webmain
|
||||
|
@ -17,7 +17,7 @@ class EDI(openerp.http.Controller):
|
|||
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in webmain.manifest_list(req, modules_str, 'css'))
|
||||
|
||||
# `url` may contain a full URL with a valid query string, we basically want to watch out for XML brackets and double-quotes
|
||||
safe_url = urllib.quote_plus(url,':/?&;=')
|
||||
safe_url = werkzeug.url_quote_plus(url,':/?&;=')
|
||||
|
||||
return webmain.html_template % {
|
||||
'js': js,
|
||||
|
|
|
@ -23,5 +23,3 @@ import event
|
|||
import wizard
|
||||
import report
|
||||
import res_partner
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -160,6 +160,8 @@ class event_event(osv.osv):
|
|||
'email_confirmation_id' : fields.many2one('email.template','Event Confirmation Email', help="If you set an email template, each participant will receive this email announcing the confirmation of the event."),
|
||||
'reply_to': fields.char('Reply-To Email', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one."),
|
||||
'address_id': fields.many2one('res.partner','Location', readonly=False, states={'done': [('readonly', True)]}),
|
||||
'country_id': fields.related('address_id', 'country_id',
|
||||
type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
|
||||
'description': fields.html(
|
||||
'Description', readonly=False,
|
||||
states={'done': [('readonly', True)]},
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class product(osv.osv):
|
||||
class product_template(osv.osv):
|
||||
_inherit = 'product.template'
|
||||
_columns = {
|
||||
'event_ok': fields.boolean('Event Subscription', help='Determine if a product needs to create automatically an event registration at the confirmation of a sales order line.'),
|
||||
|
@ -30,7 +30,18 @@ class product(osv.osv):
|
|||
}
|
||||
|
||||
def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
|
||||
return {'value': {'type': event_ok and 'service' or type != 'service' and type or False}}
|
||||
if event_ok:
|
||||
return {'value': {'type': 'service'}}
|
||||
return {}
|
||||
|
||||
class product(osv.osv):
|
||||
_inherit = 'product.product'
|
||||
|
||||
def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
|
||||
# cannot directly forward to product.template as the ids are theoretically different
|
||||
if event_ok:
|
||||
return {'value': {'type': 'service'}}
|
||||
return {}
|
||||
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="event_sale_order">
|
||||
<field name="name">event.product</field>
|
||||
<field name="model">product.template</field>
|
||||
<record model="ir.ui.view" id="event_sale_product_form">
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="options" position="inside">
|
||||
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"/>
|
||||
<label for="event_ok"
|
||||
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
|
||||
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"
|
||||
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
|
||||
<label for="event_ok"/>
|
||||
</div>
|
||||
<div name='ean' position="after">
|
||||
<field name="event_type_id" attrs="{'readonly': ['|', ('event_ok', '=', False), ('is_only_child', '=', False)]}"/>
|
||||
<field name="event_type_id" attrs="{'invisible': [('event_ok', '=', False)],
|
||||
'readonly': [('is_only_child', '=', False)]}"/>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="event_sale_product_template_form">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="options" position="inside">
|
||||
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"/>
|
||||
<label for="event_ok"/>
|
||||
</div>
|
||||
<field name='company_id' position="after">
|
||||
<field name="event_type_id" attrs="{'invisible': [('event_ok', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="event_order_line">
|
||||
<field name="name">event.sale.order</field>
|
||||
<field name="model">sale.order</field>
|
||||
|
|
|
@ -23,7 +23,7 @@ from datetime import datetime
|
|||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from openerp.addons.hr_holidays.tests.common import TestHrHolidaysBase
|
||||
from openerp.exceptions import Warning, AccessError
|
||||
from openerp.exceptions import AccessError
|
||||
from openerp.osv.orm import except_orm
|
||||
from openerp.tools import mute_logger
|
||||
|
||||
|
@ -204,5 +204,5 @@ class TestHolidaysFlow(TestHrHolidaysBase):
|
|||
'date_to': (datetime.today() + relativedelta(days=7)),
|
||||
'number_of_days_temp': 4,
|
||||
})
|
||||
with self.assertRaises(Warning):
|
||||
with self.assertRaises(except_orm):
|
||||
self.hr_holidays.signal_confirm(cr, self.user_hrmanager_id, [hol2_id])
|
||||
|
|
|
@ -263,12 +263,12 @@ hw_proxy.drivers['escpos'] = driver
|
|||
|
||||
class EscposProxy(hw_proxy.Proxy):
|
||||
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none')
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
|
||||
def open_cashbox(self):
|
||||
_logger.info('ESC/POS: OPEN CASHBOX')
|
||||
driver.push_task('cashbox')
|
||||
|
||||
@http.route('/hw_proxy/print_receipt', type='json', auth='none')
|
||||
@http.route('/hw_proxy/print_receipt', type='json', auth='none', cors='*')
|
||||
def print_receipt(self, receipt):
|
||||
_logger.info('ESC/POS: PRINT RECEIPT')
|
||||
driver.push_task('receipt',receipt)
|
||||
|
|
|
@ -33,20 +33,15 @@ class Proxy(http.Controller):
|
|||
statuses[driver] = drivers[driver].get_status()
|
||||
return statuses
|
||||
|
||||
@http.route('/hw_proxy/hello', type='http', auth='none')
|
||||
@http.route('/hw_proxy/hello', type='http', auth='none', cors='*')
|
||||
def hello(self):
|
||||
return request.make_response('ping', {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET',
|
||||
})
|
||||
return "ping"
|
||||
|
||||
@http.route('/hw_proxy/handshake', type='json', auth='none')
|
||||
@http.route('/hw_proxy/handshake', type='json', auth='none', cors='*')
|
||||
def handshake(self):
|
||||
return True
|
||||
|
||||
@http.route('/hw_proxy/status', type='http', auth='none')
|
||||
@http.route('/hw_proxy/status', type='http', auth='none', cors='*')
|
||||
def status_http(self):
|
||||
resp = '<html>\n<body>\n<h1>Hardware Proxy Status</h1>\n'
|
||||
statuses = self.get_status()
|
||||
|
@ -75,39 +70,39 @@ class Proxy(http.Controller):
|
|||
'Access-Control-Allow-Methods': 'GET',
|
||||
})
|
||||
|
||||
@http.route('/hw_proxy/status_json', type='json', auth='none')
|
||||
@http.route('/hw_proxy/status_json', type='json', auth='none', cors='*')
|
||||
def status_json(self):
|
||||
return self.get_status()
|
||||
|
||||
@http.route('/hw_proxy/scan_item_success', type='json', auth='none')
|
||||
@http.route('/hw_proxy/scan_item_success', type='json', auth='none', cors='*')
|
||||
def scan_item_success(self, ean):
|
||||
"""
|
||||
A product has been scanned with success
|
||||
"""
|
||||
print 'scan_item_success: ' + str(ean)
|
||||
|
||||
@http.route('/hw_proxy/scan_item_error_unrecognized', type='json', auth='none')
|
||||
@http.route('/hw_proxy/scan_item_error_unrecognized', type='json', auth='none', cors='*')
|
||||
def scan_item_error_unrecognized(self, ean):
|
||||
"""
|
||||
A product has been scanned without success
|
||||
"""
|
||||
print 'scan_item_error_unrecognized: ' + str(ean)
|
||||
|
||||
@http.route('/hw_proxy/help_needed', type='json', auth='none')
|
||||
@http.route('/hw_proxy/help_needed', type='json', auth='none', cors='*')
|
||||
def help_needed(self):
|
||||
"""
|
||||
The user wants an help (ex: light is on)
|
||||
"""
|
||||
print "help_needed"
|
||||
|
||||
@http.route('/hw_proxy/help_canceled', type='json', auth='none')
|
||||
@http.route('/hw_proxy/help_canceled', type='json', auth='none', cors='*')
|
||||
def help_canceled(self):
|
||||
"""
|
||||
The user stops the help request
|
||||
"""
|
||||
print "help_canceled"
|
||||
|
||||
@http.route('/hw_proxy/weighting_start', type='json', auth='none')
|
||||
@http.route('/hw_proxy/weighting_start', type='json', auth='none', cors='*')
|
||||
def weighting_start(self):
|
||||
if self.scale == 'closed':
|
||||
print "Opening (Fake) Connection to Scale..."
|
||||
|
@ -118,7 +113,7 @@ class Proxy(http.Controller):
|
|||
else:
|
||||
print "WARNING: Scale already Connected !!!"
|
||||
|
||||
@http.route('/hw_proxy/weighting_read_kg', type='json', auth='none')
|
||||
@http.route('/hw_proxy/weighting_read_kg', type='json', auth='none', cors='*')
|
||||
def weighting_read_kg(self):
|
||||
if self.scale == 'open':
|
||||
print "Reading Scale..."
|
||||
|
@ -130,7 +125,7 @@ class Proxy(http.Controller):
|
|||
print "WARNING: Reading closed scale !!!"
|
||||
return 0.0
|
||||
|
||||
@http.route('/hw_proxy/weighting_end', type='json', auth='none')
|
||||
@http.route('/hw_proxy/weighting_end', type='json', auth='none', cors='*')
|
||||
def weighting_end(self):
|
||||
if self.scale == 'open':
|
||||
print "Closing Connection to Scale ..."
|
||||
|
@ -141,7 +136,7 @@ class Proxy(http.Controller):
|
|||
else:
|
||||
print "WARNING: Scale already Closed !!!"
|
||||
|
||||
@http.route('/hw_proxy/payment_request', type='json', auth='none')
|
||||
@http.route('/hw_proxy/payment_request', type='json', auth='none', cors='*')
|
||||
def payment_request(self, price):
|
||||
"""
|
||||
The PoS will activate the method payment
|
||||
|
@ -149,55 +144,55 @@ class Proxy(http.Controller):
|
|||
print "payment_request: price:"+str(price)
|
||||
return 'ok'
|
||||
|
||||
@http.route('/hw_proxy/payment_status', type='json', auth='none')
|
||||
@http.route('/hw_proxy/payment_status', type='json', auth='none', cors='*')
|
||||
def payment_status(self):
|
||||
print "payment_status"
|
||||
return { 'status':'waiting' }
|
||||
|
||||
@http.route('/hw_proxy/payment_cancel', type='json', auth='none')
|
||||
@http.route('/hw_proxy/payment_cancel', type='json', auth='none', cors='*')
|
||||
def payment_cancel(self):
|
||||
print "payment_cancel"
|
||||
|
||||
@http.route('/hw_proxy/transaction_start', type='json', auth='none')
|
||||
@http.route('/hw_proxy/transaction_start', type='json', auth='none', cors='*')
|
||||
def transaction_start(self):
|
||||
print 'transaction_start'
|
||||
|
||||
@http.route('/hw_proxy/transaction_end', type='json', auth='none')
|
||||
@http.route('/hw_proxy/transaction_end', type='json', auth='none', cors='*')
|
||||
def transaction_end(self):
|
||||
print 'transaction_end'
|
||||
|
||||
@http.route('/hw_proxy/cashier_mode_activated', type='json', auth='none')
|
||||
@http.route('/hw_proxy/cashier_mode_activated', type='json', auth='none', cors='*')
|
||||
def cashier_mode_activated(self):
|
||||
print 'cashier_mode_activated'
|
||||
|
||||
@http.route('/hw_proxy/cashier_mode_deactivated', type='json', auth='none')
|
||||
@http.route('/hw_proxy/cashier_mode_deactivated', type='json', auth='none', cors='*')
|
||||
def cashier_mode_deactivated(self):
|
||||
print 'cashier_mode_deactivated'
|
||||
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none')
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
|
||||
def open_cashbox(self):
|
||||
print 'open_cashbox'
|
||||
|
||||
@http.route('/hw_proxy/print_receipt', type='json', auth='none')
|
||||
@http.route('/hw_proxy/print_receipt', type='json', auth='none', cors='*')
|
||||
def print_receipt(self, receipt):
|
||||
print 'print_receipt' + str(receipt)
|
||||
|
||||
@http.route('/hw_proxy/is_scanner_connected', type='json', auth='none')
|
||||
@http.route('/hw_proxy/is_scanner_connected', type='json', auth='none', cors='*')
|
||||
def print_receipt(self, receipt):
|
||||
print 'is_scanner_connected?'
|
||||
return False
|
||||
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none')
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
|
||||
def print_receipt(self, receipt):
|
||||
print 'scanner'
|
||||
time.sleep(10)
|
||||
return ''
|
||||
|
||||
@http.route('/hw_proxy/log', type='json', auth='none')
|
||||
@http.route('/hw_proxy/log', type='json', auth='none', cors='*')
|
||||
def log(self, arguments):
|
||||
_logger.info(' '.join(str(v) for v in arguments))
|
||||
|
||||
@http.route('/hw_proxy/print_pdf_invoice', type='json', auth='none')
|
||||
@http.route('/hw_proxy/print_pdf_invoice', type='json', auth='none', cors='*')
|
||||
def print_pdf_invoice(self, pdfinvoice):
|
||||
print 'print_pdf_invoice' + str(pdfinvoice)
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ class Scanner(Thread):
|
|||
self.set_status('error',str(e))
|
||||
return None
|
||||
|
||||
@http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='none')
|
||||
@http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='none', cors='*')
|
||||
def is_scanner_connected(self):
|
||||
return self.get_device() != None
|
||||
|
||||
|
@ -207,7 +207,7 @@ s = Scanner()
|
|||
hw_proxy.drivers['scanner'] = s
|
||||
|
||||
class ScannerDriver(hw_proxy.Proxy):
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none')
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
|
||||
def scanner(self):
|
||||
if not s.isAlive():
|
||||
s.start()
|
||||
|
|
|
@ -6,7 +6,6 @@ class MassMailController(http.Controller):
|
|||
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='none')
|
||||
def track_mail_open(self, mail_id):
|
||||
""" Email tracking. """
|
||||
request.disable_db = False
|
||||
mail_mail_stats = request.registry.get('mail.mail.statistics')
|
||||
mail_mail_stats.set_opened(request.cr, SUPERUSER_ID, mail_mail_ids=[mail_id])
|
||||
return "data:image/gif;base64,R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
|
||||
|
|
|
@ -22,7 +22,6 @@ class AdyenController(http.Controller):
|
|||
], type='http', auth='none')
|
||||
def adyen_return(self, pspReference, **post):
|
||||
""" Paypal IPN."""
|
||||
request.disable_db = False
|
||||
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, SUPERUSER_ID, post, 'adyen', context=request.context)
|
||||
|
|
|
@ -23,7 +23,6 @@ class OgoneController(http.Controller):
|
|||
], type='http', auth='none')
|
||||
def ogone_form_feedback(self, **post):
|
||||
""" Ogone contacts using GET, at least for accept """
|
||||
request.disable_db = False
|
||||
_logger.info('Ogone: entering form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
cr, uid, context = request.cr, SUPERUSER_ID, request.context
|
||||
request.registry['payment.transaction'].form_feedback(cr, uid, post, 'ogone', context=context)
|
||||
|
|
|
@ -56,7 +56,6 @@ class PaypalController(http.Controller):
|
|||
@http.route('/payment/paypal/ipn/', type='http', auth='none', methods=['POST'])
|
||||
def paypal_ipn(self, **post):
|
||||
""" Paypal IPN. """
|
||||
request.disable_db = False
|
||||
_logger.info('Beginning Paypal IPN form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
self.paypal_validate_data(**post)
|
||||
return ''
|
||||
|
@ -64,7 +63,6 @@ class PaypalController(http.Controller):
|
|||
@http.route('/payment/paypal/dpn', type='http', auth="none", methods=['POST'])
|
||||
def paypal_dpn(self, **post):
|
||||
""" Paypal DPN """
|
||||
request.disable_db = False
|
||||
_logger.info('Beginning Paypal DPN form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
return_url = self._get_return_url(**post)
|
||||
self.paypal_validate_data(**post)
|
||||
|
@ -73,7 +71,6 @@ class PaypalController(http.Controller):
|
|||
@http.route('/payment/paypal/cancel', type='http', auth="none")
|
||||
def paypal_cancel(self, **post):
|
||||
""" When the user cancels its Paypal payment: GET on this route """
|
||||
request.disable_db = False
|
||||
cr, uid, context = request.cr, SUPERUSER_ID, request.context
|
||||
_logger.info('Beginning Paypal cancel with post data %s', pprint.pformat(post)) # debug
|
||||
return_url = self._get_return_url(**post)
|
||||
|
|
|
@ -16,7 +16,6 @@ class OgoneController(http.Controller):
|
|||
'/payment/transfer/feedback',
|
||||
], type='http', auth='none')
|
||||
def transfer_form_feedback(self, **post):
|
||||
request.disable_db = False
|
||||
cr, uid, context = request.cr, SUPERUSER_ID, request.context
|
||||
_logger.info('Beginning form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
request.registry['payment.transaction'].form_feedback(cr, uid, post, 'transfer', context)
|
||||
|
|
|
@ -102,6 +102,8 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
|
|||
};
|
||||
this.custom_payment_status = this.default_payment_status;
|
||||
|
||||
this.receipt_queue = [];
|
||||
|
||||
this.notifications = {};
|
||||
this.bypass_proxy = false;
|
||||
|
||||
|
@ -113,6 +115,13 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
|
|||
|
||||
this.set_connection_status('disconnected');
|
||||
|
||||
this.on('change:status',this,function(eh,status){
|
||||
status = status.newValue;
|
||||
if(status.status === 'connected'){
|
||||
self.print_receipt();
|
||||
}
|
||||
});
|
||||
|
||||
window.hw_proxy = this;
|
||||
},
|
||||
set_connection_status: function(status,drivers){
|
||||
|
@ -502,7 +511,23 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
|
|||
* }
|
||||
*/
|
||||
print_receipt: function(receipt){
|
||||
return this.message('print_receipt',{receipt: receipt});
|
||||
var self = this;
|
||||
if(receipt){
|
||||
this.receipt_queue.push(receipt);
|
||||
}
|
||||
var aborted = false;
|
||||
function send_printing_job(){
|
||||
if (self.receipt_queue.length > 0){
|
||||
var r = self.receipt_queue.shift();
|
||||
self.message('print_receipt',{ receipt: r },{ timeout: 5000 })
|
||||
.then(function(){
|
||||
send_printing_job();
|
||||
},function(){
|
||||
self.receipt_queue.unshift(r)
|
||||
});
|
||||
}
|
||||
}
|
||||
send_printing_job();
|
||||
},
|
||||
|
||||
// asks the proxy to log some information, as with the debug.log you can provide several arguments.
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
import logging
|
||||
import random
|
||||
import time
|
||||
from urllib import quote_plus
|
||||
import uuid
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
group_ids = [g.id for g in user.groups_id]
|
||||
|
||||
view = request.registry.get("ir.ui.view")
|
||||
views = view._views_get(request.cr, request.uid, xml_id, request.context)
|
||||
views = view._views_get(request.cr, request.uid, xml_id, context=request.context)
|
||||
done = {}
|
||||
result = []
|
||||
for v in views:
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="language_ids" eval="[(6, 0, [ ref('base.lang_en')])]"/>
|
||||
<field name="default_lang_id" ref="base.lang_en"/>
|
||||
<field name="user_id" ref="base.public_user"/>
|
||||
</record>
|
||||
|
||||
<record id="main_menu" model="website.menu">
|
||||
|
|
|
@ -103,20 +103,16 @@ class ir_http(orm.AbstractModel):
|
|||
traceback=traceback.format_exc(exception),
|
||||
)
|
||||
if exception:
|
||||
current_exception = exception
|
||||
code = getattr(exception, 'code', code)
|
||||
if isinstance(exception, ir_qweb.QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
if exception.inner:
|
||||
current_exception = exception.inner
|
||||
if isinstance(current_exception, openerp.exceptions.AccessError):
|
||||
code = 403
|
||||
else:
|
||||
code = getattr(exception, 'code', code)
|
||||
if isinstance(exception.qweb.get('inner'), openerp.exceptions.AccessError):
|
||||
code = 403
|
||||
if code == 500:
|
||||
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
if values.get('qweb_exception'):
|
||||
if 'qweb_exception' in values:
|
||||
view = request.registry.get("ir.ui.view")
|
||||
views = view._views_get(request.cr, request.uid, values['qweb_exception'].template, request.context)
|
||||
views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
|
||||
to_reset = [v for v in views if v.model_data_id.noupdate is True]
|
||||
values['views'] = to_reset
|
||||
elif code == 403:
|
||||
|
@ -132,7 +128,7 @@ class ir_http(orm.AbstractModel):
|
|||
|
||||
try:
|
||||
html = request.website._render('website.%s' % code, values)
|
||||
except:
|
||||
except Exception:
|
||||
html = request.website._render('website.http_error', values)
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
|
||||
|
|
|
@ -46,6 +46,16 @@ class view(osv.osv):
|
|||
view = view.inherit_id
|
||||
|
||||
result = [view]
|
||||
|
||||
node = etree.fromstring(view.arch)
|
||||
for child in node.xpath("//t[@t-call]"):
|
||||
try:
|
||||
call_view = view_obj(child.get('t-call'))
|
||||
except ValueError:
|
||||
continue
|
||||
if call_view not in result:
|
||||
result += self._views_get(cr, uid, call_view, options=options, context=context, stack_result=result)
|
||||
|
||||
todo = view.inherit_children_ids
|
||||
if options:
|
||||
todo += filter(lambda x: not x.inherit_id, view.inherited_option_ids)
|
||||
|
@ -55,14 +65,6 @@ class view(osv.osv):
|
|||
for r in self._views_get(cr, uid, child_view, options=bool(child_view.inherit_id), context=context, root=False, stack_result=result):
|
||||
if r not in result:
|
||||
result.append(r)
|
||||
node = etree.fromstring(view.arch)
|
||||
for child in node.xpath("//t[@t-call]"):
|
||||
try:
|
||||
call_view = view_obj(child.get('t-call'))
|
||||
except ValueError:
|
||||
continue
|
||||
if call_view not in result:
|
||||
result += self._views_get(cr, uid, call_view, options=options, context=context, stack_result=result)
|
||||
return result
|
||||
|
||||
def extract_embedded_fields(self, cr, uid, arch, context=None):
|
||||
|
|
|
@ -24,8 +24,8 @@ class test_converter(orm.Model):
|
|||
('B', "Qu'il était supposé arriver à Toronto"),
|
||||
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
|
||||
('D', "La réponse D"),
|
||||
], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
|
||||
"qu'il fait une escale technique à St Claude, on dit:"),
|
||||
], string=u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
|
||||
u"qu'il fait une escale technique à St Claude, on dit:"),
|
||||
'html': fields.html(),
|
||||
'text': fields.text(),
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import itertools
|
|||
import logging
|
||||
import math
|
||||
import re
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
import simplejson
|
||||
|
@ -38,24 +37,38 @@ def keep_query(*args, **kw):
|
|||
|
||||
def url_for(path_or_uri, lang=None):
|
||||
location = path_or_uri.strip()
|
||||
force_lang = lang is not None
|
||||
url = urlparse.urlparse(location)
|
||||
if request and url.path and not url.netloc and not url.scheme:
|
||||
|
||||
if request and not url.netloc and not url.scheme and (url.path or force_lang):
|
||||
location = urlparse.urljoin(request.httprequest.path, location)
|
||||
force_lang = lang is not None
|
||||
lang = lang or request.context.get('lang')
|
||||
langs = [lg[0] for lg in request.website.get_languages()]
|
||||
if lang != request.website.default_lang_code and (force_lang or (location[0] == '/' and len(langs) > 1)):
|
||||
if is_multilang_url(location):
|
||||
ps = location.split('/')
|
||||
if ps[1] in langs:
|
||||
|
||||
if (len(langs) > 1 or force_lang) and is_multilang_url(location, langs):
|
||||
ps = location.split('/')
|
||||
if ps[1] in langs:
|
||||
# Replace the language only if we explicitly provide a language to url_for
|
||||
if force_lang:
|
||||
ps[1] = lang
|
||||
else:
|
||||
ps.insert(1, lang)
|
||||
location = '/'.join(ps)
|
||||
# Remove the default language unless it's explicitly provided
|
||||
elif ps[1] == request.website.default_lang_code:
|
||||
ps.pop(1)
|
||||
# Insert the context language or the provided language
|
||||
elif lang != request.website.default_lang_code or force_lang:
|
||||
ps.insert(1, lang)
|
||||
location = '/'.join(ps)
|
||||
|
||||
return location
|
||||
|
||||
def is_multilang_url(path):
|
||||
def is_multilang_url(path, langs=None):
|
||||
if not langs:
|
||||
langs = [lg[0] for lg in request.website.get_languages()]
|
||||
spath = path.split('/')
|
||||
# if a language is already in the path, remove it
|
||||
if spath[1] in langs:
|
||||
spath.pop(1)
|
||||
path = '/'.join(spath)
|
||||
try:
|
||||
router = request.httprequest.app.get_db_router(request.db).bind('')
|
||||
func = router.match(path)[0]
|
||||
|
@ -80,17 +93,7 @@ def slug(value):
|
|||
return "%s-%d" % (slugify(name), id)
|
||||
|
||||
def urlplus(url, params):
|
||||
if not params:
|
||||
return url
|
||||
|
||||
# can't use urlencode because it encodes to (ascii, replace) in p2
|
||||
return "%s?%s" % (url, '&'.join(
|
||||
k + '=' + urllib.quote_plus(v.encode('utf-8') if isinstance(v, unicode) else str(v))
|
||||
for k, v in params.iteritems()
|
||||
))
|
||||
|
||||
def quote_plus(value):
|
||||
return urllib.quote_plus(value.encode('utf-8') if isinstance(value, unicode) else str(value))
|
||||
return werkzeug.Href(url)(params or None)
|
||||
|
||||
class website(osv.osv):
|
||||
def _get_menu_website(self, cr, uid, ids, context=None):
|
||||
|
@ -122,6 +125,7 @@ class website(osv.osv):
|
|||
'social_youtube': fields.char('Youtube Account'),
|
||||
'social_googleplus': fields.char('Google+ Account'),
|
||||
'google_analytics_key': fields.char('Google Analytics Key'),
|
||||
'user_id': fields.many2one('res.users', string='Public User'),
|
||||
'public_user': fields.function(_get_public_user, relation='res.users', type='many2one', string='Public User'),
|
||||
'menu_id': fields.function(_get_menu, relation='website.menu', type='many2one', string='Main Menu',
|
||||
store= {
|
||||
|
@ -243,7 +247,7 @@ class website(osv.osv):
|
|||
slug=slug,
|
||||
res_company=request.website.company_id,
|
||||
user_id=user.browse(cr, uid, uid),
|
||||
quote_plus=quote_plus,
|
||||
quote_plus=werkzeug.url_quote_plus,
|
||||
)
|
||||
qweb_values.setdefault('editable', False)
|
||||
|
||||
|
@ -605,9 +609,10 @@ class res_partner(osv.osv):
|
|||
def google_map_link(self, cr, uid, ids, zoom=8, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
params = {
|
||||
'q': '%s, %s %s, %s' % (partner.street, partner.city, partner.zip, partner.country_id and partner.country_id.name_get()[0][1] or ''),
|
||||
'q': '%s, %s %s, %s' % (partner.street or '', partner.city or '', partner.zip or '', partner.country_id and partner.country_id.name_get()[0][1] or ''),
|
||||
'z': 10
|
||||
}
|
||||
return urlplus('https://maps.google.be/maps' , params)
|
||||
return urlplus('https://maps.google.com/maps' , params)
|
||||
|
||||
class res_company(osv.osv):
|
||||
_inherit = "res.company"
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.2 KiB |
|
@ -488,6 +488,7 @@
|
|||
$('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
|
||||
|
||||
this.rte.start_edition().then(this.check_height.bind(this));
|
||||
this.trigger('rte:called');
|
||||
},
|
||||
rte_changed: function () {
|
||||
this.$buttons.save.prop('disabled', false);
|
||||
|
|
|
@ -18,53 +18,43 @@
|
|||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome',
|
||||
title: "Welcome to your website!",
|
||||
content: "This tutorial will guide you to build your home page. We will start by adding a banner.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
backdrop: true,
|
||||
},
|
||||
{
|
||||
stepId: 'edit-page',
|
||||
waitNot: '.popover.tour',
|
||||
element: 'button[data-action=edit]',
|
||||
placement: 'bottom',
|
||||
title: "Edit this page",
|
||||
content: "Every page of your website can be modified through the <i>Edit</i> button.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: {
|
||||
id: 'rte:ready',
|
||||
type: 'openerp',
|
||||
emitter: editor,
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'add-banner',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Insert building blocks",
|
||||
content: "To add content in a page, you can insert building blocks.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-banner',
|
||||
snippet: 'carousel',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a Banner",
|
||||
content: "Drag the Banner block and drop it in your page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'edit-title',
|
||||
waitFor: '.oe_overlay_options .oe_options:visible',
|
||||
element: '#wrap [data-snippet-id=carousel]:first .carousel-caption',
|
||||
sampleText: 'My Title',
|
||||
placement: 'top',
|
||||
title: "Customize banner's text",
|
||||
content: "Click in the text and start editing it. Click continue once it's done.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'customize-banner',
|
||||
waitNot: '#wrap [data-snippet-id=carousel]:first .carousel-caption:contains("Your Banner Title")',
|
||||
element: '.oe_overlay_options .oe_options',
|
||||
placement: 'left',
|
||||
title: "Customize the banner",
|
||||
|
@ -72,67 +62,54 @@
|
|||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-three-cols',
|
||||
waitNot: '.popover.tour',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Add Another Block",
|
||||
content: "Let's add another building block to your page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-three-columns',
|
||||
snippet: 'three-columns',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a Block",
|
||||
content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
waitFor: '.oe_overlay_options .oe_options:visible',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
title: "Save your modifications",
|
||||
content: "Publish your page by clicking on the <em>'Save'</em> button.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'part-2',
|
||||
waitFor: 'button[data-action=edit]:visible',
|
||||
title: "Congratulation!",
|
||||
content: "Your homepage has been updated.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'show-mobile',
|
||||
waitNot: '.popover.tour',
|
||||
element: 'a[data-action=show-mobile-preview]',
|
||||
placement: 'bottom',
|
||||
title: "Test Your Mobile Version",
|
||||
content: "Let's check how your homepage looks like on mobile devices.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: {
|
||||
id: 'shown.bs.modal',
|
||||
emitter: $(document),
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'show-mobile-close',
|
||||
element: 'button[data-dismiss=modal]',
|
||||
placement: 'right',
|
||||
title: "Close Mobile Preview",
|
||||
content: "Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog.",
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'show-tutorials',
|
||||
element: '#help-menu-button',
|
||||
placement: 'left',
|
||||
title: "More Tutorials",
|
||||
content: "Get more tutorials through this <em>'Help'</em> menu or click on the left <em>'Edit'</em> button to continue modifying this page.",
|
||||
template: self.popover({ fixed: true, end: "Close Tutorial" }),
|
||||
trigger: 'click',
|
||||
}
|
||||
waitNot: '.modal',
|
||||
title: "Congrats",
|
||||
content: "Congratulation. This tour is finished.",
|
||||
template: self.popover({ fixed: true, next: "Close Tutorial" }),
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
|
|
|
@ -1,531 +1,439 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website/static/src/xml/website.tour.xml');
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website/static/src/xml/website.tour.xml');
|
||||
|
||||
website.Tour = openerp.Class.extend({
|
||||
steps: [], // Override
|
||||
tourStorage: window.localStorage, // FIXME: will break on iPad in private mode
|
||||
init: function () {
|
||||
this.tour = new Tour({
|
||||
name: this.id,
|
||||
storage: this.tourStorage,
|
||||
keyboard: false,
|
||||
template: this.popover(),
|
||||
onHide: function () {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
website.EditorBar.include({
|
||||
tours: [],
|
||||
start: function () {
|
||||
// $('.tour-backdrop').click(function (e) {
|
||||
// e.stopImmediatePropagation();
|
||||
// e.preventDefault();
|
||||
// });
|
||||
var self = this;
|
||||
var menu = $('#help-menu');
|
||||
_.each(this.tours, function (tour) {
|
||||
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
|
||||
$menuItem.click(function () {
|
||||
tour.reset();
|
||||
tour.trigger();
|
||||
});
|
||||
this.registerSteps();
|
||||
},
|
||||
registerStep: function (step) {
|
||||
var self = this;
|
||||
step.tour = self;
|
||||
step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title });
|
||||
if (!step.element) {
|
||||
step.orphan = true;
|
||||
menu.append($menuItem);
|
||||
});
|
||||
|
||||
this.waitRTEReady = false;
|
||||
this.on('rte:called', this, function () {self.waitRTEReady = true; });
|
||||
this.on('rte:ready', this, function () {self.waitRTEReady = false;});
|
||||
|
||||
var res = this._super();
|
||||
website.Tour.waitReady.call(this, this.testRunning);
|
||||
return res;
|
||||
},
|
||||
registerTour: function (tour) {
|
||||
website.Tour.add(tour);
|
||||
this.tours.push(tour);
|
||||
},
|
||||
testRunning: function () {
|
||||
if (this.waitRTEReady) {
|
||||
this.on('rte:ready', this, function () {
|
||||
website.Tour.each(function () {
|
||||
this.running();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
website.Tour.each(function () {
|
||||
this.running();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
/* jQuery selector to match exact text inside an element
|
||||
* :containsExact() - case insensitive
|
||||
* :containsExactCase() - case sensitive
|
||||
* :containsRegex() - set by user ( use: $(el).find(':containsRegex(/(red|blue|yellow)/gi)') )
|
||||
*/
|
||||
$.extend($.expr[':'],{
|
||||
containsExact: function(a,i,m){
|
||||
return $.trim(a.innerHTML.toLowerCase()) === m[3].toLowerCase();
|
||||
},
|
||||
containsExactCase: function(a,i,m){
|
||||
return $.trim(a.innerHTML) === m[3];
|
||||
},
|
||||
// Note all escaped characters need to be double escaped
|
||||
// inside of the containsRegex, so "\(" needs to be "\\("
|
||||
containsRegex: function(a,i,m){
|
||||
var regreg = /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})$/,
|
||||
reg = regreg.exec(m[3]);
|
||||
return reg ? new RegExp(reg[1], reg[2]).test($.trim(a.innerHTML)) : false;
|
||||
}
|
||||
});
|
||||
$.ajaxSetup({
|
||||
beforeSend:function(){
|
||||
$.ajaxBusy = ($.ajaxBusy|0) + 1;
|
||||
},
|
||||
complete:function(){
|
||||
$.ajaxBusy--;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
website.Tour = openerp.Class.extend({
|
||||
steps: [],
|
||||
defaultDelay: 50, //ms
|
||||
localStorage: window.localStorage,
|
||||
init: function (url) {
|
||||
this.tour = new Tour({
|
||||
name: this.id,
|
||||
storage: this.tourStorage,
|
||||
keyboard: false,
|
||||
template: this.popover(),
|
||||
onHide: function () {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
this.registerSteps();
|
||||
},
|
||||
|
||||
run: function (automatic, force) {
|
||||
if (force) this.reset();
|
||||
|
||||
for (var k in this.localStorage) {
|
||||
if (!k.indexOf("tour-") && k.indexOf("-test") > -1) return;
|
||||
}
|
||||
|
||||
// only one test running
|
||||
if (website.Tour.busy) return;
|
||||
|
||||
website.Tour.busy = true;
|
||||
this.localStorage.setItem("tour-"+this.id+"-test", 0);
|
||||
|
||||
website.Tour.waitReady.call(this, function () {this._run(automatic, force);});
|
||||
},
|
||||
running: function () {
|
||||
if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length) {
|
||||
this.endTour();
|
||||
return;
|
||||
}
|
||||
|
||||
if (website.Tour.busy || !this.testUrl()) return;
|
||||
|
||||
var self = this;
|
||||
website.Tour.waitReady.call(this, function () {
|
||||
self._running();
|
||||
}, 500);
|
||||
},
|
||||
|
||||
_run: function (automatic, force) {
|
||||
this.reset();
|
||||
this.localStorage.setItem("tour-"+this.id+"-test", 0);
|
||||
if (automatic) {
|
||||
this.localStorage.setItem("tour-"+this.id+"-test-automatic", true);
|
||||
}
|
||||
this.nextStep(null, automatic ? this.autoNextStep : null, automatic ? 5000 : null);
|
||||
},
|
||||
_running: function () {
|
||||
var stepId = this.localStorage.getItem("tour-"+this.id+"-test");
|
||||
var automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic");
|
||||
|
||||
if (stepId || this.checkRuningUrl()) {
|
||||
|
||||
if (!this.check(this.step(stepId))) {
|
||||
var step = this.next(stepId);
|
||||
stepId = step ? step.stepId : stepId;
|
||||
}
|
||||
website.Tour.busy = true;
|
||||
this.nextStep(stepId, automatic ? this.autoNextStep : null, automatic ? 5000 : null);
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
website.Tour.busy = false;
|
||||
for (var k in this.steps) {
|
||||
this.steps[k].busy = false;
|
||||
}
|
||||
clearTimeout(self.timer);
|
||||
clearTimeout(self.testtimer);
|
||||
|
||||
for (var k in this.localStorage) {
|
||||
if (!k.indexOf("tour-") || !k.indexOf(this.id)) {
|
||||
this.localStorage.removeItem(k);
|
||||
}
|
||||
}
|
||||
|
||||
$('.popover.tour').remove();
|
||||
},
|
||||
trigger: function (automatic) {
|
||||
this.reset();
|
||||
if (this.path) {
|
||||
this.localStorage.setItem("tour-"+this.id+"-test", 0);
|
||||
if (automatic) this.localStorage.setItem("tour-"+this.id+"-test-automatic", true);
|
||||
var path = this.path.split('?');
|
||||
window.location.href = path[0] + "?tutorial."+this.id+"=true" + path.slice(1, path.length).join("?");
|
||||
} else {
|
||||
this.run(automatic);
|
||||
}
|
||||
},
|
||||
testUrl: function () {
|
||||
return !this.testPath || this.testPath.test(window.location.href);
|
||||
},
|
||||
checkRuningUrl: function () {
|
||||
return window.location.search.indexOf("tutorial."+this.id+"=true") > -1;
|
||||
},
|
||||
|
||||
registerSteps: function () {
|
||||
for (var index=0, len=this.steps.length; index<len; index++) {
|
||||
var step = this.steps[index];
|
||||
step.stepId = step.stepId || ""+index;
|
||||
|
||||
if (!step.waitNot && index > 0 && $(this.steps[index-1].template).has("button[data-role='next']").size()) {
|
||||
step.waitNot = '.popover.tour';
|
||||
}
|
||||
if (!step.waitFor && index > 0 && this.steps[index-1].snippet) {
|
||||
step.waitFor = '.oe_overlay_options .oe_options:visible';
|
||||
}
|
||||
|
||||
step._title = step.title;
|
||||
step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title });
|
||||
if (!step.element) step.orphan = true;
|
||||
if (step.snippet) {
|
||||
step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail';
|
||||
}
|
||||
if (step.trigger) {
|
||||
if (step.trigger === 'click') {
|
||||
step.triggers = function (callback) {
|
||||
$(step.element).one('click', function () {
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
});
|
||||
};
|
||||
} else if (step.trigger === 'reload') {
|
||||
step.triggers = function (callback) {
|
||||
var stack = JSON.parse(step.tour.tourStorage.getItem("website-reloads")) || [];
|
||||
var index = stack.indexOf(step.stepId);
|
||||
if (index !== -1) {
|
||||
setTimeout(function () {
|
||||
$(step.element).popover("destroy");
|
||||
setTimeout(function () {
|
||||
stack.splice(index,1);
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
step.tour.tourStorage.setItem("website-reloads", JSON.stringify(stack));
|
||||
},10);
|
||||
},0);
|
||||
} else {
|
||||
stack.push(step.stepId);
|
||||
step.tour.tourStorage.setItem("website-reloads", JSON.stringify(stack));
|
||||
}
|
||||
};
|
||||
} else if (step.trigger === 'drag') {
|
||||
step.triggers = function (callback) {
|
||||
self.onSnippetDragged(callback || self.moveToNextStep);
|
||||
};
|
||||
} else if (step.trigger.id) {
|
||||
if (step.trigger.emitter && step.trigger.type === 'openerp') {
|
||||
step.triggers = function (callback) {
|
||||
step.trigger.emitter.on(step.trigger.id, self, function customHandler () {
|
||||
step.trigger.emitter.off(step.trigger.id, customHandler);
|
||||
(callback || self.moveToNextStep).apply(self, arguments);
|
||||
});
|
||||
};
|
||||
} else {
|
||||
step.triggers = function (callback) {
|
||||
var emitter = _.isString(step.trigger.emitter) ? $(step.trigger.emitter) : (step.trigger.emitter || $(step.element));
|
||||
if (!emitter.size()) throw "Emitter is undefined";
|
||||
emitter.on(step.trigger.id, function () {
|
||||
(callback || self.moveToNextStep).apply(self, arguments);
|
||||
});
|
||||
};
|
||||
}
|
||||
} else if (step.trigger.url) {
|
||||
step.triggers = function (callback) {
|
||||
var stack = JSON.parse(step.tour.tourStorage.getItem("website-geturls")) || [];
|
||||
var id = step.trigger.url.toString();
|
||||
var index = stack.indexOf(id);
|
||||
if (index !== -1) {
|
||||
var url = new website.UrlParser(window.location.href);
|
||||
var test = typeof step.trigger.url === "string" ?
|
||||
step.trigger.url == url.pathname+url.search :
|
||||
step.trigger.url.test(url.pathname+url.search);
|
||||
if (!test) return;
|
||||
setTimeout(function () {
|
||||
$(step.element).popover("destroy");
|
||||
setTimeout(function () {
|
||||
stack.splice(index,1);
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
step.tour.tourStorage.setItem("website-geturls", JSON.stringify(stack));
|
||||
},10);
|
||||
},0);
|
||||
} else {
|
||||
stack.push(id);
|
||||
step.tour.tourStorage.setItem("website-geturls", JSON.stringify(stack));
|
||||
}
|
||||
return index !== -1;
|
||||
};
|
||||
} else if (step.trigger.modal) {
|
||||
step.triggers = function (callback, auto) {
|
||||
var $doc = $(document);
|
||||
function onStop () {
|
||||
if (step.trigger.modal.stopOnClose) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
$doc.on('hide.bs.modal', onStop);
|
||||
$doc.one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').one('click', function () {
|
||||
$doc.off('hide.bs.modal', onStop);
|
||||
if (!callback) {
|
||||
self.moveToStep(step.trigger.modal.afterSubmit);
|
||||
}
|
||||
});
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
});
|
||||
};
|
||||
} else if (step.trigger === 'ajax') {
|
||||
step.triggers = function (callback) {
|
||||
$( document ).ajaxSuccess(function(event, xhr, settings) {
|
||||
$( document ).unbind('ajaxSuccess');
|
||||
xhr.then(function () {
|
||||
setTimeout(function () {
|
||||
$(step.element).popover("destroy");
|
||||
setTimeout(function () {
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
},10);
|
||||
},0);
|
||||
});
|
||||
});
|
||||
};
|
||||
} else {
|
||||
step.triggers = function (callback) {
|
||||
var emitter = $(step.element);
|
||||
if (!emitter.size()) throw "Emitter is undefined";
|
||||
var trigger = function () {
|
||||
emitter.off(step.trigger, trigger);
|
||||
(callback || self.moveToNextStep).apply(self, arguments);
|
||||
};
|
||||
emitter.on(step.trigger, trigger);
|
||||
};
|
||||
}
|
||||
}
|
||||
step.onShow = (function () {
|
||||
var executed = false;
|
||||
return function () {
|
||||
if (!executed) {
|
||||
_.isFunction(step.onStart) && step.onStart();
|
||||
_.isFunction(step.triggers) && step.triggers();
|
||||
executed = true;
|
||||
}
|
||||
};
|
||||
}());
|
||||
return step;
|
||||
},
|
||||
registerSteps: function () {
|
||||
var self = this;
|
||||
this.tour.addSteps(_.map(this.steps, function (step) {
|
||||
return self.registerStep(step);
|
||||
}));
|
||||
},
|
||||
reset: function () {
|
||||
this.tourStorage.removeItem(this.id+'_current_step');
|
||||
this.tourStorage.removeItem(this.id+'_end');
|
||||
this.tourStorage.removeItem("website-reloads");
|
||||
this.tourStorage.removeItem("website-geturls");
|
||||
this.tour._current = 0;
|
||||
$('.popover.tour').remove();
|
||||
},
|
||||
start: function () {
|
||||
window.Tour.prototype._isOrphan = function(step) {
|
||||
return (step.element == null) || !$(step.element).length;
|
||||
};
|
||||
if (this.resume() || ((this.currentStepIndex() === 0) && !this.tour.ended())) {
|
||||
this.tour.start();
|
||||
}
|
||||
},
|
||||
currentStepIndex: function () {
|
||||
var index = this.tourStorage.getItem(this.id+'_current_step') || 0;
|
||||
return parseInt(index, 10);
|
||||
},
|
||||
indexOfStep: function (stepId) {
|
||||
var index = -1;
|
||||
_.each(this.steps, function (step, i) {
|
||||
if (step.stepId === stepId) {
|
||||
index = i;
|
||||
}
|
||||
});
|
||||
return index;
|
||||
},
|
||||
isCurrentStep: function (stepId) {
|
||||
return this.currentStepIndex() === this.indexOfStep(stepId);
|
||||
},
|
||||
moveToStep: function (step) {
|
||||
var index = _.isNumber(step) ? step : this.indexOfStep(step);
|
||||
if (index >= this.steps.length) {
|
||||
this.stop();
|
||||
} else if (index >= 0) {
|
||||
var self = this;
|
||||
$('.popover.tour').remove();
|
||||
self.tour.goto(index);
|
||||
}
|
||||
},
|
||||
moveToNextStep: function () {
|
||||
var nextStepIndex = this.currentStepIndex() + 1;
|
||||
this.moveToStep(nextStepIndex);
|
||||
},
|
||||
stop: function () {
|
||||
this.tour.end();
|
||||
},
|
||||
redirect: function (url) {
|
||||
url = url || new website.UrlParser(window.location.href);
|
||||
var path = (this.path && url.pathname !== this.path) ? this.path : url.pathname;
|
||||
var search = url.activateTutorial(this.id);
|
||||
var newUrl = path + search;
|
||||
window.location.replace(newUrl);
|
||||
},
|
||||
ended: function () {
|
||||
return this.tourStorage.getItem(this.id+'_end') === "yes";
|
||||
},
|
||||
resume: function () {
|
||||
// Override if necessary
|
||||
return this.tourStorage.getItem(this.id+'_current_step') && !this.ended();
|
||||
},
|
||||
trigger: function (url) {
|
||||
// Override if necessary
|
||||
url = url || new website.UrlParser(window.location.href);
|
||||
return url.isActive(this.id);
|
||||
},
|
||||
testUrl: function (pattern) {
|
||||
var url = new website.UrlParser(window.location.href);
|
||||
return pattern.test(url.pathname+url.search);
|
||||
},
|
||||
popover: function (options) {
|
||||
return openerp.qweb.render('website.tour_popover', options);
|
||||
},
|
||||
onSnippetDragged: function (callback) {
|
||||
var self = this;
|
||||
var selector = '#website-top-navbar [data-snippet-id].ui-draggable';
|
||||
var beginDrag = function beginDrag () {
|
||||
$('.popover.tour').remove();
|
||||
var advance = function advance () {
|
||||
if (_.isFunction(callback)) {
|
||||
callback.apply(self);
|
||||
}
|
||||
$(selector).off('mouseup', advance);
|
||||
};
|
||||
$(document.body).one('mouseup', advance);
|
||||
$(selector).off('mousedown', beginDrag);
|
||||
};
|
||||
$(selector).one('mousedown', beginDrag);
|
||||
},
|
||||
onSnippetDraggedAdvance: function () {
|
||||
onSnippetDragged(self.moveToNextStep);
|
||||
},
|
||||
});
|
||||
|
||||
website.UrlParser = openerp.Class.extend({
|
||||
init: function (url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
this.href = a.href;
|
||||
this.host = a.host;
|
||||
this.protocol = a.protocol;
|
||||
this.port = a.port;
|
||||
this.hostname = a.hostname;
|
||||
this.pathname = a.pathname;
|
||||
this.origin = a.origin;
|
||||
this.search = a.search;
|
||||
this.hash = a.hash;
|
||||
function generateTrigger (id) {
|
||||
return "tutorial."+id+"=true";
|
||||
}
|
||||
this.activateTutorial = function (id) {
|
||||
var urlTrigger = generateTrigger(id);
|
||||
var querystring = _.filter(this.search.split('?'), function (str) {
|
||||
return str;
|
||||
});
|
||||
if (querystring.length > 0) {
|
||||
var queries = _.filter(querystring[0].split("&"), function (query) {
|
||||
return query.indexOf("tutorial.") < 0
|
||||
});
|
||||
queries.push(urlTrigger);
|
||||
return "?"+_.uniq(queries).join("&");
|
||||
} else {
|
||||
return "?"+urlTrigger;
|
||||
}
|
||||
};
|
||||
this.isActive = function (id) {
|
||||
var urlTrigger = generateTrigger(id);
|
||||
return this.search.indexOf(urlTrigger) >= 0;
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
var TestConsole = openerp.Class.extend({
|
||||
tests: [],
|
||||
editor: null,
|
||||
init: function (editor) {
|
||||
if (!editor) {
|
||||
throw new Error("Editor cannot be null or undefined");
|
||||
}
|
||||
this.editor = editor;
|
||||
},
|
||||
test: function (id) {
|
||||
return _.find(this.tests, function (tour) {
|
||||
return tour.id === id;
|
||||
if ($(this.steps[index-1].template).has("button[data-role='next']").size()) {
|
||||
var step = {
|
||||
stepId: index,
|
||||
waitNot: '.popover.tour:visible'
|
||||
};
|
||||
this.steps.push(step);
|
||||
}
|
||||
|
||||
this.tour.addSteps(this.steps);
|
||||
},
|
||||
|
||||
popover: function (options) {
|
||||
return openerp.qweb.render('website.tour_popover', options);
|
||||
},
|
||||
|
||||
timer: null,
|
||||
testtimer: null,
|
||||
check: function (step) {
|
||||
return (step &&
|
||||
(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) &&
|
||||
(!step.waitNot || !$(step.waitNot).size()) &&
|
||||
(!step.waitFor || $(step.waitFor).size()));
|
||||
},
|
||||
waitNextStep: function (step, callback, overlaps) {
|
||||
var self = this;
|
||||
var time = new Date().getTime();
|
||||
var timer;
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
clearTimeout(self.timer);
|
||||
clearTimeout(self.testtimer);
|
||||
};
|
||||
|
||||
// check popover activity
|
||||
$(".popover.tour button")
|
||||
.off()
|
||||
.on("click", function () {
|
||||
$(".popover.tour").remove();
|
||||
if (step.busy) return;
|
||||
if (!$(this).is("[data-role='next']")) {
|
||||
clearTimeout(self.timer);
|
||||
step.busy = true;
|
||||
self.tour.end();
|
||||
self.endTour(callback);
|
||||
}
|
||||
});
|
||||
},
|
||||
snippetSelector: function (snippetId) {
|
||||
return '#oe_snippets div.oe_snippet[data-snippet-id="'+snippetId+'"] .oe_snippet_thumbnail';
|
||||
},
|
||||
snippetThumbnail: function (snippetId) {
|
||||
return $(this.snippetSelector(snippetId)).first();
|
||||
},
|
||||
snippetThumbnailExists: function (snippetId) {
|
||||
return this.snippetThumbnail(snippetId).length > 0;
|
||||
},
|
||||
dragAndDropSnippet: function (snippetId) {
|
||||
function actualDragAndDrop ($thumbnail) {
|
||||
var thumbnailPosition = $thumbnail.position();
|
||||
$thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top }));
|
||||
$thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 }));
|
||||
var $dropZone = $(".oe_drop_zone").first();
|
||||
var dropPosition = $dropZone.position();
|
||||
$dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top }));
|
||||
}
|
||||
if (this.snippetThumbnailExists(snippetId)) {
|
||||
actualDragAndDrop(this.snippetThumbnail(snippetId));
|
||||
|
||||
function checkNext () {
|
||||
clearTimeout(self.timer);
|
||||
if (step.busy) return;
|
||||
if (self.check(step)) {
|
||||
step.busy = true;
|
||||
// use an other timeout for cke dom loading
|
||||
setTimeout(function () {
|
||||
self.nextStep(step.stepId, callback, overlaps);
|
||||
}, self.defaultDelay);
|
||||
} else if (!overlaps || new Date().getTime() - time < overlaps) {
|
||||
self.timer = setTimeout(checkNext, self.defaultDelay);
|
||||
} else {
|
||||
this.editor.on('rte:ready', this, function () {
|
||||
actualDragAndDrop(this.snippetThumbnail(snippetId));
|
||||
});
|
||||
self.reset();
|
||||
throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
checkNext();
|
||||
},
|
||||
step: function (stepId) {
|
||||
var steps = this.steps.slice(0,this.steps.length),
|
||||
step;
|
||||
while (step = steps.shift()) {
|
||||
if (!stepId || step.stepId === stepId)
|
||||
return step;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
next: function (stepId) {
|
||||
var steps = this.steps.slice(0,this.steps.length),
|
||||
step, next, index=0;
|
||||
while (step = steps.shift()) {
|
||||
if (!stepId || step.stepId === stepId) {
|
||||
// clear popover (fix for boostrap tour if the element is removed before destroy popover)
|
||||
$(".popover.tour").remove();
|
||||
// go to step in bootstrap tour
|
||||
this.tour.goto(index);
|
||||
if (step.callback) step.callback();
|
||||
next = steps.shift();
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return next;
|
||||
},
|
||||
nextStep: function (stepId, callback, overlaps) {
|
||||
var self = this;
|
||||
if (!this.localStorage.getItem("tour-"+this.id+"-test")) return;
|
||||
|
||||
this.localStorage.setItem("tour-"+this.id+"-test", stepId || 0);
|
||||
|
||||
website.EditorBar.include({
|
||||
tours: [],
|
||||
init: function () {
|
||||
var result = this._super();
|
||||
website.TestConsole = new TestConsole(this);
|
||||
return result;
|
||||
},
|
||||
start: function () {
|
||||
$('.tour-backdrop').click(function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
var menu = $('#help-menu');
|
||||
_.each(this.tours, function (tour) {
|
||||
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
|
||||
$menuItem.click(function () {
|
||||
tour.reset();
|
||||
tour.redirect(new website.UrlParser(window.location.href));
|
||||
});
|
||||
menu.append($menuItem);
|
||||
if (tour.trigger()) {
|
||||
setTimeout(function () {
|
||||
setTimeout(function () {
|
||||
tour.start();
|
||||
},100);
|
||||
},0);
|
||||
this.current = this.step(stepId);
|
||||
var next = this.next(stepId);
|
||||
|
||||
if (next) {
|
||||
setTimeout(function () {
|
||||
self.waitNextStep(next, callback, overlaps);
|
||||
if (callback) setTimeout(function(){callback.call(self, next);}, self.defaultDelay);
|
||||
}, next && next.wait || 0);
|
||||
} else {
|
||||
this.endTour();
|
||||
}
|
||||
},
|
||||
endTour: function () {
|
||||
console.log('{ "event": "success" }');
|
||||
this.reset();
|
||||
},
|
||||
autoNextStep: function () {
|
||||
var self = this;
|
||||
clearTimeout(self.testtimer);
|
||||
|
||||
function autoStep () {
|
||||
var step = self.current;
|
||||
if (!step) return;
|
||||
|
||||
if (step.autoComplete) {
|
||||
step.autoComplete(tour);
|
||||
}
|
||||
|
||||
var $popover = $(".popover.tour");
|
||||
if ($popover.find("button[data-role='next']:visible").size()) {
|
||||
$popover.find("button[data-role='next']:visible").click();
|
||||
$popover.remove();
|
||||
}
|
||||
|
||||
var $element = $(step.element);
|
||||
if (!$element.size()) return;
|
||||
|
||||
if (step.snippet) {
|
||||
|
||||
var selector = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail';
|
||||
self.autoDragAndDropSnippet(selector);
|
||||
|
||||
} else if (step.element.match(/#oe_snippets .* \.oe_snippet_thumbnail/)) {
|
||||
|
||||
self.autoDragAndDropSnippet($element);
|
||||
|
||||
} else if (step.sampleText) {
|
||||
|
||||
$element.trigger($.Event("keydown", { srcElement: $element }));
|
||||
if ($element.is("select") || $element.is("input") ) {
|
||||
$element.val(step.sampleText);
|
||||
} else {
|
||||
$element.html(step.sampleText);
|
||||
}
|
||||
});
|
||||
return this._super();
|
||||
},
|
||||
registerTour: function (tour) {
|
||||
var self = this;
|
||||
var testId = 'test_'+tour.id+'_tour';
|
||||
this.tours.push(tour);
|
||||
var defaultDelay = 250; //ms
|
||||
var overlapsCrash;
|
||||
var test = {
|
||||
id: tour.id,
|
||||
run: function (force) {
|
||||
if (force === true) {
|
||||
this.reset();
|
||||
tour.reset();
|
||||
}
|
||||
var actionSteps = _.filter(tour.steps, function (step) {
|
||||
return step.trigger || step.triggers || step.sampleText;
|
||||
});
|
||||
window.onbeforeunload = function () {
|
||||
clearTimeout(overlapsCrash);
|
||||
};
|
||||
function throwError (message) {
|
||||
console.log(tour.tourStorage.getItem("test-report"));
|
||||
test.reset();
|
||||
tour.reset();
|
||||
throw message;
|
||||
}
|
||||
function initReport () {
|
||||
// set last time for report
|
||||
if (!tour.tourStorage.getItem("test-last-time")) {
|
||||
tour.tourStorage.setItem("test-last-time", new Date().getTime());
|
||||
}
|
||||
}
|
||||
function setReport (step) {
|
||||
var report = JSON.parse(tour.tourStorage.getItem("test-report")) || {};
|
||||
report[step.stepId] = (new Date().getTime() - tour.tourStorage.getItem("test-last-time")) + " ms";
|
||||
tour.tourStorage.setItem("test-report", JSON.stringify(report));
|
||||
}
|
||||
function testCycling (step) {
|
||||
var lastStep = tour.tourStorage.getItem(testId);
|
||||
var tryStep = lastStep != step.stepId ? 0 : (+(tour.tourStorage.getItem("test-last-"+testId) || 0) + 1);
|
||||
tour.tourStorage.setItem("test-last-"+testId, tryStep);
|
||||
if (tryStep > 2) {
|
||||
throwError("Test: '" + testId + "' cycling step: '" + step.stepId + "'");
|
||||
}
|
||||
return tryStep;
|
||||
}
|
||||
function getDelay (step) {
|
||||
return step.delay || defaultDelay;
|
||||
}
|
||||
function executeStep (step) {
|
||||
if (testCycling(step) === 0) initReport();
|
||||
setTimeout(function () {
|
||||
$element.trigger($.Event("keyup", { srcElement: $element }));
|
||||
$element.trigger($.Event("change", { srcElement: $element }));
|
||||
}, self.defaultDelay<<1);
|
||||
|
||||
} else if ($element.is(":visible")) {
|
||||
|
||||
tour.tourStorage.setItem(testId, step.stepId);
|
||||
$element.trigger($.Event("mouseenter", { srcElement: $element[0] }));
|
||||
$element.trigger($.Event("mousedown", { srcElement: $element[0] }));
|
||||
|
||||
var evt = document.createEvent("MouseEvents");
|
||||
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
$element[0].dispatchEvent(evt);
|
||||
|
||||
var delay = getDelay (step);
|
||||
|
||||
overlapsCrash = setTimeout(function () {
|
||||
throwError("Test: '" + testId + "' can't resolve step: '" + step.stepId + "'");
|
||||
}, delay + 3000);
|
||||
|
||||
|
||||
var _next = false;
|
||||
tour.tourStorage.setItem(testId, step.stepId);
|
||||
function next () {
|
||||
_next = true;
|
||||
clearTimeout(overlapsCrash);
|
||||
|
||||
setReport(step);
|
||||
|
||||
var nextStep = actionSteps.shift();
|
||||
|
||||
if (nextStep) {
|
||||
executeStep(nextStep);
|
||||
} else {
|
||||
tour.tourStorage.removeItem(testId);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
var $element = $(step.element);
|
||||
|
||||
var flag = step.triggers && (!step.trigger || !step.trigger.modal);
|
||||
if (flag) {
|
||||
try {
|
||||
step.triggers(next, true);
|
||||
} catch (e) {
|
||||
throwError(e);
|
||||
}
|
||||
}
|
||||
if ((step.trigger === 'reload' || (step.trigger && step.trigger.url)) && _next) return;
|
||||
|
||||
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.trigger($.Event("keydown", { srcElement: $element }));
|
||||
if ($element.is("select") || $element.is("input") ) {
|
||||
$element.val(step.sampleText);
|
||||
} else {
|
||||
$element.html(step.sampleText);
|
||||
}
|
||||
$element.trigger($.Event("change", { srcElement: $element }));
|
||||
$element.trigger($.Event("keyup", { srcElement: $element }));
|
||||
} else if ($element.is(":visible")) { // Click by default
|
||||
$element.trigger($.Event("mouseenter", { srcElement: $element }));
|
||||
$element.trigger($.Event("mousedown", { srcElement: $element }));
|
||||
var evt = document.createEvent("MouseEvents");
|
||||
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
$element[0].dispatchEvent(evt);
|
||||
$element.trigger($.Event("mouseup", { srcElement: $element }));
|
||||
$element.trigger($.Event("mouseleave", { srcElement: $element }));
|
||||
|
||||
// trigger after for step like: mouseenter, next step click on button display with mouseenter
|
||||
$element.trigger($.Event("mouseenter", { srcElement: $element }));
|
||||
}
|
||||
if (!flag) next();
|
||||
},delay);
|
||||
}
|
||||
var url = new website.UrlParser(window.location.href);
|
||||
if (tour.path && url.pathname !== tour.path && !tour.tourStorage.getItem(testId)) {
|
||||
tour.tourStorage.setItem(testId, actionSteps[0].stepId);
|
||||
window.location.href = tour.path;
|
||||
} else {
|
||||
var lastStepId = tour.tourStorage.getItem(testId);
|
||||
var currentStep = actionSteps.shift();
|
||||
if (lastStepId) {
|
||||
while (currentStep && lastStepId !== currentStep.stepId) {
|
||||
currentStep = actionSteps.shift();
|
||||
}
|
||||
}
|
||||
if (currentStep.snippet && $(currentStep.element).length === 0) {
|
||||
self.on('rte:ready', this, function () {
|
||||
executeStep(currentStep);
|
||||
});
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
executeStep(currentStep);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
},
|
||||
reset: function () {
|
||||
tour.tourStorage.removeItem(testId);
|
||||
tour.tourStorage.removeItem("test-report");
|
||||
for (var k in tour.tourStorage) {
|
||||
if (tour.tourStorage[k].indexOf("test-last")) {
|
||||
tour.tourStorage.removeItem(k);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
website.TestConsole.tests.push(test);
|
||||
if (window.localStorage.getItem(testId)) {
|
||||
test.run();
|
||||
// trigger after for step like: mouseenter, next step click on button display with mouseenter
|
||||
setTimeout(function () {
|
||||
$element.trigger($.Event("mouseup", { srcElement: $element[0] }));
|
||||
$element.trigger($.Event("mouseleave", { srcElement: $element[0] }));
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
}
|
||||
self.testtimer = setTimeout(autoStep, 100);
|
||||
},
|
||||
autoDragAndDropSnippet: function (selector) {
|
||||
var $thumbnail = $(selector).first();
|
||||
var thumbnailPosition = $thumbnail.position();
|
||||
$thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top }));
|
||||
$thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 }));
|
||||
var $dropZone = $(".oe_drop_zone").first();
|
||||
var dropPosition = $dropZone.position();
|
||||
$dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top }));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
website.Tour.tours = {};
|
||||
website.Tour.busy = false;
|
||||
website.Tour.add = function (tour) {
|
||||
website.Tour.waitReady(function () {
|
||||
tour = tour.id ? tour : new tour();
|
||||
website.Tour.tours[tour.id] = tour;
|
||||
});
|
||||
};
|
||||
website.Tour.get = function (id) {
|
||||
return website.Tour.tours[id];
|
||||
};
|
||||
website.Tour.each = function (callback) {
|
||||
website.Tour.waitReady(function () {
|
||||
for (var k in website.Tour.tours) {
|
||||
callback.call(website.Tour.tours[k]);
|
||||
}
|
||||
});
|
||||
};
|
||||
website.Tour.waitReady = function (callback) {
|
||||
var self = this;
|
||||
$(document).ready(function () {
|
||||
if ($.ajaxBusy == null || $.ajaxBusy) {
|
||||
$(document).ajaxStop(function() {
|
||||
setTimeout(function () {
|
||||
callback.call(self);
|
||||
},0);
|
||||
});
|
||||
}
|
||||
else {
|
||||
setTimeout(function () {
|
||||
callback.call(self);
|
||||
},0);
|
||||
}
|
||||
});
|
||||
};
|
||||
website.Tour.run_test = function (id) {
|
||||
website.Tour.get(id).trigger(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}());
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import collections
|
||||
import urllib
|
||||
import urlparse
|
||||
import unittest2
|
||||
import urllib2
|
||||
|
@ -66,7 +65,7 @@ class CrawlSuite(unittest2.TestSuite):
|
|||
# blow up in multidb situations
|
||||
self.opener.open('http://localhost:{port}/web/?db={db}'.format(
|
||||
port=tools.config['xmlrpc_port'],
|
||||
db=urllib.quote_plus(tools.config['db_name']),
|
||||
db=werkzeug.url_quote_plus(tools.config['db_name']),
|
||||
))
|
||||
if user is not None:
|
||||
url = 'http://localhost:{port}/login?{query}'.format(
|
||||
|
|
|
@ -58,8 +58,9 @@ class WebsiteUiSuite(unittest.TestSuite):
|
|||
return iter([self])
|
||||
|
||||
def run(self, result):
|
||||
# clean slate
|
||||
if sql_db._Pool is not None:
|
||||
return
|
||||
# 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:
|
||||
|
|
|
@ -7,30 +7,11 @@ testRunner.run(function homepageTest (page, timeout) {
|
|||
waitFor(function clientReady () {
|
||||
return page.evaluate(function () {
|
||||
return window.$ && window.openerp && window.openerp.website
|
||||
&& window.openerp.website.TestConsole
|
||||
&& window.openerp.website.TestConsole.test
|
||||
&& window.openerp.website.TestConsole.test('banner');
|
||||
&& window.openerp.website.Tour;
|
||||
});
|
||||
}, function executeTest () {
|
||||
var before = page.evaluate(function () {
|
||||
var result = {
|
||||
carousel: $('#wrap [data-snippet-id=carousel]').length,
|
||||
columns: $('#wrap [data-snippet-id=three-columns]').length,
|
||||
};
|
||||
window.openerp.website.TestConsole.test('banner').run(true);
|
||||
return result;
|
||||
page.evaluate(function () {
|
||||
window.openerp.website.Tour.run_test('banner');
|
||||
});
|
||||
waitFor(function testExecuted () {
|
||||
var after = page.evaluate(function () {
|
||||
return window.$ && $('button[data-action=edit]').is(":visible") && {
|
||||
carousel: $('#wrap [data-snippet-id=carousel]').length,
|
||||
columns: $('#wrap [data-snippet-id=three-columns]').length,
|
||||
};
|
||||
});
|
||||
return after && (after.carousel === before.carousel + 1) && (after.columns === before.columns + 1);
|
||||
}, function finish () {
|
||||
console.log('{ "event": "success" }');
|
||||
phantom.exit();
|
||||
}, 4*timeout/5);
|
||||
}, timeout/5);
|
||||
}, timeout);
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
Have a look at <a href="/">your homepage</a> or try another theme below.
|
||||
</p>
|
||||
</div>
|
||||
<h1 class="text-center">Try a New Theme</h1>
|
||||
<h1 class="text-center">Try a New Bootswatch Theme</h1>
|
||||
<h3 class="text-center text-muted">You'll be able to change the theme at anytime</h3>
|
||||
|
||||
<div class="row mt32" id="themes-list">
|
||||
|
@ -26,11 +26,11 @@
|
|||
<div class="col-md-4">
|
||||
<div class="well text-center">
|
||||
<div class="image">
|
||||
<img src="http://bootswatch.com/spacelab/thumbnail.png" class="img-responsive" alt="Bootstrap Theme"/>
|
||||
<img src="/website/static/src/img/bootswatch_default_thumbnail.png" class="img-responsive" alt="Default Theme"/>
|
||||
</div>
|
||||
<div class="options">
|
||||
<h3>Default</h3>
|
||||
<p>Pure bootstrap</p>
|
||||
<p>Pure Bootstrap</p>
|
||||
<a class="btn btn-info" href="/website/theme_change">Apply</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -178,6 +178,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="well text-center">
|
||||
<div class="image">
|
||||
<img class="img-responsive" src="http://bootswatch.com/yeti/thumbnail.png" alt="Yeti"/>
|
||||
</div>
|
||||
<div class="options">
|
||||
<h3>Yeti</h3>
|
||||
<p>A friendly foundation</p>
|
||||
<a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_yeti">Apply</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -255,5 +269,11 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="website.theme_yeti" name="Yeti" inherit_option_id="website.theme">
|
||||
<xpath expr="//link[@id='bootstrap_css']" position="replace">
|
||||
<link rel='stylesheet' href='/website/static/src/css/bootswatch/yeti.min.css' t-ignore="true"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
((submenu.url != '/' and request.httprequest.path.startswith(submenu.url)) or
|
||||
request.httprequest.path == submenu.url) and 'active'
|
||||
">
|
||||
<a t-att-href="url_for(submenu.url)" t-ignore="true" t-att-target="'blank' if submenu.new_window else None">
|
||||
<a t-att-href="submenu.url" t-ignore="true" t-att-target="'blank' if submenu.new_window else None">
|
||||
<span t-field="submenu.name"/>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -216,7 +216,7 @@
|
|||
<template id="layout_logo_show" inherit_option_id="website.layout" name="Show Logo">
|
||||
<xpath expr="//header//a[@class='navbar-brand']" position="replace">
|
||||
<a href="/" class="navbar-brand logo">
|
||||
<img src="/website/static/src/img/odoo_logo.png"/>
|
||||
<img src="/logo.png"/>
|
||||
</a>
|
||||
</xpath>
|
||||
</template>
|
||||
|
@ -452,7 +452,10 @@
|
|||
<div id="error_main" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<p t-if="website_controller">The following error was raised in the website controller <code t-esc="website_controller"/></p>
|
||||
<p><strong>Error message:</strong> <pre t-esc="exception.message"/></p>
|
||||
<p>
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="exception.message"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -467,11 +470,15 @@
|
|||
<div id="error_qweb" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
The error occured while rendering the template <code t-esc="qweb_exception.template"/>
|
||||
<t t-if="qweb_exception.expression">and evaluating the following expression: <code t-esc="qweb_exception.expression"/></t>
|
||||
<strong>Error message:</strong>
|
||||
<pre t-esc="exception.qweb['message']"/>
|
||||
</p>
|
||||
<t t-if="qweb_exception.node">
|
||||
<pre id="exception_node" t-esc="qweb_exception.node.toxml()"/>
|
||||
<p>
|
||||
The error occured while rendering the template <code t-esc="qweb_exception.qweb.get('template')"/>
|
||||
<t t-if="'expression' in qweb_exception.qweb">and evaluating the following expression: <code t-esc="qweb_exception.qweb['expression']"/></t>
|
||||
</p>
|
||||
<t t-if="'node' in qweb_exception.qweb">
|
||||
<pre id="exception_node" t-esc="qweb_exception.qweb['node'].toxml()"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -634,8 +641,8 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
|||
<address>
|
||||
<div t-field="res_company.name">Name</div>
|
||||
<br />
|
||||
<div>&#x2706; <span t-field="res_company.phone"></span></div>
|
||||
<div class="fa fa-envelope" t-field="res_company.email"></div>
|
||||
<div><i class="fa fa-phone"/> <span t-field="res_company.phone"/></div>
|
||||
<div><i class="fa fa-envelope"/> <span t-field="res_company.email"/></div>
|
||||
</address>
|
||||
<a t-att-href="res_company.google_map_link()" target="_BLANK">
|
||||
<img class="thumbnail img-responsive" t-att-src="res_company.google_map_img()" />
|
||||
|
|
|
@ -13,148 +13,107 @@
|
|||
website.BlogTour = website.Tour.extend({
|
||||
id: 'blog',
|
||||
name: "Create a blog post",
|
||||
testPath: /\/blogpost\/[0-9]+\//,
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome-blog',
|
||||
title: "New Blog Post",
|
||||
content: "Let's go through the first steps to write beautiful blog posts.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip" }),
|
||||
backdrop: true,
|
||||
},
|
||||
{
|
||||
stepId: 'content-menu',
|
||||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
title: "Add Content",
|
||||
content: "Create new pages, blogs, menu items and products through the <em>'Content'</em> menu.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'new-post-entry',
|
||||
element: 'a[data-action=new_blog_post]',
|
||||
placement: 'left',
|
||||
title: "New Blog Post",
|
||||
content: "Select this menu item to create a new blog post.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: {
|
||||
modal: {
|
||||
stopOnClose: true,
|
||||
afterSubmit: 'post-page',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'continue-blog',
|
||||
element: '.modal button.btn-primary',
|
||||
placement: 'bottom',
|
||||
title: "Create Blog Post",
|
||||
content: "Click <em>Continue</em> to create the blog post.",
|
||||
trigger: {
|
||||
url: /blogpost\/[0-9]+\/.*/,
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'post-page',
|
||||
waitNot: '.modal',
|
||||
title: "Blog Post Created",
|
||||
content: "This is your new blog post. Let's edit it.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'post-title',
|
||||
element: 'h1[data-oe-expression="blog_post.name"]',
|
||||
placement: 'bottom',
|
||||
sampleText: 'New Blog',
|
||||
title: "Set a Title",
|
||||
content: "Click on this area and set a catchy title for your blog post.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-image-text',
|
||||
waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Layout Your Blog Post",
|
||||
content: "Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-image-text',
|
||||
snippet: 'image-text',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a Block",
|
||||
content: "Drag the <em>'Image-Text'</em> block and drop it in your page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'add-text-block',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Add Another Block",
|
||||
content: "Let's add another block to your post.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-text-block',
|
||||
snippet: 'text-block',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the <em>'Text Block'</em> block and drop it below the image block.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'activate-text-block-title',
|
||||
element: '#wrap [data-snippet-id=text-block] .text-center[data-snippet-id=colmd]',
|
||||
placement: 'top',
|
||||
title: "Edit an Area",
|
||||
content: "Select any area of the page to modify it. Click on this subtitle.",
|
||||
trigger: {
|
||||
id: 'snippet-activated',
|
||||
}
|
||||
},
|
||||
{
|
||||
stepId: 'remove-text-block-title',
|
||||
element: '.oe_active .oe_snippet_remove',
|
||||
placement: 'top',
|
||||
title: "Delete the Title",
|
||||
content: "From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the title.",
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
waitNot: '.oe_active .oe_snippet_remove:visible',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
title: "Save Your Blog",
|
||||
content: "Click the <em>Save</em> button to record changes on the page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'publish-post',
|
||||
waitFor: 'button[data-action=edit]:visible',
|
||||
element: 'button.btn-danger.js_publish_btn',
|
||||
placement: 'top',
|
||||
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',
|
||||
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
|
||||
title: "Thanks!",
|
||||
content: "This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu.",
|
||||
template: self.popover({ end: "Close Tutorial" }),
|
||||
backdrop: true,
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/^\/blogpost\/[0-9]+\//)) || this._super();
|
||||
},
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -7,25 +7,11 @@ testRunner.run(function blogTest (page, timeout) {
|
|||
waitFor(function clientReady () {
|
||||
return page.evaluate(function () {
|
||||
return window.$ && window.openerp && window.openerp.website
|
||||
&& window.openerp.website.TestConsole
|
||||
&& window.openerp.website.TestConsole.test('blog');
|
||||
&& window.openerp.website.Tour;
|
||||
});
|
||||
}, function executeTest () {
|
||||
page.evaluate(function () {
|
||||
window.openerp.website.TestConsole.test('blog').run(true);
|
||||
window.openerp.website.Tour.run_test('blog');
|
||||
});
|
||||
waitFor(function testExecuted () {
|
||||
var after = page.evaluate(function () {
|
||||
return window.$ && $('button[data-action=edit]').is(":visible") && {
|
||||
image: $('#wrap [data-snippet-id=image-text]').length,
|
||||
text: $('#wrap [data-snippet-id=text-block]').length,
|
||||
};
|
||||
});
|
||||
var result = after && (after.image === 1) && (after.text === 1);
|
||||
return result;
|
||||
}, function finish () {
|
||||
console.log('{ "event": "success" }');
|
||||
phantom.exit();
|
||||
}, 4*timeout/5);
|
||||
}, timeout/5);
|
||||
}, timeout);
|
||||
});
|
|
@ -4,13 +4,13 @@ from openerp.addons.web import http
|
|||
from openerp.addons.web.http import request
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
from urllib import quote_plus
|
||||
import werkzeug.urls
|
||||
|
||||
|
||||
class contactus(http.Controller):
|
||||
|
||||
def generate_google_map_url(self, street, city, city_zip, country_name):
|
||||
url = "http://maps.googleapis.com/maps/api/staticmap?center=%s&sensor=false&zoom=8&size=298x298" % quote_plus(
|
||||
url = "http://maps.googleapis.com/maps/api/staticmap?center=%s&sensor=false&zoom=8&size=298x298" % werkzeug.url_quote_plus(
|
||||
'%s, %s %s, %s' % (street, city, city_zip, country_name)
|
||||
)
|
||||
return url
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import werkzeug.urls
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.addons.web import http
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website_partner.controllers import main as website_partner
|
||||
|
||||
import werkzeug.urls
|
||||
|
||||
class WebsiteCrmPartnerAssign(http.Controller):
|
||||
_references_per_page = 20
|
||||
|
@ -31,13 +29,34 @@ class WebsiteCrmPartnerAssign(http.Controller):
|
|||
# format displayed membership lines domain
|
||||
base_partner_domain = [('is_company', '=', True), ('grade_id', '!=', False), ('website_published', '=', True)]
|
||||
partner_domain = list(base_partner_domain)
|
||||
if post_name:
|
||||
partner_domain += ['|', ('name', 'ilike', post_name), ('website_description', 'ilike', post_name)]
|
||||
if grade_id and grade_id != "all":
|
||||
partner_domain += [('grade_id', '=', int(grade_id))] # try/catch int
|
||||
|
||||
# group by country
|
||||
countries = partner_obj.read_group(
|
||||
request.cr, openerp.SUPERUSER_ID, partner_domain, ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
countries_partners = partner_obj.search(
|
||||
request.cr, openerp.SUPERUSER_ID, partner_domain,
|
||||
context=request.context, count=True)
|
||||
|
||||
if country_id:
|
||||
country = country_obj.browse(request.cr, request.uid, country_id, request.context)
|
||||
partner_domain += [('country_id', '=', country_id)]
|
||||
if post_name:
|
||||
partner_domain += ['|', ('name', 'ilike', post_name), ('website_description', 'ilike', post_name)]
|
||||
if not any(x['country_id'][0] == country_id for x in countries):
|
||||
countries.append({
|
||||
'country_id_count': 0,
|
||||
'country_id': (country_id, country.name)
|
||||
})
|
||||
countries.sort(key=lambda d: d['country_id'][1])
|
||||
|
||||
countries.insert(0, {
|
||||
'country_id_count': countries_partners,
|
||||
'country_id': (0, _("All Countries"))
|
||||
})
|
||||
|
||||
|
||||
# format pager
|
||||
partner_ids = partner_obj.search(
|
||||
|
@ -55,18 +74,6 @@ class WebsiteCrmPartnerAssign(http.Controller):
|
|||
context=request.context)
|
||||
google_map_partner_ids = ",".join([str(p['id']) for p in partners_data])
|
||||
|
||||
# group by country
|
||||
countries = partner_obj.read_group(
|
||||
request.cr, openerp.SUPERUSER_ID, base_partner_domain, ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
countries_partners = partner_obj.search(
|
||||
request.cr, openerp.SUPERUSER_ID, base_partner_domain,
|
||||
context=request.context, count=True)
|
||||
countries.insert(0, {
|
||||
'country_id_count': countries_partners,
|
||||
'country_id': (0, _("All Countries"))
|
||||
})
|
||||
|
||||
# group by grade
|
||||
grades = partner_obj.read_group(
|
||||
request.cr, openerp.SUPERUSER_ID, base_partner_domain, ["id", "grade_id"],
|
||||
|
|
|
@ -41,8 +41,9 @@
|
|||
<t t-foreach="countries" t-as="country_dict">
|
||||
<t t-if="country_dict['country_id']">
|
||||
<li t-att-class="country_dict['country_id'][0] == current_country_id and 'active' or ''">
|
||||
<a t-attf-href="#{ country_dict['country_id'][0] and '/partners/country/%s' % slug(country_dict['country_id']) or '/partners/' }">
|
||||
<t t-esc="country_dict['country_id'][1]"/> <small>(<t t-esc="country_dict['country_id_count']"/>)</small>
|
||||
<a t-attf-href="#{ country_dict['country_id'][0] and '/partners/country/%s' % slug(country_dict['country_id']) or '/partners/' }#{ search_path }">
|
||||
<span class="badge pull-right" t-esc="country_dict['country_id_count'] or ''"/>
|
||||
<t t-esc="country_dict['country_id'][1]"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
@ -55,9 +56,9 @@
|
|||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-left</t>
|
||||
</t>
|
||||
<form action="/partners/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<form action="" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="name_search"/>
|
||||
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search', '')"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select class="search-query col-md-2 mt4 form-control" name="grade" t-if="len(grades) > 1" onchange="submit()">
|
||||
|
@ -74,6 +75,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<t t-if="not partners_data">
|
||||
<p>No result found.</p>
|
||||
</t>
|
||||
<t t-foreach="partners_data" t-as="partner_data">
|
||||
<t t-if="internal_gid != partner_data['grade_id'][1]">
|
||||
<h3 class="text-center">
|
||||
|
|
|
@ -8,7 +8,6 @@ from openerp.addons.web.http import request
|
|||
from openerp.addons.website_partner.controllers import main as website_partner
|
||||
import werkzeug.urls
|
||||
|
||||
|
||||
class WebsiteCustomer(http.Controller):
|
||||
_references_per_page = 20
|
||||
|
||||
|
@ -22,6 +21,7 @@ class WebsiteCustomer(http.Controller):
|
|||
], type='http', auth="public", website=True, multilang=True)
|
||||
def customers(self, country_id=0, page=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
country_obj = request.registry['res.country']
|
||||
partner_obj = request.registry['res.partner']
|
||||
partner_name = post.get('search', '')
|
||||
|
||||
|
@ -34,15 +34,23 @@ class WebsiteCustomer(http.Controller):
|
|||
('website_description', 'ilike', post.get("search"))
|
||||
]
|
||||
|
||||
if country_id:
|
||||
domain += [('country_id', '=', country_id)]
|
||||
|
||||
# group by country, based on all customers (base domain)
|
||||
# group by country, based on customers found with the search(domain)
|
||||
countries = partner_obj.read_group(
|
||||
cr, openerp.SUPERUSER_ID, base_domain, ["id", "country_id"],
|
||||
cr, openerp.SUPERUSER_ID, domain, ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
country_count = partner_obj.search(
|
||||
cr, openerp.SUPERUSER_ID, base_domain, count=True, context=request.context)
|
||||
cr, openerp.SUPERUSER_ID, domain, count=True, context=request.context)
|
||||
|
||||
if country_id:
|
||||
domain += [('country_id', '=', country_id)]
|
||||
if not any(x['country_id'][0] == country_id for x in countries):
|
||||
country = country_obj.browse(cr, uid, country_id, context)
|
||||
countries.append({
|
||||
'country_id_count': 0,
|
||||
'country_id': (country_id, country.name)
|
||||
})
|
||||
countries.sort(key=lambda d: d['country_id'][1])
|
||||
|
||||
countries.insert(0, {
|
||||
'country_id_count': country_count,
|
||||
'country_id': (0, _("All Countries"))
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<t t-call="website.pager">
|
||||
<t t-set="classname" t-value="'pull-left'"/>
|
||||
</t>
|
||||
<form action="/customers/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<form action="" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query form-control"
|
||||
placeholder="Search" t-att-value="post.get('search', '')"/>
|
||||
|
@ -39,16 +39,18 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<t t-if="not partners_data">
|
||||
<p>No result found.</p>
|
||||
</t>
|
||||
<t t-foreach="partners_data" t-as="partner_data" class="media">
|
||||
<div class="col-md-2">
|
||||
<a t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/">
|
||||
<img class="img img-thumbnail" t-attf-src="data:image/png;base64,#{partner_data.get('image')}"/>
|
||||
<div class="media thumbnail">
|
||||
<a class="pull-left" t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/">
|
||||
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/>
|
||||
</a>
|
||||
</div><div class="col-md-10">
|
||||
<h4>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/" t-esc="partner_data.get('name')"/>
|
||||
</h4>
|
||||
<div t-raw="partner_data.get('website_short_description') or ''"/>
|
||||
<div t-raw="partner_data.get('website_short_description') or ''"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix mb8"/>
|
||||
</t>
|
||||
|
@ -73,13 +75,13 @@
|
|||
|
||||
<template id="opt_country_list" inherit_id="website_customer.index" inherit_option_id="website_customer.index" name="Filter on Countries">
|
||||
<xpath expr="//div[@id='ref_left_column']" position="inside">
|
||||
|
||||
<h3>References by Country</h3>
|
||||
<ul class="nav nav-pills nav-stacked mt16 mb32">
|
||||
<t t-foreach="countries" t-as="country_dict">
|
||||
<t t-if="country_dict['country_id']">
|
||||
<li t-att-class="country_dict['country_id'][0] == current_country_id and 'active' or ''">
|
||||
<a t-attf-href="/customers/#{ country_dict['country_id'][0] and 'country/%s/' % slug(country_dict['country_id']) or '' }">
|
||||
<span class="badge pull-right" t-esc="country_dict['country_id_count']"/>
|
||||
<a t-attf-href="/customers/#{ country_dict['country_id'][0] and 'country/%s/' % slug(country_dict['country_id']) or '' }#{ search_path }">
|
||||
<span class="badge pull-right" t-esc="country_dict['country_id_count'] or '0'"/>
|
||||
<t t-esc="country_dict['country_id'][1]"/>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
'version': '1.0',
|
||||
'description': """
|
||||
Online Events
|
||||
=============
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website', 'website_partner', 'website_mail', 'event'],
|
||||
|
|
|
@ -184,7 +184,6 @@ class website_event(http.Controller):
|
|||
'event': event,
|
||||
'main_object': event,
|
||||
'range': range,
|
||||
'main_object': event,
|
||||
}
|
||||
return request.website.render("website_event.event_description_full", values)
|
||||
|
||||
|
@ -202,25 +201,15 @@ class website_event(http.Controller):
|
|||
|
||||
@http.route('/event/add_event/', type='http', auth="user", multilang=True, methods=['POST'], website=True)
|
||||
def add_event(self, event_name="New Event", **kwargs):
|
||||
Event = request.registry.get('event.event')
|
||||
date_begin = datetime.today() + timedelta(days=(15)) # FIXME: better defaults
|
||||
return self._add_event(event_name, request.context, **kwargs)
|
||||
|
||||
def _add_event(self, event_name="New Event", context={}, **kwargs):
|
||||
Event = request.registry.get('event.event')
|
||||
date_begin = datetime.today() + timedelta(days=(14))
|
||||
vals = {
|
||||
'name': event_name,
|
||||
'date_begin': date_begin.strftime('%Y-%m-%d'),
|
||||
'date_end': (date_begin + timedelta(days=(1))).strftime('%Y-%m-%d'),
|
||||
}
|
||||
try:
|
||||
dummy, res_id = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'event_sale', 'product_product_event')
|
||||
vals['event_ticket_ids'] = [[0,0,{
|
||||
'name': _('Subscription'),
|
||||
'product_id': res_id,
|
||||
'deadline' : vals.get('date_begin'),
|
||||
'price': 0,
|
||||
}]]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
event_id = Event.create(request.cr, request.uid, vals, context=request.context)
|
||||
|
||||
event_id = Event.create(request.cr, request.uid, vals, context=context)
|
||||
return request.redirect("/event/%s/?enable_editor=1" % event_id)
|
||||
|
|
|
@ -25,6 +25,8 @@ from openerp import SUPERUSER_ID
|
|||
from openerp.tools.translate import _
|
||||
import re
|
||||
|
||||
from openerp.addons.website.models.website import slug
|
||||
|
||||
|
||||
class event(osv.osv):
|
||||
_name = 'event.event'
|
||||
|
@ -41,7 +43,7 @@ class event(osv.osv):
|
|||
for name,path in todo:
|
||||
name2 = name+' '+event.name
|
||||
newpath = web.new_page(cr, uid, name2, path, ispage=False, context=context)
|
||||
url = "/event/"+str(event.id)+"/page/" + newpath
|
||||
url = "/event/"+slug(event)+"/page/" + newpath
|
||||
result.append((name, url))
|
||||
return result
|
||||
|
||||
|
@ -56,7 +58,7 @@ class event(osv.osv):
|
|||
'name': event.name
|
||||
}, context=context)
|
||||
tocreate = self._get_new_menu_pages(cr, uid, event, context)
|
||||
tocreate.append((_('Register'), '/event/%s/register' % str(event.id)))
|
||||
tocreate.append((_('Register'), '/event/%s/register' % slug(event)))
|
||||
sequence = 0
|
||||
for name,url in tocreate:
|
||||
menuobj.create(cr, uid, {
|
||||
|
@ -77,14 +79,13 @@ class event(osv.osv):
|
|||
|
||||
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = dict.fromkeys(ids, '')
|
||||
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
|
||||
for event in self.browse(cr, uid, ids, context=context):
|
||||
res[event.id] = "%s/event/%s/" % (base_url, event.id)
|
||||
res[event.id] = "/event/" + slug(event)
|
||||
return res
|
||||
|
||||
def _default_hashtag(self, cr, uid, context={}):
|
||||
name = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name
|
||||
return re.sub("[- \\.\\(\\)]+", "", name).lower()
|
||||
return re.sub("[- \\.\\(\\)\\@\\#\\&]+", "", name).lower()
|
||||
|
||||
_columns = {
|
||||
'twitter_hashtag': fields.char('Twitter Hashtag'),
|
||||
|
@ -101,8 +102,6 @@ class event(osv.osv):
|
|||
'website_url': fields.function(_website_url, string="Website url", type="char"),
|
||||
'show_menu': fields.function(_get_show_menu, fnct_inv=_set_show_menu, type='boolean', string='Dedicated Menu'),
|
||||
'menu_id': fields.many2one('website.menu', 'Event Menu'),
|
||||
'country_id': fields.related('address_id', 'country_id',
|
||||
type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
|
||||
}
|
||||
_defaults = {
|
||||
'show_menu': False,
|
||||
|
@ -110,12 +109,14 @@ class event(osv.osv):
|
|||
}
|
||||
|
||||
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
if partner.address_id:
|
||||
event = self.browse(cr, uid, ids[0], context=context)
|
||||
if event.address_id:
|
||||
return self.browse(cr, SUPERUSER_ID, ids[0], context=context).address_id.google_map_img()
|
||||
return None
|
||||
|
||||
def google_map_link(self, cr, uid, ids, zoom=8, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
if partner.address_id:
|
||||
event = self.browse(cr, uid, ids[0], context=context)
|
||||
if event.address_id:
|
||||
return self.browse(cr, SUPERUSER_ID, ids[0], context=context).address_id.google_map_link()
|
||||
return None
|
||||
|
||||
|
|
|
@ -13,161 +13,105 @@
|
|||
website.EventTour = website.Tour.extend({
|
||||
id: 'event',
|
||||
name: "Create an event",
|
||||
testPath: /\/event\/[0-9]+\/register/,
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome-event',
|
||||
title: "Create an Event",
|
||||
content: "Let's go through the first steps to publish a new event.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
backdrop: true,
|
||||
},
|
||||
{
|
||||
stepId: 'content-menu',
|
||||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
title: "Add Content",
|
||||
content: "The <em>Content</em> menu allows you to create new pages, events, menus, etc.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'new-post-entry',
|
||||
element: 'a[data-action=new_event]',
|
||||
placement: 'left',
|
||||
title: "New Event",
|
||||
content: "Click here to create a new event.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: {
|
||||
modal: {
|
||||
stopOnClose: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'choose-name',
|
||||
element: '.modal input',
|
||||
element: '.modal:contains("New Event") input[type=text]',
|
||||
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",
|
||||
trigger: 'keyup',
|
||||
},
|
||||
{
|
||||
stepId: 'continue-name',
|
||||
waitNot: '.modal input[type=text]:not([value!=""])',
|
||||
element: '.modal button.btn-primary',
|
||||
placement: 'right',
|
||||
title: "Create Event",
|
||||
content: "Click <em>Continue</em> to create the event.",
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'event-page',
|
||||
waitFor: '#website-top-navbar button[data-action="save"]:visible',
|
||||
title: "New Event Created",
|
||||
content: "This is your new event page. We will edit the event presentation page.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-banner',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Layout your event",
|
||||
content: "Insert blocks to layout the body of your event.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-banner',
|
||||
snippet: 'image-text',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Image-Text' block and drop it in your page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'add-text-block',
|
||||
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Layout your event",
|
||||
content: "Insert another block to your event.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-text-block',
|
||||
snippet: 'text-block',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Text Block' in your event page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'add-three-columns',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Layout your event",
|
||||
content: "Insert a last block to your event.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-three-columns',
|
||||
snippet: 'three-columns',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Three Columns' block at the bottom.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your event is updated.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'publish-event',
|
||||
waitFor: 'button[data-action=edit]:visible',
|
||||
element: 'button.btn-danger.js_publish_btn',
|
||||
placement: 'top',
|
||||
title: "Publish your event",
|
||||
content: "Click to publish your event.",
|
||||
trigger: 'ajax'
|
||||
},
|
||||
{
|
||||
stepId: 'customize-event',
|
||||
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
|
||||
element: '.js_publish_management button[data-toggle="dropdown"]',
|
||||
placement: 'left',
|
||||
title: "Customize your event",
|
||||
content: "Click here to customize your event further.",
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'edit-event-backend',
|
||||
element: '.js_publish_management ul>li>a:last',
|
||||
placement: 'left',
|
||||
title: "Customize your event",
|
||||
content: "Click here to edit your event in the backend.",
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'end-tutorial',
|
||||
title: "Thanks!",
|
||||
content: "This tutorial is finished. Congratulations on creating your first event.",
|
||||
template: self.popover({ end: "Close Tutorial" }),
|
||||
backdrop: true,
|
||||
element: '.js_publish_management ul>li>a:last:visible',
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/^\/event\/[0-9]+\/register/)) || this._super();
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -7,26 +7,11 @@ testRunner.run(function eventTest (page, timeout) {
|
|||
waitFor(function clientReady () {
|
||||
return page.evaluate(function () {
|
||||
return window.$ && window.openerp && window.openerp.website
|
||||
&& window.openerp.website.TestConsole
|
||||
&& window.openerp.website.TestConsole.test('event');
|
||||
&& window.openerp.website.Tour;
|
||||
});
|
||||
}, function executeTest () {
|
||||
page.evaluate(function () {
|
||||
window.openerp.website.TestConsole.test('event').run(true);
|
||||
window.openerp.website.Tour.run_test('event');
|
||||
});
|
||||
waitFor(function testExecuted () {
|
||||
var after = page.evaluate(function () {
|
||||
return window.$ && $('button[data-action=edit]').is(":visible") && {
|
||||
banner: $('#wrap [data-snippet-id=image-text]').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);
|
||||
}, timeout);
|
||||
});
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
<div class="row mt8">
|
||||
<div class="col-sm-5">
|
||||
<ol class="breadcrumb mb0">
|
||||
<li class="active">
|
||||
Our Events
|
||||
<li>
|
||||
<a href="/event">Our Events</a>
|
||||
</li>
|
||||
<li t-if="current_date"><t t-esc="current_date"/></li>
|
||||
<li t-if="current_type"><t t-esc="current_type.name"/></li>
|
||||
<li t-if="current_country"><t t-esc="current_country.name"/></li>
|
||||
</ol>
|
||||
</div><div class="col-sm-7">
|
||||
<t t-call="website.pager" >
|
||||
|
@ -35,12 +38,6 @@
|
|||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-center text-muted">
|
||||
<t t-esc="current_date or ''"/><span t-if="current_type"><t t-if="current_date">,</t>
|
||||
<t t-esc="current_type.name"/></span><span t-if="current_country"><t t-if="current_type or current_date">,</t>
|
||||
<t t-esc="current_country.name"/>
|
||||
</span>
|
||||
</h3>
|
||||
<div class="row mt32 mb32">
|
||||
<div class="col-md-9" id="middle_column">
|
||||
<t t-if="not event_ids">
|
||||
|
@ -75,7 +72,7 @@
|
|||
"widget": "contact",
|
||||
"fields": ["city"]
|
||||
}'/>
|
||||
<div class="text-muted">
|
||||
<div class="text-muted" t-if="event.type">
|
||||
<i class="fa fa-tag"></i> <span t-field="event.type"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,7 +124,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="event_left_column" inherit_option_id="website_event.index" name="Filters">
|
||||
<template id="event_left_column" inherit_option_id="website_event.index" inherit_id="website_event.index" name="Filters">
|
||||
<xpath expr="//div[@id='middle_column']" position="attributes">
|
||||
<attribute name="class">col-md-6</attribute>
|
||||
</xpath>
|
||||
|
@ -136,7 +133,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-attf-href="/event/?{{ keep_query('country', 'type', date=date[0] }}"><t t-esc="date[1]"/>
|
||||
<a t-attf-href="/event/?{{ keep_query('country', 'type', date=date[0]) }}"><t t-esc="date[1]"/>
|
||||
<span t-if="date[3]" class="badge pull-right"><t t-esc="date[3]"/></span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -151,7 +148,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-attf-href="/event/?{{ keep_query('country', 'date', type=type[0] }}"><t t-esc="type[1]"/>
|
||||
<a t-attf-href="/event/?{{ keep_query('country', 'date', type=type[0]) }}"><t t-esc="type[1]"/>
|
||||
<span class="badge pull-right"><t t-esc="type_count"/></span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -159,12 +156,13 @@
|
|||
</ul>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="event_location" inherit_option_id="website_event.event_left_column" name="Filter by Country">
|
||||
<xpath expr="//div[@id='left_column']" position="inside">
|
||||
<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-attf-href="/event/?{{ keep_query('type', 'data', country=country_id[0] }}"><t t-esc="country_id[1]"/>
|
||||
<a t-attf-href="/event/?{{ keep_query('type', 'data', country=country_id[0]) }}"><t t-esc="country_id[1]"/>
|
||||
<span class="badge pull-right"><t t-esc="country_id_count"/></span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -309,8 +307,8 @@
|
|||
<h4>When</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<i class="fa fa-clock-o"></i> <span t-field="event.date_begin"> </span><br/>
|
||||
<i class="fa fa-clock-o"></i> <span t-field="event.date_end"> </span>
|
||||
<i class="fa fa-clock-o"></i> from <span t-field="event.date_begin"> </span><br/>
|
||||
<i class="fa fa-clock-o"></i> to <span t-field="event.date_end"> </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -337,7 +335,7 @@
|
|||
and join the conversation.
|
||||
</p>
|
||||
<p><strong>Use this tag:
|
||||
<a t-att-href="'http://twitter.com/search?q=#'+event.twitter_hashtag" class="label label-primary">#<span t-field="event.twitter_hashtag"/></a>
|
||||
<a t-att-href="'http://twitter.com/search?q=%23'+event.twitter_hashtag" target="_blank" class="label label-primary">#<span t-field="event.twitter_hashtag"/></a>
|
||||
</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<field name="inherit_id" ref="event.view_event_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- add state field in header -->
|
||||
<xpath expr="//sheet/div" position="before">
|
||||
<xpath expr="//div[@class='oe_right oe_button_box']" position="before">
|
||||
<field name="website_url" invisible="1"/>
|
||||
<field name="website_published" class="pull-right" widget="website_button"/>
|
||||
</xpath>
|
||||
|
|
|
@ -23,6 +23,7 @@ from openerp import SUPERUSER_ID
|
|||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website_event.controllers.main import website_event
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class website_event(website_event):
|
||||
|
@ -79,3 +80,21 @@ class website_event(website_event):
|
|||
if not _values:
|
||||
return request.redirect("/event/%s/" % event_id)
|
||||
return request.redirect("/shop/checkout")
|
||||
|
||||
def _add_event(self, event_name="New Event", context={}, **kwargs):
|
||||
try:
|
||||
print kwargs
|
||||
dummy, res_id = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'event_sale', 'product_product_event')
|
||||
context['default_event_ticket_ids'] = [[0,0,{
|
||||
'name': _('Subscription'),
|
||||
'product_id': res_id,
|
||||
'deadline' : False,
|
||||
'seats_max': 1000,
|
||||
'price': 0,
|
||||
}]]
|
||||
except ValueError:
|
||||
pass
|
||||
return super(website_event, self)._add_event(event_name, context, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -6,9 +6,16 @@
|
|||
'summary': 'Sponsors, Tracks, Agenda, Event News',
|
||||
'version': '1.0',
|
||||
'description': """
|
||||
Online Events
|
||||
=============
|
||||
Online Advanced Events
|
||||
======================
|
||||
|
||||
Adds support for:
|
||||
- sponsors
|
||||
- dedicated menu per event
|
||||
- news per event
|
||||
- tracks
|
||||
- agenda
|
||||
- call for proposals
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website_event', 'website_blog'],
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import openerp
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
|
@ -31,10 +32,12 @@ controllers = controllers()
|
|||
class website_event(http.Controller):
|
||||
@http.route(['/event/<model("event.event"):event>/track/<model("event.track"):track>'], type='http', auth="public", website=True, multilang=True)
|
||||
def event_track_view(self, event, track, **post):
|
||||
# TODO: not implemented
|
||||
track_obj = request.registry.get('event.track')
|
||||
track = track_obj.browse(request.cr, openerp.SUPERUSER_ID, track.id, context=request.context)
|
||||
values = { 'track': track, 'event': track.event_id, 'main_object': track }
|
||||
return request.website.render("website_event_track.track_view", values)
|
||||
|
||||
# TODO: not implemented
|
||||
@http.route(['/event/<model("event.event"):event>/agenda/'], type='http', auth="public", website=True, multilang=True)
|
||||
def event_agenda(self, event, tag=None, **post):
|
||||
values = {
|
||||
|
@ -49,7 +52,6 @@ class website_event(http.Controller):
|
|||
], type='http', auth="public", website=True, multilang=True)
|
||||
def event_tracks(self, event, tag=None, **post):
|
||||
searches = {}
|
||||
|
||||
if tag:
|
||||
searches.update(tag=tag.id)
|
||||
track_obj = request.registry.get('event.track')
|
||||
|
@ -72,8 +74,6 @@ class website_event(http.Controller):
|
|||
}
|
||||
return request.website.render("website_event_track.tracks", values)
|
||||
|
||||
|
||||
|
||||
@http.route(['/event/<model("event.event"):event>/track_proposal/'], type='http', auth="public", website=True, multilang=True)
|
||||
def event_track_proposal(self, event, **post):
|
||||
values = { 'event': event }
|
||||
|
@ -109,7 +109,7 @@ class website_event(http.Controller):
|
|||
</section>''' % (e(post['track_name']),
|
||||
e(post['description']), e(post['biography']))
|
||||
|
||||
track_id = tobj.create(cr, uid, {
|
||||
track_id = tobj.create(cr, openerp.SUPERUSER_ID, {
|
||||
'name': post['track_name'],
|
||||
'event_id': event.id,
|
||||
'tag_ids': [(6, 0, tags)],
|
||||
|
@ -117,11 +117,11 @@ class website_event(http.Controller):
|
|||
'description': track_description
|
||||
}, context=context)
|
||||
|
||||
tobj.message_post(cr, uid, [track_id], body="""Proposed By: %s<br/>
|
||||
tobj.message_post(cr, openerp.SUPERUSER_ID, [track_id], body="""Proposed By: %s<br/>
|
||||
Mail: <a href="mailto:%s">%s</a><br/>
|
||||
Phone: %s""" % (e(post['partner_name']), e(post['email_from']),
|
||||
e(post['email_from']), e(post['phone'])), context=context)
|
||||
|
||||
track = tobj.browse(cr, uid, track_id, context=context)
|
||||
values = {'track': track}
|
||||
values = {'track': track, 'event':event}
|
||||
return request.website.render("website_event_track.event_track_proposal_success", values)
|
||||
|
|
|
@ -59,12 +59,14 @@
|
|||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="sponsor_type_id" ref="event_sponsor_type1"/>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="url">http://openerp.com</field>
|
||||
</record>
|
||||
|
||||
<record id="event_sponsor_1" model="event.sponsor">
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="sponsor_type_id" ref="event_sponsor_type2"/>
|
||||
<field name="partner_id" ref="base.res_partner_12"/>
|
||||
<field name="url">http://openerp.com</field>
|
||||
</record>
|
||||
|
||||
<record id="event_sponsor_2" model="event.sponsor">
|
||||
|
@ -77,6 +79,7 @@
|
|||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="sponsor_type_id" ref="event_sponsor_type3"/>
|
||||
<field name="partner_id" ref="base.res_partner_14"/>
|
||||
<field name="url">http://openerp.com</field>
|
||||
</record>
|
||||
|
||||
<!-- Tracks -->
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.website.models.website import slug
|
||||
|
||||
class event_track_tag(osv.osv):
|
||||
_name = "event.track.tag"
|
||||
|
@ -63,22 +64,20 @@ class event_track(osv.osv):
|
|||
|
||||
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = dict.fromkeys(ids, '')
|
||||
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
|
||||
for track in self.browse(cr, uid, ids, context=context):
|
||||
res[track.id] = "%s/event/%d/track/%d" % (base_url, track.event_id.id, track.id)
|
||||
res[track.id] = "/event/%s/track/%s" % (slug(track.event_id), slug(track))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Track Title', required=True),
|
||||
'name': fields.char('Track Title', required=True, translate=True),
|
||||
'user_id': fields.many2one('res.users', 'Responsible'),
|
||||
'speaker_ids': fields.many2many('res.partner', string='Speakers'),
|
||||
'tag_ids': fields.many2many('event.track.tag', string='Tags'),
|
||||
'stage_id': fields.many2one('event.track.stage'),
|
||||
'description': fields.html('Track Description'),
|
||||
'stage_id': fields.many2one('event.track.stage', 'Stage'),
|
||||
'description': fields.html('Track Description', translate=True),
|
||||
'date': fields.datetime('Track Date'),
|
||||
'duration': fields.integer('Duration'),
|
||||
'location_id': fields.many2one('event.track.location', 'Location'),
|
||||
'show_attachments': fields.boolean('Show Documents'),
|
||||
'event_id': fields.many2one('event.event', 'Event', required=True),
|
||||
'color': fields.integer('Color Index'),
|
||||
'priority': fields.selection([('3','Low'),('2','Medium (*)'),('1','High (**)'),('0','Highest (***)')], 'Priority', required=True),
|
||||
|
@ -97,7 +96,6 @@ class event_track(osv.osv):
|
|||
_defaults = {
|
||||
'user_id': lambda self, cr, uid, ctx: uid,
|
||||
'website_published': lambda self, cr, uid, ctx: False,
|
||||
'show_attachments': lambda self, cr, uid, ctx: True,
|
||||
'duration': lambda *args: 60,
|
||||
'stage_id': _default_stage_id,
|
||||
'priority': '2'
|
||||
|
@ -116,7 +114,6 @@ class event_track(osv.osv):
|
|||
#
|
||||
class event_event(osv.osv):
|
||||
_inherit = "event.event"
|
||||
|
||||
def _get_tracks_tag_ids(self, cr, uid, ids, field_names, arg=None, context=None):
|
||||
res = dict.fromkeys(ids, [])
|
||||
for event in self.browse(cr, uid, ids, context=context):
|
||||
|
@ -144,12 +141,12 @@ class event_event(osv.osv):
|
|||
context = context or {}
|
||||
result = super(event_event, self)._get_new_menu_pages(cr, uid, event, context=context)
|
||||
if event.show_tracks:
|
||||
result.append( (_('Talks'), '/event/%s/track/' % event.id))
|
||||
result.append( (_('Agenda'), '/event/%s/agenda/' % event.id))
|
||||
result.append( (_('Talks'), '/event/%s/track/' % slug(event)))
|
||||
result.append( (_('Agenda'), '/event/%s/agenda/' % slug(event)))
|
||||
if event.blog_id:
|
||||
result.append( (_('News'), '/blogpost/'+str(event.blog_ig.id)))
|
||||
result.append( (_('News'), '/blogpost/'+slug(event.blog_ig)))
|
||||
if event.show_track_proposal:
|
||||
result.append( (_('Talk Proposals'), '/event/%s/track_proposal/' % event.id))
|
||||
result.append( (_('Talk Proposals'), '/event/%s/track_proposal/' % slug(event)))
|
||||
return result
|
||||
|
||||
#
|
||||
|
@ -171,6 +168,7 @@ class event_sponsors(osv.osv):
|
|||
'event_id': fields.many2one('event.event', 'Event', required=True),
|
||||
'sponsor_type_id': fields.many2one('event.sponsor.type', 'Sponsoring Type', required=True),
|
||||
'partner_id': fields.many2one('res.partner', 'Sponsor/Customer', required=True),
|
||||
'url': fields.text('Sponsor Website'),
|
||||
'sequence': fields.related('sponsor_type_id', 'sequence', string='Sequence', store=True),
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,9 @@
|
|||
<field name="inherit_id" ref="event.view_event_form"/>
|
||||
<field name="model">event.event</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@class='oe_right oe_button_box']" position="inside">
|
||||
<button name="%(website_event_track.act_event_list_tracks)d" type="action" string="Tracks"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='oe_title']" position="inside">
|
||||
<label for="tag_ids" class="oe_edit_only"/>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
|
@ -173,9 +176,6 @@
|
|||
</xpath>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Tracks">
|
||||
<div class="oe_right oe_button_box">
|
||||
<button name="%(website_event_track.act_event_list_tracks)d" type="action" string="Tracks"/>
|
||||
</div>
|
||||
<group col="2" class="oe_title">
|
||||
<field name="allowed_track_tag_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
|
@ -185,10 +185,12 @@
|
|||
<field name="sponsor_ids" context="{'default_event_id': active_id}">
|
||||
<tree editable="bottom">
|
||||
<field name="partner_id"/>
|
||||
<field name="url"/>
|
||||
<field name="sponsor_type_id"/>
|
||||
</tree>
|
||||
<form string="Sponsoring">
|
||||
<field name="partner_id"/>
|
||||
<field name="url"/>
|
||||
<field name="sponsor_type_id"/>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -256,7 +258,7 @@
|
|||
<field name="model">event.track</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="type" date_start="date" date_delay="duration" string="Event Tracks">
|
||||
<calendar color="color" date_start="date" date_delay="duration" string="Event Tracks">
|
||||
<field name="name"/>
|
||||
<field name="event_id"/>
|
||||
</calendar>
|
||||
|
@ -319,6 +321,8 @@
|
|||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="description" class="oe_edit_only"/>
|
||||
<field name="description"/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
</section>
|
||||
<div class="row">
|
||||
<div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center" t-foreach="event.sponsor_ids" t-as="sponsor">
|
||||
<t t-if="sponsor.has_access_to_partner()">
|
||||
<a t-attf-href="/partners/#{ slug([sponsor.partner_id.id, sponsor.partner_id.name]) }" style="position: relative; display: inline-block;">
|
||||
<t t-if="sponsor.url">
|
||||
<a t-att-href="sponsor.url" style="position: relative; display: inline-block;">
|
||||
<span t-field="sponsor.partner_id.image"
|
||||
t-field-options='{"widget": "image", "class": "shadow"}'/>
|
||||
<div class="ribbon-wrapper">
|
||||
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
</a>
|
||||
</t>
|
||||
<t t-if="not sponsor.has_access_to_partner()">
|
||||
<t t-if="not sponsor.url">
|
||||
<span style="position: relative; display: inline-block;">
|
||||
<span t-field="sponsor.partner_id.image"
|
||||
t-field-options='{"widget": "image", "class": "shadow"}'/>
|
||||
|
@ -210,7 +210,7 @@
|
|||
<div t-foreach="track.speaker_ids" t-as="speaker" class="well mt32">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span t-field="speaker.image"
|
||||
<span t-field="track.image"
|
||||
t-field-options='{"widget": "image", "class": "img-circle"}'/>
|
||||
</div><div class="col-sm-10">
|
||||
<h4 t-field="speaker.name" class="mb4"/>
|
||||
|
@ -418,14 +418,15 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="event_track_proposal_success">
|
||||
<t t-call="website_event.event_details">
|
||||
<div class="col-md-8">
|
||||
Thanks
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
<p>
|
||||
Thank you for your proposal.
|
||||
</p><p>
|
||||
We will evaluate your proposition and get back to you shortly.
|
||||
</p>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -9,7 +9,7 @@ Website for browsing Associations, Groups and Memberships
|
|||
=========================================================
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website_partner', 'website_google_map', 'association'],
|
||||
'depends': ['website_partner', 'website_google_map', 'association', 'website_sale'],
|
||||
'data': [
|
||||
'views/website_membership.xml',
|
||||
'security/ir.model.access.csv',
|
||||
|
|
|
@ -32,12 +32,14 @@ class WebsiteMembership(http.Controller):
|
|||
def members(self, membership_id=None, country_name=None, country_id=0, page=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
product_obj = request.registry['product.product']
|
||||
country_obj = request.registry['res.country']
|
||||
membership_line_obj = request.registry['membership.membership_line']
|
||||
partner_obj = request.registry['res.partner']
|
||||
post_name = post.get('name', '')
|
||||
current_country = None
|
||||
|
||||
# base domain for groupby / searches
|
||||
base_line_domain = [('state', 'in', ['free', 'paid'])]
|
||||
base_line_domain = [("partner.website_published", "=", True),('state', 'in', ['free', 'paid'])]
|
||||
if membership_id:
|
||||
base_line_domain.append(('membership_id', '=', membership_id))
|
||||
membership = product_obj.browse(cr, uid, membership_id, context=context)
|
||||
|
@ -53,16 +55,24 @@ class WebsiteMembership(http.Controller):
|
|||
cr, uid, [('member_lines', 'in', membership_line_ids), ("website_published", "=", True)], ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
countries_total = sum(country_dict['country_id_count'] for country_dict in countries)
|
||||
|
||||
line_domain = list(base_line_domain)
|
||||
if country_id:
|
||||
line_domain.append(('partner.country_id', '=', country_id))
|
||||
current_country = country_obj.read(cr, uid, country_id, ['id', 'name'], context)
|
||||
if not any(x['country_id'][0] == country_id for x in countries):
|
||||
countries.append({
|
||||
'country_id_count': 0,
|
||||
'country_id': (country_id, current_country["name"])
|
||||
})
|
||||
countries.sort(key=lambda d: d['country_id'][1])
|
||||
|
||||
countries.insert(0, {
|
||||
'country_id_count': countries_total,
|
||||
'country_id': (0, _("All Countries"))
|
||||
})
|
||||
|
||||
# displayed membership lines
|
||||
line_domain = list(base_line_domain)
|
||||
if country_id:
|
||||
line_domain.append(('partner.country_id', '=', country_id))
|
||||
|
||||
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
|
||||
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
|
||||
membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
|
||||
|
@ -86,6 +96,8 @@ class WebsiteMembership(http.Controller):
|
|||
'memberships': memberships,
|
||||
'membership': membership,
|
||||
'countries': countries,
|
||||
'current_country': current_country and [current_country['id'], current_country['name']] or None,
|
||||
'current_country_id': current_country and current_country['id'] or 0,
|
||||
'google_map_partner_ids': google_map_partner_ids,
|
||||
'pager': pager,
|
||||
'post': post,
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<li t-att-class="'' if membership else 'active'"><a href="/members/">All</a></li>
|
||||
<t t-foreach="memberships" t-as="membership_id">
|
||||
<li t-att-class="membership and membership_id.id == membership.id and 'active' or ''">
|
||||
<a t-attf-href="/members/association/#{ membership_id.id }"><t t-esc="membership_id.name"/></a>
|
||||
<a t-attf-href="/members/association/#{ membership_id.id }/#{current_country and 'country/%s/' % slug(current_country) or ''}#{ search }"><t t-esc="membership_id.name"/></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
|
@ -44,7 +44,7 @@
|
|||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-left</t>
|
||||
</t>
|
||||
<form action="/members/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<form action="" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="name" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="post.get('name', '')"/>
|
||||
</div>
|
||||
|
@ -60,7 +60,7 @@
|
|||
<h3 class="text-center"><span t-field="membership_line_id.membership_id"/></h3>
|
||||
</t>
|
||||
<t t-set="partner_data" t-value="partners_data[membership_line_id.partner.id]"/>
|
||||
<div class="media">
|
||||
<div class="media thumbnail">
|
||||
<a class="pull-left" t-attf-href="/members/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/">
|
||||
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/>
|
||||
</a>
|
||||
|
@ -86,9 +86,9 @@
|
|||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li class="nav-header"><h3>Location</h3></li>
|
||||
<t t-foreach="countries">
|
||||
<li t-if="country_id" t-att-class="post.get('country_id', '0') == str(country_id and country_id[0]) and 'active' or ''">
|
||||
<li t-if="country_id" t-att-class="country_id[0] == current_country_id and 'active' or ''">
|
||||
<a t-attf-href="/members/#{ membership and 'association/%s/' % membership.id or '' }#{ country_id[0] and 'country/%s/' % slug(country_id) or '' }#{ search }"><t t-esc="country_id[1]"/>
|
||||
<span class="badge pull-right"><t t-esc="country_id_count"/></span>
|
||||
<span class="badge pull-right"><t t-esc="country_id_count or '0'"/></span>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
|
|
@ -112,10 +112,10 @@ class Ecommerce(http.Controller):
|
|||
|
||||
_order = 'website_published desc, website_sequence desc'
|
||||
|
||||
def get_characteristic_ids(self):
|
||||
characteristics_obj = request.registry['product.characteristic']
|
||||
characteristics_ids = characteristics_obj.search(request.cr, request.uid, [], context=request.context)
|
||||
return characteristics_obj.browse(request.cr, request.uid, characteristics_ids, context=request.context)
|
||||
def get_attribute_ids(self):
|
||||
attributes_obj = request.registry['product.attribute']
|
||||
attributes_ids = attributes_obj.search(request.cr, request.uid, [], context=request.context)
|
||||
return attributes_obj.browse(request.cr, request.uid, attributes_ids, context=request.context)
|
||||
|
||||
def get_pricelist(self):
|
||||
""" Shortcut to get the pricelist from the website model """
|
||||
|
@ -132,13 +132,13 @@ class Ecommerce(http.Controller):
|
|||
product_ids = [id for id in product_ids if id in product_obj.search(request.cr, request.uid, [("id", 'in', product_ids)], context=request.context)]
|
||||
return product_obj.browse(request.cr, request.uid, product_ids, context=request.context)
|
||||
|
||||
def has_search_filter(self, characteristic_id, value_id=None):
|
||||
def has_search_filter(self, attribute_id, value_id=None):
|
||||
if request.httprequest.args.get('filters'):
|
||||
filters = simplejson.loads(request.httprequest.args['filters'])
|
||||
else:
|
||||
filters = []
|
||||
for key_val in filters:
|
||||
if key_val[0] == characteristic_id and (not value_id or value_id in key_val[1:]):
|
||||
if key_val[0] == attribute_id and (not value_id or value_id in key_val[1:]):
|
||||
return key_val
|
||||
return False
|
||||
|
||||
|
@ -174,11 +174,11 @@ class Ecommerce(http.Controller):
|
|||
post.get("category") and ("&category=%s" % post.get("category")) or ""
|
||||
))
|
||||
|
||||
def characteristics_to_ids(self, characteristics):
|
||||
obj = request.registry.get('product.characteristic.product')
|
||||
def attributes_to_ids(self, attributes):
|
||||
obj = request.registry.get('product.attribute.line')
|
||||
domain = []
|
||||
for key_val in characteristics:
|
||||
domain.append(("characteristic_id", "=", key_val[0]))
|
||||
for key_val in attributes:
|
||||
domain.append(("attribute_id", "=", key_val[0]))
|
||||
if isinstance(key_val[1], list):
|
||||
domain.append(("value", ">=", key_val[1][0]))
|
||||
domain.append(("value", "<=", key_val[1][1]))
|
||||
|
@ -212,7 +212,7 @@ class Ecommerce(http.Controller):
|
|||
if filters:
|
||||
filters = simplejson.loads(filters)
|
||||
if filters:
|
||||
ids = self.characteristics_to_ids(filters)
|
||||
ids = self.attributes_to_ids(filters)
|
||||
domain.append(('id', 'in', ids or [0]))
|
||||
|
||||
product_count = product_obj.search_count(cr, uid, domain, context=context)
|
||||
|
@ -225,7 +225,7 @@ class Ecommerce(http.Controller):
|
|||
|
||||
styles = []
|
||||
try:
|
||||
style_obj = request.registry.get('website.product.style')
|
||||
style_obj = request.registry.get('product.style')
|
||||
style_ids = style_obj.search(request.cr, request.uid, [], context=request.context)
|
||||
styles = style_obj.browse(request.cr, request.uid, style_ids, context=request.context)
|
||||
except:
|
||||
|
@ -320,7 +320,7 @@ class Ecommerce(http.Controller):
|
|||
product_ids = []
|
||||
if order:
|
||||
for line in order.order_line:
|
||||
suggested_ids += [p.id for p in line.product_id and line.product_id.suggested_product_ids or []]
|
||||
suggested_ids += [p.id for p in line.product_id and line.product_id.accessory_product_ids or []]
|
||||
product_ids.append(line.product_id.id)
|
||||
suggested_ids = list(set(suggested_ids) - set(product_ids))
|
||||
if suggested_ids:
|
||||
|
@ -769,7 +769,7 @@ class Ecommerce(http.Controller):
|
|||
active = True
|
||||
break
|
||||
|
||||
style = request.registry.get('website.product.style').browse(request.cr, request.uid, style_id, context=request.context)
|
||||
style = request.registry.get('product.style').browse(request.cr, request.uid, style_id, context=request.context)
|
||||
|
||||
if remove:
|
||||
product.write({'website_style_ids': [(3, rid) for rid in remove]})
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="product.group_product_characteristics" model="res.groups">
|
||||
<field name="name">Product Characteristic (not supported)</field>
|
||||
<record id="product.group_product_attributes" model="res.groups">
|
||||
<field name="name">Product attribute (not supported)</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
|
@ -23,11 +23,11 @@
|
|||
<field name="state">open</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale.image_promo" model="website.product.style">
|
||||
<record id="website_sale.image_promo" model="product.style">
|
||||
<field name="name">Sale Ribbon</field>
|
||||
<field name="html_class">oe_ribbon_promo</field>
|
||||
</record>
|
||||
<record id="website_sale.image_full" model="website.product.style">
|
||||
<record id="website_sale.image_full" model="product.style">
|
||||
<field name="name">Image Full</field>
|
||||
<field name="html_class">oe_image_full</field>
|
||||
</record>
|
||||
|
|
|
@ -536,7 +536,7 @@ Weight: 1.1 ounces</field>
|
|||
</div>
|
||||
</section>
|
||||
</field>
|
||||
<field name="suggested_product_ids" eval="[(6, 0, [ref('product.product_template_7')])]"/>
|
||||
<field name="accessory_product_ids" eval="[(6, 0, [ref('product.product_template_7')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="item1" model="product.pricelist.item">
|
||||
|
|
|
@ -53,11 +53,11 @@ class product_template(osv.Model):
|
|||
string='Website Messages',
|
||||
help="Website communication history",
|
||||
),
|
||||
'suggested_product_id': fields.many2one('product.template', 'Suggested For Product'),
|
||||
'suggested_product_ids': fields.one2many('product.template', 'suggested_product_id', 'Suggested Products'),
|
||||
'alternative_product_ids': fields.many2many('product.template','product_alternative_rel','src_id','dest_id', string='Alternative Products', help='Appear on the product page'),
|
||||
'accessory_product_ids': fields.many2many('product.template','product_accessory_rel','src_id','dest_id', string='Accessory Products', help='Appear on the shopping cart'),
|
||||
'website_size_x': fields.integer('Size X'),
|
||||
'website_size_y': fields.integer('Size Y'),
|
||||
'website_style_ids': fields.many2many('website.product.style', 'product_website_style_rel', 'product_id', 'style_id', 'Styles'),
|
||||
'website_style_ids': fields.many2many('product.style', 'product_website_style_rel', 'product_id', 'style_id', 'Styles'),
|
||||
'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"),
|
||||
}
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class characteristics(osv.Model):
|
||||
_name = "product.characteristic"
|
||||
class attributes(osv.Model):
|
||||
_name = "product.attribute"
|
||||
|
||||
def _get_float_max(self, cr, uid, ids, field_name, arg, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
if ids:
|
||||
cr.execute("""
|
||||
SELECT characteristic_id, MAX(value)
|
||||
FROM product_characteristic_product
|
||||
WHERE characteristic_id in (%s)
|
||||
GROUP BY characteristic_id
|
||||
SELECT attribute_id, MAX(value)
|
||||
FROM product_attribute_line
|
||||
WHERE attribute_id in (%s)
|
||||
GROUP BY attribute_id
|
||||
""" % ",".join(map(str, ids)))
|
||||
result.update(dict(cr.fetchall()))
|
||||
return result
|
||||
|
@ -21,32 +21,32 @@ class characteristics(osv.Model):
|
|||
result = dict.fromkeys(ids, 0)
|
||||
if ids:
|
||||
cr.execute("""
|
||||
SELECT characteristic_id, MIN(value)
|
||||
FROM product_characteristic_product
|
||||
WHERE characteristic_id in (%s)
|
||||
GROUP BY characteristic_id
|
||||
SELECT attribute_id, MIN(value)
|
||||
FROM product_attribute_line
|
||||
WHERE attribute_id in (%s)
|
||||
GROUP BY attribute_id
|
||||
""" % ",".join(map(str, ids)))
|
||||
result.update(dict(cr.fetchall()))
|
||||
return result
|
||||
|
||||
def _get_min_max(self, cr, uid, ids, context=None):
|
||||
result = {}
|
||||
for value in self.pool.get('product.characteristic.product').browse(cr, uid, ids, context=context):
|
||||
for value in self.pool.get('product.attribute.line').browse(cr, uid, ids, context=context):
|
||||
if value.type == 'float':
|
||||
result[value.characteristic_id.id] = True
|
||||
result[value.attribute_id.id] = True
|
||||
return result.keys()
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, translate=True, required=True),
|
||||
'name': fields.char('Name', translate=True, required=True),
|
||||
'type': fields.selection([('distinct', 'Textual Value'), ('float', 'Numeric Value')], "Type", required=True),
|
||||
'value_ids': fields.one2many('product.characteristic.value', 'characteristic_id', 'Values'),
|
||||
'attr_product_ids': fields.one2many('product.characteristic.product', 'characteristic_id', 'Products'),
|
||||
'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
|
||||
'attr_product_ids': fields.one2many('product.attribute.line', 'attribute_id', 'Products'),
|
||||
|
||||
'float_max': fields.function(_get_float_max, type='float', string="Max", store={
|
||||
'product.characteristic.product': (_get_min_max, ['value','characteristic_id'], 20),
|
||||
'product.attribute.line': (_get_min_max, ['value','attribute_id'], 20),
|
||||
}),
|
||||
'float_min': fields.function(_get_float_min, type='float', string="Min", store={
|
||||
'product.characteristic.product': (_get_min_max, ['value','characteristic_id'], 20),
|
||||
'product.attribute.line': (_get_min_max, ['value','attribute_id'], 20),
|
||||
}),
|
||||
'visible': fields.boolean('Display Filter on Website'),
|
||||
}
|
||||
|
@ -55,33 +55,33 @@ class characteristics(osv.Model):
|
|||
'visible': True,
|
||||
}
|
||||
|
||||
class characteristics_value(osv.Model):
|
||||
_name = "product.characteristic.value"
|
||||
class attributes_value(osv.Model):
|
||||
_name = "product.attribute.value"
|
||||
_columns = {
|
||||
'name': fields.char('Value', size=64, translate=True, required=True),
|
||||
'characteristic_id': fields.many2one('product.characteristic', 'Characteristic', required=True),
|
||||
'atr_product_ids': fields.one2many('product.characteristic.product', 'value_id', 'Products'),
|
||||
'name': fields.char('Value', translate=True, required=True),
|
||||
'attribute_id': fields.many2one('product.attribute', 'attribute', required=True),
|
||||
'atr_product_ids': fields.one2many('product.attribute.line', 'value_id', 'Products'),
|
||||
}
|
||||
|
||||
class characteristics_product(osv.Model):
|
||||
_name = "product.characteristic.product"
|
||||
_order = 'characteristic_id, value_id, value'
|
||||
class attributes_product(osv.Model):
|
||||
_name = "product.attribute.line"
|
||||
_order = 'attribute_id, value_id, value'
|
||||
_columns = {
|
||||
'value': fields.float('Numeric Value'),
|
||||
'value_id': fields.many2one('product.characteristic.value', 'Textual Value'),
|
||||
'characteristic_id': fields.many2one('product.characteristic', 'Characteristic', required=True),
|
||||
'value_id': fields.many2one('product.attribute.value', 'Textual Value'),
|
||||
'attribute_id': fields.many2one('product.attribute', 'attribute', required=True),
|
||||
'product_tmpl_id': fields.many2one('product.template', 'Product', required=True),
|
||||
|
||||
'type': fields.related('characteristic_id', 'type', type='selection',
|
||||
'type': fields.related('attribute_id', 'type', type='selection',
|
||||
selection=[('distinct', 'Distinct'), ('float', 'Float')], string='Type'),
|
||||
}
|
||||
|
||||
def onchange_characteristic_id(self, cr, uid, ids, characteristic_id, context=None):
|
||||
characteristic = self.pool.get('product.characteristic').browse(cr, uid, characteristic_id, context=context)
|
||||
return {'value': {'type': characteristic.type, 'value_id': False, 'value': ''}}
|
||||
def onchange_attribute_id(self, cr, uid, ids, attribute_id, context=None):
|
||||
attribute = self.pool.get('product.attribute').browse(cr, uid, attribute_id, context=context)
|
||||
return {'value': {'type': attribute.type, 'value_id': False, 'value': ''}}
|
||||
|
||||
class product_template(osv.Model):
|
||||
_inherit = "product.template"
|
||||
_columns = {
|
||||
'website_characteristic_ids': fields.one2many('product.characteristic.product', 'product_tmpl_id', 'Product Characteristics'),
|
||||
'attribute_lines': fields.one2many('product.attribute.line', 'product_tmpl_id', 'Product attributes'),
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ class sale_configuration(osv.osv_memory):
|
|||
_inherit = 'sale.config.settings'
|
||||
|
||||
_columns = {
|
||||
'group_product_characteristics': fields.boolean("Support custom product attributes",
|
||||
'group_product_attributes': fields.boolean("Support custom product attributes",
|
||||
group='base.group_user,base.group_portal,base.group_public',
|
||||
implied_group='product.group_product_characteristics',
|
||||
implied_group='product.group_product_attributes',
|
||||
help="Lets you add multiple custom attributes on products, "
|
||||
"usable to filter and compare them. "
|
||||
"For example if you sell computers, you could add custom attributes such as RAM size "
|
||||
|
|
|
@ -114,7 +114,7 @@ class Website(orm.Model):
|
|||
|
||||
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
values['name'] = "%s: %s" % (product.name, product.variants) if product.variants else product.name
|
||||
|
||||
values['tax_id'] = [(6, 0, [tax.id for tax in product.taxes_id])]
|
||||
if order_line_id:
|
||||
order_line_obj.write(cr, SUPERUSER_ID, order_line_ids, values, context=context)
|
||||
else:
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class website_product_style(osv.Model):
|
||||
_name = "website.product.style"
|
||||
class product_style(osv.Model):
|
||||
_name = "product.style"
|
||||
_columns = {
|
||||
'name' : fields.char('Style Name', required=True, translate=True),
|
||||
'html_class': fields.char('HTML Classes'),
|
||||
|
|
|
@ -7,8 +7,8 @@ access_product_pricelist_version_public,product.pricelist.version.public,product
|
|||
access_product_pricelist_public,product.pricelist.public,product.model_product_pricelist,,1,0,0,0
|
||||
access_product_pricelist_item_public,product.pricelist.item.public,product.model_product_pricelist_item,,1,0,0,0
|
||||
access_product_product_price_type_public,product.price.type.public,product.model_product_price_type,,1,0,0,0
|
||||
access_product_characteristic,product.characteristic.public,website_sale.model_product_characteristic,,1,0,0,0
|
||||
access_product_characteristic_value,product.characteristic.value.public,website_sale.model_product_characteristic_value,,1,0,0,0
|
||||
access_product_characteristic_product,product.characteristic.product.public,website_sale.model_product_characteristic_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_attribute,product.attribute.public,website_sale.model_product_attribute,,1,0,0,0
|
||||
access_product_attribute_value,product.attribute.value.public,website_sale.model_product_attribute_value,,1,0,0,0
|
||||
access_product_attribute_line,product.attribute.line.public,website_sale.model_product_attribute_line,,1,0,0,0
|
||||
access_product_style,product.style.public,website_sale.model_product_style,,1,0,0,0
|
||||
access_product_supplierinfo,product.supplierinfo.public,product.model_product_supplierinfo,,1,0,0,0
|
||||
|
|
|
|
@ -7,7 +7,6 @@
|
|||
start: function () {
|
||||
this.registerTour(new website.EditorShopTour(this));
|
||||
var res = this._super();
|
||||
this.registerTour(new website.EditorShopTest(this));
|
||||
return res;
|
||||
},
|
||||
});
|
||||
|
@ -15,282 +14,196 @@
|
|||
website.EditorShopTour = website.Tour.extend({
|
||||
id: 'shop',
|
||||
name: "Create a product",
|
||||
testPath: /\/shop\/.*/,
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome-shop',
|
||||
title: "Welcome to your shop",
|
||||
content: "You successfully installed the e-commerce. This guide will help you to create your product and promote your sales.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
backdrop: true,
|
||||
},
|
||||
{
|
||||
stepId: 'content-menu',
|
||||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
title: "Create your first product",
|
||||
content: "Click here to add a new product.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'edit-entry',
|
||||
element: '#create-new-product',
|
||||
element: 'a[data-action=new_product]',
|
||||
placement: 'left',
|
||||
title: "Create a new product",
|
||||
content: "Select 'New Product' to create it and manage its properties to boost your sales.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: {
|
||||
modal: {
|
||||
stopOnClose: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'enter-name',
|
||||
element: '.modal input[type=text]',
|
||||
element: '.modal:contains("New Product") input[type=text]',
|
||||
sampleText: 'New Product',
|
||||
placement: 'right',
|
||||
title: "Choose name",
|
||||
content: "Enter a name for your new product then click 'Continue'.",
|
||||
trigger: 'keyup',
|
||||
},
|
||||
{
|
||||
stepId: 'continue-name',
|
||||
waitNot: '.modal input[type=text]:not([value!=""])',
|
||||
element: '.modal button.btn-primary',
|
||||
placement: 'right',
|
||||
title: "Create Product",
|
||||
content: "Click <em>Continue</em> to create the product.",
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'product-page',
|
||||
waitFor: '#website-top-navbar button[data-action="save"]:visible',
|
||||
title: "New product created",
|
||||
content: "This page contains all the information related to the new product.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'edit-price-cke',
|
||||
element: '.product_price .oe_currency_value',
|
||||
sampleText: '20.50',
|
||||
placement: 'left',
|
||||
title: "Change the price",
|
||||
content: "Edit the price of this product by clicking on the amount.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
},
|
||||
{
|
||||
stepId: 'update-image',
|
||||
waitNot: '.product_price .oe_currency_value:containsExact(1.00)',
|
||||
element: '#wrap img.img:first',
|
||||
placement: 'top',
|
||||
title: "Update image",
|
||||
content: "Click here to set an image describing your product.",
|
||||
triggers: function (callback) {
|
||||
var self = this;
|
||||
$(self.element).on('mouseenter', function () {
|
||||
$(this).off('mouseenter');
|
||||
setTimeout(function () {
|
||||
(callback || self.tour.moveToNextStep).apply(self.tour);
|
||||
},0);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'update-image-button',
|
||||
element: 'button.hover-edition-button:visible',
|
||||
placement: 'top',
|
||||
title: "Update image",
|
||||
content: "Click here to set an image describing your product.",
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'upload-image',
|
||||
wait: 500,
|
||||
element: '.well a.pull-right',
|
||||
placement: 'bottom',
|
||||
title: "Select an Image",
|
||||
content: "Let's select an existing image.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'ajax'
|
||||
},
|
||||
{
|
||||
stepId: 'select-image',
|
||||
element: 'img[alt=imac]',
|
||||
placement: 'bottom',
|
||||
title: "Select an Image",
|
||||
content: "Let's select an imac image.",
|
||||
template: self.popover({ fixed: true }),
|
||||
triggers: function (callback) {
|
||||
var self = this;
|
||||
var click = function () {
|
||||
$('.modal-dialog.select-image img').off('click', click);
|
||||
setTimeout(function () {
|
||||
(callback || self.tour.moveToNextStep).apply(self.tour);
|
||||
},0);
|
||||
};
|
||||
$('.modal-dialog.select-image img').on('click', click);
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'save-image',
|
||||
element: 'button.save',
|
||||
waitNot: 'img[alt=imac]',
|
||||
element: '.modal-content button.save',
|
||||
placement: 'bottom',
|
||||
title: "Select this Image",
|
||||
content: "Click to add the image to the product decsription.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'add-block',
|
||||
waitNot: '.modal-content:visible',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Describe the Product",
|
||||
content: "Insert blocks like text-image, or gallery to fully describe the product.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'click',
|
||||
},
|
||||
{
|
||||
stepId: 'drag-big-picture',
|
||||
snippet: 'big-picture',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Big Picture' block and drop it in your page.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'drag',
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your product is updated.",
|
||||
template: self.popover({ fixed: true }),
|
||||
trigger: 'reload',
|
||||
|
||||
},
|
||||
{
|
||||
stepId: 'publish-product',
|
||||
waitFor: '#website-top-navbar button[data-action="edit"]:visible',
|
||||
element: '.js_publish_management button.js_publish_btn.btn-danger',
|
||||
placement: 'top',
|
||||
title: "Publish your product",
|
||||
content: "Click to publish your product so your customers can see it.",
|
||||
trigger: 'ajax'
|
||||
},
|
||||
{
|
||||
stepId: 'congratulations',
|
||||
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
|
||||
title: "Congratulations",
|
||||
content: "Congratulations! You just created and published your first product.",
|
||||
template: self.popover({ end: "Close Tutorial" }),
|
||||
backdrop: true,
|
||||
template: self.popover({ next: "Close Tutorial" }),
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/^\/shop\/product\/[0-9]+\//)) || this._super();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
website.Test = website.Tour.extend({
|
||||
registerStep: function (step) {
|
||||
var self = this;
|
||||
var step = this._super(step);
|
||||
if (step.beforeTrigger || step.afterTrigger) {
|
||||
var fn = step.triggers;
|
||||
step.triggers = function (callback) {
|
||||
if (step.beforeTrigger) step.beforeTrigger(self);
|
||||
if (!step.afterTrigger) {
|
||||
fn.call(step, callback);
|
||||
} else {
|
||||
fn.call(step, function () {
|
||||
(callback || self.moveToNextStep).apply(self);
|
||||
step.afterTrigger(self);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
return step;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
website.EditorShopTest = website.Test.extend({
|
||||
website.EditorShopTest = website.Tour.extend({
|
||||
id: 'shop_buy_product',
|
||||
name: "Try to buy products",
|
||||
path: '/shop',
|
||||
testPath: /\/shop/,
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'begin-test',
|
||||
title: 'begin-test',
|
||||
template: self.popover({ next: "Start Test"}),
|
||||
backdrop: true,
|
||||
},
|
||||
{
|
||||
stepId: 'display-ipod',
|
||||
title: "select ipod",
|
||||
element: '.oe_product_cart a:contains("iPod")',
|
||||
trigger: {
|
||||
url: /shop\/product\/.*/,
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'choose-ipod',
|
||||
title: "select ipod 32Go",
|
||||
element: 'input[name="product_id"]:not([checked])',
|
||||
trigger: 'mouseup',
|
||||
},
|
||||
{
|
||||
stepId: 'add-ipod',
|
||||
title: "click on add to cart",
|
||||
waitFor: 'input[name="product_id"]:eq(1)[checked]',
|
||||
element: 'form[action="/shop/add_cart/"] button',
|
||||
trigger: {
|
||||
url: '/shop/mycart/',
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'add-suggested-product',
|
||||
element: 'form[action="/shop/add_cart/"] button:contains("Add to Cart")',
|
||||
trigger: 'reload',
|
||||
title: "add suggested",
|
||||
element: 'form[action="/shop/add_cart/"] button.btn-link:contains("Add to Cart")',
|
||||
},
|
||||
{
|
||||
stepId: 'more-product',
|
||||
element: '.oe_mycart a.js_add_cart_json:eq(1)',
|
||||
trigger: 'ajax',
|
||||
title: "add one more iPod",
|
||||
waitFor: '.my_cart_quantity:contains(2)',
|
||||
element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)',
|
||||
},
|
||||
{
|
||||
stepId: 'less-product',
|
||||
element: '.oe_mycart a.js_add_cart_json:eq(2)',
|
||||
trigger: 'reload',
|
||||
title: "remove Headphones",
|
||||
waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]',
|
||||
element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first',
|
||||
},
|
||||
{
|
||||
stepId: 'number-product',
|
||||
element: '.oe_mycart input.js_quantity',
|
||||
title: "set one iPod",
|
||||
waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")',
|
||||
element: '#mycart_products input.js_quantity',
|
||||
sampleText: '1',
|
||||
trigger: 'reload',
|
||||
},
|
||||
{
|
||||
stepId: 'go-checkout-product',
|
||||
title: "go to checkout",
|
||||
waitFor: '#mycart_products input.js_quantity[value=1]',
|
||||
element: 'a[href="/shop/checkout/"]',
|
||||
trigger: {
|
||||
url: '/shop/checkout/',
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'confirm-false-checkout-product',
|
||||
title: "test with input error",
|
||||
element: 'form[action="/shop/confirm_order/"] button',
|
||||
trigger: {
|
||||
url: '/shop/confirm_order/',
|
||||
},
|
||||
beforeTrigger: function (tour) {
|
||||
callback: function (tour) {
|
||||
$("input[name='phone']").val("");
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'confirm-checkout-product',
|
||||
title: "test without input error",
|
||||
waitFor: 'form[action="/shop/confirm_order/"] .has-error',
|
||||
element: 'form[action="/shop/confirm_order/"] button',
|
||||
trigger: {
|
||||
url: '/shop/payment/',
|
||||
},
|
||||
beforeTrigger: function (tour) {
|
||||
callback: function (tour) {
|
||||
if ($("input[name='name']").val() === "")
|
||||
$("input[name='name']").val("website_sale-test-shoptest");
|
||||
if ($("input[name='email']").val() === "")
|
||||
|
@ -303,26 +216,24 @@
|
|||
},
|
||||
},
|
||||
{
|
||||
stepId: 'acquirer-checkout-product',
|
||||
title: "select acquirer",
|
||||
element: 'input[name="acquirer"]',
|
||||
trigger: 'mouseup',
|
||||
},
|
||||
{
|
||||
stepId: 'pay-checkout-product',
|
||||
title: "confirm",
|
||||
element: 'button:contains("Pay Now")',
|
||||
trigger: {
|
||||
url: /shop\/confirmation\//,
|
||||
},
|
||||
afterTrigger: function (tour) {
|
||||
console.log('{ "event": "success" }');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "finish",
|
||||
waitFor: '.oe_website_sale:contains("Thank you for your order")',
|
||||
}
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/\/shop\//)) || this._super();
|
||||
},
|
||||
});
|
||||
// for test without editor bar
|
||||
$(document).ready(function () {
|
||||
website.Tour.add(website.EditorShopTest);
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -71,10 +71,10 @@ $(document).ready(function () {
|
|||
$price.html($price.data("price")+parseFloat($label.find(".badge span").text() || 0));
|
||||
});
|
||||
|
||||
// characteristics
|
||||
// attributes
|
||||
|
||||
var js_slider_time = null;
|
||||
var $form = $("form.characteristics");
|
||||
var $form = $("form.attributes");
|
||||
$form.on("change", "label input", function () {
|
||||
clearTimeout(js_slider_time);
|
||||
$form.submit();
|
||||
|
|
|
@ -2,7 +2,7 @@ import openerp.addons.website.tests.test_ui as test_ui
|
|||
|
||||
def load_tests(loader, base, _):
|
||||
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'),
|
||||
{ 'action': 'website.action_website_homepage' }, 120.0))
|
||||
{ 'action': 'website.action_website_homepage' }))
|
||||
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test-2.js'),
|
||||
{ 'action': 'website.action_website_homepage' }, 120.0))
|
||||
{ 'action': 'website.action_website_homepage' }))
|
||||
return base
|
|
@ -7,22 +7,11 @@ testRunner.run(function websiteSaleTest (page, timeout) {
|
|||
waitFor(function clientReady () {
|
||||
return page.evaluate(function () {
|
||||
return window.$ && window.openerp && window.openerp.website
|
||||
&& window.openerp.website.TestConsole
|
||||
&& window.openerp.website.TestConsole.test('shop');
|
||||
&& window.openerp.website.Tour;
|
||||
});
|
||||
}, function executeTest () {
|
||||
page.evaluate(function () {
|
||||
window.openerp.website.TestConsole.test('shop').run(true);
|
||||
window.openerp.website.Tour.run_test('shop');
|
||||
});
|
||||
waitFor(function testExecuted () {
|
||||
var after = page.evaluate(function () {
|
||||
return window.$ && $('button[data-action=edit]').is(":visible") &&
|
||||
$('data-snippet-id="big-picture"').length;
|
||||
});
|
||||
return after;
|
||||
}, function finish () {
|
||||
console.log('{ "event": "success" }');
|
||||
phantom.exit();
|
||||
}, 4*timeout/5);
|
||||
}, timeout/5);
|
||||
}, timeout);
|
||||
});
|
||||
|
|
|
@ -7,12 +7,11 @@ testRunner.run(function websiteSaleTest (page, timeout) {
|
|||
waitFor(function clientReady () {
|
||||
return page.evaluate(function () {
|
||||
return window.$ && window.openerp && window.openerp.website
|
||||
&& window.openerp.website.TestConsole
|
||||
&& window.openerp.website.TestConsole.test('shop_buy_product');
|
||||
&& window.openerp.website.Tour;
|
||||
});
|
||||
}, function executeTest () {
|
||||
page.evaluate(function () {
|
||||
window.openerp.website.TestConsole.test('shop_buy_product').run(true);
|
||||
window.openerp.website.Tour.run_test('shop_buy_product');
|
||||
});
|
||||
}, timeout);
|
||||
});
|
|
@ -358,7 +358,7 @@
|
|||
<t t-foreach="product.recommended_products()" t-as="product">
|
||||
<div class='col-md-2 thumbnail' style='width: 170px; margin-right: 16px;'>
|
||||
<div class='mt16 text-center'>
|
||||
<span t-field="product.image_small"/>
|
||||
<span t-field="product.image_small" t-field-options='{"widget": "image", "class": "img-rounded shadow" }'/>
|
||||
<h5>
|
||||
<a t-attf-href="/shop/product/#{ slug(product) }/"
|
||||
style="display: block">
|
||||
|
@ -374,13 +374,13 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Product option: characteristics -->
|
||||
<template id="product_characteristics" inherit_id="website_sale.product" inherit_option_id="website_sale.product" name="Product Characteristics" groups="product.group_product_characteristics">
|
||||
<!-- Product option: attributes -->
|
||||
<template id="product_attributes" inherit_id="website_sale.product" inherit_option_id="website_sale.product" name="Product attributes" groups="product.group_product_attributes">
|
||||
<xpath expr="//p[@t-field='product.description_sale']" position="after">
|
||||
<hr t-if="product.website_characteristic_ids"/>
|
||||
<hr t-if="product.attribute_lines"/>
|
||||
<p class="text-muted">
|
||||
<t t-set="attr" t-value="None"/>
|
||||
<t t-foreach="product.website_characteristic_ids" t-as="characteristic"><br t-if="attr and characteristic.characteristic_id.id != attr"/><t t-if="characteristic.characteristic_id.id != attr"><span t-field="characteristic.characteristic_id"/>: </t><t t-if="characteristic.characteristic_id.id == attr">, </t><t t-if="characteristic.characteristic_id.type == 'distinct'"><span t-field="characteristic.value_id"/></t><t t-if="characteristic.characteristic_id.type == 'float'"><span t-field="characteristic.value"/></t><t t-set="attr" t-value="characteristic.characteristic_id.id"/></t>
|
||||
<t t-foreach="product.attribute_lines" t-as="attribute"><br t-if="attr and attribute.attribute_id.id != attr"/><t t-if="attribute.attribute_id.id != attr"><span t-field="attribute.attribute_id"/>: </t><t t-if="attribute.attribute_id.id == attr">, </t><t t-if="attribute.attribute_id.type == 'distinct'"><span t-field="attribute.value_id"/></t><t t-if="attribute.attribute_id.type == 'float'"><span t-field="attribute.value"/></t><t t-set="attr" t-value="attribute.attribute_id.id"/></t>
|
||||
</p>
|
||||
</xpath>
|
||||
</template>
|
||||
|
@ -553,7 +553,7 @@
|
|||
|
||||
<template id="continue_shopping" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Continue Shopping Button">
|
||||
<xpath expr="//a[@href='/shop/checkout/']" position="before">
|
||||
<a href="/shop" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/>Continue Shopping</a>
|
||||
<a href="/shop" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Continue Shopping</a>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
@ -577,36 +577,36 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="products_characteristics" inherit_id="website_sale.products" inherit_option_id="website_sale.products" name="Product Characteristic's Filters" groups="product.group_product_characteristics">
|
||||
<template id="products_attributes" inherit_id="website_sale.products" inherit_option_id="website_sale.products" name="Product attribute's Filters" groups="product.group_product_attributes">
|
||||
<xpath expr="//div[@id='products_grid_before']" position="inside">
|
||||
<form t-attf-action="/shop/filters/?{{ keep_query('category', 'search') }}" class="characteristics" method="post">
|
||||
<form t-attf-action="/shop/filters/?{{ keep_query('category', 'search') }}" class="attributes" method="post">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<t t-set="characteristic_ids" t-value="Ecommerce.get_characteristic_ids()"/>
|
||||
<t t-foreach="characteristic_ids" t-as="characteristic_id">
|
||||
<t t-if="characteristic_id.visible">
|
||||
<li t-if="characteristic_id.value_ids and characteristic_id.type == 'distinct'">
|
||||
<div t-field="characteristic_id.name"/>
|
||||
<t t-set="attribute_ids" t-value="Ecommerce.get_attribute_ids()"/>
|
||||
<t t-foreach="attribute_ids" t-as="attribute_id">
|
||||
<t t-if="attribute_id.visible">
|
||||
<li t-if="attribute_id.value_ids and attribute_id.type == 'distinct'">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-foreach="characteristic_id.value_ids" t-as="value_id">
|
||||
<li t-att-class="Ecommerce.has_search_filter(characteristic_id.id, value_id.id) and 'active' or ''">
|
||||
<t t-foreach="attribute_id.value_ids" t-as="value_id">
|
||||
<li t-att-class="Ecommerce.has_search_filter(attribute_id.id, value_id.id) and 'active' or ''">
|
||||
<label style="margin: 0 20px;">
|
||||
<input type="checkbox" t-att-name="'att-%s-%s' % (characteristic_id.id, value_id.id)"
|
||||
t-att-checked="Ecommerce.has_search_filter(characteristic_id.id, value_id.id) and 'checked' or ''"/>
|
||||
<input type="checkbox" t-att-name="'att-%s-%s' % (attribute_id.id, value_id.id)"
|
||||
t-att-checked="Ecommerce.has_search_filter(attribute_id.id, value_id.id) and 'checked' or ''"/>
|
||||
<span style="font-weight: normal" t-field="value_id.name"/>
|
||||
</label>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
<li t-if="characteristic_id.type == 'float' and characteristic_id.float_min != characteristic_id.float_max">
|
||||
<div t-field="characteristic_id.name"/>
|
||||
<t t-set="characteristic" t-value="Ecommerce.has_search_filter(characteristic_id.id)"/>
|
||||
<li t-if="attribute_id.type == 'float' and attribute_id.float_min != attribute_id.float_max">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<t t-set="attribute" t-value="Ecommerce.has_search_filter(attribute_id.id)"/>
|
||||
<div style="margin: 0 20px;" class="js_slider"
|
||||
t-att-data-id="characteristic_id.id"
|
||||
t-att-data-value-min="characteristic and characteristic[1][0] or characteristic_id.float_min"
|
||||
t-att-data-value-max="characteristic and characteristic[1][1] or characteristic_id.float_max"
|
||||
t-att-data-min="characteristic_id.float_min"
|
||||
t-att-data-max="characteristic_id.float_max"></div>
|
||||
t-att-data-id="attribute_id.id"
|
||||
t-att-data-value-min="attribute and attribute[1][0] or attribute_id.float_min"
|
||||
t-att-data-value-max="attribute and attribute[1][1] or attribute_id.float_max"
|
||||
t-att-data-min="attribute_id.float_min"
|
||||
t-att-data-max="attribute_id.float_max"></div>
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
|
@ -720,7 +720,7 @@
|
|||
<div class="col-md-8 oe_mycart">
|
||||
<h3 class="page-header mt16">Billing Information
|
||||
<small groups="base.group_public"> or
|
||||
<a t-if="not partner" t-attf-href="/web#action=redirect&url=#{ request.httprequest.url }">sign in</a>
|
||||
<a class='btn btn-primary' t-if="not partner" t-attf-href="/web?redirect=#{ request.httprequest.url }">Sign in</a>
|
||||
</small>
|
||||
</h3>
|
||||
<div class="row">
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='Product Features']/div" position="inside">
|
||||
<div>
|
||||
<field name="group_product_characteristics" class="oe_inline"/>
|
||||
<label for="group_product_characteristics"/>
|
||||
<field name="group_product_attributes" class="oe_inline"/>
|
||||
<label for="group_product_attributes"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
<group name="sale" position="inside">
|
||||
<group name="website" string="Website">
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="accessory_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
<field name="website_sequence"/>
|
||||
</group>
|
||||
|
@ -61,17 +61,17 @@
|
|||
</xpath>
|
||||
<xpath expr="//field[@name='description']" position="before">
|
||||
<group colspan="4" string="Website Options">
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="accessory_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
<field colspan="4" name="website_characteristic_ids" nolabel="1" groups="product.group_product_characteristics">
|
||||
<tree string="Product Characteristics" editable="bottom">
|
||||
<field name="characteristic_id" on_change="onchange_characteristic_id(characteristic_id)"/>
|
||||
<field colspan="4" name="attribute_lines" nolabel="1" groups="product.group_product_attributes">
|
||||
<tree string="Product attributes" editable="bottom">
|
||||
<field name="attribute_id" on_change="onchange_attribute_id(attribute_id)"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="value" attrs="{'required': [('type','=','float')]}"/>
|
||||
<field name="value_id"
|
||||
attrs="{'required': [('type','=','distinct')]}"
|
||||
context="{'default_characteristic_id': characteristic_id}"
|
||||
domain="[('characteristic_id', '=', characteristic_id)]"/>
|
||||
context="{'default_attribute_id': attribute_id}"
|
||||
domain="[('attribute_id', '=', attribute_id)]"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
|
@ -80,11 +80,11 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_product_characteristic_form">
|
||||
<field name="name">product.characteristic.form</field>
|
||||
<field name="model">product.characteristic</field>
|
||||
<record model="ir.ui.view" id="view_product_attribute_form">
|
||||
<field name="name">product.attribute.form</field>
|
||||
<field name="model">product.attribute</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Product Characteristics" version="7.0">
|
||||
<form string="Product attributes" version="7.0">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
|
|
|
@ -11,13 +11,11 @@ class Ecommerce(Ecommerce):
|
|||
def payment(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
order = self.get_order()
|
||||
|
||||
carrier_id = post.get('carrier_id')
|
||||
|
||||
if order and carrier_id:
|
||||
# recompute delivery costs
|
||||
SaleOrder = request.registry['sale.order']
|
||||
SaleOrder.write(cr, SUPERUSER_ID, [order.id], {'carrier_id': carrier_id}, context=context)
|
||||
SaleOrder.delivery_set(cr, SUPERUSER_ID, [order.id], context=context)
|
||||
# recompute delivery costs
|
||||
request.registry['website']._check_carrier_quotation(cr,uid,order,carrier_id,context=context)
|
||||
return request.redirect("/shop/payment/")
|
||||
|
||||
res = super(Ecommerce, self).payment(**post)
|
||||
|
|
|
@ -19,17 +19,17 @@ class delivery_carrier(orm.Model):
|
|||
class SaleOrder(orm.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None):
|
||||
def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None):
|
||||
""" Wrapper because of direct method passing as parameter for function fields """
|
||||
return self._amount_all(cr, uid, ids, field_name, arg, context=context)
|
||||
|
||||
def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = super(SaleOrder, self)._amount_all(cr, uid, ids, field_name, arg, context=context)
|
||||
Currency = self.pool.get('res.currency')
|
||||
currency_pool = self.pool.get('res.currency')
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
line_amount = sum([line.price_subtotal for line in order.order_line if line.is_delivery])
|
||||
currency = order.pricelist_id.currency_id
|
||||
res[order.id]['amount_delivery'] = Currency.round(cr, uid, currency, line_amount)
|
||||
res[order.id]['amount_delivery'] = currency_pool.round(cr, uid, currency, line_amount)
|
||||
return res
|
||||
|
||||
def _get_order(self, cr, uid, ids, context=None):
|
||||
|
@ -56,13 +56,9 @@ class SaleOrder(orm.Model):
|
|||
),
|
||||
}
|
||||
|
||||
def _add_delivery(self, cr, uid, order, context=None):
|
||||
pass
|
||||
|
||||
def _get_website_data(self, cr, uid, order, context=None):
|
||||
""" Override to add delivery-related website data. """
|
||||
values = super(SaleOrder, self)._get_website_data(cr, uid, order, context=context)
|
||||
|
||||
# We need a delivery only if we have stockable products
|
||||
has_stockable_products = False
|
||||
for line in order.order_line:
|
||||
|
@ -73,6 +69,6 @@ class SaleOrder(orm.Model):
|
|||
|
||||
delivery_ctx = dict(context, order_id=order.id)
|
||||
DeliveryCarrier = self.pool.get('delivery.carrier')
|
||||
delivery_ids = DeliveryCarrier.search(cr, uid, [], context=context)
|
||||
delivery_ids = DeliveryCarrier.search(cr, uid, [('website_published','=',True)], context=context)
|
||||
values['deliveries'] = DeliveryCarrier.browse(cr, SUPERUSER_ID, delivery_ids, context=delivery_ctx)
|
||||
return values
|
||||
|
|
|
@ -9,7 +9,7 @@ class Website(orm.Model):
|
|||
def _ecommerce_create_quotation(self, cr, uid, context=None):
|
||||
order_id = super(Website, self)._ecommerce_create_quotation(cr, uid, context=context)
|
||||
order = self.pool['sale.order'].browse(cr, SUPERUSER_ID, order_id, context=context)
|
||||
self._check_carrier_quotation(cr, uid, order, context=context)
|
||||
self._check_carrier_quotation(cr, uid, order, force_carrier_id=None, context=context)
|
||||
return order_id
|
||||
|
||||
def _ecommerce_add_product_to_cart(self, cr, uid, product_id=0, order_line_id=0, number=1, set_number=-1, context=None):
|
||||
|
@ -17,9 +17,9 @@ class Website(orm.Model):
|
|||
product_id=product_id, order_line_id=order_line_id, number=number, set_number=set_number,
|
||||
context=context)
|
||||
order = self.ecommerce_get_current_order(cr, uid, context=context)
|
||||
return self._check_carrier_quotation(cr, uid, order, context=context) and quantity or None
|
||||
|
||||
def _check_carrier_quotation(self, cr, uid, order, context=None):
|
||||
return self._check_carrier_quotation(cr, uid, order, force_carrier_id=None, context=context) and quantity or None
|
||||
|
||||
def _check_carrier_quotation(self, cr, uid, order, force_carrier_id=None, context=None):
|
||||
# check to add or remove carrier_id
|
||||
carrier_id = False
|
||||
for line in order.website_order_line:
|
||||
|
@ -31,10 +31,14 @@ class Website(orm.Model):
|
|||
order.write({'carrier_id': None}, context=context)
|
||||
self.pool['sale.order']._delivery_unset(cr, SUPERUSER_ID, order, context=context)
|
||||
return True
|
||||
elif not order.carrier_id:
|
||||
carrier_ids = self.pool.get('delivery.carrier').search(cr, uid, [], context=context)
|
||||
carrier_id = carrier_ids and carrier_ids[0]
|
||||
else:
|
||||
if order.carrier_id:
|
||||
self.pool['sale.order']._delivery_unset(cr, SUPERUSER_ID, order, context=context)
|
||||
|
||||
carrier_ids = self.pool.get('delivery.carrier').search(cr, uid, [('website_published','=',True)], context=context)
|
||||
carrier_id = force_carrier_id or (carrier_ids and carrier_ids[0])
|
||||
order.write({'carrier_id': carrier_id}, context=context)
|
||||
#If carrier_id have no grid, we don't have delivery !
|
||||
if carrier_id:
|
||||
order.delivery_set(context=context)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue