[MERGE] trunk
bzr revid: sle@openerp.com-20140324162043-dzdwcxdt32sfjy57
This commit is contained in:
commit
9556cadbff
|
@ -19,6 +19,7 @@
|
|||
<newline/>
|
||||
<field name="result_selection"/>
|
||||
<field name="direction_selection"/>
|
||||
<field name="target_move"/>
|
||||
</group>
|
||||
<field name="journal_ids" required="0" invisible="1"/>
|
||||
<footer>
|
||||
|
|
|
@ -78,6 +78,9 @@ class OAuthLogin(openerp.addons.web.controllers.main.Home):
|
|||
@http.route()
|
||||
def web_login(self, *args, **kw):
|
||||
ensure_db()
|
||||
if request.httprequest.method == 'GET' and request.session.uid and request.params.get('redirect'):
|
||||
# Redirect if already logged in and redirect param is present
|
||||
return http.redirect_with_hash(request.params.get('redirect'))
|
||||
providers = self.list_providers()
|
||||
|
||||
response = super(OAuthLogin, self).web_login(*args, **kw)
|
||||
|
|
|
@ -37,6 +37,9 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home):
|
|||
ensure_db()
|
||||
response = super(AuthSignupHome, self).web_login(*args, **kw)
|
||||
response.qcontext.update(self.get_auth_signup_config())
|
||||
if request.httprequest.method == 'GET' and request.session.uid and request.params.get('redirect'):
|
||||
# Redirect if already logged in and redirect param is present
|
||||
return http.redirect_with_hash(request.params.get('redirect'))
|
||||
return response
|
||||
|
||||
@http.route('/web/signup', type='http', auth='public', website=True, multilang=True)
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
##############################################################################
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
from urllib import urlencode
|
||||
from urlparse import urljoin
|
||||
import werkzeug
|
||||
|
||||
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException
|
||||
from openerp.osv import osv, fields
|
||||
|
@ -53,7 +53,7 @@ class res_partner(osv.Model):
|
|||
(not partner.signup_expiration or dt <= partner.signup_expiration)
|
||||
return res
|
||||
|
||||
def _get_signup_url_for_action(self, cr, uid, ids, action='login', view_type=None, menu_id=None, res_id=None, model=None, context=None):
|
||||
def _get_signup_url_for_action(self, cr, uid, ids, action=None, view_type=None, menu_id=None, res_id=None, model=None, context=None):
|
||||
""" generate a signup url for the given partner ids and action, possibly overriding
|
||||
the url state components (menu_id, id, view_type) """
|
||||
if context is None:
|
||||
|
@ -81,6 +81,8 @@ class res_partner(osv.Model):
|
|||
continue # no signup token, no user, thus no signup url!
|
||||
|
||||
fragment = dict()
|
||||
if action:
|
||||
fragment['action'] = action
|
||||
if view_type:
|
||||
fragment['view_type'] = view_type
|
||||
if menu_id:
|
||||
|
@ -90,7 +92,10 @@ class res_partner(osv.Model):
|
|||
if res_id:
|
||||
fragment['id'] = res_id
|
||||
|
||||
res[partner.id] = urljoin(base_url, "/web/%s?%s#%s" % (route, urlencode(query), urlencode(fragment)))
|
||||
if fragment:
|
||||
query['redirect'] = '/web#' + werkzeug.url_encode(fragment)
|
||||
|
||||
res[partner.id] = urljoin(base_url, "/web/%s?%s" % (route, werkzeug.url_encode(query)))
|
||||
|
||||
return res
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class crm_lead2opportunity_partner(osv.osv_memory):
|
|||
def onchange_action(self, cr, uid, ids, action, context=None):
|
||||
return {'value': {'partner_id': False if action != 'exist' else self._find_matching_partner(cr, uid, context=context)}}
|
||||
|
||||
def _get_duplicated_leads(self, cr, uid, partner_id, email, context=None):
|
||||
def _get_duplicated_leads(self, cr, uid, partner_id, email, include_lost=False, context=None):
|
||||
"""
|
||||
Search for opportunities that have the same partner and that arent done or cancelled
|
||||
"""
|
||||
|
@ -56,8 +56,11 @@ class crm_lead2opportunity_partner(osv.osv_memory):
|
|||
partner_match_domain.append(('partner_id', '=', partner_id))
|
||||
partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain
|
||||
if not partner_match_domain:
|
||||
return []
|
||||
return lead_obj.search(cr, uid, partner_match_domain + final_stage_domain)
|
||||
return []
|
||||
domain = partner_match_domain
|
||||
if not include_lost:
|
||||
domain += final_stage_domain
|
||||
return lead_obj.search(cr, uid, domain)
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
"""
|
||||
|
@ -75,7 +78,7 @@ class crm_lead2opportunity_partner(osv.osv_memory):
|
|||
lead = lead_obj.browse(cr, uid, int(context['active_id']), context=context)
|
||||
email = lead.partner_id and lead.partner_id.email or lead.email_from
|
||||
|
||||
tomerge.extend(self._get_duplicated_leads(cr, uid, partner_id, email))
|
||||
tomerge.extend(self._get_duplicated_leads(cr, uid, partner_id, email, include_lost=True, context=context))
|
||||
tomerge = list(set(tomerge))
|
||||
|
||||
if 'action' in fields:
|
||||
|
|
|
@ -76,8 +76,8 @@ openerp.mail = function (session) {
|
|||
*/
|
||||
get_text2html: function (text) {
|
||||
return text
|
||||
.replace(/[\n\r]/g,'<br/>')
|
||||
.replace(/((?:https?|ftp):\/\/[\S]+)/g,'<a href="$1">$1</a> ')
|
||||
.replace(/[\n\r]/g,'<br/>')
|
||||
},
|
||||
|
||||
/* Returns the complete domain with "&"
|
||||
|
|
|
@ -19,5 +19,11 @@
|
|||
<field name="global" eval="True" />
|
||||
<field name="domain_force">[('company_id', '=', user.company_id.id)]</field>
|
||||
</record>
|
||||
<record id="rule_pos_config_multi_company" model="ir.rule">
|
||||
<field name="name">Point Of Sale Config</field>
|
||||
<field name="model_id" ref="model_pos_config" />
|
||||
<field name="global" eval="True" />
|
||||
<field name="domain_force">[('warehouse_id.company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<separator string="Select your Point of Sale" colspan="4" attrs="{'invisible' : [('show_config', '=', False)]}" />
|
||||
<group attrs="{'invisible' : [('show_config', '=', False)]}">
|
||||
<field name="pos_config_id" on_change="on_change_config(pos_config_id)"
|
||||
widget="selection" domain="[('state','=','active')]"
|
||||
options="{'no_create': True}" domain="[('state','=','active')]"
|
||||
class="oe_inline"/>
|
||||
<field name="pos_state" invisible="1" />
|
||||
</group>
|
||||
|
|
|
@ -39,7 +39,7 @@ class mail_mail(osv.Model):
|
|||
if partner and not partner.user_ids:
|
||||
contex_signup = dict(context, signup_valid=True)
|
||||
signup_url = partner_obj._get_signup_url_for_action(cr, SUPERUSER_ID, [partner.id],
|
||||
action='login', model=mail.model, res_id=mail.res_id,
|
||||
model=mail.model, res_id=mail.res_id,
|
||||
context=contex_signup)[partner.id]
|
||||
return _("""<span class='oe_mail_footer_access'><small>Access your messages and documents through <a style='color:inherit' href="%s">our Customer Portal</a></small></span>""") % signup_url
|
||||
else:
|
||||
|
|
|
@ -134,7 +134,8 @@ class test_portal(TestMail):
|
|||
'invite: subject of invitation email is incorrect')
|
||||
self.assertIn('Administrator invited you to follow Discussion group document: Pigs', sent_email.get('body'),
|
||||
'invite: body of invitation email is incorrect')
|
||||
self.assertTrue(partner_carine.signup_url in sent_email.get('body'),
|
||||
invite_url = partner_carine._get_signup_url_for_action(model='mail.group', res_id=self.group_pigs_id)[partner_carine.id]
|
||||
self.assertTrue(invite_url in sent_email.get('body'),
|
||||
'invite: body of invitation email does not contain signup url')
|
||||
|
||||
def test_20_notification_url(self):
|
||||
|
|
|
@ -745,7 +745,7 @@ class task(osv.osv):
|
|||
|
||||
_columns = {
|
||||
'active': fields.function(_is_template, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."),
|
||||
'name': fields.char('Task Summary', size=128, required=True, select=True),
|
||||
'name': fields.char('Task Summary', track_visibility='onchange', size=128, required=True, select=True),
|
||||
'description': fields.text('Description'),
|
||||
'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True),
|
||||
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."),
|
||||
|
|
|
@ -629,10 +629,9 @@ class purchase_order(osv.osv):
|
|||
'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in'),
|
||||
'origin': order.name + ((order.origin and (':' + order.origin)) or ''),
|
||||
'date': self.date_to_datetime(cr, uid, order.date_order, context),
|
||||
'partner_id': order.dest_address_id.id or order.partner_id.id,
|
||||
'partner_id': order.partner_id.id,
|
||||
'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
|
||||
'type': 'in',
|
||||
'partner_id': order.dest_address_id.id or order.partner_id.id,
|
||||
'purchase_id': order.id,
|
||||
'company_id': order.company_id.id,
|
||||
'move_lines' : [],
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
|
||||
|
|
|
@ -112,6 +112,7 @@ def webkit_report_extender(report_name):
|
|||
return fct
|
||||
return fct1
|
||||
|
||||
|
||||
class WebKitParser(report_sxw):
|
||||
"""Custom class that use webkit to render HTML reports
|
||||
Code partially taken from report openoffice. Thanks guys :)
|
||||
|
@ -173,7 +174,7 @@ class WebKitParser(report_sxw):
|
|||
),
|
||||
'w'
|
||||
)
|
||||
head_file.write(header.encode('utf-8'))
|
||||
head_file.write(self._sanitize_html(header.encode('utf-8')))
|
||||
head_file.close()
|
||||
file_to_del.append(head_file.name)
|
||||
command.extend(['--header-html', head_file.name])
|
||||
|
@ -184,7 +185,7 @@ class WebKitParser(report_sxw):
|
|||
),
|
||||
'w'
|
||||
)
|
||||
foot_file.write(footer.encode('utf-8'))
|
||||
foot_file.write(self._sanitize_html(footer.encode('utf-8')))
|
||||
foot_file.close()
|
||||
file_to_del.append(foot_file.name)
|
||||
command.extend(['--footer-html', foot_file.name])
|
||||
|
@ -205,7 +206,7 @@ class WebKitParser(report_sxw):
|
|||
for html in html_list :
|
||||
html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
|
||||
count += 1
|
||||
html_file.write(html.encode('utf-8'))
|
||||
html_file.write(self._sanitize_html(html.encode('utf-8')))
|
||||
html_file.close()
|
||||
file_to_del.append(html_file.name)
|
||||
command.append(html_file.name)
|
||||
|
@ -366,7 +367,6 @@ class WebKitParser(report_sxw):
|
|||
pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
|
||||
return (pdf, 'pdf')
|
||||
|
||||
|
||||
def create(self, cursor, uid, ids, data, context=None):
|
||||
"""We override the create function in order to handle generator
|
||||
Code taken from report openoffice. Thanks guys :) """
|
||||
|
@ -387,11 +387,18 @@ class WebKitParser(report_sxw):
|
|||
report_xml.report_sxw = None
|
||||
else:
|
||||
return super(WebKitParser, self).create(cursor, uid, ids, data, context)
|
||||
if report_xml.report_type != 'webkit' :
|
||||
if report_xml.report_type != 'webkit':
|
||||
return super(WebKitParser, self).create(cursor, uid, ids, data, context)
|
||||
result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context)
|
||||
if not result:
|
||||
return (False,False)
|
||||
return result
|
||||
|
||||
def _sanitize_html(self, html):
|
||||
"""wkhtmltopdf expects the html page to declare a doctype.
|
||||
"""
|
||||
if html and html[:9].upper() != "<!DOCTYPE":
|
||||
html = "<!DOCTYPE html>\n" + html
|
||||
return html
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -55,11 +55,12 @@ class sale_make_invoice(osv.osv_memory):
|
|||
raise osv.except_osv(_('Warning!'), _("You shouldn't manually invoice the following sale order %s") % (sale_order.name))
|
||||
|
||||
order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_invoice=data['invoice_date'])
|
||||
|
||||
for o in order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
|
||||
orders = order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context)
|
||||
for o in orders:
|
||||
for i in o.invoice_ids:
|
||||
newinv.append(i.id)
|
||||
|
||||
# Dummy call to workflow, will not create another invoice but bind the new invoice to the subflow
|
||||
order_obj.signal_manual_invoice(cr, uid, [o.id for o in orders if o.order_policy == 'manual'])
|
||||
result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
|
||||
id = result and result[1] or False
|
||||
result = act_obj.read(cr, uid, [id], context=context)[0]
|
||||
|
|
|
@ -33,6 +33,10 @@ class stock_change_product_qty(osv.osv_memory):
|
|||
'prodlot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"),
|
||||
'location_id': fields.many2one('stock.location', 'Location', required=True, domain="[('usage', '=', 'internal')]"),
|
||||
}
|
||||
_defaults = {
|
||||
'new_quantity': 1,
|
||||
'product_id': lambda self, cr, uid, ctx: ctx and ctx.get('active_id', False) or False
|
||||
}
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
if context is None: context = {}
|
||||
|
@ -54,20 +58,22 @@ class stock_change_product_qty(osv.osv_memory):
|
|||
@param context: A standard dictionary
|
||||
@return: A dictionary which of fields with values.
|
||||
"""
|
||||
product_id = context and context.get('active_id', False) or False
|
||||
|
||||
res = super(stock_change_product_qty, self).default_get(cr, uid, fields, context=context)
|
||||
|
||||
if 'new_quantity' in fields:
|
||||
res.update({'new_quantity': 1})
|
||||
if 'product_id' in fields:
|
||||
res.update({'product_id': product_id})
|
||||
if 'location_id' in fields:
|
||||
try:
|
||||
model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock')
|
||||
self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context)
|
||||
except (orm.except_orm, ValueError):
|
||||
location_id = False
|
||||
res.update({'location_id': location_id})
|
||||
location_id = res.get('location_id', False)
|
||||
if not location_id:
|
||||
try:
|
||||
model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock')
|
||||
except (orm.except_orm, ValueError):
|
||||
pass
|
||||
if location_id:
|
||||
try:
|
||||
self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context)
|
||||
except (orm.except_orm, ValueError):
|
||||
pass
|
||||
res['location_id'] = location_id
|
||||
return res
|
||||
|
||||
def change_product_qty(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'form_view_ref': False}</field>
|
||||
</record>
|
||||
|
||||
<record id="view_split_in_lots" model="ir.ui.view">
|
||||
|
@ -124,6 +125,7 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'form_view_ref': False}</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -37,7 +37,7 @@ e.g. To have an invoice generated automatically periodically:
|
|||
above. Specify the interval information and partner to be invoice.
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': [],
|
||||
'depends': ['base'],
|
||||
'data': ['security/subcription_security.xml', 'security/ir.model.access.csv', 'subscription_view.xml'],
|
||||
'demo': ['subscription_demo.xml',],
|
||||
'installable': True,
|
||||
|
|
|
@ -128,23 +128,14 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we
|
|||
for use in exist_user:
|
||||
new_user.append(use.id)
|
||||
for id in survey_ref.browse(cr, uid, survey_ids):
|
||||
report = self.create_report(cr, uid, [id.id], 'report.survey.form', id.title)
|
||||
file = open(get_module_resource('survey', 'report') + id.title +".pdf")
|
||||
file_data = ""
|
||||
while 1:
|
||||
line = file.readline()
|
||||
file_data += line
|
||||
if not line:
|
||||
break
|
||||
file.close()
|
||||
attachments[id.title +".pdf"] = file_data
|
||||
os.remove(get_module_resource('survey', 'report') + id.title +".pdf")
|
||||
result, format = openerp.report.render_report(cr, uid, [id.id], 'survey.form', {}, {})
|
||||
attachments[id.title +".pdf"] = result
|
||||
|
||||
for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids):
|
||||
if not partner.email:
|
||||
skipped+= 1
|
||||
continue
|
||||
user = user_ref.search(cr, uid, [('login', "=", partner.email)])
|
||||
user = user_ref.search(cr, uid, [('partner_id', "=", partner.id)])
|
||||
if user:
|
||||
if user[0] not in new_user:
|
||||
new_user.append(user[0])
|
||||
|
@ -185,6 +176,8 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we
|
|||
if ans:
|
||||
res_data = {'name': partner.name or _('Unknown'),
|
||||
'login': partner.email,
|
||||
'email': partner.email,
|
||||
'partner_id': partner.id,
|
||||
'password': passwd,
|
||||
'address_id': partner.id,
|
||||
'groups_id': [[6, 0, [group_id]]],
|
||||
|
|
|
@ -168,20 +168,9 @@ class view(osv.osv):
|
|||
arch_no_whitespace = etree.fromstring(
|
||||
etree.tostring(arch, encoding='utf-8'),
|
||||
parser=etree.XMLParser(encoding='utf-8', remove_blank_text=True))
|
||||
arch_pretty_indent_2 = etree.tostring(
|
||||
return etree.tostring(
|
||||
arch_no_whitespace, encoding='unicode', pretty_print=True)
|
||||
|
||||
# pretty_print uses a fixed indent level of 2, we want an indent of 4,
|
||||
# double up leading spaces.
|
||||
def repl(m):
|
||||
indent = len(m.group(0)) / 2
|
||||
return u' ' * 4 * indent
|
||||
# FIXME: If py2.7 only, can use re.M in sub and don't have to do replacement line by line
|
||||
return u'\n'.join(
|
||||
re.sub(ur'^((?: )+)', repl, line)
|
||||
for line in arch_pretty_indent_2.split(u'\n')
|
||||
)
|
||||
|
||||
def save(self, cr, uid, res_id, value, xpath=None, context=None):
|
||||
""" Update a view section. The view section may embed fields to write
|
||||
|
||||
|
|
|
@ -1789,6 +1789,9 @@
|
|||
if (node.nodeName === 'BR' && node.getAttribute('type') === '_moz') {
|
||||
// <br type="_moz"> appears when focusing RTE in FF, ignore
|
||||
continue;
|
||||
} else if (node.nodeName === 'DIV' && $(node).hasClass('oe_drop_zone')) {
|
||||
// ignore dropzone inserted by snippets
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,136 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* Building block / Snippet Editor
|
||||
|
||||
The building blocks appear in the edit bar website. These prebuilt html block
|
||||
allowing the designer to easily generate content on a page (drag and drop).
|
||||
Options allow snippets to add customizations part html code according to their
|
||||
selector (jQuery) and javascript object.
|
||||
|
||||
How to create content?
|
||||
|
||||
Designers can add their own html block in the "snippets" (/website/views/snippets.xml).
|
||||
The block must be added in one of four menus (structure, content, feature or effect).
|
||||
Structure:
|
||||
<div>
|
||||
<div class="oe_snippet_thumbnail">
|
||||
<img class="oe_snippet_thumbnail_img" src="...image src..."/>
|
||||
<span class="oe_snippet_thumbnail_title">...Block Name...</span>
|
||||
</div>
|
||||
<div class="oe_snippet_body">
|
||||
...
|
||||
<!--
|
||||
The block with class 'oe_snippet_body' is inserted in the page.
|
||||
This class is removed when the block is dropped.
|
||||
The block can be made of any html tag and content. -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
How to create options?
|
||||
|
||||
Designers can add their own html block in the "snippet_options" (/website/views/snippets.xml).
|
||||
Structure:
|
||||
|
||||
<div data-snippet-option-id='...' <!-- Required: javascript object id (but javascript
|
||||
for this option object is not required) -->
|
||||
data-selector="..." <!-- Required: jQuery selector.
|
||||
Apply options on all The part of html who
|
||||
match with this jQuery selector.
|
||||
E.g.: If the selector is div, all div will be selected
|
||||
and can be highlighted and assigned an editor. -->
|
||||
data-selector-siblings="..." <!-- Optional: jQuery selector.
|
||||
The html part can be insert or move beside
|
||||
the selected html block -->
|
||||
data-selector-children="..." <!-- Optional: jQuery selector.
|
||||
The html part can be insert or move inside
|
||||
the selected html block -->
|
||||
data-selector-vertical-children='...'> <!-- Optional: jQuery selector.
|
||||
The html part can be insert or move inside
|
||||
the selected html block. The drop zone is
|
||||
displayed vertically -->
|
||||
...
|
||||
<li><a href="#">...</a></li> <!-- Optional: html li list.
|
||||
List of menu items displayed in customize
|
||||
menu. If the li tag have 'data-class', the
|
||||
class is automaticcally added or removed to
|
||||
the html content when the user select this item. -->
|
||||
...
|
||||
<li class="dropdown-submenu" <!-- Optional: html li list exemple. !-->
|
||||
data-required="true"> <!-- Optional: if only one item can be selected
|
||||
and can't be unselect. !-->
|
||||
<a tabindex="-1" href="#">...</a> <!-- bootstrap dropdown button !-->
|
||||
<ul class="dropdown-menu">
|
||||
<li data-value="text_only"><a>...</a></li> <!-- by default data-value is apply
|
||||
like a class to html block !-->
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
How to create a javascript object for an options?
|
||||
|
||||
openerp.website.snippet.options["...option-id..."] = website.snippet.Option.extend({
|
||||
// start is called when the user click into a block or when the user drop a block
|
||||
// into the page (just after the init method).
|
||||
// start is usually used to bind event.
|
||||
//
|
||||
// this.$target: block html inserted inside the page
|
||||
// this.$el: html li list of this options
|
||||
// this.$overlay: html editor overlay who content resize bar, customize menu...
|
||||
start: function () {},
|
||||
|
||||
|
||||
// onFocus is called when the user click inside the block inserted in page
|
||||
// and when the user drop on block into the page
|
||||
onFocus : function () {},
|
||||
|
||||
|
||||
// onBlur is called when the user click outside the block inserted in page, if
|
||||
// the block is focused
|
||||
onBlur : function () {},
|
||||
|
||||
|
||||
// on_clone is called when the snippet is duplicate
|
||||
// @variables: $clone is allready inserted is the page
|
||||
on_clone: function ($clone) {},
|
||||
|
||||
|
||||
// on_remove is called when the snippet is removed (dom is removing after this tigger)
|
||||
on_remove: function () {},
|
||||
|
||||
|
||||
// drop_and_build_snippet is called just after that a thumbnail is drag and dropped
|
||||
// into a drop zone. The content is already inserted in the page.
|
||||
drop_and_build_snippet: function () {},
|
||||
|
||||
// select is called when a user select an item in the li list of options
|
||||
// By default, if the li item have a data-value attribute, the data-vlue it's apply
|
||||
// like a class to the html block (this.$target)
|
||||
// @variables: next_previous = {$next, $prev}
|
||||
// $next = next item selected or false
|
||||
// $prev = previous item selected or false
|
||||
select: function (event, next_previous) {}
|
||||
|
||||
// preview is called when a user is on mouse over or mouse out of an item
|
||||
// variables: next_previous = {$next, $prev}
|
||||
// $next = next item selected or false
|
||||
// $prev = previous item selected or false
|
||||
preview: function (event, next_previous) {}
|
||||
|
||||
// clean_for_save
|
||||
// clean_for_save is called just before to save the vue
|
||||
// Sometime it's important to remove or add some datas (contentEditable, added
|
||||
// classes to a running animation...)
|
||||
clean_for_save: function () {}
|
||||
});
|
||||
|
||||
|
||||
// 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped
|
||||
// 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated
|
||||
|
||||
*/
|
||||
|
||||
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website/static/src/xml/website.snippets.xml');
|
||||
|
||||
|
@ -19,23 +149,11 @@
|
|||
edit: function () {
|
||||
var self = this;
|
||||
$("[data-oe-model] *, [data-oe-type=html] *").off('click');
|
||||
website.snippet.stop_animation();
|
||||
window.snippets = this.snippets = new website.snippet.BuildingBlock(this);
|
||||
this.snippets.appendTo(this.$el);
|
||||
this.on('rte:ready', this, function () {
|
||||
self.snippets.$button.removeClass("hidden");
|
||||
website.snippet.start_animation();
|
||||
$(website.snippet.readyAnimation).each(function() {
|
||||
var animation = $(this).data("snippet-view");
|
||||
if (animation) {
|
||||
animation.$target.on('focus', '*', function(){
|
||||
animation.stop();
|
||||
});
|
||||
animation.$target.on('blur', '*', function(){
|
||||
animation.start();
|
||||
});
|
||||
}
|
||||
});
|
||||
website.snippet.stop_animation();
|
||||
});
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
|
@ -56,9 +174,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
// 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped
|
||||
// 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated
|
||||
|
||||
if (!website.snippet) website.snippet = {};
|
||||
website.snippet.templateOptions = {};
|
||||
website.snippet.globalSelector = "";
|
||||
|
@ -397,18 +512,17 @@
|
|||
$("#oe_snippets").trigger('snippet-dropped', $target);
|
||||
|
||||
website.snippet.start_animation(true, $target);
|
||||
|
||||
// drop_and_build_snippet
|
||||
self.create_overlay($target);
|
||||
if ($target.data("snippet-editor")) {
|
||||
$target.data("snippet-editor").drop_and_build_snippet($target);
|
||||
$target.data("snippet-editor").drop_and_build_snippet();
|
||||
}
|
||||
for (var k in website.snippet.templateOptions) {
|
||||
$target.find(website.snippet.templateOptions[k].selector).each(function () {
|
||||
var $snippet = $(this);
|
||||
self.create_overlay($snippet);
|
||||
if ($snippet.data("snippet-editor")) {
|
||||
$snippet.data("snippet-editor").drop_and_build_snippet($snippet);
|
||||
$snippet.data("snippet-editor").drop_and_build_snippet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -665,80 +779,12 @@
|
|||
self.set_active();
|
||||
self.$target.trigger("snippet-style-change", [self, np]);
|
||||
},0);
|
||||
this.select(event, {'$next': $next, '$prev': $prev});
|
||||
this.select({'$next': $next, '$prev': $prev});
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
self.$target.trigger("snippet-style-preview", [self, np]);
|
||||
},0);
|
||||
this.preview(event, np);
|
||||
}
|
||||
},
|
||||
// start is call just after the init
|
||||
start: function () {
|
||||
},
|
||||
/* onFocus
|
||||
* This method is called when the user click inside the snippet in the dom
|
||||
*/
|
||||
onFocus : function () {
|
||||
},
|
||||
|
||||
/* onFocus
|
||||
* This method is called when the user click outside the snippet in the dom, after a focus
|
||||
*/
|
||||
onBlur : function () {
|
||||
},
|
||||
|
||||
/* on_clone
|
||||
* This method is called when the snippet is cloned ($clone is allready inserted)
|
||||
*/
|
||||
on_clone: function ($clone) {
|
||||
},
|
||||
|
||||
/* on_remove
|
||||
* This method is called when the snippet is removed (dom is removing after this call)
|
||||
*/
|
||||
on_remove: function () {
|
||||
},
|
||||
|
||||
/*
|
||||
* drop_and_build_snippet
|
||||
* This method is called just after that a thumbnail is drag and dropped into a drop zone
|
||||
* (after the insertion of this.$body, if this.$body exists)
|
||||
*/
|
||||
drop_and_build_snippet: function ($target) {
|
||||
},
|
||||
/* select
|
||||
* called when a user select an item
|
||||
* li must have data-value attribute
|
||||
* variables: np = {$next, $prev}
|
||||
* $next is false if they are no next item selected
|
||||
* $prev is false if they are no previous item selected
|
||||
*/
|
||||
select: function (event, np) {
|
||||
var self = this;
|
||||
// add or remove html class
|
||||
if (np.$prev) {
|
||||
this.$target.removeClass(np.$prev.data('value' || ""));
|
||||
}
|
||||
if (np.$next) {
|
||||
this.$target.addClass(np.$next.data('value') || "");
|
||||
}
|
||||
},
|
||||
/* preview
|
||||
* called when a user is on mouse over or mouse out of an item
|
||||
* variables: np = {$next, $prev}
|
||||
* $next is false if they are no next item selected
|
||||
* $prev is false if they are no previous item selected
|
||||
*/
|
||||
preview: function (event, np) {
|
||||
var self = this;
|
||||
|
||||
// add or remove html class
|
||||
if (np.$prev) {
|
||||
this.$target.removeClass(np.$prev.data('value') || "");
|
||||
}
|
||||
if (np.$next) {
|
||||
this.$target.addClass(np.$next.data('value') || "");
|
||||
this.preview(np);
|
||||
}
|
||||
},
|
||||
/* set_active
|
||||
|
@ -757,9 +803,48 @@
|
|||
.addClass("active");
|
||||
this.$el.find('li:has(li[data-value].active)').addClass("active");
|
||||
},
|
||||
/* clean_for_save
|
||||
* function called just before save vue
|
||||
*/
|
||||
|
||||
start: function () {
|
||||
},
|
||||
|
||||
onFocus : function () {
|
||||
},
|
||||
|
||||
onBlur : function () {
|
||||
},
|
||||
|
||||
on_clone: function ($clone) {
|
||||
},
|
||||
|
||||
on_remove: function () {
|
||||
},
|
||||
|
||||
drop_and_build_snippet: function () {
|
||||
},
|
||||
|
||||
select: function (np) {
|
||||
var self = this;
|
||||
// add or remove html class
|
||||
if (np.$prev && this.required) {
|
||||
this.$target.removeClass(np.$prev.data('value' || ""));
|
||||
}
|
||||
if (np.$next) {
|
||||
this.$target.addClass(np.$next.data('value') || "");
|
||||
}
|
||||
},
|
||||
|
||||
preview: function (np) {
|
||||
var self = this;
|
||||
|
||||
// add or remove html class
|
||||
if (np.$prev && this.required) {
|
||||
this.$target.removeClass(np.$prev.data('value') || "");
|
||||
}
|
||||
if (np.$next) {
|
||||
this.$target.addClass(np.$next.data('value') || "");
|
||||
}
|
||||
},
|
||||
|
||||
clean_for_save: function () {
|
||||
}
|
||||
});
|
||||
|
@ -776,9 +861,9 @@
|
|||
var src = this._get_bg();
|
||||
this.$el.find("li[data-value].active.oe_custom_bg").data("src", src);
|
||||
},
|
||||
select: function(event, np) {
|
||||
select: function(np) {
|
||||
var self = this;
|
||||
this._super(event, np);
|
||||
this._super(np);
|
||||
if (np.$next) {
|
||||
if (np.$next.hasClass("oe_custom_bg")) {
|
||||
var editor = new website.editor.ImageDialog();
|
||||
|
@ -803,8 +888,8 @@
|
|||
this.$target.removeClass(np.$prev.data("value"));
|
||||
}
|
||||
},
|
||||
preview: function (event, np) {
|
||||
this._super(event, np);
|
||||
preview: function (np) {
|
||||
this._super(np);
|
||||
if (np.$next) {
|
||||
this._set_bg(np.$next.data("src"));
|
||||
}
|
||||
|
@ -1291,6 +1376,7 @@
|
|||
this.grid.size = 8;
|
||||
return this.grid;
|
||||
},
|
||||
this.$target.carousel({interval: false});
|
||||
});
|
||||
|
||||
website.snippet.options.parallax = website.snippet.Option.extend({
|
||||
|
@ -1539,9 +1625,9 @@
|
|||
* This method is called just after that a thumbnail is drag and dropped into a drop zone
|
||||
* (after the insertion of this.$body, if this.$body exists)
|
||||
*/
|
||||
drop_and_build_snippet: function ($target) {
|
||||
drop_and_build_snippet: function () {
|
||||
for (var i in this.styles){
|
||||
this.styles[i].drop_and_build_snippet($target);
|
||||
this.styles[i].drop_and_build_snippet();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -923,7 +923,7 @@
|
|||
</div>
|
||||
|
||||
<div data-snippet-option-id='carousel-style'
|
||||
data-selector="div[data-snippet-id='carousel']">
|
||||
data-selector=".carousel:not(.quotecarousel)">
|
||||
<li class="dropdown-submenu" data-required="true">
|
||||
<a tabindex="-1" href="#">Layout</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
|
|
@ -226,7 +226,8 @@ class Ecommerce(http.Controller):
|
|||
def shop(self, category=None, page=0, filters='', search='', **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
product_obj = request.registry.get('product.template')
|
||||
domain = request.registry.get('website').ecommerce_get_product_domain()
|
||||
base_domain = request.registry.get('website').ecommerce_get_product_domain()
|
||||
domain = list(base_domain)
|
||||
if search:
|
||||
domain += ['|',
|
||||
('name', 'ilike', search),
|
||||
|
@ -265,9 +266,15 @@ class Ecommerce(http.Controller):
|
|||
pass
|
||||
|
||||
category_obj = request.registry.get('product.public.category')
|
||||
category_ids = category_obj.search(cr, uid, [], context=context)
|
||||
category_ids = [product['public_categ_id'][0] for product in product_obj.read_group(cr, uid, base_domain, ['public_categ_id'], ['public_categ_id'], context=context) if product['public_categ_id']]
|
||||
categories = category_obj.browse(cr, uid, category_ids, context=context)
|
||||
categs = filter(lambda x: not x.parent_id, categories)
|
||||
all_categories = set(categories)
|
||||
for cat in categories:
|
||||
parent = cat.parent_id
|
||||
while parent:
|
||||
all_categories.add(parent)
|
||||
parent = parent.parent_id
|
||||
categories = list(all_categories)
|
||||
|
||||
values = {
|
||||
'products': products,
|
||||
|
@ -282,7 +289,8 @@ class Ecommerce(http.Controller):
|
|||
'pager': pager,
|
||||
'styles': styles,
|
||||
'category': category,
|
||||
'categories': categs,
|
||||
'categories': filter(lambda x: not x.parent_id, categories),
|
||||
'all_categories': categories,
|
||||
'Ecommerce': self, # TODO fp: Should be removed
|
||||
'style_in_product': lambda style, product: style.id in [s.id for s in product.website_style_ids],
|
||||
}
|
||||
|
|
|
@ -33,12 +33,14 @@
|
|||
|
||||
<template id="categories_recursive" name="Category list">
|
||||
<li t-att-class="int(categ) == int(category or 0) and 'active' or ''">
|
||||
<t t-if="categ in all_categories">
|
||||
<a t-attf-href="/shop/category/#{ slug(categ) }/" t-field="categ.name"></a>
|
||||
<ul t-if="categ.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
|
||||
<t t-foreach="categ.child_id" t-as="categ">
|
||||
<t t-call="website_sale.categories_recursive"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
|
@ -730,7 +732,7 @@
|
|||
<div class="col-md-8 oe_mycart">
|
||||
<h3 class="page-header mt16">Billing Information
|
||||
<small groups="base.group_public"> or
|
||||
<a class='btn btn-primary' t-if="not partner" t-attf-href="/web?redirect=#{ request.httprequest.url }">Sign in</a>
|
||||
<a class='btn btn-primary' t-if="not partner" t-attf-href="/web/login?redirect=#{ request.httprequest.url }">Sign in</a>
|
||||
</small>
|
||||
</h3>
|
||||
<div class="row">
|
||||
|
|
Loading…
Reference in New Issue