[MERGE] Merged with main Addons
bzr revid: mka@tinyerp.com-20140410103213-kq36gxiih5xdmt4l bzr revid: mka@tinyerp.com-20140411053339-yic5dugaubzbyfhq
|
@ -47,7 +47,7 @@ class account_installer(osv.osv_memory):
|
|||
|
||||
# try get the list on apps server
|
||||
try:
|
||||
apps_server = self.pool.get('ir.config_parameter').get_param(cr, uid, 'apps.server', 'https://apps.openerp.com')
|
||||
apps_server = self.pool.get('ir.module.module').get_apps_server(cr, uid, context=context)
|
||||
|
||||
up = urlparse.urlparse(apps_server)
|
||||
url = '{0.scheme}://{0.netloc}/apps/charts?serie={1}'.format(up, serie)
|
||||
|
|
|
@ -94,7 +94,7 @@ class account_entries_report(osv.osv):
|
|||
return super(account_entries_report, self).search(cr, uid, args=args, offset=offset, limit=limit, order=order,
|
||||
context=context, count=count)
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False,lazy=True):
|
||||
if context is None:
|
||||
context = {}
|
||||
fiscalyear_obj = self.pool.get('account.fiscalyear')
|
||||
|
@ -108,7 +108,7 @@ class account_entries_report(osv.osv):
|
|||
domain.append(['period_id','in',ids])
|
||||
else:
|
||||
domain = domain
|
||||
return super(account_entries_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby)
|
||||
return super(account_entries_report, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby,lazy)
|
||||
|
||||
def init(self, cr):
|
||||
tools.drop_view_if_exists(cr, 'account_entries_report')
|
||||
|
|
|
@ -328,7 +328,7 @@ class account_asset_asset(osv.osv):
|
|||
default = {}
|
||||
if context is None:
|
||||
context = {}
|
||||
default.update({'depreciation_line_ids': [], 'state': 'draft'})
|
||||
default.update({'depreciation_line_ids': [], 'account_move_line_ids': [], 'history_ids': [], 'state': 'draft'})
|
||||
return super(account_asset_asset, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
def _compute_entries(self, cr, uid, ids, period_id, context=None):
|
||||
|
|
|
@ -26,6 +26,7 @@ import openerp.addons.decimal_precision as dp
|
|||
class analytic_user_funct_grid(osv.osv):
|
||||
_name="analytic.user.funct.grid"
|
||||
_description= "Price per User"
|
||||
_rec_name="user_id"
|
||||
_columns={
|
||||
'user_id': fields.many2one("res.users", "User", required=True,),
|
||||
'product_id': fields.many2one("product.product", "Service", required=True,),
|
||||
|
|
|
@ -2,6 +2,7 @@ import functools
|
|||
import logging
|
||||
|
||||
import simplejson
|
||||
import urlparse
|
||||
import werkzeug.utils
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
|
@ -68,7 +69,8 @@ class OAuthLogin(openerp.addons.web.controllers.main.Home):
|
|||
def get_state(self, provider):
|
||||
state = dict(
|
||||
d=request.session.db,
|
||||
p=provider['id']
|
||||
p=provider['id'],
|
||||
r=request.httprequest.full_path
|
||||
)
|
||||
token = request.params.get('token')
|
||||
if token:
|
||||
|
@ -137,8 +139,12 @@ class OAuthController(http.Controller):
|
|||
cr.commit()
|
||||
action = state.get('a')
|
||||
menu = state.get('m')
|
||||
redirect = state.get('r')
|
||||
url = '/web'
|
||||
if action:
|
||||
if redirect and not redirect.startswith('/auth_oauth/signin') and \
|
||||
(not redirect.startswith('/web/login') or 'redirect' in urlparse.urlsplit(redirect).query):
|
||||
url = redirect
|
||||
elif action:
|
||||
url = '/web#action=%s' % action
|
||||
elif menu:
|
||||
url = '/web#menu_id=%s' % menu
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<field name="password"></field>
|
||||
<field name="active" eval="False"/>
|
||||
<!-- Avoid auto-including this user in any default group, just like a typical portal member -->
|
||||
<field name="groups_id" eval="[(4,ref('base.group_portal'))]"/>
|
||||
<field name="groups_id" eval="[(6, 0, [ref('base.group_portal')])]"/>
|
||||
<!-- allow signuped users to have a alias -->
|
||||
<field name="alias_name">_usertemplate</field>
|
||||
</record>
|
||||
|
|
|
@ -22,5 +22,6 @@
|
|||
import res_company
|
||||
import ir_translation
|
||||
import wizard
|
||||
import controller
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
import gengo_callback
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import openerp
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
|
||||
import json
|
||||
|
||||
class website_gengo(http.Controller):
|
||||
@http.route('/website/gengo_callback', type='http', auth='none')
|
||||
def gengo_callback(self,**post):
|
||||
cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
|
||||
translation_pool = request.registry['ir.translation']
|
||||
if post and post.get('job'):
|
||||
job = json.loads(post['job'])
|
||||
tid = job.get('custom_data', False)
|
||||
if (job.get('status') == 'approved') and tid:
|
||||
term = translation_pool.browse(cr, uid, int(tid), context=context)
|
||||
if term.job_id <> job.get('job_id'):
|
||||
raise 'Error'
|
||||
vals = {'state': 'translated', 'value': job.get('body_tgt')}
|
||||
translation_pool.write(cr, uid, [int(tid)], vals, context=context)
|
|
@ -5,8 +5,8 @@
|
|||
<record id="gengo_sync_receive_request_scheduler" model="ir.cron">
|
||||
<field name="name" >Gengo Sync Translation (Response)</field>
|
||||
<field eval="True" name="active"/>
|
||||
<field name="interval_number">20</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="interval_number">6</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="'base.gengo.translations'" name="model"></field>
|
||||
<field eval="'_sync_response'" name="function"/>
|
||||
|
@ -17,8 +17,8 @@
|
|||
<record id="gengo_sync_send_request_scheduler" model="ir.cron">
|
||||
<field name="name" >Gengo Sync Translation (Request)</field>
|
||||
<field eval="True" name="active"/>
|
||||
<field name="interval_number">20</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="interval_number">6</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="'base.gengo.translations'" name="model"></field>
|
||||
<field eval="'_sync_request'" name="function"/>
|
||||
|
|
|
@ -33,10 +33,6 @@ try:
|
|||
from mygengo import MyGengo
|
||||
except ImportError:
|
||||
_logger.warning('Gengo library not found, Gengo features disabled. If you plan to use it, please install the mygengo library from http://pypi.python.org/pypi/mygengo')
|
||||
class MyGengo(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
# no context for translations - so don't bother
|
||||
raise ImportError('Gengo library not found, please install mygengo from http://pypi.python.org/pypi/mygengo')
|
||||
|
||||
GENGO_DEFAULT_LIMIT = 20
|
||||
|
||||
|
@ -120,51 +116,45 @@ class base_gengo_translations(osv.osv_memory):
|
|||
all_translation_ids = translation_pool.search(cr, uid, [('state', '=', 'inprogress'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('job_id', "!=", False)], context=context)
|
||||
while True:
|
||||
translation_ids = all_translation_ids[offset:offset + limit]
|
||||
if translation_ids:
|
||||
offset += limit
|
||||
translation_terms = translation_pool.browse(cr, uid, translation_ids, context=context)
|
||||
gengo_job_id = [term.job_id for term in translation_terms]
|
||||
if gengo_job_id:
|
||||
gengo_ids = ','.join(gengo_job_id)
|
||||
job_response = gengo.getTranslationJobBatch(id=gengo_ids)
|
||||
if job_response['opstat'] == 'ok':
|
||||
job_response_dict = dict([(job['job_id'], job) for job in job_response['response']['jobs']])
|
||||
for term in translation_terms:
|
||||
up_term = up_comment = 0
|
||||
vals = {}
|
||||
if job_response_dict[term.job_id]['status'] == 'approved':
|
||||
vals.update({'state': 'translated',
|
||||
'value': job_response_dict[term.job_id]['body_tgt']})
|
||||
up_term += 1
|
||||
job_comment = gengo.getTranslationJobComments(id=term.job_id)
|
||||
if job_comment['opstat'] == 'ok':
|
||||
gengo_comments = ""
|
||||
for comment in job_comment['response']['thread']:
|
||||
gengo_comments += _('%s\n-- Commented on %s by %s.\n\n') % (comment['body'], time.ctime(comment['ctime']), comment['author'])
|
||||
vals.update({'gengo_comment': gengo_comments})
|
||||
up_comment += 1
|
||||
if vals:
|
||||
translation_pool.write(cr, uid, term.id, vals)
|
||||
_logger.info("Successfully Updated `%d` terms and %d Comments." % (up_term, up_comment))
|
||||
if not len(translation_ids) == limit:
|
||||
offset += limit
|
||||
if not translation_ids:
|
||||
break
|
||||
translation_terms = translation_pool.browse(cr, uid, translation_ids, context=context)
|
||||
gengo_job_id = [term.job_id for term in translation_terms]
|
||||
if gengo_job_id:
|
||||
gengo_ids = ','.join(gengo_job_id)
|
||||
try:
|
||||
job_response = gengo.getTranslationJobBatch(id=gengo_ids)
|
||||
except:
|
||||
continue
|
||||
if job_response['opstat'] == 'ok':
|
||||
for job in job_response['response'].get('jobs', []):
|
||||
self._update_terms_job(cr, uid, job, context=context)
|
||||
return True
|
||||
|
||||
def _update_terms_job(self, cr, uid, job, context=None):
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
tid = int(job['custom_data'])
|
||||
vals = {}
|
||||
if job.get('job_id', False):
|
||||
vals['job_id'] = job['job_id']
|
||||
vals['state'] = 'inprogress'
|
||||
if job.get('status', False) in ('queued','available','pending','reviewable'):
|
||||
vals['state'] = 'inprogress'
|
||||
if job.get('body_tgt', False) and job.get('status', False)=='approved':
|
||||
vals['value'] = job['body_tgt']
|
||||
if job.get('status', False) in ('approved', 'canceled'):
|
||||
vals['state'] = 'translated'
|
||||
if vals:
|
||||
translation_pool.write(cr, uid, [tid], vals, context=context)
|
||||
|
||||
def _update_terms(self, cr, uid, response, context=None):
|
||||
"""
|
||||
Update the terms after their translation were requested to Gengo
|
||||
"""
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
for jobs in response['jobs']:
|
||||
for jobs in response.get('jobs', []):
|
||||
for t_id, res in jobs.items():
|
||||
vals = {}
|
||||
t_id = int(t_id)
|
||||
tier = translation_pool.read(cr, uid, [t_id], ['gengo_translation'], context=context)[0]['gengo_translation']
|
||||
if tier == "machine":
|
||||
vals.update({'value': res['body_tgt'], 'state': 'translated'})
|
||||
else:
|
||||
vals.update({'job_id': res['job_id'], 'state': 'inprogress'})
|
||||
translation_pool.write(cr, uid, [t_id], vals, context=context)
|
||||
self._update_terms_job(cr, uid, res, context=context)
|
||||
return
|
||||
|
||||
def pack_jobs_request(self, cr, uid, term_ids, context=None):
|
||||
|
@ -181,17 +171,22 @@ class base_gengo_translations(osv.osv_memory):
|
|||
auto_approve = 1 if user.company_id.gengo_auto_approve else 0
|
||||
for term in translation_pool.browse(cr, uid, term_ids, context=context):
|
||||
if re.search(r"\w", term.src or ""):
|
||||
jobs[term.id] = {'type': 'text',
|
||||
'slug': 'single::English to ' + term.lang,
|
||||
'tier': tools.ustr(term.gengo_translation),
|
||||
'body_src': term.src,
|
||||
'lc_src': 'en',
|
||||
'lc_tgt': translation_pool._get_gengo_corresponding_language(term.lang),
|
||||
'auto_approve': auto_approve,
|
||||
'comment': user.company_id.gengo_comment and "%s %s"%(user.company_id.gengo_comment,term.gengo_comment) or term.gengo_comment,
|
||||
'callback_url': self.pool.get('ir.config_parameter').get_param(cr, uid,'web.base.url') + '/website/gengo_callback/' + str(term.id)
|
||||
comment = user.company_id.gengo_comment or ''
|
||||
if term.gengo_comment:
|
||||
comment+='\n' + term.gengo_comment
|
||||
jobs[time.strftime('%Y%m%d%H%M%S') + '-' + str(term.id)] = {
|
||||
'type': 'text',
|
||||
'slug': 'Single :: English to ' + term.lang,
|
||||
'tier': tools.ustr(term.gengo_translation),
|
||||
'custom_data': str(term.id),
|
||||
'body_src': term.src,
|
||||
'lc_src': 'en',
|
||||
'lc_tgt': translation_pool._get_gengo_corresponding_language(term.lang),
|
||||
'auto_approve': auto_approve,
|
||||
'comment': comment,
|
||||
'callback_url': self.pool.get('ir.config_parameter').get_param(cr, uid,'web.base.url') + '/website/gengo_callback'
|
||||
}
|
||||
return {'jobs': jobs}
|
||||
return {'jobs': jobs, 'as_group': 1}
|
||||
|
||||
|
||||
def _send_translation_terms(self, cr, uid, term_ids, context=None):
|
||||
|
@ -223,15 +218,14 @@ class base_gengo_translations(osv.osv_memory):
|
|||
context = {}
|
||||
language_pool = self.pool.get('res.lang')
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
domain = [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('job_id', "=", False)]
|
||||
if context.get('gengo_language', False):
|
||||
lc = language_pool.browse(cr, uid, context['gengo_language'], context=context).code
|
||||
domain.append( ('lang', '=', lc) )
|
||||
|
||||
all_term_ids = translation_pool.search(cr, uid, domain, context=context)
|
||||
try:
|
||||
#by default, the request will be made for all terms that needs it, whatever the language
|
||||
lang_ids = language_pool.search(cr, uid, [], context=context)
|
||||
if context.get('gengo_language'):
|
||||
#but if this specific key is given, then we restrict the request on terms of this language only
|
||||
lang_ids = [context.get('gengo_language')]
|
||||
langs = [lang.code for lang in language_pool.browse(cr, uid, lang_ids, context=context)]
|
||||
offset = 0
|
||||
all_term_ids = translation_pool.search(cr, uid, [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('lang', 'in', langs), ('job_id', "=", False)], context=context)
|
||||
while True:
|
||||
#search for the n first terms to translate
|
||||
term_ids = all_term_ids[offset:offset + limit]
|
||||
|
|
|
@ -1476,7 +1476,7 @@ class calendar_event(osv.Model):
|
|||
self.create_attendees(cr, uid, [res], context=context)
|
||||
return res
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
|
@ -1484,7 +1484,7 @@ class calendar_event(osv.Model):
|
|||
raise osv.except_osv(_('Warning!'), _('Group by date is not supported, use the calendar view instead.'))
|
||||
virtual_id = context.get('virtual_id', True)
|
||||
context.update({'virtual_id': False})
|
||||
res = super(calendar_event, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
|
||||
res = super(calendar_event, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy)
|
||||
for result in res:
|
||||
#remove the count, since the value is not consistent with the result of the search when expand the group
|
||||
for groupname in groupby:
|
||||
|
|
|
@ -2005,7 +2005,7 @@ class nodefd_content(StringIO, node_descriptor):
|
|||
|
||||
par = self._get_parent()
|
||||
uid = par.context.uid
|
||||
cr = openerp.registry(par.context.dbname).db.cursor()
|
||||
cr = openerp.registry(par.context.dbname).cursor()
|
||||
try:
|
||||
if self.mode in ('w', 'w+', 'r+'):
|
||||
data = self.getvalue()
|
||||
|
@ -2058,7 +2058,7 @@ class nodefd_static(StringIO, node_descriptor):
|
|||
|
||||
par = self._get_parent()
|
||||
# uid = par.context.uid
|
||||
cr = openerp.registry(par.context.dbname).db.cursor()
|
||||
cr = openerp.registry(par.context.dbname).cursor()
|
||||
try:
|
||||
if self.mode in ('w', 'w+', 'r+'):
|
||||
data = self.getvalue()
|
||||
|
|
|
@ -34,18 +34,14 @@ def _edi_dispatch(db_name, method_name, *method_args):
|
|||
try:
|
||||
registry = openerp.modules.registry.RegistryManager.get(db_name)
|
||||
assert registry, 'Unknown database %s' % db_name
|
||||
edi = registry['edi.edi']
|
||||
cr = registry.db.cursor()
|
||||
res = None
|
||||
res = getattr(edi, method_name)(cr, *method_args)
|
||||
cr.commit()
|
||||
with registry.cursor() as cr:
|
||||
edi = registry['edi.edi']
|
||||
return getattr(edi, method_name)(cr, *method_args)
|
||||
|
||||
except Exception, e:
|
||||
_logger.exception('Failed to execute EDI method %s with args %r.',
|
||||
method_name, method_args)
|
||||
raise
|
||||
finally:
|
||||
cr.close()
|
||||
return res
|
||||
|
||||
def exp_import_edi_document(db_name, uid, passwd, edi_document, context=None):
|
||||
return _edi_dispatch(db_name, 'import_edi', uid, edi_document, None)
|
||||
|
|
|
@ -105,9 +105,9 @@
|
|||
<field name="phone"/>
|
||||
<field name="nb_register" />
|
||||
<field name="state"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object" icon="gtk-jump-to"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object"/>
|
||||
</tree>
|
||||
<form string="Registration">
|
||||
<field name="partner_id" attrs="{'readonly':[('state','!=', 'draft')]}" on_change="onchange_partner_id(partner_id, context)" />
|
||||
|
@ -125,9 +125,9 @@
|
|||
<separator string="" colspan="4"/>
|
||||
<newline/>
|
||||
<field name="state" colspan="2"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object" icon="gtk-jump-to"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -263,21 +263,21 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Events">
|
||||
<field name="name" string="Events"/>
|
||||
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<separator/>
|
||||
<filter icon="terp-check" string="Unconfirmed" name="draft" domain="[('state','=','draft')]" help="Events in New state"/>
|
||||
<filter icon="terp-camera_test" string="Confirmed" domain="[('state','=','confirm')]" help="Confirmed events"/>
|
||||
<filter string="Unconfirmed" name="draft" domain="[('state','=','draft')]" help="Events in New state"/>
|
||||
<filter string="Confirmed" domain="[('state','=','confirm')]" help="Confirmed events"/>
|
||||
<separator/>
|
||||
<filter icon="terp-personal" string="My Events" help="My Events" domain="[('user_id','=',uid)]"/>
|
||||
<filter string="My Events" help="My Events" domain="[('user_id','=',uid)]"/>
|
||||
<separator/>
|
||||
<filter icon="terp-go-today" string="Upcoming" name="upcoming" domain="[('date_begin','>=', time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Upcoming events from today" />
|
||||
<filter string="Upcoming" name="upcoming" domain="[('date_begin','>=', time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Upcoming events from today" />
|
||||
<field name="type"/>
|
||||
<field name="user_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Responsible" icon="terp-personal" context="{'group_by': 'user_id'}"/>
|
||||
<filter string="Event Type" icon="terp-crm" context="{'group_by':'type'}"/>
|
||||
<filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/>
|
||||
<filter string="Start Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_begin'}"/>
|
||||
<filter string="Responsible" context="{'group_by': 'user_id'}"/>
|
||||
<filter string="Event Type" context="{'group_by':'type'}"/>
|
||||
<filter string="Status" context="{'group_by':'state'}"/>
|
||||
<filter string="Start Month" domain="[]" context="{'group_by':'date_begin'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
@ -333,9 +333,9 @@
|
|||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
<field name="message_unread" invisible="1"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object" icon="gtk-jump-to"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/>
|
||||
<button name="registration_open" string="Confirm Registration" states="draft" type="object"/>
|
||||
<button name="button_reg_close" string="Attended the Event" states="open" type="object"/>
|
||||
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -367,7 +367,7 @@
|
|||
<field name="email" class="oe_inline"/>
|
||||
<button class="oe_inline oe_right" string="Send Email"
|
||||
name="%(mail.action_email_compose_message_wizard)d"
|
||||
icon="terp-mail-message-new" context= '{"default_email_to":email}' type="action"/>
|
||||
context= '{"default_email_to":email}' type="action"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
|
@ -420,20 +420,20 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Event Registration">
|
||||
<field name="name" string="Participant" filter_domain="['|','|',('name','ilike',self),('email','ilike',self),('origin','ilike',self)]"/>
|
||||
<filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<separator/>
|
||||
<filter icon="terp-check" string="New" name="draft" domain="[('state','=','draft')]" help="Registrations in unconfirmed state"/>
|
||||
<filter icon="terp-camera_test" string="Confirmed" domain="[('state','=','open')]" help="Confirmed registrations"/>
|
||||
<filter string="New" name="draft" domain="[('state','=','draft')]" help="Registrations in unconfirmed state"/>
|
||||
<filter string="Confirmed" domain="[('state','=','open')]" help="Confirmed registrations"/>
|
||||
<separator/>
|
||||
<filter icon="terp-personal" string="My Registrations" help="My Registrations" domain="[('user_id','=',uid)]"/>
|
||||
<filter string="My Registrations" help="My Registrations" domain="[('user_id','=',uid)]"/>
|
||||
<field name="event_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="partner_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Event" icon="terp-crm" domain="[]" context="{'group_by':'event_id'}"/>
|
||||
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Event" name="group_event" domain="[]" context="{'group_by':'event_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -17,6 +17,28 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_registration_ticket_search">
|
||||
<field name="name">event.registration.ticket.search</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_registration_search" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="group_event" position="after">
|
||||
<filter string="Ticket Type" domain="[]" context="{'group_by':'event_ticket_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_registration_ticket_tree">
|
||||
<field name="name">event.registration.ticket.tree</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field name="event_ticket_id"/>
|
||||
</field>
|
||||
</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" />
|
||||
|
|
|
@ -91,7 +91,7 @@ class IdeaIdea(osv.Model):
|
|||
# Technical stuff
|
||||
#------------------------------------------------------
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
|
||||
""" Override read_group to always display all states. """
|
||||
if groupby and groupby[0] == "state":
|
||||
# Default result structure
|
||||
|
@ -103,7 +103,7 @@ class IdeaIdea(osv.Model):
|
|||
'state_count': 0,
|
||||
} for state_value, state_name in states]
|
||||
# Get standard results
|
||||
read_group_res = super(IdeaIdea, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby)
|
||||
read_group_res = super(IdeaIdea, self).read_group(cr, uid, domain, fields, groupby, offset, limit, context, orderby, lazy)
|
||||
# Update standard results with default results
|
||||
result = []
|
||||
for state_value, state_name in states:
|
||||
|
@ -114,7 +114,7 @@ class IdeaIdea(osv.Model):
|
|||
result.append(res[0])
|
||||
return result
|
||||
else:
|
||||
return super(IdeaIdea, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
|
||||
return super(IdeaIdea, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Workflow / Actions
|
||||
|
|
|
@ -16,5 +16,43 @@
|
|||
<field name="category_id" ref="module_lunch_category"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="lunch_mind_your_own_food" model="ir.rule">
|
||||
<field name="name">lunch.order: do not see and create other people's order</field>
|
||||
<field name="model_id" ref="model_lunch_order"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_user'))]"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
</record>
|
||||
<record id="lunch_mind_other_food" model="ir.rule">
|
||||
<field name="name">lunch.order: do not see and create other people's order</field>
|
||||
<field name="model_id" ref="model_lunch_order"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_manager'))]"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
</record>
|
||||
<record id="lunch_mind_your_own_food_line" model="ir.rule">
|
||||
<field name="name">lunch.order.line: do not see and create other people's order line</field>
|
||||
<field name="model_id" ref="model_lunch_order_line"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_user'))]"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
</record>
|
||||
<record id="lunch_mind_other_food_line" model="ir.rule">
|
||||
<field name="name">lunch.order.line: do not see and create other people's order line</field>
|
||||
<field name="model_id" ref="model_lunch_order_line"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_manager'))]"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
</record>
|
||||
<record id="lunch_mind_your_own_food_money" model="ir.rule">
|
||||
<field name="name">lunch.cashmove: do not see and create other people's cashmove</field>
|
||||
<field name="model_id" ref="model_lunch_cashmove"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_user'))]"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
</record>
|
||||
<record id="lunch_mind_other_food_money" model="ir.rule">
|
||||
<field name="name">lunch.cashmove: do not see and create other people's cashmove</field>
|
||||
<field name="model_id" ref="model_lunch_cashmove"/>
|
||||
<field name="groups" eval="[(4, ref('group_lunch_manager'))]"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -116,7 +116,7 @@ class note_note(osv.osv):
|
|||
}
|
||||
_order = 'sequence'
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
|
||||
if groupby and groupby[0]=="stage_id":
|
||||
|
||||
#search all stages
|
||||
|
@ -169,7 +169,7 @@ class note_note(osv.osv):
|
|||
|
||||
else:
|
||||
return super(note_note, self).read_group(self, cr, uid, domain, fields, groupby,
|
||||
offset=offset, limit=limit, context=context, orderby=orderby)
|
||||
offset=offset, limit=limit, context=context, orderby=orderby,lazy=lazy)
|
||||
|
||||
|
||||
#upgrade config setting page to configure pad, fancy and tags mode
|
||||
|
|
|
@ -13,6 +13,7 @@ openerp.pad = function(instance) {
|
|||
event.preventDefault();
|
||||
self.set("configured", true);
|
||||
});
|
||||
this.pad_loading_request = null;
|
||||
},
|
||||
initialize_content: function() {
|
||||
var self = this;
|
||||
|
@ -30,14 +31,14 @@ openerp.pad = function(instance) {
|
|||
},
|
||||
render_value: function() {
|
||||
var self = this;
|
||||
this._configured_deferred.always(function() {
|
||||
$.when(this._configured_deferred, this.pad_loading_request).always(function() {
|
||||
if (! self.get('configured')) {
|
||||
return;
|
||||
};
|
||||
var value = self.get('value');
|
||||
if (self.get('effective_readonly')) {
|
||||
if (_.str.startsWith(value, 'http')) {
|
||||
this.pad_loading_request = self.view.dataset.call('pad_get_content', {url: value}).done(function(data) {
|
||||
self.pad_loading_request = self.view.dataset.call('pad_get_content', {url: value}).done(function(data) {
|
||||
self.$('.oe_pad_content').removeClass('oe_pad_loading').html('<div class="oe_pad_readonly"><div>');
|
||||
self.$('.oe_pad_readonly').html(data);
|
||||
}).fail(function() {
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
'version': '1.0',
|
||||
'description': """Payment Acquirer Base Module""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['mail', 'account'],
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'views/payment_acquirer.xml',
|
||||
'views/res_config_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class procurement_order(osv.osv):
|
|||
context = {}
|
||||
try:
|
||||
if use_new_cursor:
|
||||
cr = openerp.registry(use_new_cursor).db.cursor()
|
||||
cr = openerp.registry(use_new_cursor).cursor()
|
||||
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
if not skip_exception:
|
||||
|
@ -199,7 +199,7 @@ class procurement_order(osv.osv):
|
|||
if context is None:
|
||||
context = {}
|
||||
if use_new_cursor:
|
||||
cr = openerp.registry(use_new_cursor).db.cursor()
|
||||
cr = openerp.registry(use_new_cursor).cursor()
|
||||
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
|
||||
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
|
|
|
@ -49,7 +49,7 @@ class procurement_compute(osv.osv_memory):
|
|||
"""
|
||||
proc_obj = self.pool.get('procurement.order')
|
||||
#As this function is in a new thread, I need to open a new cursor, because the old one may be closed
|
||||
new_cr = self.pool.db.cursor()
|
||||
new_cr = self.pool.cursor()
|
||||
for proc in self.browse(new_cr, uid, ids, context=context):
|
||||
proc_obj._procure_orderpoint_confirm(new_cr, uid, automatic=proc.automatic, use_new_cursor=new_cr.dbname, context=context)
|
||||
#close the new cursor
|
||||
|
|
|
@ -45,7 +45,7 @@ class procurement_compute_all(osv.osv_memory):
|
|||
"""
|
||||
proc_obj = self.pool.get('procurement.order')
|
||||
#As this function is in a new thread, i need to open a new cursor, because the old one may be closed
|
||||
new_cr = self.pool.db.cursor()
|
||||
new_cr = self.pool.cursor()
|
||||
for proc in self.browse(new_cr, uid, ids, context=context):
|
||||
proc_obj.run_scheduler(new_cr, uid, automatic=proc.automatic, use_new_cursor=new_cr.dbname,\
|
||||
context=context)
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
</div>
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"
|
||||
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
|
||||
<field name="active" />
|
||||
</group>
|
||||
</group>
|
||||
<field name="description" placeholder="describe the product characteristics..."
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
<div attrs="{'invisible':[('type','=','service')]}">
|
||||
<field name="produce_delay" class="oe_inline"/> days
|
||||
</div>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
</group>
|
||||
<xpath expr="//group[@string='Sale Conditions']" position="inside">
|
||||
|
|
|
@ -247,37 +247,47 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
return True
|
||||
|
||||
@http.route('/website/attach', type='http', auth='user', methods=['POST'], website=True)
|
||||
def attach(self, func, upload):
|
||||
def attach(self, func, upload=None, url=None):
|
||||
Attachments = request.registry['ir.attachment']
|
||||
|
||||
url = message = None
|
||||
try:
|
||||
image_data = upload.read()
|
||||
image = Image.open(cStringIO.StringIO(image_data))
|
||||
w, h = image.size
|
||||
if w*h > 42e6: # Nokia Lumia 1020 photo resolution
|
||||
raise ValueError(
|
||||
u"Image size excessive, uploaded images must be smaller "
|
||||
u"than 42 million pixel")
|
||||
|
||||
Attachments = request.registry['ir.attachment']
|
||||
website_url = message = None
|
||||
if not upload:
|
||||
website_url = url
|
||||
name = url.split("/").pop()
|
||||
attachment_id = Attachments.create(request.cr, request.uid, {
|
||||
'name': upload.filename,
|
||||
'datas': image_data.encode('base64'),
|
||||
'datas_fname': upload.filename,
|
||||
'name':name,
|
||||
'type': 'url',
|
||||
'url': url,
|
||||
'res_model': 'ir.ui.view',
|
||||
}, request.context)
|
||||
else:
|
||||
try:
|
||||
image_data = upload.read()
|
||||
image = Image.open(cStringIO.StringIO(image_data))
|
||||
w, h = image.size
|
||||
if w*h > 42e6: # Nokia Lumia 1020 photo resolution
|
||||
raise ValueError(
|
||||
u"Image size excessive, uploaded images must be smaller "
|
||||
u"than 42 million pixel")
|
||||
|
||||
[attachment] = Attachments.read(
|
||||
request.cr, request.uid, [attachment_id], ['website_url'],
|
||||
context=request.context)
|
||||
url = attachment['website_url']
|
||||
except Exception, e:
|
||||
logger.exception("Failed to upload image to attachment")
|
||||
message = unicode(e)
|
||||
attachment_id = Attachments.create(request.cr, request.uid, {
|
||||
'name': upload.filename,
|
||||
'datas': image_data.encode('base64'),
|
||||
'datas_fname': upload.filename,
|
||||
'res_model': 'ir.ui.view',
|
||||
}, request.context)
|
||||
|
||||
[attachment] = Attachments.read(
|
||||
request.cr, request.uid, [attachment_id], ['website_url'],
|
||||
context=request.context)
|
||||
website_url = attachment['website_url']
|
||||
except Exception, e:
|
||||
logger.exception("Failed to upload image to attachment")
|
||||
message = unicode(e)
|
||||
|
||||
return """<script type='text/javascript'>
|
||||
window.parent['%s'](%s, %s);
|
||||
</script>""" % (func, json.dumps(url), json.dumps(message))
|
||||
</script>""" % (func, json.dumps(website_url), json.dumps(message))
|
||||
|
||||
@http.route(['/website/publish'], type='json', auth="public", website=True)
|
||||
def publish(self, id, object):
|
||||
|
@ -288,8 +298,6 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
values = {}
|
||||
if 'website_published' in _object._all_columns:
|
||||
values['website_published'] = not obj.website_published
|
||||
if 'website_published_datetime' in _object._all_columns and values.get('website_published'):
|
||||
values['website_published_datetime'] = fields.datetime.now()
|
||||
_object.write(request.cr, request.uid, [_id],
|
||||
values, context=request.context)
|
||||
|
||||
|
@ -341,9 +349,10 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
id = int(id)
|
||||
|
||||
ids = Model.search(request.cr, request.uid,
|
||||
[('id', '=', id)], context=request.context) \
|
||||
or Model.search(request.cr, openerp.SUPERUSER_ID,
|
||||
[('id', '=', id), ('website_published', '=', True)], context=request.context)
|
||||
[('id', '=', id)], context=request.context)
|
||||
if not ids and 'website_published' in Model._all_columns:
|
||||
ids = Model.search(request.cr, openerp.SUPERUSER_ID,
|
||||
[('id', '=', id), ('website_published', '=', True)], context=request.context)
|
||||
|
||||
if not ids:
|
||||
return self.placeholder(response)
|
||||
|
|
|
@ -638,7 +638,7 @@ class ir_attachment(osv.osv):
|
|||
# in-document URLs are html-escaped, a straight search will not
|
||||
# find them
|
||||
url = werkzeug.utils.escape(attachment.website_url)
|
||||
ids = Views.search(cr, uid, [('arch', 'like', url)], context=context)
|
||||
ids = Views.search(cr, uid, ["|", ('arch', 'like', '"%s"' % url), ('arch', 'like', "'%s'" % url)], context=context)
|
||||
|
||||
if ids:
|
||||
removal_blocked_by[attachment.id] = Views.read(
|
||||
|
|
|
@ -94,6 +94,10 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.cke_editable .css_editable_mode_display {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
|
||||
background-image: url("/website/static/src/img/edit_here.png") !important;
|
||||
}
|
||||
|
@ -180,7 +184,7 @@ ul.oe_menu_editor .disclose {
|
|||
cursor: text !important;
|
||||
}
|
||||
|
||||
.modal-dialog.select-image {
|
||||
.modal-dialog.select-media {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
|
@ -212,7 +216,7 @@ ul.oe_menu_editor .disclose {
|
|||
|
||||
.modal .font-icons-icons {
|
||||
font-size: 2em;
|
||||
max-height: 6em;
|
||||
max-height: 9em;
|
||||
overflow: auto;
|
||||
}
|
||||
.modal .font-icons-icons .font-icons-icon {
|
||||
|
@ -271,6 +275,10 @@ ul.oe_menu_editor .disclose {
|
|||
-webkit-border-bottom-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
.existing-attachments .existing-attachment-cell.media_selected > i, .existing-attachments .existing-attachment-cell.media_selected > img {
|
||||
border-width: 5px;
|
||||
border-color: #00f8f8;
|
||||
}
|
||||
|
||||
.cke_widget_wrapper {
|
||||
position: static !important;
|
||||
|
@ -296,17 +304,34 @@ ul.oe_menu_editor .disclose {
|
|||
background-color: #ffd9dd !important;
|
||||
}
|
||||
|
||||
.hover-edition-button {
|
||||
.hover-edition {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.preview-container > * {
|
||||
max-height: 100px;
|
||||
line-height: 100px;
|
||||
margin: 0 auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cke_editable .fa {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.img-responsive {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- MOBILE PREVIEW ---- {{{ */
|
||||
.oe_mobile_preview.modal .modal-content {
|
||||
height: 660px;
|
||||
|
|
|
@ -77,6 +77,9 @@
|
|||
.cke_editable .css_editable_mode_hidden
|
||||
display: none
|
||||
|
||||
.cke_editable .css_editable_mode_display
|
||||
display: block !important
|
||||
|
||||
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
|
||||
background-image: url('/website/static/src/img/edit_here.png') !important
|
||||
|
||||
|
@ -150,7 +153,7 @@ ul.oe_menu_editor
|
|||
+user-select(auto)
|
||||
cursor: text !important
|
||||
|
||||
.modal-dialog.select-image
|
||||
.modal-dialog.select-media
|
||||
width: 80%
|
||||
|
||||
.modal .existing-attachments
|
||||
|
@ -179,7 +182,7 @@ ul.oe_menu_editor
|
|||
.modal
|
||||
.font-icons-icons
|
||||
font-size: 2em
|
||||
max-height: 6em
|
||||
max-height: 9em
|
||||
overflow: auto
|
||||
|
||||
.font-icons-icon
|
||||
|
@ -232,6 +235,10 @@ $attachment-border-color: #848490
|
|||
border-top: none
|
||||
border-left: none
|
||||
+border-bottom-right-radius(8px)
|
||||
&.media_selected
|
||||
> i, > img
|
||||
border-width: 5px
|
||||
border-color: rgb(0, 248, 248)
|
||||
|
||||
// wrapper positioned relatively for drag&drop widget which is disabled below.
|
||||
// Breaks completely horribly crazy products listing page, so take it out.
|
||||
|
@ -259,17 +266,30 @@ $attachment-border-color: #848490
|
|||
outline: 1px solid red !important
|
||||
background-color: #ffd9dd !important
|
||||
|
||||
.hover-edition-button
|
||||
.hover-edition
|
||||
display: inline-block
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
// This z-index is due to .navbar of bootstrap
|
||||
z-index: 1000
|
||||
// This z-index is due to .navbar of bootstrap & jQuery-transfo
|
||||
z-index: 1001
|
||||
.preview-container
|
||||
text-align: center
|
||||
line-height: 100px
|
||||
height: 100px
|
||||
> *
|
||||
max-height: 100px
|
||||
line-height: 100px
|
||||
margin: 0 auto
|
||||
display: inline-block
|
||||
|
||||
// fontawesome
|
||||
.cke_editable .fa
|
||||
cursor: pointer
|
||||
|
||||
.img-responsive
|
||||
text-align: center
|
||||
|
||||
// }}}
|
||||
|
||||
/* ---- MOBILE PREVIEW ---- {{{ */
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
height: 0;
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
z-index: 1001;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-ms-border-radius: 3px;
|
||||
|
@ -325,11 +326,13 @@
|
|||
top: auto;
|
||||
left: 50%;
|
||||
bottom: -6px;
|
||||
margin-left: -50px;
|
||||
}
|
||||
.oe_overlay .oe_handle.size .oe_handle_button {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
text-align: center;
|
||||
margin-left: -32px;
|
||||
margin-left: -52px;
|
||||
margin-top: -10px;
|
||||
left: 0px;
|
||||
}
|
||||
|
@ -341,15 +344,17 @@
|
|||
box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.oe_overlay .oe_handle.size .size {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
cursor: row-resize;
|
||||
top: 9px;
|
||||
margin-left: 52px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.oe_overlay .oe_handle.size .auto_size {
|
||||
width: 20px;
|
||||
padding: 0 5px;
|
||||
top: 1px;
|
||||
margin-left: 36px;
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
top: 9px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_overlay .oe_handle.readonly {
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
height: 0
|
||||
position: absolute
|
||||
background: transparent
|
||||
z-index: 1001
|
||||
//@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.02) ,rgba(255,255,255,.02) 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 75px))
|
||||
+border-radius(3px)
|
||||
@include transition(opacity 100ms linear)
|
||||
|
@ -238,10 +239,12 @@
|
|||
top: auto
|
||||
left: 50%
|
||||
bottom: -6px
|
||||
margin-left: -50px
|
||||
.oe_handle_button
|
||||
position: relative
|
||||
z-index: 3
|
||||
text-align: center
|
||||
margin-left: -32px
|
||||
margin-left: -52px
|
||||
margin-top: -10px
|
||||
left: 0px
|
||||
&:hover
|
||||
|
@ -249,14 +252,16 @@
|
|||
color: #fff
|
||||
+box-shadow(0 0 5px 3px rgba(255,255,255,.7))
|
||||
.size
|
||||
position: absolute
|
||||
width: 64px
|
||||
cursor: row-resize
|
||||
top: 9px
|
||||
.auto_size
|
||||
width: 20px
|
||||
margin-left: 52px
|
||||
padding: 0 5px
|
||||
top: 1px
|
||||
margin-left: 36px
|
||||
.auto_size
|
||||
position: absolute
|
||||
width: 100px
|
||||
top: 9px
|
||||
cursor: pointer
|
||||
&.readonly
|
||||
cursor: auto !important
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
@charset "utf-8";
|
||||
/* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY
|
||||
*
|
||||
* css for editor buttons, openerp widget included in the website and other
|
||||
|
@ -284,10 +285,6 @@ ul.nav-stacked > li > a {
|
|||
background-size: 100%;
|
||||
}
|
||||
|
||||
section, .carousel, .parallax, .row, .hr, .blockquote {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.carousel, .parallax, .blockquote {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -376,17 +373,33 @@ section, .carousel, .parallax, .row, .hr, .blockquote {
|
|||
position: absolute;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.carousel .carousel-control.left span {
|
||||
left: 10px;
|
||||
.carousel .carousel-control.left {
|
||||
left: -10px;
|
||||
}
|
||||
.carousel .carousel-control.right span {
|
||||
right: 10px;
|
||||
.carousel .carousel-control.left * {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 5;
|
||||
right: 50%;
|
||||
}
|
||||
.carousel .carousel-control.right {
|
||||
right: -10px;
|
||||
}
|
||||
.carousel .carousel-control.right * {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 5;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.quotecarousel {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.hr {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* Parallax Theme */
|
||||
div.carousel .carousel-indicators li {
|
||||
border: 1px solid grey;
|
||||
|
@ -527,3 +540,33 @@ span[data-oe-type="monetary"] {
|
|||
width: 400px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
div.media_iframe_video {
|
||||
height: 0;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding-bottom: 66.5%;
|
||||
}
|
||||
div.media_iframe_video iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
div.media_iframe_video div {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Mobile view */
|
||||
@media (max-width: 768px) {
|
||||
img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa {
|
||||
-webkit-transform: none !important;
|
||||
-moz-transform: none !important;
|
||||
-ms-transform: none !important;
|
||||
-o-transform: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,8 +234,6 @@ ul.nav-stacked > li > a
|
|||
.oe_img_bg
|
||||
background-size: 100%
|
||||
|
||||
section, .carousel, .parallax, .row, .hr, .blockquote
|
||||
min-height: 30px
|
||||
.carousel, .parallax, .blockquote
|
||||
overflow: hidden
|
||||
|
||||
|
@ -308,14 +306,27 @@ section, .carousel, .parallax, .row, .hr, .blockquote
|
|||
top: 50%
|
||||
position: absolute
|
||||
margin-top: -8px
|
||||
&.left span
|
||||
left: 10px
|
||||
&.right span
|
||||
right: 10px
|
||||
&.left
|
||||
left: -10px
|
||||
*
|
||||
position: absolute
|
||||
top: 50%
|
||||
z-index: 5
|
||||
right: 50%
|
||||
&.right
|
||||
right: -10px
|
||||
*
|
||||
position: absolute
|
||||
top: 50%
|
||||
z-index: 5
|
||||
left: 50%
|
||||
|
||||
.quotecarousel
|
||||
padding-bottom: 16px
|
||||
|
||||
.hr
|
||||
padding: 4px 0
|
||||
|
||||
/* Parallax Theme */
|
||||
|
||||
div.carousel
|
||||
|
@ -434,3 +445,28 @@ span[data-oe-type="monetary"]
|
|||
overflow:hidden
|
||||
text-overflow:ellipsis
|
||||
|
||||
div.media_iframe_video
|
||||
height: 0
|
||||
margin: 0 auto
|
||||
text-align: center
|
||||
position: relative
|
||||
overflow: hidden
|
||||
padding-bottom: 66.5%
|
||||
iframe
|
||||
width: 100%
|
||||
height: 100%
|
||||
div
|
||||
position: absolute
|
||||
width: 100%
|
||||
height: 100%
|
||||
display: none
|
||||
|
||||
/* Mobile view */
|
||||
|
||||
@media (max-width: 768px)
|
||||
img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa
|
||||
-webkit-transform: none !important
|
||||
-moz-transform: none !important
|
||||
-ms-transform: none !important
|
||||
-o-transform: none !important
|
||||
transform: none !important
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
Copyright (c) 2014 Christophe Matthieu,
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
(function($){
|
||||
'use strict';
|
||||
var rad = Math.PI/180;
|
||||
|
||||
// public methods
|
||||
var methods = {
|
||||
init : function(settings) {
|
||||
return this.each(function() {
|
||||
var $this = $(this), transfo = $this.data('transfo');
|
||||
if (!transfo) {
|
||||
_init($this, settings);
|
||||
} else {
|
||||
_overwriteOptions($this, transfo, settings);
|
||||
_targetCss($this, transfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
if ($this.data('transfo')) {
|
||||
_destroy($this);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
reset : function() {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
if ($this.data('transfo')) {
|
||||
_reset($this);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
toggle : function() {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var transfo = $this.data('transfo');
|
||||
if (transfo) {
|
||||
transfo.settings.hide = !transfo.settings.hide;
|
||||
_showHide($this, transfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
hide : function() {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var transfo = $this.data('transfo');
|
||||
if (transfo) {
|
||||
transfo.settings.hide = true;
|
||||
_showHide($this, transfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
show : function() {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var transfo = $this.data('transfo');
|
||||
if (transfo) {
|
||||
transfo.settings.hide = false;
|
||||
_showHide($this, transfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
settings : function() {
|
||||
if(this.length > 1) {
|
||||
this.map(function () {
|
||||
var $this = $(this);
|
||||
return $this.data('transfo') && $this.data('transfo').settings;
|
||||
});
|
||||
}
|
||||
return this.data('transfo') && $this.data('transfo').settings;
|
||||
},
|
||||
center : function() {
|
||||
if(this.length > 1) {
|
||||
this.map(function () {
|
||||
var $this = $(this);
|
||||
return $this.data('transfo') && $this.data('transfo').$center.offset();
|
||||
});
|
||||
}
|
||||
return this.data('transfo') && this.data('transfo').$center.offset();
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.transfo = function( method ) {
|
||||
if ( methods[method] ) {
|
||||
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
|
||||
} else if ( typeof method === 'object' || ! method ) {
|
||||
return methods.init.apply( this, arguments );
|
||||
} else {
|
||||
$.error( 'Method ' + method + ' does not exist on jQuery.transfo' );
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function _init ($this, settings) {
|
||||
var transfo = {};
|
||||
$this.data('transfo', transfo);
|
||||
transfo.settings = settings;
|
||||
|
||||
// generate all the controls markup
|
||||
var css = "box-sizing: border-box; position: absolute; background-color: #fff; border: 1px solid #ccc; width: 8px; height: 8px; margin-left: -4px; margin-top: -4px;";
|
||||
transfo.$markup = $(''
|
||||
+ '<div class="transfo-controls">'
|
||||
+ '<div style="cursor: crosshair; position: absolute; margin: -30px; top: 0; right: 0; padding: 1px 0 0 1px;" class="transfo-rotator">'
|
||||
+ '<span class="fa-stack fa-lg">'
|
||||
+ '<i class="fa fa-circle fa-stack-2x"></i>'
|
||||
+ '<i class="fa fa-repeat fa-stack-1x fa-inverse"></i>'
|
||||
+ '</span>'
|
||||
+ '</div>'
|
||||
+ '<div style="' + css + 'top: 0%; left: 0%; cursor: nw-resize;" class="transfo-scaler-tl"></div>'
|
||||
+ '<div style="' + css + 'top: 0%; left: 100%; cursor: ne-resize;" class="transfo-scaler-tr"></div>'
|
||||
+ '<div style="' + css + 'top: 100%; left: 100%; cursor: se-resize;" class="transfo-scaler-br"></div>'
|
||||
+ '<div style="' + css + 'top: 100%; left: 0%; cursor: sw-resize;" class="transfo-scaler-bl"></div>'
|
||||
+ '<div style="' + css + 'top: 0%; left: 50%; cursor: n-resize;" class="transfo-scaler-tc"></div>'
|
||||
+ '<div style="' + css + 'top: 100%; left: 50%; cursor: s-resize;" class="transfo-scaler-bc"></div>'
|
||||
+ '<div style="' + css + 'top: 50%; left: 0%; cursor: w-resize;" class="transfo-scaler-ml"></div>'
|
||||
+ '<div style="' + css + 'top: 50%; left: 100%; cursor: e-resize;" class="transfo-scaler-mr"></div>'
|
||||
+ '<div style="' + css + 'border: 0; width: 0px; height: 0px; top: 50%; left: 50%;" class="transfo-scaler-mc"></div>'
|
||||
+ '</div>');
|
||||
transfo.$center = transfo.$markup.find(".transfo-scaler-mc");
|
||||
|
||||
// init setting and get css to set wrap
|
||||
_setOptions($this, transfo);
|
||||
_overwriteOptions ($this, transfo, settings);
|
||||
|
||||
// append controls to container
|
||||
$("body").append(transfo.$markup);
|
||||
|
||||
// set transfo container and markup
|
||||
setTimeout(function () {
|
||||
_targetCss($this, transfo);
|
||||
},0);
|
||||
|
||||
_bind($this, transfo);
|
||||
|
||||
_targetCss($this, transfo);
|
||||
}
|
||||
|
||||
function _overwriteOptions ($this, transfo, settings) {
|
||||
transfo.settings = $.extend(transfo.settings, settings || {});
|
||||
}
|
||||
|
||||
function _setOptions ($this, transfo) {
|
||||
var style = $this.attr("style") || "";
|
||||
var transform = style.match(/transform\s*:([^;]+)/) ? style.match(/transform\s*:([^;]+)/)[1] : "";
|
||||
|
||||
transfo.settings = {};
|
||||
|
||||
transfo.settings.angle= transform.indexOf('rotate') != -1 ? parseFloat(transform.match(/rotate\(([^)]+)deg\)/)[1]) : 0;
|
||||
transfo.settings.scalex= transform.indexOf('scaleX') != -1 ? parseFloat(transform.match(/scaleX\(([^)]+)\)/)[1]) : 1;
|
||||
transfo.settings.scaley= transform.indexOf('scaleY') != -1 ? parseFloat(transform.match(/scaleY\(([^)]+)\)/)[1]) : 1;
|
||||
|
||||
transfo.settings.style = style.replace(/[^;]*transform[^;]+/g, '').replace(/;+/g, ';');
|
||||
$this.attr("style", transfo.settings.style);
|
||||
|
||||
transfo.settings.height = $this.innerHeight();
|
||||
transfo.settings.width = $this.innerWidth();
|
||||
|
||||
var translatex = transform.match(/translateX\(([0-9.-]+)(%|px)\)/);
|
||||
var translatey = transform.match(/translateY\(([0-9.-]+)(%|px)\)/);
|
||||
transfo.settings.translate = "%";
|
||||
|
||||
if (translatex && translatex[2] === "%") {
|
||||
transfo.settings.translatexp = parseFloat(translatex[1]);
|
||||
transfo.settings.translatex = transfo.settings.translatexp / 100 * transfo.settings.width;
|
||||
} else {
|
||||
transfo.settings.translatex = translatex ? parseFloat(translatex[1]) : 0;
|
||||
}
|
||||
if (translatey && translatey[2] === "%") {
|
||||
transfo.settings.translateyp = parseFloat(translatey[1]);
|
||||
transfo.settings.translatey = transfo.settings.translateyp / 100 * transfo.settings.height;
|
||||
} else {
|
||||
transfo.settings.translatey = translatey ? parseFloat(translatey[1]) : 0;
|
||||
}
|
||||
|
||||
transfo.settings.css = window.getComputedStyle($this[0], null);
|
||||
transfo.settings.pos = $this.offset();
|
||||
|
||||
transfo.settings.rotationStep = 5;
|
||||
transfo.settings.hide = false;
|
||||
transfo.settings.callback = function () {};
|
||||
}
|
||||
|
||||
function _bind ($this, transfo) {
|
||||
function mousedown (event) {
|
||||
_mouseDown($this, this, transfo, event);
|
||||
$(document).on("mousemove", mousemove).on("mouseup", mouseup);
|
||||
}
|
||||
function mousemove (event) {
|
||||
_mouseMove($this, this, transfo, event);
|
||||
}
|
||||
function mouseup (event) {
|
||||
_mouseUp($this, this, transfo, event);
|
||||
$(document).off("mousemove", mousemove).off("mouseup", mouseup);
|
||||
}
|
||||
|
||||
transfo.$markup.off().on("mousedown", mousedown);
|
||||
transfo.$markup.find(">:not(.transfo-scaler-mc)").off().on("mousedown", mousedown);
|
||||
}
|
||||
|
||||
function _mouseDown($this, div, transfo, event) {
|
||||
event.preventDefault();
|
||||
if (transfo.active || event.which !== 1) return;
|
||||
|
||||
var type = "position", $e = $(div);
|
||||
if ($e.hasClass("transfo-rotator")) type = "rotator";
|
||||
else if ($e.hasClass("transfo-scaler-tl")) type = "tl";
|
||||
else if ($e.hasClass("transfo-scaler-tr")) type = "tr";
|
||||
else if ($e.hasClass("transfo-scaler-br")) type = "br";
|
||||
else if ($e.hasClass("transfo-scaler-bl")) type = "bl";
|
||||
else if ($e.hasClass("transfo-scaler-tc")) type = "tc";
|
||||
else if ($e.hasClass("transfo-scaler-bc")) type = "bc";
|
||||
else if ($e.hasClass("transfo-scaler-ml")) type = "ml";
|
||||
else if ($e.hasClass("transfo-scaler-mr")) type = "mr";
|
||||
|
||||
transfo.active = {
|
||||
"type": type,
|
||||
"scalex": transfo.settings.scalex,
|
||||
"scaley": transfo.settings.scaley,
|
||||
"pageX": event.pageX,
|
||||
"pageY": event.pageY,
|
||||
"center": transfo.$center.offset(),
|
||||
};
|
||||
}
|
||||
function _mouseUp($this, div, transfo, event) {
|
||||
transfo.active = null;
|
||||
}
|
||||
|
||||
function _mouseMove($this, div, transfo, event) {
|
||||
event.preventDefault();
|
||||
if (!transfo.active) return;
|
||||
var settings = transfo.settings;
|
||||
var center = transfo.active.center;
|
||||
var cdx = center.left - event.pageX;
|
||||
var cdy = center.top - event.pageY;
|
||||
|
||||
if (transfo.active.type == "rotator") {
|
||||
var ang, dang = Math.atan((settings.width * settings.scalex) / (settings.height * settings.scaley)) / rad;
|
||||
|
||||
if (cdy) ang = Math.atan(- cdx / cdy) / rad;
|
||||
else ang = 0;
|
||||
if (event.pageY >= center.top && event.pageX >= center.left) ang += 180;
|
||||
else if (event.pageY >= center.top && event.pageX < center.left) ang += 180;
|
||||
else if (event.pageY < center.top && event.pageX < center.left) ang += 360;
|
||||
|
||||
ang -= dang;
|
||||
if (settings.scaley < 0 && settings.scalex < 0) ang += 180;
|
||||
|
||||
if (!event.ctrlKey) {
|
||||
settings.angle = Math.round(ang / transfo.settings.rotationStep) * transfo.settings.rotationStep;
|
||||
} else {
|
||||
settings.angle = ang;
|
||||
}
|
||||
|
||||
// reset position : don't move center
|
||||
_targetCss($this, transfo);
|
||||
var new_center = transfo.$center.offset();
|
||||
var x = center.left - new_center.left;
|
||||
var y = center.top - new_center.top;
|
||||
var angle = ang * rad;
|
||||
settings.translatex += x*Math.cos(angle) - y*Math.sin(-angle);
|
||||
settings.translatey += - x*Math.sin(angle) + y*Math.cos(-angle);
|
||||
}
|
||||
else if (transfo.active.type == "position") {
|
||||
var angle = settings.angle * rad;
|
||||
var x = event.pageX - transfo.active.pageX;
|
||||
var y = event.pageY - transfo.active.pageY;
|
||||
transfo.active.pageX = event.pageX;
|
||||
transfo.active.pageY = event.pageY;
|
||||
var dx = x*Math.cos(angle) - y*Math.sin(-angle);
|
||||
var dy = - x*Math.sin(angle) + y*Math.cos(-angle);
|
||||
|
||||
settings.translatex += dx;
|
||||
settings.translatey += dy;
|
||||
}
|
||||
else if (transfo.active.type.length === 2) {
|
||||
var angle = settings.angle * rad;
|
||||
var dx = cdx*Math.cos(angle) - cdy*Math.sin(-angle);
|
||||
var dy = - cdx*Math.sin(angle) + cdy*Math.cos(-angle);
|
||||
if (transfo.active.type.indexOf("t") != -1) {
|
||||
settings.scaley = dy / (settings.height/2);
|
||||
}
|
||||
if (transfo.active.type.indexOf("b") != -1) {
|
||||
settings.scaley = - dy / (settings.height/2);
|
||||
}
|
||||
if (transfo.active.type.indexOf("l") != -1) {
|
||||
settings.scalex = dx / (settings.width/2);
|
||||
}
|
||||
if (transfo.active.type.indexOf("r") != -1) {
|
||||
settings.scalex = - dx / (settings.width/2);
|
||||
}
|
||||
if (settings.scaley > 0 && settings.scaley < 0.05) settings.scaley = 0.05;
|
||||
if (settings.scalex > 0 && settings.scalex < 0.05) settings.scalex = 0.05;
|
||||
if (settings.scaley < 0 && settings.scaley > -0.05) settings.scaley = -0.05;
|
||||
if (settings.scalex < 0 && settings.scalex > -0.05) settings.scalex = -0.05;
|
||||
|
||||
if (event.shiftKey &&
|
||||
(transfo.active.type === "tl" || transfo.active.type === "bl" ||
|
||||
transfo.active.type === "tr" || transfo.active.type === "br")) {
|
||||
settings.scaley = settings.scalex;
|
||||
}
|
||||
}
|
||||
|
||||
settings.angle = Math.round(settings.angle);
|
||||
settings.translatex = Math.round(settings.translatex);
|
||||
settings.translatey = Math.round(settings.translatey);
|
||||
settings.scalex = Math.round(settings.scalex*100)/100;
|
||||
settings.scaley = Math.round(settings.scaley*100)/100;
|
||||
|
||||
_targetCss($this, transfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setCss($this, css, settings) {
|
||||
var transform = "";
|
||||
var trans = false;
|
||||
if (settings.angle !== 0) {
|
||||
trans = true;
|
||||
transform += " rotate("+settings.angle+"deg) ";
|
||||
}
|
||||
if (settings.translatex) {
|
||||
trans = true;
|
||||
transform += " translateX("+(settings.translate === "%" ? settings.translatexp+"%" : settings.translatex+"px")+") ";
|
||||
}
|
||||
if (settings.translatey) {
|
||||
trans = true;
|
||||
transform += " translateY("+(settings.translate === "%" ? settings.translateyp+"%" : settings.translatey+"px")+") ";
|
||||
}
|
||||
if (settings.scalex != 1) {
|
||||
trans = true;
|
||||
transform += " scaleX("+settings.scalex+") ";
|
||||
}
|
||||
if (settings.scaley != 1){
|
||||
trans = true;
|
||||
transform += " scaleY("+settings.scaley+") ";
|
||||
}
|
||||
|
||||
if (trans) {
|
||||
css += ";"
|
||||
/* Safari */
|
||||
css += "-webkit-transform:" + transform + ";"
|
||||
/* Firefox */
|
||||
+ "-moz-transform:" + transform + ";"
|
||||
/* IE */
|
||||
+ "-ms-transform:" + transform + ";"
|
||||
/* Opera */
|
||||
+ "-o-transform:" + transform + ";"
|
||||
/* Other */
|
||||
+ "transform:" + transform + ";";
|
||||
}
|
||||
|
||||
css = css.replace(/(\s*;)+/g, ';').replace(/^\s*;|;\s*$/g, '');
|
||||
|
||||
$this.attr("style", css);
|
||||
}
|
||||
|
||||
function _targetCss ($this, transfo) {
|
||||
var settings = transfo.settings;
|
||||
var width = parseFloat(settings.css.width);
|
||||
var height = parseFloat(settings.css.height);
|
||||
settings.translatexp = Math.round(settings.translatex/width*1000)/10;
|
||||
settings.translateyp = Math.round(settings.translatey/height*1000)/10;
|
||||
|
||||
_setCss($this, settings.style, settings);
|
||||
|
||||
_setCss(transfo.$markup,
|
||||
"position: absolute;" +
|
||||
"top:" + settings.pos.top + "px;" +
|
||||
"left:" + settings.pos.left + "px;" +
|
||||
"width:" + width + "px;" +
|
||||
"height:" + height + "px;" +
|
||||
"cursor: move;",
|
||||
settings);
|
||||
transfo.$markup.find(">").css("transform", "scaleX("+(1/settings.scalex)+") scaleY("+(1/settings.scaley)+")");
|
||||
|
||||
_showHide($this, transfo);
|
||||
|
||||
transfo.settings.callback.call($this[0], $this);
|
||||
}
|
||||
|
||||
function _showHide ($this, transfo) {
|
||||
transfo.$markup.css("z-index", transfo.settings.hide ? -1 : 1000);
|
||||
if (transfo.settings.hide) {
|
||||
transfo.$markup.find(">").hide();
|
||||
transfo.$markup.find(".transfo-scaler-mc").show();
|
||||
} else {
|
||||
transfo.$markup.find(">").show();
|
||||
}
|
||||
}
|
||||
|
||||
function _destroy ($this) {
|
||||
$this.data('transfo').$markup.remove();
|
||||
$this.removeData('transfo');
|
||||
}
|
||||
|
||||
function _reset ($this) {
|
||||
var transfo = $this.data('transfo');
|
||||
_destroy($this);
|
||||
$this.transfo(transfo.settings);
|
||||
}
|
||||
|
||||
})(jQuery);
|
|
@ -34,7 +34,6 @@
|
|||
var $snipped_id = $(this);
|
||||
if ($snipped_id.data("snippet-view")) {
|
||||
$snipped_id.data("snippet-view").stop();
|
||||
$snipped_id.data("snippet-view", false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -148,4 +147,11 @@
|
|||
});
|
||||
},
|
||||
});
|
||||
|
||||
website.snippet.animationRegistry.media_video = website.snippet.Animation.extend({
|
||||
selector: ".media_iframe_video",
|
||||
start: function () {
|
||||
this.$target.html('<div class="css_editable_mode_display"> </div><iframe src="'+this.$target.data("src")+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -151,9 +151,11 @@
|
|||
$("[data-oe-model] *, [data-oe-type=html] *").off('click');
|
||||
window.snippets = this.snippets = new website.snippet.BuildingBlock(this);
|
||||
this.snippets.appendTo(this.$el);
|
||||
website.snippet.stop_animation();
|
||||
this.on('rte:ready', this, function () {
|
||||
self.snippets.$button.removeClass("hidden");
|
||||
website.snippet.stop_animation();
|
||||
website.snippet.start_animation();
|
||||
$("#wrapwrap *").off('mousedown mouseup click');
|
||||
});
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
|
@ -265,7 +267,8 @@
|
|||
'$el': $style,
|
||||
'selector-siblings': $style.data('selector-siblings'),
|
||||
'selector-children': $style.data('selector-children'),
|
||||
'selector-vertical-children': $style.data('selector-vertical-children')
|
||||
'selector-vertical-children': $style.data('selector-vertical-children'),
|
||||
'data': $style.data()
|
||||
};
|
||||
selector.push($style.data('selector'));
|
||||
});
|
||||
|
@ -332,6 +335,7 @@
|
|||
if (!$target.is(website.snippet.globalSelector)) {
|
||||
$target = $target.parents(website.snippet.globalSelector).first();
|
||||
}
|
||||
|
||||
if (!self.dom_filter($target).length) {
|
||||
$target = false;
|
||||
}
|
||||
|
@ -732,12 +736,15 @@
|
|||
this.$target.data("snippet-option-ids", styles);
|
||||
this.$overlay = this.$target.data('overlay');
|
||||
this['snippet-option-id'] = snippet_id;
|
||||
this.$el = website.snippet.templateOptions[snippet_id].$el.find(">li").clone();
|
||||
var $option = website.snippet.templateOptions[snippet_id].$el;
|
||||
this.$el = $option.find(">li").clone();
|
||||
this.data = $option.data();
|
||||
|
||||
this.required = this.$el.data("required");
|
||||
|
||||
this.set_active();
|
||||
this.$el.find('li[data-value] a').on('mouseover mouseout click', _.bind(this._mouse, this));
|
||||
this.$el.find('li[data-value] a').on('mouseenter mouseleave click', _.bind(this._mouse, this));
|
||||
this.$el.not(':not([data-value])').find("a").on('mouseenter mouseleave click', _.bind(this._mouse, this));
|
||||
this.$target.on('snippet-style-reset', _.bind(this.set_active, this));
|
||||
|
||||
this.start();
|
||||
|
@ -745,7 +752,7 @@
|
|||
_mouse: function (event) {
|
||||
var self = this;
|
||||
|
||||
if (event.type === 'mouseout') {
|
||||
if (event.type === 'mouseleave') {
|
||||
if (!this.over) return;
|
||||
this.over = false;
|
||||
} else if (event.type === 'click') {
|
||||
|
@ -755,7 +762,7 @@
|
|||
}
|
||||
|
||||
var $prev, $next;
|
||||
if (event.type === 'mouseout') {
|
||||
if (event.type === 'mouseleave') {
|
||||
$prev = $(event.currentTarget).parent();
|
||||
$next = this.$el.find("li[data-value].active");
|
||||
} else {
|
||||
|
@ -837,7 +844,7 @@
|
|||
var self = this;
|
||||
|
||||
// add or remove html class
|
||||
if (np.$prev && this.required) {
|
||||
if (np.$prev) {
|
||||
this.$target.removeClass(np.$prev.data('value') || "");
|
||||
}
|
||||
if (np.$next) {
|
||||
|
@ -927,7 +934,7 @@
|
|||
drop_and_build_snippet: function() {
|
||||
this.id = this.unique_id();
|
||||
this.$target.attr("id", this.id);
|
||||
this.$target.find("[data-slide]").attr("href", "#" + this.id);
|
||||
this.$target.find("[data-slide]").attr("data-target", "#" + this.id);
|
||||
this.$target.find("[data-slide-to]").attr("data-target", "#" + this.id);
|
||||
|
||||
this.rebind_event();
|
||||
|
@ -1171,6 +1178,7 @@
|
|||
dy = dy - dy%resize;
|
||||
if (dy <= 0) dy = resize;
|
||||
self.$target.css("height", dy+"px");
|
||||
self.$target.css("overflow", "hidden");
|
||||
self.on_resize(compass, null, dy);
|
||||
self.BuildingBlock.cover_target(self.$overlay, self.$target);
|
||||
return;
|
||||
|
@ -1211,6 +1219,7 @@
|
|||
});
|
||||
this.$overlay.find(".oe_handle.size .auto_size").on('click', function (event){
|
||||
self.$target.css("height", "");
|
||||
self.$target.css("overflow", "");
|
||||
self.BuildingBlock.cover_target(self.$overlay, self.$target);
|
||||
return false;
|
||||
});
|
||||
|
@ -1391,6 +1400,9 @@
|
|||
start : function () {
|
||||
var self = this;
|
||||
this._super();
|
||||
if (!self.$target.data("snippet-view")) {
|
||||
this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target));
|
||||
}
|
||||
this.scroll();
|
||||
this.$target.on('snippet-style-change snippet-style-preview', function () {
|
||||
self.$target.data("snippet-view").set_values();
|
||||
|
@ -1425,6 +1437,88 @@
|
|||
}
|
||||
});
|
||||
|
||||
website.snippet.options.transform = website.snippet.Option.extend({
|
||||
start: function () {
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
this.$el.find(".clear-style").click(function (event) {
|
||||
self.$target.removeClass("fa-spin").attr("style", "");
|
||||
self.resetTransfo();
|
||||
});
|
||||
|
||||
this.$el.find(".style").click(function (event) {
|
||||
var settings = self.$target.data("transfo").settings;
|
||||
self.$target.transfo({ hide: (settings.hide = !settings.hide) });
|
||||
});
|
||||
|
||||
this.$overlay.find('.oe_snippet_clone, .oe_handles').addClass('hidden');
|
||||
|
||||
this.$overlay.find('[data-toggle="dropdown"]')
|
||||
.on("mousedown", function () {
|
||||
self.$target.transfo("hide");
|
||||
});
|
||||
},
|
||||
resetTransfo: function () {
|
||||
var self = this;
|
||||
this.$target.transfo("destroy");
|
||||
this.$target.transfo({
|
||||
hide: true,
|
||||
callback: function () {
|
||||
var pos = $(this).data("transfo").$center.offset();
|
||||
self.$overlay.css({
|
||||
'top': pos.top,
|
||||
'left': pos.left,
|
||||
'position': 'absolute',
|
||||
});
|
||||
self.$overlay.find(".oe_overlay_options").attr("style", "width:0; left:0!important; top:0;");
|
||||
self.$overlay.find(".oe_overlay_options > .btn-group").attr("style", "width:160px; left:-80px;");
|
||||
}});
|
||||
this.$target.data('transfo').$markup
|
||||
.on("mouseover", function () {
|
||||
self.$target.trigger("mouseover");
|
||||
})
|
||||
.mouseover();
|
||||
},
|
||||
onFocus : function () {
|
||||
this.resetTransfo();
|
||||
},
|
||||
onBlur : function () {
|
||||
this.$target.transfo("hide");
|
||||
},
|
||||
});
|
||||
|
||||
website.snippet.options.media = website.snippet.Option.extend({
|
||||
start: function () {
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
website.snippet.start_animation(true, this.$target);
|
||||
|
||||
$(document.body).on("media-saved", self, function (event, prev , item) {
|
||||
self.editor.onBlur();
|
||||
self.BuildingBlock.make_active(false);
|
||||
self.BuildingBlock.make_active($(item));
|
||||
});
|
||||
|
||||
this.$el.find(".edition").click(function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
self.element = new CKEDITOR.dom.element(self.$target[0]);
|
||||
new website.editor.MediaDialog(self, self.element).appendTo(document.body);
|
||||
});
|
||||
},
|
||||
onFocus : function () {
|
||||
var self = this;
|
||||
if (this.$target.parent().data("oe-field") === "image") {
|
||||
this.$overlay.addClass("hidden");
|
||||
self.element = new CKEDITOR.dom.element(self.$target[0]);
|
||||
new website.editor.MediaDialog(self, self.element).appendTo(document.body);
|
||||
self.BuildingBlock.make_active(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
website.snippet.Editor = openerp.Class.extend({
|
||||
init: function (BuildingBlock, dom) {
|
||||
|
@ -1564,6 +1658,10 @@
|
|||
if (this.selector_vertical_children === "")
|
||||
this.selector_vertical_children = false;
|
||||
|
||||
if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) {
|
||||
this.$overlay.find(".oe_snippet_move").addClass('hidden');
|
||||
}
|
||||
|
||||
|
||||
if ($ul.find("li").length) {
|
||||
$styles.removeClass("hidden");
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: _t("Insert building blocks"),
|
||||
content: _t("Click here to insert blocks of centent in the page."),
|
||||
content: _t("Click here to insert blocks of content in the page."),
|
||||
popover: { fixed: true },
|
||||
},
|
||||
{
|
||||
|
|
|
@ -88,49 +88,85 @@
|
|||
</form>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="website.editor.dialog.image">
|
||||
<t t-call="website.editor.dialog">
|
||||
<t t-set="title">Image:</t>
|
||||
<div class="row">
|
||||
<form method="POST" action="/website/attach"
|
||||
enctype="multipart/form-data"
|
||||
target="fileframe"
|
||||
class="col-sm-8">
|
||||
<div class="text-center">
|
||||
<input type="file" name="upload" accept="image/*" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
|
||||
<button type="button" class="btn btn-primary btn-lg filepicker">
|
||||
Upload an image from your computer
|
||||
</button>
|
||||
<button type="button" class="btn btn-lg hidden wait" disabled="disabled"/>
|
||||
<p class="text-muted mt16">— or —</p>
|
||||
<t t-name="website.editor.dialog.media">
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog select-media">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 class="modal-title">Select a Media</h3>
|
||||
</div>
|
||||
<div class="well">
|
||||
<a href="#existing" class="pull-right">Browse existing images</a>
|
||||
<h3 class="list-group-item-heading">Image URL</h3>
|
||||
<input type="text" class="form-control url"
|
||||
placeholder="http://openerp.com"/>
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#editor-media-image" data-toggle="tab">Image</a></li>
|
||||
<li><a href="#editor-media-icon" data-toggle="tab">Pictogram</a></li>
|
||||
<li><a href="#editor-media-video" data-toggle="tab">Video</a></li>
|
||||
<li class="search" style="float: right;">
|
||||
<ul class="pager mb0 mt0">
|
||||
<li class="previous disabled"><a href="#">← Previous</a></li>
|
||||
<li class="next disabled"><a href="#">Next →</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="search" style="float: right;">
|
||||
<form action="#">
|
||||
<div class="form-group font-icons fa fa-search mb0">
|
||||
<input type="search" class="form-control" id="icon-search"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade in active" id="editor-media-image">
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" id="editor-media-icon">
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" id="editor-media-video">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary save">Save</button>
|
||||
<button type="button" class="btn hidden wait" disabled="disabled"/>
|
||||
or
|
||||
<a href="#" data-dismiss="modal" aria-hidden="true">Discard</a>
|
||||
</div>
|
||||
<input type="hidden" name="func"/>
|
||||
<div class="help-block"/>
|
||||
</form>
|
||||
<div class="col-sm-4 image-preview-container">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAC0lEQVQIHWP4zwAAAgEBAMVfG14AAAAASUVORK5CYII%3D"
|
||||
class="pull-right img-rounded image-preview"
|
||||
width="100%"/>
|
||||
<img src="/web/static/src/img/throbber-large.gif"
|
||||
class="pull-right img-rounded wait hidden"
|
||||
width="100%"/>
|
||||
<select class="form-control image-style">
|
||||
<option value="">No styling</option>
|
||||
<option value="img-rounded">Rounded corners</option>
|
||||
<option value="img-thumbnail">Box</option>
|
||||
<option value="img-circle">Circle</option>
|
||||
<option value="shadow">Shadow</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<iframe src="about:blank" name="fileframe" class="hidden"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website.editor.dialog.image">
|
||||
<div>
|
||||
<form method="POST"
|
||||
action="/website/attach"
|
||||
enctype="multipart/form-data"
|
||||
target="fileframe"
|
||||
class="form-inline">
|
||||
<div class="well">
|
||||
<div class="form-group pull-left">
|
||||
<input type="file" name="upload" accept="image/*" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
|
||||
<button type="button" class="btn btn-primary filepicker">Upload an image from your computer</button>
|
||||
<button type="button" class="btn hidden wait" disabled="disabled">Uploading...</button>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted"> — or — </span>
|
||||
<label for="iamgeurl">Add an image URL</label>
|
||||
<div class="form-group btn-group">
|
||||
<input type="text" name="url" class="form-control url pull-left" style="width: 320px;" id="iamgeurl" placeholder="http://openerp.com/logo.png"/>
|
||||
<button class="btn btn-default" type="submit">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="func"/>
|
||||
<div class="help-block"/>
|
||||
<div class="existing-attachments"/>
|
||||
</form>
|
||||
</div>
|
||||
<iframe src="about:blank" name="fileframe" class="hidden"/>
|
||||
</t>
|
||||
<t t-name="website.editor.dialog.image.existing">
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
|
@ -153,15 +189,11 @@
|
|||
</t>
|
||||
<t t-name="website.editor.dialog.image.existing.content">
|
||||
<div class="existing-attachments">
|
||||
<ul class="pager">
|
||||
<li class="previous disabled"><a href="#">← Previous</a></li>
|
||||
<li class="next disabled"><a href="#">Next →</a></li>
|
||||
</ul>
|
||||
<div class="row mt16" t-foreach="rows" t-as="row">
|
||||
<div class="col-sm-2 existing-attachment-cell"
|
||||
t-foreach="row" t-as="attachment">
|
||||
<i class="fa fa-times existing-attachment-remove" t-att-data-id="attachment.id"/>
|
||||
<img t-att-src="attachment.website_url" t-att-alt="attachment.name" class="img img-responsive"/>
|
||||
<img t-att-src="attachment.website_url" t-att-alt="attachment.name" t-att-title="attachment.name" class="img img-responsive"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -174,7 +206,7 @@
|
|||
<li>
|
||||
<a t-attf-href="/web#model=ir.ui.view&id=#{view.id}">
|
||||
<t t-esc="view.name"/>
|
||||
</a> (id <t t-esc="view.id"/>)
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -217,49 +249,42 @@
|
|||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.editor.hoverbutton">
|
||||
<button type="button" contenteditable="false"
|
||||
t-attf-class="btn btn-primary hover-edition-button #{classes or ''}">
|
||||
<t t-esc="label"/>
|
||||
</button>
|
||||
<t t-name="website.editor.hoverbutton.link">
|
||||
<button contentEditable="false" type="button" class="btn btn-primary hover-edition-button btn-xs">Change</button>
|
||||
</t>
|
||||
|
||||
<t t-name="website.editor.hoverbutton.media">
|
||||
<div contentEditable="false" class="hover-edition dropdown">
|
||||
<a class="btn btn-primary btn-sm" data-toggle="dropdown" href="#">Customize</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
|
||||
<li><a href="#" class="hover-edition-button">Change Media</a></li>
|
||||
<li><a href="#" class="hover-style-button">Style</a></li>
|
||||
<li class="dropdown-submenu">
|
||||
<a href="#" tabindex="-1">Rotation</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li data-value="fa-spin"><a>Spin</a></li>
|
||||
<li data-value="fa-flip-horizontal"><a>Horizontal flip</a></li>
|
||||
<li data-value="fa-flip-vertical"><a>Vertical flip</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#" data-value="fa-border">border</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.editor.dialog.font-icons">
|
||||
<t t-call="website.editor.dialog">
|
||||
<t t-set="title">Icon:</t>
|
||||
<form>
|
||||
<div class="form-group" id="fa-preview">
|
||||
<form action="#">
|
||||
<input type="hidden" id="fa-icon"/>
|
||||
<input type="hidden" id="fa-size"/>
|
||||
<div class="font-icons-icons">
|
||||
<t t-call="website.editor.dialog.font-icons.icons">
|
||||
<t t-set="icons" t-value="widget.icons"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="form-group mt32" id="fa-preview">
|
||||
|
||||
</div>
|
||||
<div class="form-group font-icons fa fa-search">
|
||||
<input type="hidden" id="fa-icon" class="form-control"/>
|
||||
<input type="hidden" id="fa-size" class="form-control"/>
|
||||
<input type="search" class="form-control" id="icon-search"/>
|
||||
<div class="font-icons-icons">
|
||||
<t t-call="website.editor.dialog.font-icons.icons">
|
||||
<t t-set="icons" t-value="widget.icons"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="fa-rotation">Rotation</label>
|
||||
<select id="fa-rotation" class="form-control">
|
||||
<option value="">None</option>
|
||||
<option value="fa-spin">Spin</option>
|
||||
<option value="fa-rotate-90">Rotate 90º</option>
|
||||
<option value="fa-rotate-180">Rotate 180º</option>
|
||||
<option value="fa-rotate-270">Rotate 270º</option>
|
||||
<option value="fa-flip-horizontal">Horizontal flip</option>
|
||||
<option value="fa-flip-vertical">Vertical flip</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="fa-border"/> border
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
<t t-name="website.editor.dialog.font-icons.icons">
|
||||
<span t-foreach="icons" t-as="icon"
|
||||
|
@ -269,4 +294,50 @@
|
|||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="website.editor.dialog.video">
|
||||
<form action="#" class="form-inline">
|
||||
<div class="well">
|
||||
<div class="form-group btn-group">
|
||||
<span class="text-muted pull-right" style="margin-left:10px; line-height: 2em;">(Youtube, Vimeo, Dailymotion)</span>
|
||||
<label for="urlvideo" style="width: 220px; line-height: 2em;" class="pull-left">Set a video URL</label>
|
||||
<input type="text"
|
||||
name="url"
|
||||
class="form-control url pull-left"
|
||||
style="width: 400px;"
|
||||
id="urlvideo"
|
||||
placeholder="//www.youtube.com/embed/yws1tbgNV7k"/>
|
||||
<button class="btn btn-default">Preview</button>
|
||||
</div>
|
||||
<div class="form-group btn-group">
|
||||
<label for="urlvideo" style="width: 220px; line-height: 2em;" class="pull-left"><span class="text-muted">— or —</span> Embed Video (HTML)</label>
|
||||
<input type="text"
|
||||
name="embed"
|
||||
class="form-control url pull-left"
|
||||
style="width: 400px;"
|
||||
id="embedvideo"
|
||||
placeholder='<iframe src="//www.youtube.com/embed/yws1tbgNV7k"></iframe>'/>
|
||||
<button class="btn btn-default">Preview</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="video-preview" style="width: 450px; margin: 0 auto;">
|
||||
<div class="media_iframe_video">
|
||||
<iframe
|
||||
src=""
|
||||
frameborder="0"
|
||||
allowfullscreen="allowfullscreen"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="video_id" value=""/>
|
||||
<input type="hidden" id="video_type" value=""/>
|
||||
|
||||
<div class="text-center mt32">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="autoplay"/> Autoplay</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
<div class='oe_handle e readonly'><div></div></div>
|
||||
<div class='oe_handle w readonly'><div></div></div>
|
||||
<div class='oe_handle size readonly'>
|
||||
<div class="oe_handle_button auto_size">Auto Resize</div>
|
||||
<div class="oe_handle_button size">Resize</div>
|
||||
<div class="oe_handle_button auto_size">x</div>
|
||||
</div>
|
||||
<div class='oe_handle s readonly'><div></div></div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ import werkzeug.urls
|
|||
|
||||
import lxml.html
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
|
||||
import cases
|
||||
|
@ -43,15 +44,23 @@ class CrawlSuite(unittest2.TestSuite):
|
|||
def __init__(self, user=None, password=None):
|
||||
super(CrawlSuite, self).__init__()
|
||||
|
||||
self.opener = urllib2.OpenerDirector()
|
||||
self.opener.add_handler(urllib2.UnknownHandler())
|
||||
self.opener.add_handler(urllib2.HTTPHandler())
|
||||
self.opener.add_handler(urllib2.HTTPSHandler())
|
||||
self.opener.add_handler(urllib2.HTTPCookieProcessor())
|
||||
self.opener.add_handler(RedirectHandler())
|
||||
registry = openerp.registry(tools.config['db_name'])
|
||||
try:
|
||||
# switch registry to test mode, so that requests can be made
|
||||
registry.enter_test_mode()
|
||||
|
||||
self._authenticate(user, password)
|
||||
self.user = user
|
||||
self.opener = urllib2.OpenerDirector()
|
||||
self.opener.add_handler(urllib2.UnknownHandler())
|
||||
self.opener.add_handler(urllib2.HTTPHandler())
|
||||
self.opener.add_handler(urllib2.HTTPSHandler())
|
||||
self.opener.add_handler(urllib2.HTTPCookieProcessor())
|
||||
self.opener.add_handler(RedirectHandler())
|
||||
|
||||
self._authenticate(user, password)
|
||||
self.user = user
|
||||
|
||||
finally:
|
||||
registry.leave_test_mode()
|
||||
|
||||
def _request(self, path):
|
||||
return self.opener.open(urlparse.urlunsplit([
|
||||
|
@ -79,37 +88,45 @@ class CrawlSuite(unittest2.TestSuite):
|
|||
assert auth.getcode() < 400, "Auth failure %d" % auth.getcode()
|
||||
|
||||
def _wrapped_run(self, result, debug=False):
|
||||
paths = [URL('/'), URL('/sitemap')]
|
||||
seen = set(paths)
|
||||
registry = openerp.registry(tools.config['db_name'])
|
||||
try:
|
||||
# switch registry to test mode, so that requests can be made
|
||||
registry.enter_test_mode()
|
||||
|
||||
while paths:
|
||||
url = paths.pop(0)
|
||||
r = self._request(url.url)
|
||||
url.to_case(self.user, r).run(result)
|
||||
paths = [URL('/'), URL('/sitemap')]
|
||||
seen = set(paths)
|
||||
|
||||
if r.info().gettype() != 'text/html':
|
||||
continue
|
||||
while paths:
|
||||
url = paths.pop(0)
|
||||
r = self._request(url.url)
|
||||
url.to_case(self.user, r).run(result)
|
||||
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
for link in doc.xpath('//a[@href]'):
|
||||
href = link.get('href')
|
||||
|
||||
# avoid repeats, even for links we won't crawl no need to
|
||||
# bother splitting them if we've already ignored them
|
||||
# previously
|
||||
if href in seen: continue
|
||||
seen.add(href)
|
||||
|
||||
parts = urlparse.urlsplit(href)
|
||||
|
||||
if parts.netloc or \
|
||||
not parts.path.startswith('/') or \
|
||||
parts.path == '/web' or\
|
||||
parts.path.startswith('/web/') or \
|
||||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
if r.info().gettype() != 'text/html':
|
||||
continue
|
||||
|
||||
paths.append(URL(href, url.url))
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
for link in doc.xpath('//a[@href]'):
|
||||
href = link.get('href')
|
||||
|
||||
# avoid repeats, even for links we won't crawl no need to
|
||||
# bother splitting them if we've already ignored them
|
||||
# previously
|
||||
if href in seen: continue
|
||||
seen.add(href)
|
||||
|
||||
parts = urlparse.urlsplit(href)
|
||||
|
||||
if parts.netloc or \
|
||||
not parts.path.startswith('/') or \
|
||||
parts.path == '/web' or\
|
||||
parts.path.startswith('/web/') or \
|
||||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
continue
|
||||
|
||||
paths.append(URL(href, url.url))
|
||||
|
||||
finally:
|
||||
registry.leave_test_mode()
|
||||
|
||||
class URL(object):
|
||||
def __init__(self, url, source=None):
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control left hidden" href="#myCarousel" data-slide="prev" style="width: 10%"><span class="glyphicon glyphicon-chevron-left"><span class="hidden">.</span></span></a>
|
||||
<a class="carousel-control right hidden" href="#myCarousel" data-slide="next" style="width: 10%"><span class="glyphicon glyphicon-chevron-right"><span class="hidden">.</span></span></a>
|
||||
<div class="carousel-control left hidden" data-target="#myCarousel" data-slide="prev" style="width: 10%"><i class="fa fa-chevron-left"></i></div>
|
||||
<div class="carousel-control right hidden" data-target="#myCarousel" data-slide="next" style="width: 10%"><i class="fa fa-chevron-right"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -522,7 +522,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="page/contactus" class="btn btn-primary btn-lg pull-right mt8">
|
||||
<a href="/page/website.contactus" class="btn btn-primary btn-lg pull-right mt8">
|
||||
<i class="fa fa-arrow-right"></i>
|
||||
Contact Us Now
|
||||
</a>
|
||||
|
@ -942,6 +942,11 @@
|
|||
data-selector-children=".content">
|
||||
</div>
|
||||
|
||||
<div data-snippet-option-id='separator'
|
||||
data-selector="hr"
|
||||
data-selector-children=".oe_structure, [data-oe-type=html]">
|
||||
</div>
|
||||
|
||||
<div data-snippet-option-id='parallax'
|
||||
data-selector=".parallax">
|
||||
<li class="dropdown-submenu">
|
||||
|
@ -957,6 +962,27 @@
|
|||
</li>
|
||||
</div>
|
||||
|
||||
<div data-snippet-option-id='media'
|
||||
data-selector="img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa, .glyphicon">
|
||||
<li><a href="#" class="edition">Change...</a></li>
|
||||
</div>
|
||||
|
||||
<div data-snippet-option-id='transform'
|
||||
data-selector="img:not(.cke_iframe), .media_iframe_video, span.fa, i.fa">
|
||||
<li class="dropdown-submenu">
|
||||
<a tabindex="-1" href="#">Style</a>
|
||||
<ul class="dropdown-menu" name="parallax-scroll">
|
||||
<li data-value="img-rounded"><a>Rounded corners</a></li>
|
||||
<li data-value="img-thumbnail"><a>Box</a></li>
|
||||
<li data-value="img-circle"><a>Circle</a></li>
|
||||
<li data-value="shadow"><a>Shadow</a></li>
|
||||
<li data-value="fa-spin"><a>Spin</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#" class="style">Transform</a></li>
|
||||
<li><a href="#" class="clear-style">Reset Transformation</a></li>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
|
|
|
@ -284,6 +284,8 @@
|
|||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.snippets.editor.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
|
||||
<script t-if="translatable" type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/website/static/src/js/jQuery.transfo.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ OpenERP Blog
|
|||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['knowledge', 'website_mail'],
|
||||
'depends': ['knowledge', 'website_mail', 'website_partner'],
|
||||
'data': [
|
||||
'data/website_blog_data.xml',
|
||||
'views/website_blog_views.xml',
|
||||
|
|
|
@ -1,23 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import datetime
|
||||
import werkzeug
|
||||
|
@ -29,6 +10,7 @@ from openerp.addons.website.models.website import slug
|
|||
from openerp.osv.orm import browse_record
|
||||
from openerp.tools.translate import _
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.tools import html2plaintext
|
||||
|
||||
|
||||
class QueryURL(object):
|
||||
|
@ -62,12 +44,13 @@ class QueryURL(object):
|
|||
|
||||
|
||||
class WebsiteBlog(http.Controller):
|
||||
_blog_post_per_page = 6
|
||||
_post_comment_per_page = 6
|
||||
_blog_post_per_page = 20
|
||||
_post_comment_per_page = 10
|
||||
|
||||
def nav_list(self):
|
||||
blog_post_obj = request.registry['blog.post']
|
||||
groups = blog_post_obj.read_group(request.cr, request.uid, [], ['name', 'create_date'],
|
||||
groups = blog_post_obj.read_group(
|
||||
request.cr, request.uid, [], ['name', 'create_date'],
|
||||
groupby="create_date", orderby="create_date asc", context=request.context)
|
||||
for group in groups:
|
||||
begin_date = datetime.datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
|
||||
|
@ -108,35 +91,27 @@ class WebsiteBlog(http.Controller):
|
|||
def blog(self, blog=None, tag=None, page=1, **opt):
|
||||
""" Prepare all values to display the blog.
|
||||
|
||||
:param blog: blog currently browsed.
|
||||
:param tag: tag that is currently used to filter blog posts
|
||||
:param integer page: current page of the pager. Can be the blog or
|
||||
post pager.
|
||||
:param date: date currently used to filter blog posts (dateBegin_dateEnd)
|
||||
|
||||
:return dict values: values for the templates, containing
|
||||
|
||||
- 'blog_posts': list of browse records that are the posts to display
|
||||
in a given blog, if not blog_post_id
|
||||
- 'blog': browse of the current blog, if blog_id
|
||||
- 'blogs': list of browse records of blogs
|
||||
- 'pager': the pager to display posts pager in a blog
|
||||
- 'tag': current tag, if tag_id
|
||||
- 'blog': current blog
|
||||
- 'blogs': all blogs for navigation
|
||||
- 'pager': pager of posts
|
||||
- 'tag': current tag
|
||||
- 'tags': all tags, for navigation
|
||||
- 'nav_list': a dict [year][month] for archives navigation
|
||||
- 'date': date_begin optional parameter, used in archives navigation
|
||||
- 'blog_url': help object to create URLs
|
||||
"""
|
||||
date_begin, date_end = opt.get('date_begin'), opt.get('date_end')
|
||||
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
blog_post_obj = request.registry['blog.post']
|
||||
|
||||
blog_posts = None
|
||||
|
||||
blog_obj = request.registry['blog.blog']
|
||||
blog_ids = blog_obj.search(cr, uid, [], order="create_date asc", context=context)
|
||||
blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
|
||||
|
||||
domain = []
|
||||
|
||||
if blog:
|
||||
domain += [('blog_id', '=', blog.id)]
|
||||
if tag:
|
||||
|
@ -176,35 +151,29 @@ class WebsiteBlog(http.Controller):
|
|||
'post_url': post_url,
|
||||
'date': date_begin,
|
||||
}
|
||||
return request.website.render("website_blog.blog_post_short", values)
|
||||
response = request.website.render("website_blog.blog_post_short", values)
|
||||
return response
|
||||
|
||||
@http.route([
|
||||
'/blogpost/<model("blog.post"):blog_post>',
|
||||
'/blog/<model("blog.blog"):blog>/post/<model("blog.post"):blog_post>',
|
||||
], type='http', auth="public", website=True, multilang=True)
|
||||
def blog_post(self, blog_post, tag_id=None, page=1, enable_editor=None, **post):
|
||||
def blog_post(self, blog, blog_post, tag_id=None, page=1, enable_editor=None, **post):
|
||||
""" Prepare all values to display the blog.
|
||||
|
||||
:param blog_post: blog post currently browsed. If not set, the user is
|
||||
browsing the blog and a post pager is calculated.
|
||||
If set the user is reading the blog post and a
|
||||
comments pager is calculated.
|
||||
:param blog: blog currently browsed.
|
||||
:param tag: tag that is currently used to filter blog posts
|
||||
:param integer page: current page of the pager. Can be the blog or
|
||||
post pager.
|
||||
:param date: date currently used to filter blog posts (dateBegin_dateEnd)
|
||||
|
||||
- 'enable_editor': editor control
|
||||
|
||||
:return dict values: values for the templates, containing
|
||||
|
||||
- 'blog_post': browse of the current post, if blog_post_id
|
||||
- 'blog': browse of the current blog, if blog_id
|
||||
- 'blog_post': browse of the current post
|
||||
- 'blog': browse of the current blog
|
||||
- 'blogs': list of browse records of blogs
|
||||
- 'pager': the pager to display comments pager in a blog post
|
||||
- 'tag': current tag, if tag_id
|
||||
- 'tag': current tag, if tag_id in parameters
|
||||
- 'tags': all tags, for tag-based navigation
|
||||
- 'pager': a pager on the comments
|
||||
- 'nav_list': a dict [year][month] for archives navigation
|
||||
- 'next_post': next blog post, to direct the user towards the next interesting post
|
||||
"""
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
tag_obj = request.registry['blog.tag']
|
||||
blog_post_obj = request.registry['blog.post']
|
||||
date_begin, date_end = post.get('date_begin'), post.get('date_end')
|
||||
|
||||
pager_url = "/blogpost/%s" % blog_post.id
|
||||
|
@ -226,62 +195,125 @@ class WebsiteBlog(http.Controller):
|
|||
post_url = QueryURL('', ['blogpost'], blogpost=blog_post, tag_id=tag_id, date_begin=date_begin, date_end=date_end)
|
||||
blog_url = QueryURL('', ['blog', 'tag'], blog=blog_post.blog_id, tag=tag, date_begin=date_begin, date_end=date_end)
|
||||
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
blog_obj = request.registry['blog.blog']
|
||||
blog_ids = blog_obj.search(cr, uid, [], context=context)
|
||||
blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
|
||||
if not blog_post.blog_id.id == blog.id:
|
||||
return request.redirect("/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post)))
|
||||
|
||||
tag_obj = request.registry['blog.tag']
|
||||
tag_ids = tag_obj.search(cr, uid, [], context=context)
|
||||
tags = tag_obj.browse(cr, uid, tag_ids, context=context)
|
||||
tags = tag_obj.browse(cr, uid, tag_obj.search(cr, uid, [], context=context), context=context)
|
||||
|
||||
# Find next Post
|
||||
visited_blogs = request.httprequest.cookies.get('visited_blogs') or ''
|
||||
visited_ids = filter(None, visited_blogs.split(','))
|
||||
visited_ids = map(lambda x: int(x), visited_ids)
|
||||
if blog_post.id not in visited_ids:
|
||||
visited_ids.append(blog_post.id)
|
||||
next_post_id = blog_post_obj.search(cr, uid, [
|
||||
('id', 'not in', visited_ids),
|
||||
], order='ranking desc', limit=1, context=context)
|
||||
if not next_post_id:
|
||||
next_post_id = blog_post_obj.search(cr, uid, [('id', '!=', blog.id)], order='ranking desc', limit=1, context=context)
|
||||
next_post = next_post_id and blog_post_obj.browse(cr, uid, next_post_id[0], context=context) or False
|
||||
|
||||
values = {
|
||||
'blog': blog_post.blog_id,
|
||||
'blogs': blogs,
|
||||
'tags': tags,
|
||||
'tag': tag,
|
||||
'blog': blog,
|
||||
'blog_post': blog_post,
|
||||
'main_object': blog_post,
|
||||
'pager': pager,
|
||||
'nav_list': self.nav_list(),
|
||||
'enable_editor': enable_editor,
|
||||
'next_post': next_post,
|
||||
'date': date_begin,
|
||||
'post_url': post_url,
|
||||
'blog_url': blog_url,
|
||||
'pager': pager,
|
||||
}
|
||||
return request.website.render("website_blog.blog_post_complete", values)
|
||||
response = request.website.render("website_blog.blog_post_complete", values)
|
||||
response.set_cookie('visited_blogs', ','.join(map(str, visited_ids)))
|
||||
|
||||
request.session[request.session_id] = request.session.get(request.session_id, [])
|
||||
if not (blog_post.id in request.session[request.session_id]):
|
||||
request.session[request.session_id].append(blog_post.id)
|
||||
# Increase counter
|
||||
blog_post_obj.write(cr, SUPERUSER_ID, [blog_post.id], {
|
||||
'visits': blog_post.visits+1,
|
||||
},context=context)
|
||||
return response
|
||||
|
||||
def _blog_post_message(self, user, blog_post_id=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
blog_post = request.registry['blog.post']
|
||||
partner_obj = request.registry['res.partner']
|
||||
thread_obj = request.registry['mail.thread']
|
||||
website = request.registry['website']
|
||||
|
||||
public_id = website.get_public_user(cr, uid, context)
|
||||
if uid != public_id:
|
||||
partner_ids = [user.partner_id.id]
|
||||
else:
|
||||
partner_ids = blog_post._find_partner_from_emails(
|
||||
cr, SUPERUSER_ID, 0, [post.get('email')], context=context)
|
||||
if not partner_ids or not partner_ids[0]:
|
||||
partner_ids = [partner_obj.create(cr, SUPERUSER_ID, {'name': post.get('name'), 'email': post.get('email')}, context=context)]
|
||||
|
||||
message_id = blog_post.message_post(
|
||||
cr, SUPERUSER_ID, int(blog_post_id),
|
||||
body=post.get('comment'),
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
author_id=partner_ids[0],
|
||||
path=post.get('path', False),
|
||||
context=dict(context, mail_create_nosubcribe=True))
|
||||
return message_id
|
||||
|
||||
@http.route(['/blogpost/comment'], type='http', auth="public", methods=['POST'], website=True)
|
||||
def blog_post_comment(self, blog_post_id=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
if post.get('comment'):
|
||||
user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
|
||||
group_ids = user.groups_id
|
||||
group_id = request.registry["ir.model.data"].get_object_reference(cr, uid, 'website_mail', 'group_comment')[1]
|
||||
if group_id in [group.id for group in group_ids]:
|
||||
blog_post = request.registry['blog.post']
|
||||
blog_post.check_access_rights(cr, uid, 'read')
|
||||
blog_post.message_post(
|
||||
cr, SUPERUSER_ID, int(blog_post_id),
|
||||
body=post.get('comment'),
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
author_id=user.partner_id.id,
|
||||
context=dict(context, mail_create_nosubcribe=True))
|
||||
user = request.registry['res.users'].browse(cr, uid, uid, context=context)
|
||||
blog_post = request.registry['blog.post']
|
||||
blog_post.check_access_rights(cr, uid, 'read')
|
||||
self._blog_post_message(user, blog_post_id, **post)
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
|
||||
|
||||
def _get_discussion_detail(self, ids, publish=False, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
values = []
|
||||
mail_obj = request.registry.get('mail.message')
|
||||
for message in mail_obj.browse(cr, SUPERUSER_ID, ids, context=context):
|
||||
values.append({
|
||||
"id": message.id,
|
||||
"author_name": message.author_id.name,
|
||||
"author_image": message.author_id.image and \
|
||||
("data:image/png;base64,%s" % message.author_id.image) or \
|
||||
'/website_blog/static/src/img/anonymous.png',
|
||||
"date": message.date,
|
||||
'body': html2plaintext(message.body),
|
||||
'website_published' : message.website_published,
|
||||
'publish' : publish,
|
||||
})
|
||||
return values
|
||||
|
||||
@http.route(['/blogpost/post_discussion'], type='json', auth="public", website=True)
|
||||
def post_discussion(self, blog_post_id=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
|
||||
user = request.registry['res.users'].browse(cr, uid, uid, context=context)
|
||||
id = self._blog_post_message(user, blog_post_id, **post)
|
||||
return self._get_discussion_detail([id], publish, **post)
|
||||
|
||||
@http.route('/blogpost/new', type='http', auth="public", website=True, multilang=True)
|
||||
def blog_post_create(self, blog_id, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
create_context = dict(context, mail_create_nosubscribe=True)
|
||||
new_blog_post_id = request.registry['blog.post'].create(
|
||||
request.cr, request.uid, {
|
||||
'blog_id': blog_id,
|
||||
'name': _("Blog Post Title"),
|
||||
'content': '',
|
||||
'website_published': False,
|
||||
}, context=create_context)
|
||||
return werkzeug.utils.redirect("/blogpost/%s?enable_editor=1" % new_blog_post_id)
|
||||
new_blog_post_id = request.registry['blog.post'].create(cr, uid, {
|
||||
'blog_id': blog_id,
|
||||
'name': _("Blog Post Title"),
|
||||
'subtitle': _("Subtitle"),
|
||||
'content': '',
|
||||
'website_published': False,
|
||||
}, context=create_context)
|
||||
new_blog_post = request.registry['blog.post'].browse(cr, uid, new_blog_post_id, context=context)
|
||||
return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(new_blog_post.blog_id), slug(new_blog_post)))
|
||||
|
||||
@http.route('/blogpost/duplicate', type='http', auth="public", website=True)
|
||||
def blog_post_copy(self, blog_post_id, **post):
|
||||
|
@ -293,5 +325,31 @@ class WebsiteBlog(http.Controller):
|
|||
"""
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
create_context = dict(context, mail_create_nosubscribe=True)
|
||||
new_blog_post_id = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
|
||||
return werkzeug.utils.redirect("/blogpost/%s?enable_editor=1" % new_blog_post_id)
|
||||
nid = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
|
||||
new_blog_post = request.registry['blog.post'].browse(cr, uid, nid, context=context)
|
||||
post = request.registry['blog.post'].browse(cr, uid, nid, context)
|
||||
return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(post.blog_id), slug(new_blog_post)))
|
||||
|
||||
@http.route('/blogpost/get_discussion/', type='json', auth="public", website=True)
|
||||
def discussion(self, post_id=0, path=None, count=False, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
mail_obj = request.registry.get('mail.message')
|
||||
domain = [('res_id', '=', int(post_id)), ('model', '=', 'blog.post'), ('path', '=', path)]
|
||||
#check current user belongs to website publisher group
|
||||
publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
|
||||
if not publish:
|
||||
domain.append(('website_published', '=', True))
|
||||
ids = mail_obj.search(cr, SUPERUSER_ID, domain, count=count)
|
||||
if count:
|
||||
return ids
|
||||
return self._get_discussion_detail(ids, publish, **post)
|
||||
|
||||
@http.route('/blogpost/change_background', type='json', auth="public", website=True)
|
||||
def change_bg(self, post_id=0, image=None, **post):
|
||||
if not post_id:
|
||||
return False
|
||||
return request.registry['blog.post'].write(request.cr, request.uid, [int(post_id)], {'background_image': image}, request.context)
|
||||
|
||||
@http.route('/blog/get_user/', type='json', auth="public", website=True)
|
||||
def get_user(self, **post):
|
||||
return [False if request.session.uid else True]
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="blog_blog_1" model="blog.blog">
|
||||
<field name="name">News</field>
|
||||
<field name="name">Our News</field>
|
||||
<field name="subtitle">Sharing our evolution with passion</field>
|
||||
<field name="description">Presentation of new OpenERP features</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -8,329 +8,199 @@
|
|||
<field name="name">functional</field>
|
||||
</record>
|
||||
<record id="blog_tag_2" model="blog.tag">
|
||||
<field name="name">technical</field>
|
||||
</record>
|
||||
<record id="blog_tag_3" model="blog.tag">
|
||||
<field name="name">website</field>
|
||||
</record>
|
||||
<record id="blog_tag_4" model="blog.tag">
|
||||
<field name="name">pos</field>
|
||||
</record>
|
||||
|
||||
<!-- POSTS -->
|
||||
<record id="blog_post_1" model="blog.post">
|
||||
<field name="name">OpenERP v8 New Features</field>
|
||||
<field name="name">The Future of Emails</field>
|
||||
<field name="subtitle">Ideas behing the OpenERP communication tools.</field>
|
||||
<field name="blog_id" ref="blog_blog_1"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1')])]"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_meta_keywords">OpenERP, Point of Sale, Hardware, Interface, Payment Terminal, Store</field>
|
||||
<field name="website_meta_description">Open source Point of Sale with no installation required that runs online and offline.</field>
|
||||
<field name="website_meta_keywords">OpenERP, email</field>
|
||||
<field name="website_meta_description">The Future of Emails</field>
|
||||
<field name="background_image">/website_blog/static/src/img/post1.jpg</field>
|
||||
<field name="content"><![CDATA[
|
||||
<section data-snippet-id='image-text'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mt16 mb16">
|
||||
<img class="img-responsive shadow" src="/website/static/src/img/image_text.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-6 mt16">
|
||||
<p>
|
||||
OpenERP's Point of Sale introduces a super clean
|
||||
interface with no installation required that runs
|
||||
online and offline on modern hardwares.
|
||||
</p><p>
|
||||
It's full integration with the company inventory
|
||||
and accounting, gives you real time statistics
|
||||
without the hassle of integrating several applications.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="mt16 mb16 readable">
|
||||
<iframe width="361" height="200" src="http://www.youtube.com/embed/EkbBFmIWoTE" frameborder="0" allowfullscreen></iframe>
|
||||
<p>
|
||||
Emails are broken.
|
||||
</p><p>
|
||||
Emails make me waste my time. But I need them.
|
||||
Given the importance that emails have in our lives,
|
||||
it's incredible it's still one of the only software
|
||||
areas that did not evolve in the past 20 years!
|
||||
</p><p>
|
||||
Reading my inbox is the most unproductive task I do
|
||||
on a daily basis. I have to spend one full hour a
|
||||
day to process my emails. All the junk flows in the
|
||||
same inbox; spams, information that doesn't matter,
|
||||
quoted answers of quoted answers, etc. At the end
|
||||
of the hour, only 10 emails actually requested an
|
||||
answer from me. With a good tool, I could have done
|
||||
my job in 10 minutes!
|
||||
</p>
|
||||
</section>
|
||||
<section class="mt16 mb16" data-snippet-id='text-block'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb32">
|
||||
<h2>
|
||||
Linked with Project Management
|
||||
</h2>
|
||||
<h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
|
||||
</div>
|
||||
<div class="col-md-12 mb16 mt16">
|
||||
<p>
|
||||
OpenERP's <b>collaborative and realtime</b> project
|
||||
management helps your team get work done. Keep
|
||||
track of everything, from the big picture to the
|
||||
minute details, from the customer contract to the
|
||||
billing.
|
||||
</p><p>
|
||||
Organize projects around <b>your own processes</b>. Work
|
||||
on tasks and issues using the kanban view, schedule
|
||||
tasks using the gantt chart and control deadlines
|
||||
in the calendar view. Every project may have it's
|
||||
own stages allowing teams to optimize their job.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="mt16 mb16 readable">
|
||||
<p>
|
||||
At OpenERP, we build tools to bring productivity to
|
||||
enterprises. As emails and information flows are one of
|
||||
the biggest wastes of time in companies, we have to fix
|
||||
this.
|
||||
</p><p>
|
||||
To disrupt emails, you need more than just another user
|
||||
interface. We need to rethink the whole communication flow.
|
||||
</p>
|
||||
<h3>The Communication Mechanism of OpenERP</h3>
|
||||
<p>
|
||||
Here are the ideas behing the OpenERP communication tools:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Get Things Done: your inbox is a
|
||||
todo list. You should be able to process (not only
|
||||
read) the inbox and easily mark messages for future
|
||||
actions. Every inbox should be empty after having
|
||||
been processed; no more overload of information.
|
||||
<img class="img-responsive" src="/website_blog/static/src/img/mail-sc-00.png"/>
|
||||
</li><li>
|
||||
Keep control of what you want to receive or don't want
|
||||
to receive. People should never receive spam. You
|
||||
should follow/unfollow any kind of information in one
|
||||
click.
|
||||
</li><li>
|
||||
Productivity is key: our smart user
|
||||
interface does not require you to click on every mail
|
||||
to read a thread. Reading a full thread, replying,
|
||||
attaching documents is super fast.
|
||||
<img class="img-responsive" src="/website_blog/static/src/img/mail-sc-03.png"/>
|
||||
</li><li>
|
||||
A mix of push & pull: Today, people
|
||||
are victims of what others decide to push to them.
|
||||
OpenERP differentiates:
|
||||
<ul>
|
||||
<li>
|
||||
Messages "for information":
|
||||
you can pull them when you need some specific
|
||||
information; they are not required to be read
|
||||
every day.You receive only what you decided
|
||||
to follow.This accounts for 90% of your daily
|
||||
emails.Use the "Inbox" menu for these.
|
||||
</li><li>
|
||||
Messages "for action": they
|
||||
require your immediate attention and you need
|
||||
to process them all. This accounts for 10%
|
||||
of your daily emails. Use the "To: me" menu
|
||||
for these.
|
||||
</li>
|
||||
</ul>
|
||||
</li><li>
|
||||
Focus on the Content: Everything is
|
||||
stripped to emphasize on the real message. No more
|
||||
welcome introductions, greetings, signatures and legal
|
||||
notes.We standardize the layout of each message.
|
||||
(signatures are on the profile of a contact, not in
|
||||
every message)
|
||||
</li><li>
|
||||
Folders and mailing lists are great tools but too
|
||||
complex in traditional email clients. In OpenERP, a
|
||||
group of contacts that share a discussion can be
|
||||
created with one click. Every group should have it's
|
||||
own email address.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="oe_dark mt16 mb16" data-snippet-id='big-picture'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt32 mb32">
|
||||
<h2>Work with the hardware you already have...</h2>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<img class="img-responsive" src="/website/static/src/img/big_picture.png" style="margin: 0 auto;"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-3 mb16 mt16">
|
||||
<p class="text-center">
|
||||
<b>No installation required</b>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
OpenERP's Point of Sale introduces a super clean
|
||||
interface with no installation required that runs
|
||||
online and offline on modern hardware. Laptops,
|
||||
tablets, industrial POS, it runs on everything.
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<a href="/page/website.contactus">Get more information »</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="blog_post_2" model="blog.post">
|
||||
<field name="name">New Hardware Integration</field>
|
||||
<field name="name">Integrating your CMS and E-Commerce</field>
|
||||
<field name="subtitle">Building your company's website and selling your products online easy.</field>
|
||||
<field name="blog_id" ref="blog_blog_1"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1')])]"/>
|
||||
<field name="content">
|
||||
<![CDATA[<section class="mt16 mb16" data-snippet-id='big-picture'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-responsive" src="/website/static/src/img/big_picture.png" style="margin: 0 auto;"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-3 mb16 mt16">
|
||||
<p class="text-center">
|
||||
<b>New Features Launched</b>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
OpenERP's Point of Sale introduces a super clean
|
||||
interface with no installation required that runs
|
||||
online and offline on modern hardware. Laptops,
|
||||
tablets, industrial POS, it runs on everything.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section data-snippet-id='pricing'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb32">
|
||||
<h2>Our Offers</h2>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-info">
|
||||
<!-- Default panel contents -->
|
||||
<div class="panel-heading text-center">
|
||||
<h2 style="margin: 0">Beginner</h2>
|
||||
<p style="margin: 0" class="text-muted">
|
||||
Starter package
|
||||
</p>
|
||||
</div>
|
||||
<div class="panel-body text-center text-muted" style="background-color: rgba(0,0,0,0.1)">
|
||||
<h2 style="margin: 0"><span>$</span><b style="font-size: 60px">450</b><small>.00</small></h2>
|
||||
<div>per month</div>
|
||||
</div>
|
||||
|
||||
<!-- List group -->
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Battery: 8 hours</li>
|
||||
<li class="list-group-item">Screen: 2.5 inch</li>
|
||||
<li class="list-group-item">Weight: 1.1 ounces</li>
|
||||
<li class="list-group-item">No support</li>
|
||||
</ul>
|
||||
<div class="panel-footer text-center">
|
||||
<p class="text-muted">
|
||||
<i>Free shipping, satisfied or reimbursed.</i>
|
||||
</p>
|
||||
<a href="/page/website.contactus" class="btn btn-primary btn-lg">Order now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-primary">
|
||||
<!-- Default panel contents -->
|
||||
<div class="panel-heading text-center">
|
||||
<h2 style="margin: 0">Professional</h2>
|
||||
<p style="margin: 0">
|
||||
Enterprise package
|
||||
</p>
|
||||
</div>
|
||||
<div class="panel-body text-center text-muted" style="background-color: rgba(0,0,0,0.1)">
|
||||
<h2 style="margin: 0"><span>$</span><b style="font-size: 60px">590</b><small>.00</small></h2>
|
||||
<div>per month</div>
|
||||
</div>
|
||||
|
||||
<!-- List group -->
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Battery: 12 hours</li>
|
||||
<li class="list-group-item">Screen: 2.8 inch</li>
|
||||
<li class="list-group-item">Weight: 1.2 ounces</li>
|
||||
<li class="list-group-item">Limited support</li>
|
||||
</ul>
|
||||
<div class="panel-footer text-center">
|
||||
<p class="text-muted">
|
||||
<i>Free shipping, satisfied or reimbursed.</i>
|
||||
</p>
|
||||
<a href="/page/website.contactus" class="btn btn-primary btn-lg">Order now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-info">
|
||||
<!-- Default panel contents -->
|
||||
<div class="panel-heading text-center">
|
||||
<h2 style="margin: 0">Expert</h2>
|
||||
<p style="margin: 0" class="text-muted">
|
||||
The top of the top
|
||||
</p>
|
||||
</div>
|
||||
<div class="panel-body text-center text-muted" style="background-color: rgba(0,0,0,0.1)">
|
||||
<h2 style="margin: 0"><span>$</span><b style="font-size: 60px">890</b><small>.00</small></h2>
|
||||
<div>per month</div>
|
||||
</div>
|
||||
|
||||
<!-- List group -->
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Battery: 20 hours</li>
|
||||
<li class="list-group-item">Screen: 2.8 inch</li>
|
||||
<li class="list-group-item">Weight: 1.2 ounces</li>
|
||||
<li class="list-group-item">Unlimited support</li>
|
||||
</ul>
|
||||
<div class="panel-footer text-center">
|
||||
<p class="text-muted">
|
||||
<i>Free shipping, satisfied or reimbursed.</i>
|
||||
</p>
|
||||
<a href="/page/website.contactus" class="btn btn-primary btn-lg">Contact us</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="blog_post_3" model="blog.post">
|
||||
<field name="name">Touchscreen Point of Sale for 6.1</field>
|
||||
<field name="blog_id" ref="blog_blog_1"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1'), ref('blog_tag_2')])]"/>
|
||||
<field name="website_meta_keywords">Point of Sale, Hardware, Interface, Payment Terminal, Store</field>
|
||||
<field name="website_meta_description">Point of Sale with no installation required that runs online and offline.</field>
|
||||
<field name="content">
|
||||
<![CDATA[<p>The brand new OpenERP touchscreen point of sale is available with 6.1 which allows you
|
||||
to manage your shop sales very easily. It's fully web based so that you don't
|
||||
have to install or deploy any software and all the sales shops can be easily
|
||||
consolidated. It works in connected and disconnected modes so that you can
|
||||
continue to sell even if you lose your internet connection.</p>
|
||||
<img src="http://www.openerp.com/sites/default/files/fileattach/POS(2).png" alt="">
|
||||
<h3>Here's a summary of its main features and benefits:</h3>
|
||||
<ul>
|
||||
<li>100% WEB based</li>
|
||||
<li>available for any touchscreen device (ipod, ipad, any tablet)mobile (with portable devices)</li>
|
||||
<li>no installation required</li>
|
||||
<li>no synchronization needed, completely integrated</li>
|
||||
<li>continue working even when your connection is down or if you close your browser, data won't be lost</li>
|
||||
<li>fully web based with a clean interface smart interface</li>
|
||||
</ul>
|
||||
<p>You have different options to select your products. You can do it through the
|
||||
barcode reader, just browse through the categories you have put in place (ie.
|
||||
drinks, snacks, meals, etc.), or text search in case neither of the other
|
||||
options work for you. For example, if you need to use the POS for your restaurant,
|
||||
your employees can record multiple tickets at the same time without having to wait
|
||||
to process one transaction at a time. In addition, you can facilitate payments,
|
||||
the application allows multiple payment methods.</p>
|
||||
<p>The POS application is so simple and accessible to use that your shop or
|
||||
restaurant will never need any other tool to manage orders. Due to its smart
|
||||
and user-friendly interface you don't need any training to learn how to use it.
|
||||
Think of it as an out-of-the-box solution to boost your business' productivity.
|
||||
</p>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="blog_post_4" model="blog.post">
|
||||
<field name="name">Announcing a New Partnership</field>
|
||||
<field name="blog_id" ref="blog_blog_1"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1')])]"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_meta_keywords">OpenERP, Partnership, News, Accounting</field>
|
||||
<field name="website_meta_description">Our company partners with OpenERP to develop accounting best practices.</field>
|
||||
<field name="content"><![CDATA[
|
||||
<section data-snippet-id='image-text'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mt16 mb16">
|
||||
<img class="img-responsive shadow" src="/website/static/src/img/text_image.png"/>
|
||||
</div>
|
||||
<div class="col-md-6 mt16">
|
||||
<p>
|
||||
We are proud to announce a new partnership with
|
||||
the company OpenERP. Their open source application suite
|
||||
will allow us to reach new markets, specifically in
|
||||
the accounting area.
|
||||
</p><p>
|
||||
The full integration with the company inventory
|
||||
and accounting, will give our customers real time statistics
|
||||
without the hassle of integrating several applications.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1'), ref('blog_tag_2')])]"/>
|
||||
<field name="background_image">/website_blog/static/src/img/post2.jpg</field>
|
||||
<field name="content">
|
||||
<![CDATA[<section class="row readable">
|
||||
<div class="col-md-12 mb32">
|
||||
<img class="img-responsive" src="/website_blog/static/src/img/CMS_WMS_screens.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-3 mb16 mt16">
|
||||
<p class="text-center">
|
||||
New Features Launched
|
||||
</p>
|
||||
<p class="text-center">
|
||||
To add to an already comprehensive set of OpenERP
|
||||
features, a website content management system (CMS
|
||||
or WMS) has been developed and a beta release is
|
||||
available from today, 31st January 2014.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mt16 mb16" data-snippet-id='text-block'>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb32">
|
||||
<h2>
|
||||
OpenERP Project Management
|
||||
</h2>
|
||||
<h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
|
||||
</div>
|
||||
<div class="col-md-12 mb16 mt16">
|
||||
<p>
|
||||
OpenERP's <b>collaborative and realtime</b> project
|
||||
management helps your team get work done. Keep
|
||||
track of everything, from the big picture to the
|
||||
minute details, from the customer contract to the
|
||||
billing.
|
||||
</p><p>
|
||||
Organize projects around <b>your own processes</b>. Work
|
||||
on tasks and issues using the kanban view, schedule
|
||||
tasks using the gantt chart and control deadlines
|
||||
in the calendar view. Every project may have it's
|
||||
own stages allowing teams to optimize their job.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="readable">
|
||||
<p>
|
||||
OpenERP claims to be 'the Open Source software that makes
|
||||
building your company's website and selling your products
|
||||
online easy'. So how true is this statement?
|
||||
</p><p>
|
||||
"OpenERP's latest launch will allow a business to go from
|
||||
zero to trading online quicker than ever before,” Stuart
|
||||
Mackintosh, MD of Open Source specialist and OpenERP
|
||||
integration partner, OpusVL, explains. “The investment
|
||||
required to have a fully automated business system is
|
||||
dramatically reduced, enabling the small and medium
|
||||
enterprise to compete at a level of functionality and
|
||||
performance previously reserved for the big IT investors."
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>
|
||||
"Finally, the leading edge is being brought to the masses.
|
||||
It will now be the turn of the big players to catch up to
|
||||
the superior technologies of the SME."
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>
|
||||
"This is another clever and highly disruptive move by
|
||||
OpenERP,which will force other technology providers to
|
||||
take another look at the value they are providing to ensure
|
||||
that their 'solutions' can still compete."
|
||||
</p><p>
|
||||
"OpenERP now competes on many fronts, with no real
|
||||
competition out there to knock them off the top spot.
|
||||
With the launch of their integrated CMS and Ecommerce
|
||||
systems,it only elevates their position as one of the leading
|
||||
lights in the open source revolution. It will be at least 5
|
||||
years before another ERP or CMS provider will be able to
|
||||
compete at this level due to the technology currently
|
||||
employed by most industry providers."
|
||||
</p>
|
||||
<h4>Adding to industry leading technology</h4>
|
||||
<p>
|
||||
Like many modern website editors, with OpenERP you can edit
|
||||
content in-line, enabling you to see exactly what you are
|
||||
changing and ensure your changes suit the context.
|
||||
</p><p>
|
||||
However, unlike other web content management systems, it
|
||||
fully integrates into the back-end database. This means
|
||||
that when you edit a product description, image or price,
|
||||
it updates the product database in real time, providing a
|
||||
true self-service window into the business.
|
||||
</p><p>
|
||||
This provides a single source of data for your company and
|
||||
removes the need to create offline synchronisation between
|
||||
website and product database.
|
||||
</p><p>
|
||||
As it comes, there is a default website based on Bootstrap
|
||||
3, the latest industry standard for rapid development of
|
||||
multi-device websites backed by Twitter, so can be directly
|
||||
integrated with many web tools and works across all devices
|
||||
by default.
|
||||
</p>
|
||||
</section>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
_blog_blog:
|
||||
|
||||
blog.blog
|
||||
=========
|
||||
In ``blog.blog``, added field ``subtitle`` which Indicates the subtitle of blogs.
|
||||
- ``subtitle``: fields.char('Blog Subtitle')
|
||||
|
||||
mail.message
|
||||
============
|
||||
In ``mail.message``, added field ``discussion`` which Indicates the unique identification
|
||||
of paragraph on blog post.
|
||||
- ``discussion``: fields.char('Discussion Unique Name')
|
||||
|
||||
blog.post
|
||||
=========
|
||||
Fields
|
||||
++++++
|
||||
- ``sub_title`` : contains the subtitle of every blog post.
|
||||
- ``visits`` : Indicates the number of visits on evry blog post.
|
||||
- ``ranking`` : Indicates the ranking on every blog post.
|
|
@ -7,3 +7,29 @@ Changelog
|
|||
----------------
|
||||
|
||||
- created ``website_blog`` menu, build on defunct document_page module.
|
||||
- added new feature ``Inline Discussion`` , that will allow a user to comment
|
||||
on every paragraph on blog post
|
||||
- added new feature ``Select to Tweet``, that will alllow a user tweet a selected
|
||||
text from blog to post , directly on twitter.
|
||||
|
||||
|
||||
|
||||
WebsiteBlog(controller)
|
||||
=======================
|
||||
Methods
|
||||
+++++++
|
||||
- ``blog`` : remove routing related to date.
|
||||
- ``blog_post`` : updated with , suggestion of next post to the user based on
|
||||
cookie and number of views.
|
||||
- ``discussion`` : added method , contains a detail of discussion on every paragraph,
|
||||
if count is true it only return len of ids else return full detail.
|
||||
def discussion(self, post_id=0, discussion=None, count=False, **post)
|
||||
- ``post_discussion`` : added methodt, that allow to post discussion on any paragraph.
|
||||
def post_discussion(self, blog_post_id=0, **post)
|
||||
- ``change_bg`` : added method allow a user to change background image on blog
|
||||
post from front-end.
|
||||
def change_bg(self, post_id=0, image=None, **post)
|
||||
- ``get_user`` : added method , that will return True if user is public else False.
|
||||
def get_user(self, **post):
|
||||
return [False if request.session.uid else True]
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
.. _controller:
|
||||
|
||||
WebsiteBlog(controller)
|
||||
=======================
|
||||
Methods
|
||||
+++++++
|
||||
- ``blog`` : remove routing related to date.
|
||||
- ``blog_post`` : updated with , suggestion of next post to the user based on
|
||||
cookie and number of views.
|
||||
- ``discussion`` : added method , contains a detail of discussion on every paragraph,
|
||||
if count is true it only return len of ids else return full detail.
|
||||
def discussion(self, post_id=0, discussion=None, count=False, **post)
|
||||
- ``post_discussion`` : added methodt, that allow to post discussion on any paragraph.
|
||||
def post_discussion(self, blog_post_id=0, **post)
|
||||
- ``change_bg`` : added method allow a user to change background image on blog
|
||||
post from front-end.
|
||||
def change_bg(self, post_id=0, image=None, **post)
|
||||
- ``get_user`` : added method , that will return True if user is public else False.
|
||||
def get_user(self, **post):
|
||||
return [False if request.session.uid else True]
|
||||
|
|
@ -1 +1,2 @@
|
|||
import mail_message
|
||||
import website_blog
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class MailMessage(osv.Model):
|
||||
_inherit = 'mail.message'
|
||||
|
||||
_columns = {
|
||||
'path': fields.char(
|
||||
'Discussion Path', select=1,
|
||||
help='Used to display messages in a paragraph-based chatter using a unique path;'),
|
||||
}
|
|
@ -1,32 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from datetime import datetime
|
||||
import difflib
|
||||
import lxml
|
||||
import random
|
||||
|
||||
from openerp import tools
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import difflib
|
||||
|
||||
|
||||
class Blog(osv.Model):
|
||||
_name = 'blog.blog'
|
||||
|
@ -35,12 +18,9 @@ class Blog(osv.Model):
|
|||
_order = 'name'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
'name': fields.char('Blog Name', required=True),
|
||||
'subtitle': fields.char('Blog Subtitle'),
|
||||
'description': fields.text('Description'),
|
||||
'blog_post_ids': fields.one2many(
|
||||
'blog.post', 'blog_id',
|
||||
'Blogs',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,9 +32,6 @@ class BlogTag(osv.Model):
|
|||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
'blog_post_ids': fields.many2many(
|
||||
'blog.post', string='Posts',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,37 +39,20 @@ class BlogPost(osv.Model):
|
|||
_name = "blog.post"
|
||||
_description = "Blog Post"
|
||||
_inherit = ['mail.thread', 'website.seo.metadata']
|
||||
_order = 'write_date DESC'
|
||||
# maximum number of characters to display in summary
|
||||
_shorten_max_char = 250
|
||||
_order = 'id DESC'
|
||||
|
||||
def get_shortened_content(self, cr, uid, ids, name, arg, context=None):
|
||||
def _compute_ranking(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
for page in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
body_short = tools.html_email_clean(
|
||||
page.content,
|
||||
remove=True,
|
||||
shorten=True,
|
||||
max_length=self._shorten_max_char,
|
||||
expand_options={
|
||||
'oe_expand_container_tag': 'div',
|
||||
'oe_expand_container_class': 'oe_mail_expand text-center',
|
||||
'oe_expand_container_content': '',
|
||||
'oe_expand_a_href': '/blogpost/%d' % page.id,
|
||||
'oe_expand_a_class': 'oe_mail_expand btn btn-info',
|
||||
'oe_expand_separator_node': 'br',
|
||||
},
|
||||
protect_sections=True,
|
||||
)
|
||||
except Exception:
|
||||
body_short = False
|
||||
res[page.id] = body_short
|
||||
for blog_post in self.browse(cr, uid, ids, context=context):
|
||||
age = datetime.now() - datetime.strptime(blog_post.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
res[blog_post.id] = blog_post.visits * (0.5+random.random()) / max(3, age.days)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Title', required=True, translate=True),
|
||||
'content_image': fields.binary('Background Image'),
|
||||
'subtitle': fields.char('Sub Title', translate=True),
|
||||
'author_id': fields.many2one('res.partner', 'Author'),
|
||||
'background_image': fields.binary('Background Image'),
|
||||
'blog_id': fields.many2one(
|
||||
'blog.blog', 'Blog',
|
||||
required=True, ondelete='cascade',
|
||||
|
@ -101,32 +61,22 @@ class BlogPost(osv.Model):
|
|||
'blog.tag', string='Tags',
|
||||
),
|
||||
'content': fields.html('Content', translate=True),
|
||||
'shortened_content': fields.function(
|
||||
get_shortened_content,
|
||||
type='html',
|
||||
string='Shortened Content',
|
||||
help="Shortened content of the page that serves as a summary"
|
||||
),
|
||||
# website control
|
||||
'website_published': fields.boolean(
|
||||
'Publish', help="Publish on the website"
|
||||
),
|
||||
'website_published_datetime': fields.datetime(
|
||||
'Publish Date'
|
||||
),
|
||||
# TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
|
||||
'website_message_ids': fields.one2many(
|
||||
'mail.message', 'res_id',
|
||||
domain=lambda self: [
|
||||
'&', ('model', '=', self._name), ('type', '=', 'comment')
|
||||
'&', '&', ('model', '=', self._name), ('type', '=', 'comment'), ('path', '=', False)
|
||||
],
|
||||
string='Website Messages',
|
||||
help="Website communication history",
|
||||
),
|
||||
# technical stuff: history, menu (to keep ?)
|
||||
'history_ids': fields.one2many(
|
||||
'blog.post.history', 'post_id',
|
||||
'History', help='Last post modifications'
|
||||
'History', help='Last post modifications',
|
||||
deprecated='This field will be removed for OpenERP v9.'
|
||||
),
|
||||
# creation / update stuff
|
||||
'create_date': fields.datetime(
|
||||
|
@ -145,11 +95,71 @@ class BlogPost(osv.Model):
|
|||
'res.users', 'Last Contributor',
|
||||
select=True, readonly=True,
|
||||
),
|
||||
'visits': fields.integer('No of Views'),
|
||||
'ranking': fields.function(_compute_ranking, string='Ranking', type='float'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'website_published': False
|
||||
'name': _('Blog Post Title'),
|
||||
'subtitle': _('Subtitle'),
|
||||
'author_id': lambda self, cr, uid, ctx=None: self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id,
|
||||
}
|
||||
|
||||
def html_tag_nodes(self, html, attribute=None, tags=None, context=None):
|
||||
""" Processing of html content to tag paragraphs and set them an unique
|
||||
ID.
|
||||
:return result: (html, mappin), where html is the updated html with ID
|
||||
and mapping is a list of (old_ID, new_ID), where old_ID
|
||||
is None is the paragraph is a new one. """
|
||||
mapping = []
|
||||
if not html:
|
||||
return html, mapping
|
||||
if tags is None:
|
||||
tags = ['p']
|
||||
if attribute is None:
|
||||
attribute = 'data-unique-id'
|
||||
counter = 0
|
||||
|
||||
# form a tree
|
||||
root = lxml.html.fragment_fromstring(html, create_parent='div')
|
||||
if not len(root) and root.text is None and root.tail is None:
|
||||
return html, mapping
|
||||
|
||||
# check all nodes, replace :
|
||||
# - img src -> check URL
|
||||
# - a href -> check URL
|
||||
for node in root.iter():
|
||||
if not node.tag in tags:
|
||||
continue
|
||||
ancestor_tags = [parent.tag for parent in node.iterancestors()]
|
||||
if ancestor_tags:
|
||||
ancestor_tags.pop()
|
||||
ancestor_tags.append('counter_%s' % counter)
|
||||
new_attribute = '/'.join(reversed(ancestor_tags))
|
||||
old_attribute = node.get(attribute)
|
||||
node.set(attribute, new_attribute)
|
||||
mapping.append((old_attribute, counter))
|
||||
counter += 1
|
||||
|
||||
html = lxml.html.tostring(root, pretty_print=False, method='html')
|
||||
# this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
|
||||
if html.startswith('<div>') and html.endswith('</div>'):
|
||||
html = html[5:-6]
|
||||
return html, mapping
|
||||
|
||||
def _postproces_content(self, cr, uid, id, content=None, context=None):
|
||||
if content is None:
|
||||
content = self.browse(cr, uid, id, context=context).content
|
||||
if content is False:
|
||||
return content
|
||||
content, mapping = self.html_tag_nodes(content, attribute='data-chatter-id', tags=['p'], context=context)
|
||||
for old_attribute, new_attribute in mapping:
|
||||
if not old_attribute:
|
||||
continue
|
||||
msg_ids = self.pool['mail.message'].search(cr, SUPERUSER_ID, [('path', '=', old_attribute)], context=context)
|
||||
self.pool['mail.message'].write(cr, SUPERUSER_ID, msg_ids, {'path': new_attribute}, context=context)
|
||||
return content
|
||||
|
||||
def create_history(self, cr, uid, ids, vals, context=None):
|
||||
for i in ids:
|
||||
history = self.pool.get('blog.post.history')
|
||||
|
@ -163,12 +173,16 @@ class BlogPost(osv.Model):
|
|||
def create(self, cr, uid, vals, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
if 'content' in vals:
|
||||
vals['content'] = self._postproces_content(cr, uid, None, vals['content'], context=context)
|
||||
create_context = dict(context, mail_create_nolog=True)
|
||||
post_id = super(BlogPost, self).create(cr, uid, vals, context=create_context)
|
||||
self.create_history(cr, uid, [post_id], vals, context)
|
||||
return post_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if 'content' in vals:
|
||||
vals['content'] = self._postproces_content(cr, uid, None, vals['content'], context=context)
|
||||
result = super(BlogPost, self).write(cr, uid, ids, vals, context)
|
||||
self.create_history(cr, uid, ids, vals, context)
|
||||
return result
|
||||
|
@ -183,10 +197,6 @@ class BlogPost(osv.Model):
|
|||
})
|
||||
return super(BlogPost, self).copy(cr, uid, id, default=default, context=context)
|
||||
|
||||
def img(self, cr, uid, ids, field='image_small', context=None):
|
||||
post = self.browse(cr, SUPERUSER_ID, ids[0], context=context)
|
||||
return "/website/image?model=%s&field=%s&id=%s" % ('res.users', field, post.create_uid.id)
|
||||
|
||||
|
||||
class BlogPostHistory(osv.Model):
|
||||
_name = "blog.post.history"
|
||||
|
@ -215,5 +225,3 @@ class BlogPostHistory(osv.Model):
|
|||
raise osv.except_osv(_('Warning!'), _('There are no changes in revisions.'))
|
||||
diff = difflib.HtmlDiff()
|
||||
return diff.make_table(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=True)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
(function(){
|
||||
$.fn.share = function(options) {
|
||||
var option = $.extend($.fn.share.defaults,options);
|
||||
$.extend($.fn.share,{
|
||||
init : function(shareable) {
|
||||
var self = this;
|
||||
$.fn.share.defaults.shareable = shareable;
|
||||
$.fn.share.defaults.shareable.on('mouseup',function(){
|
||||
self.popOver();
|
||||
});
|
||||
$.fn.share.defaults.shareable.on('mousedown',function(){
|
||||
self.destroy();
|
||||
});
|
||||
},
|
||||
getContent : function() {
|
||||
var current_url = window.location.href
|
||||
var selected_text = this.getSelection('string').substring(0,option.maxLength-(current_url.length+option.author_name.length+7));
|
||||
var text = encodeURIComponent('\"'+selected_text+'\" '+'--@'+option.author_name+' '+current_url)
|
||||
return '<a onclick="window.open(\''+option.shareLink+text+'\',\'_'+option.target+'\',\'location=yes,height=570,width=520,scrollbars=yes,status=yes\')"><i class="fa fa-twitter fa-lg"/></a>';
|
||||
},
|
||||
getSelection : function(share) {
|
||||
if(window.getSelection){
|
||||
return (share=='string')?String(window.getSelection().getRangeAt(0)).replace(/\s{2,}/g, ' '):window.getSelection().getRangeAt(0);
|
||||
}
|
||||
else if(document.selection){
|
||||
return (share=='string')?document.selection.createRange().text.replace(/\s{2,}/g, ' '):document.selection.createRange();
|
||||
}
|
||||
},
|
||||
popOver : function() {
|
||||
this.destroy();
|
||||
if(this.getSelection('string').length < option.minLength)
|
||||
return;
|
||||
var data = this.getContent();
|
||||
var range = this.getSelection();
|
||||
var newNode = document.createElement("mark");
|
||||
range.surroundContents(newNode);
|
||||
$('mark').addClass(option.className);
|
||||
$('.'+option.className).popover({trigger:'manual', placement: option.placement, html: true
|
||||
, content:function(){
|
||||
return data;
|
||||
}
|
||||
});
|
||||
$('.'+option.className).popover('show');
|
||||
},
|
||||
destroy : function(){
|
||||
$('.'+option.className).popover('hide');
|
||||
$('mark').contents().unwrap();
|
||||
$('mark').remove();
|
||||
}
|
||||
});
|
||||
$.fn.share.init(this);
|
||||
};
|
||||
|
||||
$.fn.share.defaults = {
|
||||
shareLink : "http://twitter.com/intent/tweet?text=",
|
||||
minLength : 5,
|
||||
maxLength : 140,
|
||||
target : "blank",
|
||||
className : "share",
|
||||
placement : "top",
|
||||
};
|
||||
|
||||
}());
|
|
@ -9,6 +9,12 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.read_width {
|
||||
max-width: 700px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.blog_content a.oe_mail_expand:after {
|
||||
content: " →";
|
||||
}
|
||||
|
@ -20,3 +26,127 @@ p.post-meta {
|
|||
position: relative;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
div#blog_angle_down a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cover {
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: black;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
.cover .blog_title {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 20%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.cover .blog_title h1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cover_footer {
|
||||
min-height: 350px;
|
||||
height: 65vh;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*Inline Discussion */
|
||||
.discussion {
|
||||
padding: 5px 10px 10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
line-height: 16px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
z-index: 7;
|
||||
}
|
||||
.discussion > a {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: 20px;
|
||||
height: 17px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
background: #bbbbbb;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-ms-border-radius: 2px;
|
||||
-o-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
-webkit-transition: all 0.5s;
|
||||
-moz-transition: all 0.5s;
|
||||
-o-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.discussion > a.has-comments {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.discussion-contain:hover .discussion > a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.discussion > a:after {
|
||||
border-right: 7px solid transparent;
|
||||
border-top: 7px solid #bbbbbb;
|
||||
right: 19px;
|
||||
top: 22px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
display: block;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
-webkit-transition: all 0.5s;
|
||||
-moz-transition: all 0.5s;
|
||||
-o-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.discussion:hover > a, .discussion.hovered > a {
|
||||
opacity: 1;
|
||||
background: #57ad68;
|
||||
}
|
||||
.discussion:hover > a:after, .discussion.hovered > a:after {
|
||||
border-top-color: #57ad68;
|
||||
}
|
||||
|
||||
#discussions_wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#discussions_overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 8;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.discussion .popover-content {
|
||||
max-height: 250px;
|
||||
width: 250px;
|
||||
overflow: auto;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
mark + .popover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@charset "utf-8"
|
||||
|
||||
@import "compass/css3"
|
||||
|
||||
.css_website_mail
|
||||
|
@ -10,6 +9,11 @@
|
|||
&:first-of-type
|
||||
display: block
|
||||
|
||||
.read_width
|
||||
max-width: 700px
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
|
||||
.blog_content
|
||||
a.oe_mail_expand:after
|
||||
content: " →"
|
||||
|
@ -20,3 +24,113 @@ p.post-meta
|
|||
position: relative
|
||||
top: -5px
|
||||
|
||||
div#blog_angle_down
|
||||
a:hover
|
||||
text-decoration: none
|
||||
|
||||
.cover
|
||||
-webkit-background-size: cover
|
||||
-moz-background-size: cover
|
||||
-o-background-size: cover
|
||||
background-size: cover
|
||||
background-position: center
|
||||
background-repeat: no-repeat
|
||||
background-color: #000
|
||||
color: #fff
|
||||
position: relative
|
||||
.blog_title
|
||||
position: absolute
|
||||
text-align: center
|
||||
top: 20%
|
||||
left: 0
|
||||
right: 0
|
||||
h1
|
||||
font-weight: bold
|
||||
|
||||
.cover_footer
|
||||
min-height : 350px
|
||||
height: 65vh
|
||||
cursor: pointer
|
||||
|
||||
/*Inline Discussion
|
||||
|
||||
.discussion
|
||||
padding: 5px 10px 10px
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
line-height: 16px
|
||||
font-size: 13px
|
||||
font-weight: bold
|
||||
font-family: sans-serif
|
||||
text-align: center
|
||||
z-index: 7
|
||||
> a
|
||||
opacity: 0
|
||||
display: block
|
||||
overflow: hidden
|
||||
width: 20px
|
||||
height: 17px
|
||||
color: white
|
||||
text-decoration: none
|
||||
cursor: pointer
|
||||
background: #bbbbbb
|
||||
-webkit-border-radius: 2px
|
||||
-moz-border-radius: 2px
|
||||
-ms-border-radius: 2px
|
||||
-o-border-radius: 2px
|
||||
border-radius: 2px
|
||||
-webkit-transition: all 0.5s
|
||||
-moz-transition: all 0.5s
|
||||
-o-transition: all 0.5s
|
||||
transition: all 0.5s
|
||||
&.has-comments
|
||||
opacity: .6
|
||||
|
||||
.discussion-contain:hover .discussion > a
|
||||
opacity: 1
|
||||
|
||||
.discussion
|
||||
> a:after
|
||||
border-right: 7px solid transparent
|
||||
border-top: 7px solid #bbbbbb
|
||||
right: 19px
|
||||
top: 22px
|
||||
height: 0
|
||||
width: 0
|
||||
display: block
|
||||
content: " "
|
||||
position: absolute
|
||||
-webkit-transition: all 0.5s
|
||||
-moz-transition: all 0.5s
|
||||
-o-transition: all 0.5s
|
||||
transition: all 0.5s
|
||||
&:hover > a, &.hovered > a
|
||||
opacity: 1
|
||||
background: #57AD68
|
||||
&:hover > a:after, &.hovered > a:after
|
||||
border-top-color: #57AD68
|
||||
|
||||
#discussions_wrapper
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
#discussions_overlay
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
background: rgba(0, 0, 0, 0.5)
|
||||
z-index: 8
|
||||
display: none
|
||||
|
||||
.discussion .popover-content
|
||||
max-height: 250px
|
||||
width: 250px
|
||||
overflow: auto
|
||||
font-weight: normal
|
||||
|
||||
mark + .popover
|
||||
cursor: pointer
|
||||
|
|
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 67 KiB |
|
@ -27,7 +27,38 @@
|
|||
}).then(function (cat_id) {
|
||||
document.location = '/blogpost/new?blog_id=' + cat_id;
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
edit: function () {
|
||||
$('.popover').remove();
|
||||
this._super();
|
||||
var vHeight = $(window).height();
|
||||
$('body').on('click','#change_cover',_.bind(this.change_bg,{},vHeight));
|
||||
$('body').on('click', '#clear_cover',_.bind(this.clean_bg,{},vHeight));
|
||||
},
|
||||
save : function() {
|
||||
var res = this._super();
|
||||
if ($('.cover').length) {
|
||||
openerp.jsonRpc("/blogpost/change_background", 'call', {
|
||||
'post_id' : $('#blog_post_name').attr('data-oe-id'),
|
||||
'image' : $('.cover').css('background-image').replace(/url\(|\)|"|'/g,''),
|
||||
});
|
||||
}
|
||||
return res;
|
||||
},
|
||||
clean_bg : function(vHeight) {
|
||||
$('.js_fullheight').css({"background-image":'none', 'min-height': vHeight});
|
||||
},
|
||||
change_bg : function(vHeight) {
|
||||
var self = this;
|
||||
var editor = new website.editor.ImageDialog();
|
||||
editor.on('start', self, function (o) {
|
||||
o.url = $('.js_fullheight').length ? $('.js_fullheight').css('background-image').replace(/url\(|\)|"|'/g,'') : '';
|
||||
});
|
||||
editor.on('save', self, function (o) {
|
||||
$('.js_fullheight').css({"background-image": o.url && o.url !== "" ? 'url(' + o.url + ')' : "", 'min-height': vHeight})
|
||||
});
|
||||
editor.appendTo('body');
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
// Inspired from https://github.com/tsi/inlineDisqussions
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website,
|
||||
qweb = openerp.qweb;
|
||||
website.add_template_file('/website_blog/static/src/xml/website_blog.inline.discussion.xml');
|
||||
website.blog_discussion = openerp.Class.extend({
|
||||
init: function(options) {
|
||||
var self = this ;
|
||||
self.discus_identifier;
|
||||
var defaults = {
|
||||
position: 'right',
|
||||
post_id: $('#blog_post_name').attr('data-blog-id'),
|
||||
content : false,
|
||||
public_user: false,
|
||||
};
|
||||
self.settings = $.extend({}, defaults, options);
|
||||
self.do_render(self);
|
||||
},
|
||||
do_render: function(data) {
|
||||
var self = this;
|
||||
if ($('#discussions_wrapper').length === 0 && self.settings.content.length > 0) {
|
||||
$('<div id="discussions_wrapper"></div>').insertAfter($('#blog_content'));
|
||||
}
|
||||
// Attach a discussion to each paragraph.
|
||||
$(self.settings.content).each(function(i) {
|
||||
self.discussion_handler(i, $(this));
|
||||
});
|
||||
// Hide the discussion.
|
||||
$('html').click(function(event) {
|
||||
if($(event.target).parents('#discussions_wrapper, .main-discussion-link-wrp').length === 0) {
|
||||
self.hide_discussion();
|
||||
}
|
||||
if(!$(event.target).hasClass('discussion-link') && !$(event.target).parents('.popover').length){
|
||||
if($('.move_discuss').length){
|
||||
$('[enable_chatter_discuss=True]').removeClass('move_discuss');
|
||||
$('[enable_chatter_discuss=True]').animate({
|
||||
'marginLeft': "+=40%"
|
||||
});
|
||||
$('#discussions_wrapper').animate({
|
||||
'marginLeft': "+=250px"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
prepare_data : function(identifier, comment_count) {
|
||||
var self = this;
|
||||
return openerp.jsonRpc("/blogpost/get_discussion/", 'call', {
|
||||
'post_id': self.settings.post_id,
|
||||
'path': identifier,
|
||||
'count': comment_count, //if true only get length of total comment, display on discussion thread.
|
||||
})
|
||||
},
|
||||
discussion_handler : function(i, node) {
|
||||
var self = this;
|
||||
var identifier = node.attr('data-chatter-id');
|
||||
if (identifier) {
|
||||
self.prepare_data(identifier, true).then( function (data) {
|
||||
self.prepare_discuss_link(data, identifier, node);
|
||||
});
|
||||
}
|
||||
},
|
||||
prepare_discuss_link : function(data, identifier, node) {
|
||||
var self = this;
|
||||
var cls = data > 0 ? 'discussion-link has-comments' : 'discussion-link';
|
||||
var a = $('<a class="'+ cls +' css_editable_mode_hidden" />')
|
||||
.attr('data-discus-identifier', identifier)
|
||||
.attr('data-discus-position', self.settings.position)
|
||||
.text(data > 0 ? data : '+')
|
||||
.attr('data-contentwrapper', '.mycontent')
|
||||
.wrap('<div class="discussion" />')
|
||||
.parent()
|
||||
.appendTo('#discussions_wrapper');
|
||||
a.css({
|
||||
'top': node.offset().top,
|
||||
'left': self.settings.position == 'right' ? node.outerWidth() + node.offset().left: node.offset().left - a.outerWidth()
|
||||
});
|
||||
// node.attr('data-discus-identifier', identifier)
|
||||
node.mouseover(function() {
|
||||
a.addClass("hovered");
|
||||
}).mouseout(function() {
|
||||
a.removeClass("hovered");
|
||||
});
|
||||
|
||||
a.delegate('a.discussion-link', "click", function(e) {
|
||||
e.preventDefault();
|
||||
if(!$('.move_discuss').length){
|
||||
$('[enable_chatter_discuss=True]').addClass('move_discuss');
|
||||
$('[enable_chatter_discuss=True]').animate({
|
||||
'marginLeft': "-=40%"
|
||||
});
|
||||
$('#discussions_wrapper').animate({
|
||||
'marginLeft': "-=250px"
|
||||
});
|
||||
}
|
||||
if ($(this).is('.active')) {
|
||||
e.stopPropagation();
|
||||
self.hide_discussion();
|
||||
}
|
||||
else {
|
||||
self.get_discussion($(this), function(source) {});
|
||||
}
|
||||
});
|
||||
},
|
||||
get_discussion : function(source, callback) {
|
||||
var self = this;
|
||||
var identifier = source.attr('data-discus-identifier');
|
||||
self.hide_discussion();
|
||||
self.discus_identifier = identifier;
|
||||
var elt = $('a[data-discus-identifier="'+identifier+'"]');
|
||||
elt.append(qweb.render("website.blog_discussion.popover", {'identifier': identifier , 'options': self.settings}));
|
||||
var comment = '';
|
||||
self.prepare_data(identifier,false).then(function(data){
|
||||
_.each(data, function(res){
|
||||
comment += qweb.render("website.blog_discussion.comment", {'res': res});
|
||||
});
|
||||
$('.discussion_history').html('<ul class="media-list">'+comment+'</ul>');
|
||||
self.create_popover(elt, identifier);
|
||||
// Add 'active' class.
|
||||
$('a.discussion-link, a.main-discussion-link').removeClass('active').filter(source).addClass('active');
|
||||
elt.popover('hide').filter(source).popover('show');
|
||||
callback(source);
|
||||
});
|
||||
},
|
||||
create_popover : function(elt, identifier) {
|
||||
var self = this;
|
||||
elt.popover({
|
||||
placement:'right',
|
||||
trigger:'manual',
|
||||
html:true, content:function(){
|
||||
return $($(this).data('contentwrapper')).html();
|
||||
}
|
||||
}).parent().delegate(self).on('click','button#comment_post',function(e) {
|
||||
e.stopImmediatePropagation();
|
||||
self.post_discussion(identifier);
|
||||
});
|
||||
},
|
||||
validate : function(public_user){
|
||||
var comment = $(".popover textarea#inline_comment").val();
|
||||
if (public_user){
|
||||
var author_name = $('.popover input#author_name').val();
|
||||
var author_email = $('.popover input#author_email').val();
|
||||
if(!comment || !author_name || !author_email){
|
||||
if (!author_name)
|
||||
$('div#author_name').addClass('has-error');
|
||||
else
|
||||
$('div#author_name').removeClass('has-error');
|
||||
if (!author_email)
|
||||
$('div#author_email').addClass('has-error');
|
||||
else
|
||||
$('div#author_email').removeClass('has-error');
|
||||
if(!comment)
|
||||
$('div#inline_comment').addClass('has-error');
|
||||
else
|
||||
$('div#inline_comment').removeClass('has-error');
|
||||
return false
|
||||
}
|
||||
}
|
||||
else if(!comment) {
|
||||
$('div#inline_comment').addClass('has-error');
|
||||
return false
|
||||
}
|
||||
$("div#inline_comment").removeClass('has-error');
|
||||
$('div#author_name').removeClass('has-error');
|
||||
$('div#author_email').removeClass('has-error');
|
||||
$(".popover textarea#inline_comment").val('');
|
||||
$('.popover input#author_name').val('');
|
||||
$('.popover input#author_email').val('');
|
||||
return [comment, author_name, author_email]
|
||||
},
|
||||
post_discussion : function(identifier) {
|
||||
var self = this;
|
||||
var val = self.validate(self.settings.public_user)
|
||||
if(!val) return
|
||||
openerp.jsonRpc("/blogpost/post_discussion", 'call', {
|
||||
'blog_post_id': self.settings.post_id,
|
||||
'path': self.discus_identifier,
|
||||
'comment': val[0],
|
||||
'name' : val[1],
|
||||
'email': val[2],
|
||||
}).then(function(res){
|
||||
$(".popover ul.media-list").prepend(qweb.render("website.blog_discussion.comment", {'res': res[0]}))
|
||||
var ele = $('a[data-discus-identifier="'+ self.discus_identifier +'"]');
|
||||
ele.text(_.isNaN(parseInt(ele.text())) ? 1 : parseInt(ele.text())+1)
|
||||
ele.addClass('has-comments');
|
||||
});
|
||||
},
|
||||
hide_discussion : function() {
|
||||
var self = this;
|
||||
$('a[data-discus-identifier="'+ self.discus_identifier+'"]').popover('destroy');
|
||||
$('a.discussion-link').removeClass('active');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,40 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
function page_transist(event) {
|
||||
event.preventDefault();
|
||||
newLocation = $('.js_next')[0].href;
|
||||
var top = $('.cover_footer').offset().top;
|
||||
$('.cover_footer').animate({
|
||||
height: $(window).height()+'px'
|
||||
}, 300);
|
||||
$('html, body').animate({
|
||||
scrollTop: top
|
||||
}, 300, 'swing', function() {
|
||||
window.location.href = newLocation;
|
||||
});
|
||||
}
|
||||
function animate(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
var target = $(this.hash);
|
||||
$('html, body').stop().animate({
|
||||
'scrollTop': target.offset().top - 32
|
||||
}, 500, 'swing', function () {
|
||||
window.location.hash = 'blog_content';
|
||||
});
|
||||
}
|
||||
|
||||
var content = $("div[enable_chatter_discuss='True']").find('p[data-chatter-id]');
|
||||
if (content) {
|
||||
openerp.jsonRpc("/blog/get_user/", 'call', {}).then(function(data){
|
||||
$('#discussions_wrapper').empty();
|
||||
new openerp.website.blog_discussion({'content' : content, 'public_user':data[0]});
|
||||
});
|
||||
}
|
||||
|
||||
$('.js_fullheight').css('min-height', $(window).height());
|
||||
$(".js_tweet").share({'author_name':$('#blog_author').text()});
|
||||
$('.cover_footer').on('click',page_transist);
|
||||
$('a[href^="#blog_content"]').on('click', animate);
|
||||
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.blog_discussion.comment">
|
||||
<li class="media">
|
||||
<div class="pull-left">
|
||||
<img class="media-object img-circle" t-att-src="res.author_image" style="width: 30px;"/>
|
||||
</div>
|
||||
<div t-attf-class="pull-right btn-group js_publish_management #{res.website_published and 'css_published' or 'css_unpublished'} #{res.publish ? '' : 'hidden'}" data-object="mail.message" t-att-data-id="res.id">
|
||||
<button class="btn btn-danger btn-xs js_publish_btn">Not Published</button>
|
||||
<button class="btn btn-success btn-xs js_publish_btn">Published</button>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<div t-esc='res.body'/>
|
||||
<small class="text-muted">
|
||||
by
|
||||
<span t-esc='res.author_name'/>
|
||||
</small>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
<t t-name="website.blog_discussion.popover">
|
||||
<div class="mycontent hidden">
|
||||
<input name="discussion" t-att-value="identifier" type="hidden"/>
|
||||
<input name="blog_post_id" t-att-value="options.post_id" type="hidden"/>
|
||||
<div id="inline_comment">
|
||||
<textarea class="mb8 form-control" rows="2" id="inline_comment" placeholder="Write a comment..."/>
|
||||
</div>
|
||||
<div id="author_name">
|
||||
<input id="author_name" name="user_name" t-attf-class="form-control #{options.public_user ? '' : 'hidden'}" placeholder="Your name..."/>
|
||||
</div>
|
||||
<div id="author_email">
|
||||
<input id="author_email" name="user_email" t-attf-class="mt8 mb8 form-control #{options.public_user ? '' : 'hidden'}" placeholder="Your Email..."/>
|
||||
</div>
|
||||
<button id='comment_post' class="btn btn-primary btn-xs mb8">Post</button>
|
||||
<div class="discussion_history"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
|
@ -21,21 +21,17 @@
|
|||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<section groups="base.group_website_publisher">
|
||||
<div class="container text-center mt16">
|
||||
<div class="alert alert-warning alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||
<p>
|
||||
This page is great to improve your <strong>Search Engine Optimization</strong>;
|
||||
You can review titles, keywords and descriptions of all blogs at once.
|
||||
</p><p>
|
||||
You should <strong>add a banner on the top</strong> as it is a frequent landing page for new visitors.
|
||||
<span class="text-muted">This box will not be visible to your visitors.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="container">
|
||||
<div class="alert alert-warning alert-dismissable mt16" groups="base.group_website_publisher">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||
<p>
|
||||
This page is great to improve your <strong>Search Engine Optimization</strong>;
|
||||
You can review titles, keywords and descriptions of all blogs at once.
|
||||
</p><p>
|
||||
You should <strong>add a banner on the top</strong> as it is a frequent landing page for new visitors.
|
||||
<span class="text-muted">This box will not be visible to your visitors.</span>
|
||||
</p>
|
||||
</div>
|
||||
<t t-call="website.pager" >
|
||||
<t t-set="classname">pull-right</t>
|
||||
</t>
|
||||
|
@ -93,55 +89,80 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- Blog Post Summary -->
|
||||
<template id="blog_post_short" name="Blog Post Summary">
|
||||
<!-- Blog Post List: Displaying a list of Blog Posts -->
|
||||
<template id="blog_post_short" name="Blog Posts">
|
||||
<t t-call="website_blog.index">
|
||||
<div t-if="not blog_posts" class="container mb64">
|
||||
<p class="css_editable_hidden">
|
||||
<h1>No blog post yet.</h1>
|
||||
</p>
|
||||
<t groups="base.group_website_publisher">
|
||||
<t groups="base.group_document_user">
|
||||
<p>Click on "Content" on the top menu to write your first blog post.</p>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<t t-foreach="blog_posts" t-as="blog_post">
|
||||
<div t-att-data-publish="blog_post.website_published and 'on' or 'off'">
|
||||
<h2 class="text-center">
|
||||
<a t-attf-href="#{post_url(blogpost=blog_post)}" t-field="blog_post.name"></a>
|
||||
</h2>
|
||||
<p class="post-meta text-muted text-center" name='blog_post_data'>
|
||||
<span class="fa fa-calendar oe_date"> <span t-field="blog_post.create_date"/> &nbsp;</span>
|
||||
<span t-if="len(blog_post.message_ids) > 0" class="fa fa-comment-o">
|
||||
<a t-attf-href="#{post_url(blogpost=blog_post)}#comments">
|
||||
<t t-if="len(blog_post.message_ids) <= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
|
||||
<t t-if="len(blog_post.message_ids) > 1"><t t-esc="len(blog_post.message_ids)"/> comments</t>
|
||||
</a>
|
||||
</span>
|
||||
<span t-if="not blog_post.website_published" class="label label-danger">not published</span>
|
||||
</p>
|
||||
<div t-raw="blog_post.shortened_content" class="blog_content"/>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="oe_structure">
|
||||
<section class="mb0">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<t t-call="website.pager" >
|
||||
<t t-set="classname">pull-right</t>
|
||||
</t>
|
||||
<div class="col-md-12 mb32 mt16 text-center">
|
||||
<h1 t-field="blog.name"/>
|
||||
<h3 class="text-muted" t-field="blog.subtitle"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2" t-ignore="True" id="main_column">
|
||||
|
||||
</t>
|
||||
<div t-if="not blog_posts" class="container mb64">
|
||||
<p class="css_editable_hidden">
|
||||
<h1>No blog post yet.</h1>
|
||||
</p>
|
||||
<p groups="base.group_document_user">
|
||||
Click on "Content" on the top menu to write your first blog post.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div t-foreach="blog_posts" t-as="blog_post" class="mb32">
|
||||
|
||||
<img class="img-circle pull-right mt16"
|
||||
t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(blog_post.author_id.id)"
|
||||
style="width: 50px;"/>
|
||||
|
||||
<a t-attf-href="/blog/#{ slug(blog_post.blog_id) }/post/#{ slug(blog_post) }">
|
||||
<h2 t-field="blog_post.name" class="mb4"/>
|
||||
</a>
|
||||
|
||||
<div class="text-muted">
|
||||
<h4
|
||||
t-field="blog_post.subtitle"
|
||||
class="mb4 mt4"/>
|
||||
<div name='blog_post_data' class='mb0'>
|
||||
<span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{
|
||||
"widget": "contact",
|
||||
"fields": ["name"]
|
||||
}'/>
|
||||
  <span t-field="blog_post.create_date" t-field-options='{"format": "MMMM yyyy"}'/>
|
||||
<span t-if="len(blog_post.message_ids) > 0">
|
||||
  <t t-esc="len(blog_post.message_ids)"/>
|
||||
<t t-if="len(blog_post.message_ids) <= 1" >comment</t>
|
||||
<t t-if="len(blog_post.message_ids) > 1">comments</t>
|
||||
</span>
|
||||
|
||||
<span t-if="not blog_post.website_published" class="label label-danger">not published</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oe_structure"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Options: Blog Post Summary: hide author -->
|
||||
<template id="opt_blog_post_short_author" name="Author"
|
||||
inherit_option_id="website_blog.blog_post_short">
|
||||
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
|
||||
<span class="fa fa-user"> By <span t-field="blog_post.create_uid"/> &nbsp;</span>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Blog Post Summary: show tags -->
|
||||
<!-- Option: Blog Post List: show tags -->
|
||||
<template id="opt_blog_post_short_tags" name="Tags"
|
||||
inherit_option_id="website_blog.blog_post_short" inherit_id="website_blog.blog_post_short">
|
||||
<xpath expr="//p[@name='blog_post_data']" position="after">
|
||||
<xpath expr="//div[@name='blog_post_data']" position="inside">
|
||||
<p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
|
||||
<span class="fa fa-tags"/>
|
||||
<t t-foreach="blog_post.tag_ids" t-as="tag">
|
||||
|
@ -153,56 +174,95 @@
|
|||
|
||||
<!-- Blog Post Complete -->
|
||||
<template id="blog_post_complete" name="Blog Post">
|
||||
<t t-call="website_blog.index">
|
||||
<t t-call="website_blog.index">
|
||||
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="blog_post"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="action" t-value="'website_blog.action_blog_post'"/>
|
||||
<li>
|
||||
<form class="duplicate hidden" action="/blogpost/duplicate">
|
||||
<input name="blog_post_id" t-att-value="blog_post.id"/>
|
||||
</form>
|
||||
<a href="#" class="duplicate" onclick="$(this).prev('form').submit()">Duplicate</a>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<div id="title">
|
||||
<h1 class="text-center" t-field="blog_post.name"/>
|
||||
<p class="post-meta text-muted text-center" name="blog_post_data">
|
||||
<span class="fa fa-calendar oe_date"> <span t-field="blog_post.create_date"/> &nbsp;</span>
|
||||
<span t-if="len(blog_post.message_ids) > 0" class="fa fa-comment-o"> With
|
||||
<a t-attf-href="#comments">
|
||||
<t t-if="len(blog_post.message_ids) <= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
|
||||
<t t-if="len(blog_post.message_ids) > 1"><t t-esc="len(blog_post.message_ids)"/> comments</t>
|
||||
<div class="cover js_fullheight" id="title" t-attf-style="background-image: url(#{blog_post.background_image})" t-ignore="True">
|
||||
<div class="container">
|
||||
<div class="container text-right mt16">
|
||||
<div class="btn-group css_non_editable_mode_hidden">
|
||||
<a id="change_cover" class="btn btn-primary">
|
||||
Change Cover
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<a id="clear_cover" class="btn btn-danger">
|
||||
<span class="fa fa-times"/>
|
||||
</a>
|
||||
</div>
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="blog_post"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<li>
|
||||
<form class="duplicate hidden" action="/blogpost/duplicate">
|
||||
<input name="blog_post_id" t-att-value="blog_post.id"/>
|
||||
</form>
|
||||
<a href="#" class="duplicate" onclick="$(this).prev('form').submit()">Duplicate</a>
|
||||
</li>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="blog_title">
|
||||
<h1 t-field="blog_post.name" id="blog_post_name" t-att-data-blog-id="blog_post.id"/>
|
||||
<h2 t-field="blog_post.subtitle"/>
|
||||
<p class="post-meta text-muted text-center" name="blog_post_data"/>
|
||||
<div>
|
||||
<img class="img-circle" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(blog_post.author_id.id)" style="width: 30px; margin-right: 10px;"/>
|
||||
<span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{
|
||||
"widget": "contact",
|
||||
"fields": ["name"]
|
||||
}'/>
|
||||
</div>
|
||||
<div t-if="blog_post.background_image" id="blog_angle_down">
|
||||
<strong><a href="#blog_content" class="fa fa-angle-down fa-3x fa-inverse mt32"/></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-field="blog_post.content"/>
|
||||
<div id="blog_content" t-field="blog_post.content" class="mt32"/>
|
||||
|
||||
<section id="comments" class="container">
|
||||
<section id="comments" class="read_width">
|
||||
<hr/>
|
||||
<ul class="media-list" id="comments-list">
|
||||
<li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
|
||||
<span class="pull-left">
|
||||
<img class="media-object img img-circle" t-att-src="'/website/image?model=mail.message&field=author_avatar&id='+str(message.id)" style="width: 30px"/>
|
||||
</span>
|
||||
<div class="media-body">
|
||||
<img class="media-object pull-left" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
|
||||
<div class="media-body">
|
||||
<t t-call="website.publish_short">
|
||||
<t t-set="object" t-value="message"/>
|
||||
</t>
|
||||
<h5 class="media-heading">
|
||||
<span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
|
||||
</h5>
|
||||
<div t-field="message.body"/>
|
||||
</div>
|
||||
<t t-call="website.publish_short">
|
||||
<t t-set="object" t-value="message"/>
|
||||
</t>
|
||||
<!-- <strong t-field="message.author_id"/> -->
|
||||
<span t-field="message.author_id" style="display: inline-block;" t-field-options='{
|
||||
"widget": "contact",
|
||||
"fields": ["name"]
|
||||
}'/>
|
||||
<span class="text-muted">on <span t-field="message.date"/></span>
|
||||
<div t-field="message.body"/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<t t-if="next_post">
|
||||
<div class="cover cover_footer mb0 text-center" t-attf-style="background-image: url(#{next_post.background_image})" t-ignore="True">
|
||||
<div class="blog_title">
|
||||
<a class="hidden js_next" t-attf-href="/blog/#{ slug(next_post.blog_id) }/post/#{ slug(next_post) }/#wrap"/>
|
||||
<h1 t-field="next_post.name"/>
|
||||
<h2 t-field="next_post.subtitle"/>
|
||||
<div>
|
||||
<img class="img-circle" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(next_post.author_id.id)" style="width: 30px; margin-right: 10px;"/>
|
||||
<span t-field="next_post.author_id" style="display: inline-block;" t-field-options='{
|
||||
"widget": "contact",
|
||||
"fields": ["name"]
|
||||
}'/>
|
||||
</div>
|
||||
<p class="mt32">Read Next <span class="fa fa-long-arrow-right"/></p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Options: Blog Post: breadcrumb -->
|
||||
<template id="blog_breadcrumb" name="Breadcrumb"
|
||||
inherit_option_id="website_blog.blog_post_complete">
|
||||
<xpath expr="//div[@id='title']" position="before">
|
||||
|
@ -222,39 +282,44 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- Options: Blog Post: user can reply -->
|
||||
<template id="opt_blog_post_complete_comment" name="Allow Comments"
|
||||
inherit_option_id="website_blog.blog_post_complete" inherit_id="website_blog.blog_post_complete"
|
||||
<template id="opt_blog_post_complete_comment" name="Allow blog post comment"
|
||||
inherit_option_id="website_blog.blog_post_complete"
|
||||
groups="website_mail.group_comment">
|
||||
<xpath expr="//ul[@id='comments-list']" position="before">
|
||||
<section class="mb32 css_editable_mode_hidden">
|
||||
<section class="mb32 read_width css_editable_mode_hidden">
|
||||
<form id="comment" t-attf-action="/blogpost/comment" method="POST">
|
||||
<input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/>
|
||||
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
|
||||
<div class="pull-left mb32" style="width: 75%%">
|
||||
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
|
||||
<button type="submit" class="btn btn-primary mt8">Post</button>
|
||||
<div class="media">
|
||||
<span class="pull-left">
|
||||
<img class="img img-circle media-object" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(user_id.partner_id.id)" style="width: 30px"/>
|
||||
</span>
|
||||
<div class="media-body">
|
||||
<input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/>
|
||||
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
|
||||
<button type="submit" class="btn btn-primary mt8">Post</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<div class="clearfix"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Options: Blog Post: hide author -->
|
||||
<template id="opt_blog_post_complete_author" name="Authors"
|
||||
<!-- Options: Blog Post: user can select text for tweet -->
|
||||
<template id="opt_blog_post_select_to_tweet" name="Select to Tweet"
|
||||
inherit_option_id="website_blog.blog_post_complete">
|
||||
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
|
||||
<span class="fa fa-user"> By <span t-field="blog_post.create_uid"/> &nbsp;</span>
|
||||
<xpath expr="//div[@id='blog_content']" position="attributes">
|
||||
<attribute name="class">js_tweet mt32</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='title']//div[@class='blog_title']" position="attributes">
|
||||
<attribute name="class">blog_title js_tweet</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Options: Blog Post: show blog -->
|
||||
<template id="opt_blog_post_complete_blog" name="Blog"
|
||||
<!-- Options: Blog Post: user can add Inline Discussion -->
|
||||
<template id="opt_blog_post_inline_discussion" name="Allow comment in text"
|
||||
inherit_option_id="website_blog.blog_post_complete">
|
||||
<xpath expr="//span[@class='fa fa-calendar oe_date']" position="after">
|
||||
<span class="fa fa-folder-open"> In <span t-field="blog_post.blog_id"/> &nbsp;</span>
|
||||
<xpath expr="//div[@id='blog_content']" position="attributes">
|
||||
<attribute name="enable_chatter_discuss">True</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
@ -276,6 +341,9 @@
|
|||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
|
||||
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
|
||||
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
|
||||
<script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
|
||||
</t>
|
||||
<div id="wrap" class="js_blog">
|
||||
<t t-raw="0"/>
|
||||
|
@ -283,23 +351,19 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column for extra info -->
|
||||
<!-- Option:Right Column for extra info -->
|
||||
|
||||
<template id="index_right" name="Right Column"
|
||||
inherit_option_id="website_blog.index">
|
||||
<xpath expr="//div[@id='wrap']" position="replace">
|
||||
<div class="container mt16 js_website_blog">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-sm-8" id="blog_left_column">
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
<div class="col-lg-3 col-lg-offset-1 col-sm-4" id="blog_right_column"/>
|
||||
</div>
|
||||
</div>
|
||||
inherit_option_id="website_blog.blog_post_short">
|
||||
<xpath expr="//div[@id='main_column']" position="attributes">
|
||||
<attribute name="class">col-sm-8</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='main_column']" position="after">
|
||||
<div class="col-lg-3 col-lg-offset-1 col-sm-4" id="blog_right_column"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column: tags -->
|
||||
<!-- Option:Right Column: tags -->
|
||||
<template id="opt_blog_rc_tags" name="Tags"
|
||||
inherit_option_id="website_blog.index_right">
|
||||
<xpath expr="//div[@id='blog_right_column']" position="inside">
|
||||
|
@ -316,7 +380,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column: archives -->
|
||||
<!-- Option:Right Column: archives -->
|
||||
<template id="opt_blog_rc_history" name="Archives"
|
||||
inherit_option_id="website_blog.index_right">
|
||||
<xpath expr="//div[@id='blog_right_column']" position="inside">
|
||||
|
@ -333,7 +397,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column: about us -->
|
||||
<!-- Option:Right Column: about us -->
|
||||
<template id="opt_blog_rc_about_us" name="About Us" priority="2"
|
||||
inherit_option_id="website_blog.index_right">
|
||||
<xpath expr="//div[@id='blog_right_column']" position="inside">
|
||||
|
@ -350,7 +414,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column: follow us -->
|
||||
<!-- Option:Right Column: follow us -->
|
||||
<template id="opt_blog_rc_follow_us" name="Follow us" priority="4"
|
||||
inherit_option_id="website_blog.index_right">
|
||||
<xpath expr="//div[@id='blog_right_column']" position="inside">
|
||||
|
@ -377,7 +441,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Option: Right Column: blogs -->
|
||||
<!-- Option:Right Column: blogs -->
|
||||
<template id="opt_blog_rc_blogs" name="Our Blogs" priority="6"
|
||||
inherit_option_id="website_blog.index_right">
|
||||
<xpath expr="//div[@id='blog_right_column']" position="inside">
|
||||
|
|
|
@ -56,9 +56,15 @@
|
|||
<sheet>
|
||||
<h1><field name="name" placeholder="Name"/></h1>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
<field name="subtitle" placeholder="Blog Subtitle"/>
|
||||
<group>
|
||||
<field name="background_image"/>
|
||||
<field name="blog_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="visits"/>
|
||||
<field name="ranking" invisible="1"/>
|
||||
</group>
|
||||
<field name="content" placeholder="e.g. Once upon a time..." widget="html"/>
|
||||
<group string="Technical" groups="base.group_no_one">
|
||||
<field name="write_uid" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
|
||||
|
@ -155,5 +161,6 @@
|
|||
name="Page History"
|
||||
res_model="blog.post.history"
|
||||
src_model="blog.post"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -28,7 +28,8 @@ Adds support for:
|
|||
],
|
||||
'qweb': ['static/src/xml/*.xml'],
|
||||
'demo': [
|
||||
'data/event_demo.xml'
|
||||
'data/event_demo.xml',
|
||||
'data/website_event_track_demo.xml'
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
|
|
@ -23,11 +23,14 @@ 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
|
||||
import datetime
|
||||
|
||||
import re
|
||||
import werkzeug.utils
|
||||
|
||||
controllers = controllers()
|
||||
import pytz
|
||||
from pytz import timezone
|
||||
|
||||
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)
|
||||
|
@ -37,14 +40,65 @@ class website_event(http.Controller):
|
|||
values = { 'track': track, 'event': track.event_id, 'main_object': track }
|
||||
return request.website.render("website_event_track.track_view", values)
|
||||
|
||||
def _prepare_calendar(self, event, event_track_ids):
|
||||
local_tz = pytz.timezone(event.timezone_of_event or 'UTC')
|
||||
locations = {} # { location: [track, start_date, end_date, rowspan]}
|
||||
dates = [] # [ (date, {}) ]
|
||||
for track in event_track_ids:
|
||||
locations.setdefault(track.location_id or False, [])
|
||||
|
||||
forcetr = True
|
||||
for track in event_track_ids:
|
||||
start_date = (datetime.datetime.strptime(track.date, '%Y-%m-%d %H:%M:%S')).replace(tzinfo=pytz.utc).astimezone(local_tz)
|
||||
end_date = start_date + datetime.timedelta(hours = (track.duration or 30))
|
||||
location = track.location_id or False
|
||||
locations.setdefault(location, [])
|
||||
|
||||
# New TR, align all events
|
||||
if forcetr or (start_date>dates[-1][0]) or not location:
|
||||
dates.append((start_date, {}, bool(location)))
|
||||
for loc in locations.keys():
|
||||
if locations[loc] and (locations[loc][-1][2] > start_date):
|
||||
locations[loc][-1][3] += 1
|
||||
elif not locations[loc] or locations[loc][-1][2] < start_date:
|
||||
locations[loc].append([False, locations[loc] and locations[loc][-1][2] or dates[0][0], start_date, 1])
|
||||
dates[-1][1][loc] = locations[loc][-1]
|
||||
forcetr = not bool(location)
|
||||
|
||||
# Add event
|
||||
if locations[location] and locations[location][-1][1] > start_date:
|
||||
locations[location][-1][3] -= 1
|
||||
locations[location].append([track, start_date, end_date, 1])
|
||||
dates[-1][1][location] = locations[location][-1]
|
||||
return {
|
||||
'locations': locations,
|
||||
'dates': dates
|
||||
}
|
||||
|
||||
|
||||
# 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 = {
|
||||
comp = lambda x: (x.date, bool(x.location_id))
|
||||
event.track_ids.sort(lambda x,y: cmp(comp(x), comp(y)))
|
||||
|
||||
days = {}
|
||||
days_nbr = {}
|
||||
for track in event.track_ids:
|
||||
if not track.date: continue
|
||||
days.setdefault(track.date[:10], [])
|
||||
days[track.date[:10]].append(track)
|
||||
|
||||
for d in days:
|
||||
days_nbr[d] = len(days[d])
|
||||
days[d] = self._prepare_calendar(event, days[d])
|
||||
|
||||
return request.website.render("website_event_track.agenda", {
|
||||
'event': event,
|
||||
'main_object': event,
|
||||
}
|
||||
return request.website.render("website_event_track.agenda", values)
|
||||
'days': days,
|
||||
'days_nbr': days_nbr,
|
||||
'tag': tag
|
||||
})
|
||||
|
||||
@http.route([
|
||||
'/event/<model("event.event"):event>/track',
|
||||
|
|
|
@ -24,20 +24,6 @@
|
|||
<record id="event_track_tag4" model="event.track.tag">
|
||||
<field name="name">Round Table</field>
|
||||
</record>
|
||||
|
||||
<record id="event_track_location1" model="event.track.location">
|
||||
<field name="name">Room 1</field>
|
||||
</record>
|
||||
<record id="event_track_location2" model="event.track.location">
|
||||
<field name="name">Room 2</field>
|
||||
</record>
|
||||
<record id="event_track_location3" model="event.track.location">
|
||||
<field name="name">Room 3</field>
|
||||
</record>
|
||||
<record id="event_track_location4" model="event.track.location">
|
||||
<field name="name">Room 4</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
This should be done at the end so that the menu is complete
|
||||
-->
|
||||
|
@ -83,68 +69,6 @@
|
|||
</record>
|
||||
|
||||
<!-- Tracks -->
|
||||
|
||||
<record id="track_1" model="event.track">
|
||||
<field name="name">A Better Future With OpenERP eCommerce</field>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="duration" eval="60"/>
|
||||
<field eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d 10:00:00')" name="date"/>
|
||||
<field name="speaker_ids" eval="[(6, 0, [ref('base.res_partner_address_4')])]"/>
|
||||
<field name="location_id" ref="website_event_track.event_track_location2"/>
|
||||
<field name="stage_id" ref="website_event_track.event_track_stage3"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('website_event_track.event_track_tag1')])]"/>
|
||||
<field name="description" type="xml">
|
||||
<section data-snippet-id="text-block">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
Apart from being an enterprise management software, OpenERP is
|
||||
used nowadays for a great variety of enterprise frontends.
|
||||
</p><p>
|
||||
The talk illustrates the impact of OpenERP in areas such as the
|
||||
company website, online events management, eCommerce, online
|
||||
recruitments, social media marketing, etc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="track_2" model="event.track">
|
||||
<field name="name">How To Drive Sales With OpenERP CRM</field>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="duration" eval="60"/>
|
||||
<field name="priority">1</field>
|
||||
<field eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d 11:00:00')" name="date"/>
|
||||
<field name="speaker_ids" eval="[(6, 0, [ref('base.res_partner_address_13')])]"/>
|
||||
<field name="location_id" ref="website_event_track.event_track_location2"/>
|
||||
<field name="stage_id" ref="website_event_track.event_track_stage3"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('website_event_track.event_track_tag2'), ref('website_event_track.event_track_tag3')])]"/>
|
||||
<field name="description" type="xml">
|
||||
<section data-snippet-id="text-block">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
Apart from being an enterprise management software, OpenERP is
|
||||
used nowadays for a great variety of enterprise frontends.
|
||||
</p><p>
|
||||
The talk illustrates the impact of OpenERP in areas such as the
|
||||
company website, online events management, eCommerce, online
|
||||
recruitments, social media marketing, etc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_address_16" model="res.partner">
|
||||
<field name="website">http://facebook.com/openerp</field>
|
||||
<field name="website_description" type="xml">
|
||||
|
@ -160,35 +84,5 @@
|
|||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="track_3" model="event.track">
|
||||
<field name="name">Social Marketing As a Source of Leads</field>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="duration" eval="40"/>
|
||||
<field name="priority">0</field>
|
||||
<field eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d 14:00:00')" name="date"/>
|
||||
<field name="speaker_ids" eval="[(6, 0, [ref('base.res_partner_address_16')])]"/>
|
||||
<field name="location_id" ref="website_event_track.event_track_location1"/>
|
||||
<field name="stage_id" ref="website_event_track.event_track_stage4"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('website_event_track.event_track_tag4')])]"/>
|
||||
<field name="description" type="xml">
|
||||
<section data-snippet-id="text-block">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
Apart from being an enterprise management software, OpenERP is
|
||||
used nowadays for a great variety of enterprise frontends.
|
||||
</p><p>
|
||||
The talk illustrates the impact of OpenERP in areas such as the
|
||||
company website, online events management, eCommerce, online
|
||||
recruitments, social media marketing, etc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="event_track_location5" model="event.track.location">
|
||||
<field name="name">Le Foyer du lac</field>
|
||||
</record>
|
||||
<record id="event_track_location6" model="event.track.location">
|
||||
<field name="name">Theatre</field>
|
||||
</record>
|
||||
<record id="event_track_location7" model="event.track.location">
|
||||
<field name="name">Lauzelle</field>
|
||||
</record>
|
||||
<record id="event_track_location8" model="event.track.location">
|
||||
<field name="name">Foyer Royal</field>
|
||||
</record>
|
||||
<record id="event_track_location9" model="event.track.location">
|
||||
<field name="name">Biereau</field>
|
||||
</record>
|
||||
<record id="event_track_location10" model="event.track.location">
|
||||
<field name="name">Bruyère</field>
|
||||
</record>
|
||||
<record id="event_track1" model="event.track">
|
||||
<field name="name">How to develop a website module.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 06:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location5"/>
|
||||
<field name="duration" eval="1"/>
|
||||
<field eval="[(4, ref('base.res_partner_2')),(4, ref('base.res_partner_3'))]" name="speaker_ids"/>
|
||||
<field name="color">3</field>
|
||||
<field name="stage_id" ref="event_track_stage1"/>
|
||||
|
||||
</record>
|
||||
<record id="event_track2" model="event.track">
|
||||
<field name="name">How to integrate hardware materials with the OpenERP point of sale.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 8:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location5"/>
|
||||
<field name="duration" eval="0.25"/>
|
||||
<field eval="[(4, ref('base.res_partner_3'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track3" model="event.track">
|
||||
<field name="name">How to develop real time apps, the live chat module explained.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 10:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location5"/>
|
||||
<field name="duration" eval="0.3"/>
|
||||
<field eval="[(4, ref('base.res_partner_4'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage3"/>
|
||||
</record>
|
||||
<record id="event_track4" model="event.track">
|
||||
<field name="name">How to develop automated tests in the OpenERP web client.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 9:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location5"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_2'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track5" model="event.track">
|
||||
<field name="name">The new way to promote your modules in the Apps platform and OpenERP website.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location6"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_4'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage5"/>
|
||||
</record>
|
||||
<record id="event_track6" model="event.track">
|
||||
<field name="name">Detailed roadmap of accounting new modules and improvements for version 8.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location6"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_5'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track7" model="event.track">
|
||||
<field name="name">A technical explanation of OpenERP as a CMS and a eCommerce platform for version 8.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 8:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location6"/>
|
||||
<field name="duration" eval="1"/>
|
||||
<field eval="[(4, ref('base.res_partner_6'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track8" model="event.track">
|
||||
<field name="name">Discover OpenERP CRM: How to optimize your sales, from leads to sales orders.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location7"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field name="color">2</field>
|
||||
<field eval="[(4, ref('base.res_partner_7'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage3"/>
|
||||
</record>
|
||||
<record id="event_track9" model="event.track">
|
||||
<field name="name">How to use OpenERP for your HR process: recruitment, leaves management, appraisals, expenses, etc.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 8:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location7"/>
|
||||
<field name="duration" eval="1"/>
|
||||
<field name="color">2</field>
|
||||
<field eval="[(4, ref('base.res_partner_8'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track10" model="event.track">
|
||||
<field name="name">Raising qualitive insights with the survey app</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location7"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field name="color">5</field>
|
||||
<field name="stage_id" ref="event_track_stage1"/>
|
||||
</record>
|
||||
<record id="event_track11" model="event.track">
|
||||
<field name="name">Discover OpenERP Point-of-Sale: Your shop ready to use in 30 minutes.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 10:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location7"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_4'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track12" model="event.track">
|
||||
<field name="name">Manage your events with OpenERP, the new training modules.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 11:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location7"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_2')),(4, ref('base.res_partner_8'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage3"/>
|
||||
</record>
|
||||
<record id="event_track13" model="event.track">
|
||||
<field name="name">Advanced reporting with Google Spreadsheets integration.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location8"/>
|
||||
<field name="duration" eval="0.25"/>
|
||||
<field eval="[(4, ref('base.res_partner_9'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage5"/>
|
||||
</record>
|
||||
<record id="event_track14" model="event.track">
|
||||
<field name="name">New Paypal modules (portal, handling, installments).</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 7:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location8"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_10'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage5"/>
|
||||
</record>
|
||||
<record id="event_track15" model="event.track">
|
||||
<field name="name">OpenERP Mobile for Notes, Meetings and Messages.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 10:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location8"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_11'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track16" model="event.track">
|
||||
<field name="name">OpenERP as your Enterprise Social Network.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 11:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location8"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_12'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage3"/>
|
||||
</record>
|
||||
<record id="event_track17" model="event.track">
|
||||
<field name="name">The Art of Making an OpenERP Demo.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_10'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track18" model="event.track">
|
||||
<field name="name">How to build your marketing strategy for the purpose of generating leads with OpenERP.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 8:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field name="color">5</field>
|
||||
<field eval="[(4, ref('base.res_partner_13'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track19" model="event.track">
|
||||
<field name="name">Advanced lead management with OpenERP: tips and tricks from the fields</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 9:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field name="color">5</field>
|
||||
<field eval="[(4, ref('base.res_partner_14'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage1"/>
|
||||
</record>
|
||||
<record id="event_track20" model="event.track">
|
||||
<field name="name">New Certification Program (valid from Oct. 2013).</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 10:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_15'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track21" model="event.track">
|
||||
<field name="name">Recruiting high skilled talents with OpenERP HR apps</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 10:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field name="color">7</field>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track22" model="event.track">
|
||||
<field name="name">Manage your KPIs (recomended to openERP partners).</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 11:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_15'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage1"/>
|
||||
</record>
|
||||
<record id="event_track23" model="event.track">
|
||||
<field name="name">Key Success factors selling OpenERP.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 7:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location9"/>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_16'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage3"/>
|
||||
</record>
|
||||
<record id="event_track24" model="event.track">
|
||||
<field name="name">Merge proposals review, code sprint (entire day).</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 6:00:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location10"/>
|
||||
<field name="duration" eval="1.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_17')),(4, ref('base.res_partner_18'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track25" model="event.track">
|
||||
<field name="name">Merge proposals review, code sprint (entire afternoon)</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 8:30:00')"></field>
|
||||
<field name="location_id" ref="website_event_track.event_track_location10"/>
|
||||
<field name="duration" eval="2.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_17')),(4, ref('base.res_partner_18'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track27" model="event.track">
|
||||
<field name="name">OpenERP in 2014</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 04:00:00')"></field>
|
||||
<field name="duration" eval="1"/>
|
||||
<field eval="[(4, ref('base.res_partner_1'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
<field name="color">3</field>
|
||||
</record>
|
||||
<record id="event_track28" model="event.track">
|
||||
<field name="name">OpenERP Status & Strategy 2014</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 5:00:00')"></field>
|
||||
<field name="duration" eval="0.5"/>
|
||||
<field eval="[(4, ref('base.res_partner_2'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage4"/>
|
||||
</record>
|
||||
<record id="event_track29" model="event.track">
|
||||
<field name="name">The new marketing strategy.</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 5:30:00')"></field>
|
||||
<field name="duration" eval="0.25"/>
|
||||
<field eval="[(4, ref('base.res_partner_19'))]" name="speaker_ids"/>
|
||||
<field name="stage_id" ref="event_track_stage1"/>
|
||||
<field name="color">6</field>
|
||||
</record>
|
||||
<record id="event_track30" model="event.track">
|
||||
<field name="name">Morning break</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 5:45:00')"></field>
|
||||
<field name="duration" eval="0.25"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
<record id="event_track31" model="event.track">
|
||||
<field name="name">Lunch</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
<field name="date" eval="time.strftime(str(DateTime.today().year) + '-06-04 7:30:00')"></field>
|
||||
<field name="duration" eval="1"/>
|
||||
<field name="stage_id" ref="event_track_stage2"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -23,6 +23,8 @@ from openerp.osv import fields, osv
|
|||
from openerp.tools.translate import _
|
||||
from openerp.addons.website.models.website import slug
|
||||
|
||||
import pytz
|
||||
|
||||
class event_track_tag(osv.osv):
|
||||
_name = "event.track.tag"
|
||||
_order = 'name'
|
||||
|
@ -79,7 +81,7 @@ class event_track(osv.osv):
|
|||
'stage_id': fields.many2one('event.track.stage', 'Stage'),
|
||||
'description': fields.html('Track Description', translate=True),
|
||||
'date': fields.datetime('Track Date'),
|
||||
'duration': fields.integer('Duration'),
|
||||
'duration': fields.float('Duration', digits=(16,2)),
|
||||
'location_id': fields.many2one('event.track.location', 'Location'),
|
||||
'event_id': fields.many2one('event.event', 'Event', required=True),
|
||||
'color': fields.integer('Color Index'),
|
||||
|
@ -99,10 +101,11 @@ class event_track(osv.osv):
|
|||
_defaults = {
|
||||
'user_id': lambda self, cr, uid, ctx: uid,
|
||||
'website_published': lambda self, cr, uid, ctx: False,
|
||||
'duration': lambda *args: 60,
|
||||
'duration': lambda *args: 1.5,
|
||||
'stage_id': _default_stage_id,
|
||||
'priority': '2'
|
||||
}
|
||||
|
||||
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
||||
stage_obj = self.pool.get('event.track.stage')
|
||||
result = stage_obj.name_search(cr, uid, '', context=context)
|
||||
|
@ -117,6 +120,10 @@ class event_track(osv.osv):
|
|||
#
|
||||
class event_event(osv.osv):
|
||||
_inherit = "event.event"
|
||||
def _tz_get(self,cr,uid, context=None):
|
||||
# put POSIX 'Etc/*' entries at the end to avoid confusing users - see bug 1086728
|
||||
return [(tz,tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
|
||||
|
||||
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):
|
||||
|
@ -134,11 +141,13 @@ class event_event(osv.osv):
|
|||
'show_blog': fields.boolean('News'),
|
||||
'tracks_tag_ids': fields.function(_get_tracks_tag_ids, type='one2many', relation='event.track.tag', string='Tags of Tracks'),
|
||||
'allowed_track_tag_ids': fields.many2many('event.track.tag', string='Accepted Tags', help="List of available tags for track proposals."),
|
||||
'timezone_of_event': fields.selection(_tz_get, 'Event Timezone', size=64),
|
||||
}
|
||||
_defaults = {
|
||||
'show_track_proposal': False,
|
||||
'show_tracks': False,
|
||||
'show_blog': False,
|
||||
'timezone_of_event':lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).tz,
|
||||
}
|
||||
def _get_new_menu_pages(self, cr, uid, event, context=None):
|
||||
context = context or {}
|
||||
|
|
|
@ -52,3 +52,43 @@
|
|||
background-image: -ms-linear-gradient(top, #C2792A, #DB9141);
|
||||
background-image: -o-linear-gradient(top, #C2792A, #DB9141);
|
||||
}
|
||||
.event_color_0 {
|
||||
background-color: white;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
.event_color_1 {
|
||||
background-color: #cccccc;
|
||||
color: #424242;
|
||||
}
|
||||
.event_color_2 {
|
||||
background-color: #ffc7c7;
|
||||
color: #7a3737;
|
||||
}
|
||||
.event_color_3 {
|
||||
background-color: #fff1c7;
|
||||
color: #756832;
|
||||
}
|
||||
.event_color_4 {
|
||||
background-color: #e3ffc7;
|
||||
color: #5d6937;
|
||||
}
|
||||
.event_color_5 {
|
||||
background-color: #c7ffd5;
|
||||
color: #1a7759;
|
||||
}
|
||||
.event_color_6 {
|
||||
background-color: #c7ffff;
|
||||
color: #1a5d83;
|
||||
}
|
||||
.event_color_7 {
|
||||
background-color: #c7d5ff;
|
||||
color: #3b3e75;
|
||||
}
|
||||
.event_color_8 {
|
||||
background-color: #e3c7ff;
|
||||
color: #4c3668;
|
||||
}
|
||||
.event_color_9 {
|
||||
background-color: #ffc7f1;
|
||||
color: #6d2c70;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
|
||||
return function( elem ) {
|
||||
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
|
||||
};
|
||||
});
|
||||
|
||||
$("#event_track_search").bind('keyup', function(e){
|
||||
var change_text = $(this).val();
|
||||
$('.event_track').removeClass('invisible');
|
||||
|
||||
$("#search_summary").removeClass('invisible');
|
||||
if (change_text) {
|
||||
$("#search_number").text($(".event_track:Contains("+change_text+")").length);
|
||||
$(".event_track:not(:Contains("+change_text+"))").addClass('invisible');
|
||||
} else {
|
||||
$("#search_number").text(30);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
});
|
|
@ -162,6 +162,9 @@
|
|||
<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="//field[@name='organizer_id']" position="after">
|
||||
<field name="timezone_of_event" />
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='oe_title']" position="inside">
|
||||
<label for="tag_ids" class="oe_edit_only"/>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
|
@ -226,7 +229,7 @@
|
|||
<div class="oe_kanban_content">
|
||||
<h4><field name="name"/></h4>
|
||||
<field name="tag_ids"/>
|
||||
<div t-if="duration"><field name="duration"/> hours</div>
|
||||
<div t-if="duration"><field name="duration" widget="float_time"/> hours</div>
|
||||
<span class="oe_right">
|
||||
<t t-set="priority" t-value="record.priority.raw_value || 0"/>
|
||||
<a type="object" name="set_priority" args="['2']" t-if="priority == 3" title="Normal Priority">
|
||||
|
@ -258,7 +261,7 @@
|
|||
<field name="model">event.track</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="color" date_start="date" date_delay="duration" string="Event Tracks">
|
||||
<calendar color="location_id" date_start="date" date_delay="duration" string="Event Tracks">
|
||||
<field name="name"/>
|
||||
<field name="event_id"/>
|
||||
</calendar>
|
||||
|
@ -316,9 +319,10 @@
|
|||
<field name="date"/>
|
||||
<label for="duration"/>
|
||||
<div>
|
||||
<field name="duration" class="oe_inline"/> minutes
|
||||
<field name="duration" class="oe_inline" widget="float_time"/> hours
|
||||
</div>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
<field name="color"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="description" class="oe_edit_only"/>
|
||||
|
@ -357,6 +361,7 @@
|
|||
<record model="ir.actions.act_window" id="action_event_track">
|
||||
<field name="name">Event Tracks</field>
|
||||
<field name="res_model">event.track</field>
|
||||
<field name="view_mode">tree,form,calendar</field>
|
||||
</record>
|
||||
<menuitem name="Event Tracks" id="menu_event_track" action="action_event_track" parent="event.event_configuration" groups="base.group_no_one"/>
|
||||
|
||||
|
|
|
@ -42,85 +42,69 @@
|
|||
|
||||
<template id="agenda">
|
||||
<t t-call="website_event.layout">
|
||||
<section data-snippet-id="title">
|
||||
<h1 class="text-center mb0" t-field="event.name"/>
|
||||
</section>
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<div id="left_column">
|
||||
<h1 class="text-center" t-field="event.name"/>
|
||||
<div class="form-inline pull-right">
|
||||
<label class="invisible text-muted" id="search_summary"><span id="search_number">0</span> Found </label>
|
||||
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
|
||||
</div>
|
||||
<div class="col-md-12" id="right_column">
|
||||
<p class="text-muted fa fa-info-circle"> You can click on cells to highlight your interests.</p>
|
||||
<h3 class="page-header mt0">
|
||||
Monday, 3rd June
|
||||
<small>23 talks</small>
|
||||
</h3>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="active"><a href="#">Room 1</a></th>
|
||||
<th class="active"><a href="#">Room 2</a></th>
|
||||
<th class="active"><a href="#">Room 3</a></th>
|
||||
<th class="active"><a href="#">Room 5</a></th>
|
||||
<th class="active"><a href="#">Room 6</a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="active"><b>09:00</b><br/><span class="text-muted">10:00</span></td>
|
||||
<th colspan="5" class="success text-center">
|
||||
<a href="#">Plenary Session</a><br/>
|
||||
Room 5
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="active"><b>10:00</b><br/><span class="text-muted">10:30</span></td>
|
||||
<td><a href="#">fkdsj kl jkl sdfjksdfj kl</a></td>
|
||||
<td rowspan="2" class="danger"><a href="#">Learning Python</a></td>
|
||||
<td></td>
|
||||
<td><a href="#">Learning Pytdon</a></td>
|
||||
<td><a href="#">Boost your Sales Witd OpenERP CRM</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="active">10:30<br/><span class="text-muted">11:00</span></th>
|
||||
<td></td>
|
||||
<td>Boost your Sales Witd OpenERP CRM</td>
|
||||
<td>Learning Pytdon</td>
|
||||
<td rowspan="2">fkdsj kl jkl sdfjksdfj kl</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="active">11:00<br/><span class="text-muted">12:00</span></th>
|
||||
<td class="danger">Learning Pytdon</td>
|
||||
<td>Boost your Sales Witd OpenERP CRM</td>
|
||||
<td>Learning Pytdon</td>
|
||||
<td>this is a test</td>
|
||||
</tr>
|
||||
<tr class="active">
|
||||
<td colspan="6" class="text-center text-muted"><span class="fa fa-clock-o"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="active">14:00<br/><span class="text-muted">15:00</span></th>
|
||||
<td>Boost your Sales Witd OpenERP CRM</td>
|
||||
<td class="danger">Learning Pytdon</td>
|
||||
<td></td>
|
||||
<td>fkdsj kl jkl sdfjksdfj kl</td>
|
||||
<td>fkdsj kl jkl sdfjksdfj kl</td>
|
||||
</tr><tr>
|
||||
<td class="active"><b>10:00</b><br/><span class="text-muted">10:30</span></td>
|
||||
<td>fkdsj kl jkl sdfjksdfj kl</td>
|
||||
<td rowspan="2" class="danger">Learning Python</td>
|
||||
<td></td>
|
||||
<td>Learning Pytdon</td>
|
||||
<td>Boost your Sales Witd OpenERP CRM</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="active">10:30<br/><span class="text-muted">11:00</span></th>
|
||||
<td></td>
|
||||
<td>Boost your Sales Witd OpenERP CRM</td>
|
||||
<td>Learning Pytdon</td>
|
||||
<td rowspan="2">fkdsj kl jkl sdfjksdfj kl</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="container" t-foreach="days.keys()" t-as="day">
|
||||
<t t-set="locations" t-value="days[day]['locations']"/>
|
||||
<t t-set="dates" t-value="days[day]['dates']"/>
|
||||
<h3 class="page-header mt0">
|
||||
<t t-esc="day"/>
|
||||
<small><t t-esc="days_nbr[day]"/> talks</small>
|
||||
</h3>
|
||||
<table id="table_search" class="table table-bordered table-condensed">
|
||||
<tr>
|
||||
<th/>
|
||||
<t t-foreach="locations.keys()" t-as="location">
|
||||
<th t-if="location" class="active">
|
||||
<span t-esc="location and location.name or 'Unknown'"/>
|
||||
</th>
|
||||
</t>
|
||||
</tr>
|
||||
<tr t-foreach="dates" t-as="dt">
|
||||
<td class="active">
|
||||
<b t-esc="dt[0].strftime('%H:%M')"/>
|
||||
</td>
|
||||
<t t-if="dt[2]"> <!-- Not a multi location -->
|
||||
<t t-foreach="locations" t-as="location">
|
||||
<t t-if="location and dt[1].get(location, False)">
|
||||
<t t-set="track" t-value="dt[1][location][0]"/>
|
||||
<t t-if="track">
|
||||
<td t-att-rowspan="dt[1][location][3]" t-attf-class="event_color_#{track.color} #{track and 'event_track' or ''}">
|
||||
<t t-if="track">
|
||||
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }">
|
||||
<span t-esc="track and track.name"/>
|
||||
</a>
|
||||
<div class="text-muted" t-foreach="track.speaker_ids" t-as="speaker">
|
||||
<small t-esc="speaker.display_name"/>
|
||||
</div>
|
||||
</t>
|
||||
</td>
|
||||
</t>
|
||||
<td t-if="not track" t-att-rowspan="dt[1][location][3]" class="event_track"/>
|
||||
</t>
|
||||
</t>
|
||||
</t><t t-if="not dt[2]">
|
||||
<t t-set="track" t-value="dt[1][False][0]"/>
|
||||
<td t-att-colspan="len(locations)-1" t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''}">
|
||||
<a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }">
|
||||
<span t-esc="track.name"/><br/>
|
||||
<div class="text-muted" t-foreach="track.speaker_ids" t-as="speaker">
|
||||
<small t-esc="speaker.display_name"/>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
@ -238,7 +222,7 @@
|
|||
<b>Date</b><br/>
|
||||
<span t-field="track.date" t-field-options='{"hide_seconds":"True"}'/><br/>
|
||||
<b>Duration</b><br/>
|
||||
<span t-field="track.duration"/> minutes<br/>
|
||||
<span t-esc="'%.2f' % (track.duration)"/> hours<br/>
|
||||
<b>Location</b><br/>
|
||||
<span t-field="track.location_id"/><br/>
|
||||
</div>
|
||||
|
@ -388,9 +372,9 @@
|
|||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<strong>Regular Talks</strong>. These are standard talks with slides,
|
||||
alocated in slots of 30 minutes.
|
||||
alocated in slots of 60 minutes.
|
||||
</li><li>
|
||||
<strong>Lightning Talks</strong>. These are 10 minutes talks on many
|
||||
<strong>Lightning Talks</strong>. These are 30 minutes talks on many
|
||||
different topics. Most topics are accepted in lightning talks.
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -4,9 +4,6 @@ import openerp
|
|||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
import time
|
||||
import json
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
GENGO_DEFAULT_LIMIT = 20
|
||||
|
||||
|
@ -40,21 +37,3 @@ class website_gengo(http.Controller):
|
|||
def post_gengo_jobs(self):
|
||||
request.registry['base.gengo.translations']._sync_request(request.cr, request.uid, limit=GENGO_DEFAULT_LIMIT, context=request.context)
|
||||
return True
|
||||
|
||||
@http.route('/website/gengo_callback/<model("ir.translation"):term>', type='http', auth='none')
|
||||
def gengo_callback(self,term,**post):
|
||||
if post and post.get('job'):
|
||||
translation_pool = request.registry['ir.translation']
|
||||
base_gengo_pool = request.registry['base.gengo.translations']
|
||||
job, vals = json.loads(post['job']), {}
|
||||
if job.get('status') == 'approved':
|
||||
vals.update({'state': 'translated', 'value': job.get('body_tgt')})
|
||||
flag, gengo = base_gengo_pool.gengo_authentication(request.cr, openerp.SUPERUSER_ID, context=request.context)
|
||||
job_comment = gengo.getTranslationJobComments(id=job.get('job_id'))
|
||||
if job_comment['opstat']=='ok':
|
||||
gengo_comments=""
|
||||
for comment in job_comment['response']['thread']:
|
||||
gengo_comments += _('%s\n-- Commented on %s by %s.\n\n') % (comment['body'], time.ctime(comment['ctime']), comment['author'])
|
||||
vals.update({'gengo_comment': gengo_comments})
|
||||
if vals:
|
||||
translation_pool.write(request.cr, openerp.SUPERUSER_ID, term.id, vals)
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
new_content:self.getInitialContent(this),
|
||||
translation_id: data.oeTranslationId || null,
|
||||
gengo_translation: gengo_service_level,
|
||||
gengo_comment:"Original page:" + document.URL
|
||||
gengo_comment:"\nOriginal Page: " + document.URL
|
||||
});
|
||||
});
|
||||
openerp.jsonRpc('/website/set_translations', 'call', {
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="base.partner_root" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="base.partner_demo" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_9" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
|
|
|
@ -6,3 +6,4 @@ access_sale_quote_line_manager,sale.quote.line,model_sale_quote_line,base.group_
|
|||
access_sale_quote_option,sale.quote.option,model_sale_quote_option,base.group_sale_salesman,1,0,0,0
|
||||
access_sale_quote_option_manager,sale.quote.option,model_sale_quote_option,base.group_sale_manager,1,1,1,1
|
||||
access_sale_order_option,sale.order.option,model_sale_order_option,base.group_sale_salesman,1,1,1,1
|
||||
access_sale_order_option_all,sale.order.option,model_sale_order_option,,1,0,0,0
|
||||
|
|
|
|
@ -43,7 +43,7 @@ class CheckoutInfo(object):
|
|||
result = dict((prefix + field_name, getattr(partner, field_name)) for field_name in self.string_billing_fields if getattr(partner, field_name))
|
||||
result[prefix + 'state_id'] = partner.state_id and partner.state_id.id or ''
|
||||
result[prefix + 'country_id'] = partner.country_id and partner.country_id.id or ''
|
||||
result[prefix + 'company'] = partner.parent_id and partner.parent_id.name or ''
|
||||
result[prefix + 'company'] = partner.commercial_partner_id and partner.commercial_partner_id.is_company and partner.commercial_partner_id.name or ''
|
||||
return result
|
||||
|
||||
def from_post(self, post):
|
||||
|
@ -383,6 +383,8 @@ class Ecommerce(http.Controller):
|
|||
def add_cart(self, product_id, remove=None, **kw):
|
||||
request.registry['website']._ecommerce_add_product_to_cart(request.cr, request.uid,
|
||||
product_id=int(product_id),
|
||||
number=float(kw.get('number',1)),
|
||||
set_number=float(kw.get('set_number',-1)),
|
||||
context=request.context)
|
||||
return request.redirect("/shop/mycart")
|
||||
|
||||
|
|
|
@ -66,39 +66,23 @@
|
|||
},
|
||||
{
|
||||
waitNot: '.product_price .oe_currency_value:containsExact(1.00)',
|
||||
element: '#wrap img.img:first',
|
||||
element: '#wrap img.product_detail_img',
|
||||
placement: 'top',
|
||||
title: _t("Update image"),
|
||||
content: _t("Click here to set an image describing your product."),
|
||||
},
|
||||
{
|
||||
element: 'button.hover-edition-button:visible',
|
||||
element: 'img[alt=ipad]',
|
||||
placement: 'top',
|
||||
title: _t("Update image"),
|
||||
content: _t("Click here to set an image describing your product."),
|
||||
},
|
||||
{
|
||||
wait: 500,
|
||||
element: '.well a.pull-right',
|
||||
placement: 'bottom',
|
||||
title: _t("Select an Image"),
|
||||
content: _t("Let's select an existing image."),
|
||||
popover: { fixed: true },
|
||||
content: _t("Let's select an ipad image."),
|
||||
},
|
||||
{
|
||||
element: 'img[alt=imac]',
|
||||
placement: 'bottom',
|
||||
title: _t("Select an Image"),
|
||||
content: _t("Let's select an imac image."),
|
||||
popover: { fixed: true },
|
||||
},
|
||||
{
|
||||
waitNot: 'img[alt=imac]',
|
||||
waitFor: '.media_selected img[alt=ipad]',
|
||||
element: '.modal-content button.save',
|
||||
placement: 'bottom',
|
||||
title: _t("Select this Image"),
|
||||
content: _t("Click to add the image to the product decsription."),
|
||||
popover: { fixed: true },
|
||||
placement: 'top',
|
||||
title: _t("Save this Image"),
|
||||
content: _t("Click on save to add the image to the product decsription."),
|
||||
},
|
||||
{
|
||||
waitNot: '.modal-content:visible',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import models
|
||||
import controllers
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
'name': 'Twitter Roller',
|
||||
'category': 'Website',
|
||||
'summary': 'Add twitter scroller snippet in website builder',
|
||||
'version': '1.0',
|
||||
'description': """
|
||||
Display best tweets
|
||||
========================
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/twitter_data.xml',
|
||||
'views/twitter_view.xml',
|
||||
'views/twitter_snippet.xml'
|
||||
],
|
||||
'demo': [],
|
||||
'qweb': [],
|
||||
'js': [],
|
||||
'css': [],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
import main
|
|
@ -0,0 +1,32 @@
|
|||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import json
|
||||
|
||||
class Twitter(http.Controller):
|
||||
@http.route(['/twitter_reload'], type='json', auth="user", website=True)
|
||||
def twitter_reload(self):
|
||||
return request.website.fetch_favorite_tweets()
|
||||
|
||||
@http.route(['/get_favorites'], type='json', auth="public", website=True)
|
||||
def get_tweets(self, limit=20):
|
||||
key = request.website.twitter_api_key
|
||||
secret = request.website.twitter_api_secret
|
||||
screen_name = request.website.twitter_screen_name
|
||||
if not key or not secret:
|
||||
return {"error": _("Please set the Twitter API Key and Secret in the Website Settings.")}
|
||||
if not screen_name:
|
||||
return {"error": _("Please set a Twitter screen name to load favorites from, "
|
||||
"in the Website Settings (it does not have to be yours)")}
|
||||
twitter_tweets = request.registry['website.twitter.tweet']
|
||||
tweets = twitter_tweets.search_read(
|
||||
request.cr, request.uid,
|
||||
[('website_id','=', request.website.id),
|
||||
('screen_name','=', screen_name)],
|
||||
['tweet'], limit=int(limit), order="tweet_id desc", context=request.context)
|
||||
if len(tweets) < 12:
|
||||
return {"error": _("Twitter user @%(username)s has less than 12 favorite tweets. "
|
||||
"Please add more or choose a different screen name.") % \
|
||||
{'username': screen_name}}
|
||||
return [json.loads(tweet['tweet']) for tweet in tweets]
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="ir_cron_twitter_actions" model="ir.cron">
|
||||
<field name="name">Fetch new Twitter favorites</field>
|
||||
<field name="interval_number">2</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model">website</field>
|
||||
<field name="function">_refresh_favorite_tweets</field>
|
||||
<field name="args">()</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import twitter
|
||||
import twitter_config
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
from urllib2 import urlopen, Request, HTTPError
|
||||
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import werkzeug
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
API_ENDPOINT = 'https://api.twitter.com'
|
||||
API_VERSION = '1.1'
|
||||
REQUEST_TOKEN_URL = '%s/oauth2/token' % API_ENDPOINT
|
||||
REQUEST_FAVORITE_LIST_URL = '%s/%s/favorites/list.json' % (API_ENDPOINT, API_VERSION)
|
||||
URLOPEN_TIMEOUT = 10
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class TwitterClient(osv.osv):
|
||||
_inherit = "website"
|
||||
|
||||
_columns = {
|
||||
'twitter_api_key': fields.char('Twitter API key', help="Twitter API Key"),
|
||||
'twitter_api_secret': fields.char('Twitter API secret', help="Twitter API Secret"),
|
||||
'twitter_screen_name': fields.char('Get favorites from this screen name'),
|
||||
}
|
||||
|
||||
def _request(self, website, url, params=None):
|
||||
"""Send an authenticated request to the Twitter API."""
|
||||
access_token = self._get_access_token(website)
|
||||
if params:
|
||||
params = werkzeug.url_encode(params)
|
||||
url = url + '?' + params
|
||||
try:
|
||||
request = Request(url)
|
||||
request.add_header('Authorization', 'Bearer %s' % access_token)
|
||||
return json.load(urlopen(request, timeout=URLOPEN_TIMEOUT))
|
||||
except HTTPError, e:
|
||||
_logger.debug("Twitter API request failed with code: %r, msg: %r, content: %r",
|
||||
e.code, e.msg, e.fp.read())
|
||||
raise
|
||||
|
||||
def _refresh_favorite_tweets(self, cr, uid, context=None):
|
||||
''' called by cron job '''
|
||||
website = self.pool['website']
|
||||
ids = self.pool['website'].search(cr, uid, [('twitter_api_key', '!=', False),
|
||||
('twitter_api_secret', '!=', False),
|
||||
('twitter_screen_name', '!=', False)],
|
||||
context=context)
|
||||
_logger.debug("Refreshing tweets for website IDs: %r", ids)
|
||||
website.fetch_favorite_tweets(cr, uid, ids, context=context)
|
||||
|
||||
def fetch_favorite_tweets(self, cr, uid, ids, context=None):
|
||||
website_tweets = self.pool['website.twitter.tweet']
|
||||
tweet_ids = []
|
||||
for website in self.browse(cr, uid, ids, context=context):
|
||||
if not all((website.twitter_api_key, website.twitter_api_secret,
|
||||
website.twitter_screen_name)):
|
||||
_logger.debug("Skip fetching favorite tweets for unconfigured website %s",
|
||||
website)
|
||||
continue
|
||||
params = {'screen_name': website.twitter_screen_name}
|
||||
last_tweet = website_tweets.search_read(
|
||||
cr, uid, [('website_id', '=', website.id),
|
||||
('screen_name', '=', website.twitter_screen_name)],
|
||||
['tweet_id'],
|
||||
limit=1, order='tweet_id desc', context=context)
|
||||
if last_tweet:
|
||||
params['since_id'] = int(last_tweet[0]['tweet_id'])
|
||||
_logger.debug("Fetching favorite tweets using params %r", params)
|
||||
response = self._request(website, REQUEST_FAVORITE_LIST_URL, params=params)
|
||||
for tweet_dict in response:
|
||||
tweet_id = tweet_dict['id'] # unsigned 64-bit snowflake ID
|
||||
tweet_ids = website_tweets.search(cr, uid, [('tweet_id', '=', tweet_id)])
|
||||
if not tweet_ids:
|
||||
new_tweet = website_tweets.create(
|
||||
cr, uid,
|
||||
{
|
||||
'website_id': website.id,
|
||||
'tweet': json.dumps(tweet_dict),
|
||||
'tweet_id': tweet_id, # stored in NUMERIC PG field
|
||||
'screen_name': website.twitter_screen_name,
|
||||
},
|
||||
context=context)
|
||||
_logger.debug("Found new favorite: %r, %r", tweet_id, tweet_dict)
|
||||
tweet_ids.append(new_tweet)
|
||||
return tweet_ids
|
||||
|
||||
def _get_access_token(self, website):
|
||||
"""Obtain a bearer token."""
|
||||
bearer_token_cred = '%s:%s' % (website.twitter_api_key, website.twitter_api_secret)
|
||||
encoded_cred = base64.b64encode(bearer_token_cred)
|
||||
request = Request(REQUEST_TOKEN_URL)
|
||||
request.add_header('Content-Type',
|
||||
'application/x-www-form-urlencoded;charset=UTF-8')
|
||||
request.add_header('Authorization',
|
||||
'Basic %s' % encoded_cred)
|
||||
request.add_data('grant_type=client_credentials')
|
||||
data = json.load(urlopen(request, timeout=URLOPEN_TIMEOUT))
|
||||
access_token = data['access_token']
|
||||
return access_token
|
||||
|
||||
class WebsiteTwitterTweet(osv.osv):
|
||||
_name = "website.twitter.tweet"
|
||||
_description = "Twitter Tweets"
|
||||
_columns = {
|
||||
'website_id': fields.many2one('website', string="Website"),
|
||||
'screen_name': fields.char("Screen Name"),
|
||||
'tweet': fields.text('Tweets'),
|
||||
|
||||
# Twitter IDs are 64-bit unsigned ints, so we need to store them in
|
||||
# unlimited precision NUMERIC columns, which can be done with a
|
||||
# float field. Used digits=(0,0) to indicate unlimited.
|
||||
# Using VARCHAR would work too but would have sorting problems.
|
||||
'tweet_id': fields.float("Tweet ID", digits=(0,0)), # Twitter
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import logging
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class twitter_config_settings(osv.osv_memory):
|
||||
_inherit = 'website.config.settings'
|
||||
|
||||
_columns = {
|
||||
'twitter_api_key': fields.related(
|
||||
'website_id', 'twitter_api_key', type="char",
|
||||
string='Twitter API Key',
|
||||
help="Twitter API key you can get it from https://apps.twitter.com/app/new"),
|
||||
'twitter_api_secret': fields.related(
|
||||
'website_id', 'twitter_api_secret', type="char",
|
||||
string='Twitter API secret',
|
||||
help="Twitter API secret you can get it from https://apps.twitter.com/app/new"),
|
||||
'twitter_tutorial': fields.dummy(
|
||||
type="boolean", string="Show me how to obtain the Twitter API Key and Secret"),
|
||||
'twitter_screen_name': fields.related(
|
||||
'website_id', 'twitter_screen_name',
|
||||
type="char", string='Get favorites from this screen name',
|
||||
help="Screen Name of the Twitter Account from which you want to load favorites."
|
||||
"It does not have to match the API Key/Secret."),
|
||||
}
|
||||
|
||||
def _check_twitter_authorization(self, cr, uid, config_id, context=None):
|
||||
website_obj = self.pool['website']
|
||||
website_config = self.browse(cr, uid, config_id, context=context)
|
||||
try:
|
||||
website_obj.fetch_favorite_tweets(cr, uid, [website_config.website_id.id], context=context)
|
||||
except Exception:
|
||||
_logger.warning('Failed to verify twitter API authorization', exc_info=True)
|
||||
raise osv.except_osv(_('Twitter authorization error!'), _('Please double-check your Twitter API Key and Secret'))
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res_id = super(twitter_config_settings, self).create(cr, uid, vals, context=context)
|
||||
if vals.get('twitter_api_key') and vals.get('twitter_api_secret'):
|
||||
self._check_twitter_authorization(cr, uid, res_id, context=context)
|
||||
return res_id
|
|
@ -0,0 +1,2 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_website_twitter_tweet_public,access of twitter snippet,website_twitter.model_website_twitter_tweet,,1,0,0,0
|
|
|
@ -0,0 +1,3 @@
|
|||
sass:
|
||||
sass -t expanded --compass --unix-newlines --watch website.twitter.sass:website.twitter.css
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
.wrap-row {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 310px;
|
||||
}
|
||||
.wrap-row .twitter-row {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.wrap-row .twitter-row div.scrollWrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.wrap-row .twitter-row div.scrollableArea {
|
||||
position: relative;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet {
|
||||
border: 1px solid #cccccc;
|
||||
max-width: 500px;
|
||||
width: 500px;
|
||||
font-size: 0.8em;
|
||||
padding-top: 12px;
|
||||
padding-right: 10px;
|
||||
padding-bottom: 12px;
|
||||
padding-left: 10px;
|
||||
float: left;
|
||||
display: block;
|
||||
margin: 6px;
|
||||
max-height: 90px;
|
||||
height: 90px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet h4, .wrap-row .twitter-row div .tweet p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .left {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 80px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .left img {
|
||||
width: 65px;
|
||||
height: auto;
|
||||
float: left;
|
||||
display: block;
|
||||
margin: 0px 5px 0px -5px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .right {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 470px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .right .top {
|
||||
height: 20px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet h4 {
|
||||
font-size: 14px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
float: left;
|
||||
display: block;
|
||||
position: relative;
|
||||
margin-left: 70px;
|
||||
margin-top: -65px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet h4 span {
|
||||
color: #cccccc;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet p {
|
||||
line-height: 1.5em;
|
||||
float: left;
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .date {
|
||||
float: right;
|
||||
line-height: 0.5em;
|
||||
margin-top: -60px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet .right .bottom p {
|
||||
margin-top: -65px;
|
||||
margin-left: 70px;
|
||||
font-size: 12px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.wrap-row .twitter-row div .tweet:hover {
|
||||
-webkit-box-shadow: 0.5px 0.5px 0.5px 1px #428bca;
|
||||
-moz-box-shadow: 0.5px 0.5px 0.5px 1px #428bca;
|
||||
box-shadow: 0.5px 0.5px 0.5px 1px #428bca;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 580px) {
|
||||
.wrap-row {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
.wrap-row
|
||||
position: relative
|
||||
overflow: hidden
|
||||
height: 310px
|
||||
.twitter-row
|
||||
position: absolute
|
||||
width: 100%
|
||||
height: auto
|
||||
div
|
||||
&.scrollWrapper
|
||||
position: relative
|
||||
overflow: hidden
|
||||
width: 100%
|
||||
height: 100%
|
||||
&.scrollableArea
|
||||
position: relative
|
||||
width: auto
|
||||
height: 100%
|
||||
.tweet
|
||||
border: 1px solid #ccc
|
||||
max-width: 500px
|
||||
width: 500px
|
||||
font-size: 0.8em
|
||||
padding-top: 12px
|
||||
padding-right: 10px
|
||||
padding-bottom: 12px
|
||||
padding-left: 10px
|
||||
float: left
|
||||
display: block
|
||||
margin: 6px
|
||||
max-height: 90px
|
||||
height: 90px
|
||||
opacity: 0.6
|
||||
h4, p
|
||||
padding: 0
|
||||
margin: 0
|
||||
.left
|
||||
display: block
|
||||
float: left
|
||||
width: 80px
|
||||
img
|
||||
width: 65px
|
||||
height: auto
|
||||
float: left
|
||||
display: block
|
||||
margin: 0px 5px 0px -5px
|
||||
.right
|
||||
display: block
|
||||
float: left
|
||||
width: 470px
|
||||
.top
|
||||
height: 20px
|
||||
h4
|
||||
font-size: 14px
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif
|
||||
font-weight: bold
|
||||
color: #000
|
||||
float: left
|
||||
display: block
|
||||
position: relative
|
||||
margin-left: 70px
|
||||
margin-top: -65px
|
||||
span
|
||||
color: #ccc
|
||||
font-weight: bold
|
||||
font-size: 14px
|
||||
p
|
||||
line-height: 1.5em
|
||||
float: left
|
||||
position: relative
|
||||
display: block
|
||||
&.date
|
||||
float: right
|
||||
line-height: 0.5em
|
||||
margin-top: -60px
|
||||
margin-right: -10px
|
||||
.right .bottom p
|
||||
margin-top: -65px
|
||||
margin-left: 70px
|
||||
font-size: 12px
|
||||
word-break: break-word
|
||||
&:hover
|
||||
-webkit-box-shadow: 0.5px 0.5px 0.5px 1px #428BCA
|
||||
-moz-box-shadow: 0.5px 0.5px 0.5px 1px #428BCA
|
||||
box-shadow: 0.5px 0.5px 0.5px 1px #428BCA
|
||||
cursor: pointer
|
||||
opacity: 1
|
||||
|
||||
|
||||
@media screen and (max-width: 580px)
|
||||
.wrap-row
|
||||
position: relative
|
||||
overflow: hidden
|
||||
height: 100px
|
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,144 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
var website = openerp.website,
|
||||
qweb = openerp.qweb;
|
||||
|
||||
qweb.add_template('/website_twitter/static/src/xml/website.twitter.xml');
|
||||
if (!website.snippet) website.snippet = {};
|
||||
website.snippet.animationRegistry.twitter = website.snippet.Animation.extend({
|
||||
selector: ".twitter",
|
||||
start: function () {
|
||||
var self = this;
|
||||
var timeline = this.$target.find(".twitter_timeline");
|
||||
|
||||
this.$target.on('click', '.twitter_timeline .tweet', function($event) {
|
||||
if ($event.target.tagName.toLowerCase() !== "a") {
|
||||
var url = $($event.currentTarget).data('url');
|
||||
if (url) {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
else {
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
});
|
||||
$("<center><div><img src='/website_twitter/static/src/img/loadtweet.gif'></div></center>").appendTo(timeline);
|
||||
openerp.jsonRpc('/get_favorites','call', {}).then(function(data) {
|
||||
self.$target.find(".twitter_timeline").empty();
|
||||
if (data.error) {
|
||||
self.error(data);
|
||||
}
|
||||
else {
|
||||
self.render(data);
|
||||
self.setupMouseEvents();
|
||||
}
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
$(this).find('.scrollWrapper').each(function(index, el){
|
||||
self.stop_scrolling($(el));
|
||||
});
|
||||
this.clearMouseEvents();
|
||||
},
|
||||
error: function(data){
|
||||
var $error = $(qweb.render("website.Twitter.Error", {'data': data}));
|
||||
$error.appendTo(this.$target.find(".twitter_timeline"));
|
||||
},
|
||||
parse_tweet: function (tweet) {
|
||||
var create_link = function (url, text) {
|
||||
var c = $("<a>", {
|
||||
text: text,
|
||||
href: url,
|
||||
target: "_blank"
|
||||
});
|
||||
return c.prop("outerHTML");
|
||||
};
|
||||
return tweet.text.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&~\?\/.=]+/g,
|
||||
function (url) { return create_link(url, url) })
|
||||
.replace(/[@]+[A-Za-z0-9_]+/g,
|
||||
function (screen_name) { return create_link("http://twitter.com/" + screen_name.replace("@",""), screen_name); })
|
||||
.replace(/[#]+[A-Za-z0-9_]+/g,
|
||||
function (hashtag) { return create_link("http://twitter.com/search?q="+hashtag.replace("#",""), hashtag); });
|
||||
},
|
||||
parse_date: function(tweet) {
|
||||
var d = new Date(tweet.created_at);
|
||||
return d.toDateString();
|
||||
},
|
||||
setupMouseEvents: function() {
|
||||
var self = this;
|
||||
this.$scroller.mouseenter(function() {
|
||||
$(this).find('.scrollWrapper').each(function(index, el){
|
||||
self.stop_scrolling($(el));
|
||||
});
|
||||
}).mouseleave(function() {
|
||||
$(this).find('.scrollWrapper').each(function(index, el){
|
||||
self.start_scrolling($(el));
|
||||
});
|
||||
});
|
||||
},
|
||||
clearMouseEvents: function() {
|
||||
if (this.$scroller) {
|
||||
this.$scroller.off('mouseenter')
|
||||
.off('mouseleave');
|
||||
}
|
||||
},
|
||||
render: function(data){
|
||||
var self = this;
|
||||
var timeline = this.$target.find(".twitter_timeline");
|
||||
var tweets = [];
|
||||
$.each(data, function (e, tweet) {
|
||||
tweet.created_at = self.parse_date(tweet);
|
||||
tweet.text = self.parse_tweet(tweet);
|
||||
tweets.push(qweb.render("website.Twitter.Tweet", {'tweet': tweet}));
|
||||
});
|
||||
|
||||
var f = Math.floor(tweets.length / 3);
|
||||
var tweet_slice = [tweets.slice(0, f).join(" "), tweets.slice(f, f * 2).join(" "), tweets.slice(f * 2, tweets.length).join(" ")];
|
||||
|
||||
this.$scroller = $(qweb.render("website.Twitter.Scroller"));
|
||||
this.$scroller.appendTo(this.$target.find(".twitter_timeline"));
|
||||
this.$scroller.find("div[id^='scroller']").each(function(index, element){
|
||||
var scrollWrapper = $('<div class="scrollWrapper"></div>');
|
||||
var scrollableArea = $('<div class="scrollableArea"></div>');
|
||||
scrollWrapper.append(scrollableArea)
|
||||
.data('scrollableArea', scrollableArea);
|
||||
scrollableArea.append(tweet_slice[index]);
|
||||
$(element).append(scrollWrapper);
|
||||
scrollableArea.width(self.get_wrapper_width(scrollableArea));
|
||||
scrollWrapper.scrollLeft(index*180);
|
||||
self.start_scrolling(scrollWrapper);
|
||||
});
|
||||
},
|
||||
get_wrapper_width: function(wrapper){
|
||||
var total_width = 0;
|
||||
wrapper.children().each(function(){
|
||||
total_width += $(this).outerWidth(true);
|
||||
});
|
||||
return total_width;
|
||||
},
|
||||
start_scrolling: function(wrapper){
|
||||
var self = this;
|
||||
wrapper.data("getNextElementWidth", true);
|
||||
wrapper.data("autoScrollingInterval", setInterval(function () {
|
||||
wrapper.scrollLeft(wrapper.scrollLeft() + 1);
|
||||
self.swap_right(wrapper);
|
||||
}, 20));
|
||||
},
|
||||
stop_scrolling: function(wrapper){
|
||||
clearInterval(wrapper.data('autoScrollingInterval'));
|
||||
},
|
||||
swap_right: function(wrapper){
|
||||
if (wrapper.data("getNextElementWidth")) {
|
||||
wrapper.data("swapAt", wrapper.data("scrollableArea").children(":first").outerWidth(true));
|
||||
wrapper.data("getNextElementWidth", false);
|
||||
}
|
||||
if (wrapper.data("swapAt") <= wrapper.scrollLeft()){
|
||||
var swap_el = wrapper.data("scrollableArea").children(":first").detach();
|
||||
wrapper.data("scrollableArea").append(swap_el);
|
||||
wrapper.scrollLeft(wrapper.scrollLeft() - swap_el.outerWidth(true));
|
||||
wrapper.data("getNextElementWidth", true);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
})();
|