[MERGE] Merged with main Addons

bzr revid: mka@tinyerp.com-20140410103213-kq36gxiih5xdmt4l
bzr revid: mka@tinyerp.com-20140411053339-yic5dugaubzbyfhq
This commit is contained in:
Mansi Kariya (OpenERP) 2014-04-11 11:03:39 +05:30
commit fcd7f025a0
104 changed files with 4277 additions and 1368 deletions

View File

@ -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)

View File

@ -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')

View File

@ -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):

View File

@ -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,),

View File

@ -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

View File

@ -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>

View File

@ -22,5 +22,6 @@
import res_company
import ir_translation
import wizard
import controller
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1 @@
import gengo_callback

View File

@ -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)

View File

@ -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"/>

View File

@ -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]

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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','&gt;=', time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Upcoming events from today" />
<filter string="Upcoming" name="upcoming" domain="[('date_begin','&gt;=', 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>

View File

@ -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" />

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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() {

View File

@ -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,
}

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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..."

View File

@ -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">

View File

@ -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)

View File

@ -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(

View File

@ -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;

View File

@ -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 ---- {{{ */

View File

@ -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 {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

File diff suppressed because one or more lines are too long

View File

@ -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">&nbsp;</div><iframe src="'+this.$target.data("src")+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
},
});
})();

View File

@ -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");

View File

@ -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 },
},
{

View File

@ -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&amp;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='&lt;iframe src="//www.youtube.com/embed/yws1tbgNV7k"&gt;&lt;/iframe&gt;'/>
<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>

View File

@ -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>

View File

@ -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):

View File

@ -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>

View File

@ -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>

View File

@ -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',

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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.

View File

@ -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]

View File

@ -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]

View File

@ -1 +1,2 @@
import mail_message
import website_blog

View File

@ -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;'),
}

View File

@ -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:

View File

@ -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",
};
}());

View File

@ -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;
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -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');
},
});
})();

View File

@ -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');
}
});
})();

View File

@ -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);
});

View File

@ -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>

View File

@ -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">&amp;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">&amp;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"/> &amp;nbsp;</span>
<span t-if="len(blog_post.message_ids) &gt; 0" class="fa fa-comment-o">
<a t-attf-href="#{post_url(blogpost=blog_post)}#comments">
<t t-if="len(blog_post.message_ids) &lt;= 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&amp;field=image_small&amp;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"]
}'/>
&#5760; <span t-field="blog_post.create_date" t-field-options='{"format": "MMMM yyyy"}'/>
<span t-if="len(blog_post.message_ids) &gt; 0">
&#5760; <t t-esc="len(blog_post.message_ids)"/>
<t t-if="len(blog_post.message_ids) &lt;= 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"/> &amp;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"/> &amp;nbsp;</span>
<span t-if="len(blog_post.message_ids) &gt; 0" class="fa fa-comment-o"> With
<a t-attf-href="#comments">
<t t-if="len(blog_post.message_ids) &lt;= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
<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&amp;field=image_small&amp;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&amp;field=author_avatar&amp;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&amp;field=image_small&amp;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&amp;field=image_small&amp;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&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
<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&amp;field=image_small&amp;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"/> &amp;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"/> &amp;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">

View File

@ -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>

View File

@ -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,
}

View File

@ -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',

View File

@ -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>

View File

@ -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 &amp; 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>

View File

@ -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 {}

View File

@ -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;
}

View File

@ -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();
});
});

View File

@ -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"/>

View File

@ -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>

View File

@ -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)

View File

@ -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', {

View File

@ -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>

View File

@ -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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
6 access_sale_quote_option sale.quote.option model_sale_quote_option base.group_sale_salesman 1 0 0 0
7 access_sale_quote_option_manager sale.quote.option model_sale_quote_option base.group_sale_manager 1 1 1 1
8 access_sale_order_option sale.order.option model_sale_order_option base.group_sale_salesman 1 1 1 1
9 access_sale_order_option_all sale.order.option model_sale_order_option 1 0 0 0

View File

@ -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")

View File

@ -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',

View File

@ -0,0 +1,2 @@
import models
import controllers

View File

@ -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,
}

View File

@ -0,0 +1 @@
import main

View File

@ -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]

View File

@ -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>

View File

@ -0,0 +1,3 @@
import twitter
import twitter_config

View File

@ -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
}

View File

@ -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

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_website_twitter_tweet_public access of twitter snippet website_twitter.model_website_twitter_tweet 1 0 0 0

View File

@ -0,0 +1,3 @@
sass:
sass -t expanded --compass --unix-newlines --watch website.twitter.sass:website.twitter.css

View File

@ -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;
}
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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);
}
},
});
})();

Some files were not shown because too many files have changed in this diff Show More