[MERGE] from trunk

bzr revid: chm@openerp.com-20140409154520-eddr2h21bei95xxu
This commit is contained in:
chm@openerp.com 2014-04-09 17:45:20 +02:00
commit 15c035631d
44 changed files with 1486 additions and 682 deletions

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

@ -158,16 +158,16 @@ class event_event(osv.osv):
'seats_min': fields.integer('Minimum Reserved Seats', oldname='register_min', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
'seats_reserved': fields.function(_get_seats, oldname='register_current', string='Reserved Seats', type='integer', multi='seats_reserved',
store={'event.registration': (_get_events_from_registrations, ['state'], 10),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max'], 20)}),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
'seats_available': fields.function(_get_seats, oldname='register_avail', string='Available Seats', type='integer', multi='seats_reserved',
store={'event.registration': (_get_events_from_registrations, ['state'], 10),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max'], 20)}),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
'seats_unconfirmed': fields.function(_get_seats, oldname='register_prospect', string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved',
store={'event.registration': (_get_events_from_registrations, ['state'], 10),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max'], 20)}),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
'seats_used': fields.function(_get_seats, oldname='register_attended', string='Number of Participations', type='integer', multi='seats_reserved',
store={'event.registration': (_get_events_from_registrations, ['state'], 10),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max'], 20)}),
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
@ -201,7 +201,6 @@ class event_event(osv.osv):
}
def _check_seats_limit(self, cr, uid, ids, context=None):
print "event _check_seats_limit"
for event in self.browse(cr, uid, ids, context=context):
if event.seats_max and event.seats_available < 0:
return False

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

@ -40,6 +40,17 @@
<field name="args">()</field>
</record>
<!-- After installation of the module, open the related menu -->
<record id="action_client_messaging_menu" model="ir.actions.client">
<field name="name">Open Messaging Menu</field>
<field name="tag">reload</field>
<field name="params" eval="{'menu_id': ref('mail.mail_feeds_main')}"/>
</record>
<record id="base.open_menu" model="ir.actions.todo">
<field name="action_id" ref="action_client_messaging_menu"/>
<field name="state">open</field>
</record>
<!-- Admin should not receivve emails at creation -->
<record id="base.partner_root" model="res.partner">
<field name="notification_email_send">none</field>

View File

@ -579,7 +579,7 @@
.openerp .oe_followers button.oe_follower{
display: block;
text-align: center;
width:100%;
width:135px;
}
.openerp .oe_followers button.oe_follower.oe_following{
color: white;

View File

@ -70,7 +70,17 @@ openerp_mail_followers = function(session, mail) {
self.do_unfollow();
});
// event: click on a subtype, that (un)subscribe for this subtype
this.$el.on('click', '.oe_subtype_list input', self.do_update_subscription);
this.$el.on('click', '.oe_subtype_list input', function(event) {
self.do_update_subscription(event);
var $list = self.$('.oe_subtype_list');
if(!$list.hasClass('open')) {
$list.addClass('open');
}
if(self.$('.oe_subtype_list ul')[0].children.length < 1) {
$list.removeClass('open');
}
event.stopPropagation();
});
// event: click on 'invite' button, that opens the invite wizard
this.$('.oe_invite').on('click', self.on_invite_follower);
// event: click on 'edit_subtype(pencil)' button to edit subscription
@ -244,8 +254,14 @@ openerp_mail_followers = function(session, mail) {
if (user_pid) {
dialog = true;
} else {
var subtype_list_ul = this.$('.oe_subtype_list').empty();
if (! this.message_is_follower) return;
var subtype_list_ul = this.$('.oe_subtype_list ul').empty();
if (! this.message_is_follower) {
this.$('.oe_subtype_list > .dropdown-toggle').attr('disabled', true);
return;
}
else {
this.$('.oe_subtype_list > .dropdown-toggle').attr('disabled', false);
}
}
var id = this.view.datarecord.id;
this.ds_model.call('message_get_subscription_data', [[id], user_pid, new session.web.CompoundContext(this.build_context(), {})])
@ -259,9 +275,8 @@ openerp_mail_followers = function(session, mail) {
var $list = self.$dialog;
}
else {
var $list = this.$('.oe_subtype_list');
var $list = this.$('.oe_subtype_list ul');
}
$list.empty().hide();
var records = data[id].message_subtype_data;
this.records_length = $.map(records, function(value, index) { return index; }).length;
if (this.records_length > 1) { self.display_followers(); }
@ -276,15 +291,13 @@ openerp_mail_followers = function(session, mail) {
}
record.name = record_name;
record.followed = record.followed || undefined;
$(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo($list);
$(session.web.qweb.render('mail.followers.subtype', {'record': record, 'dialog': dialog})).appendTo($list);
});
if (_.size(records) > 1) {
$list.show();
}
},
do_follow: function () {
var context = new session.web.CompoundContext(this.build_context(), {});
this.$('.oe_subtype_list > .dropdown-toggle').attr('disabled', false);
this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], undefined, context])
.then(this.proxy('read_value'));
@ -294,11 +307,13 @@ openerp_mail_followers = function(session, mail) {
},
do_unfollow: function (user_pid) {
var self = this;
if (confirm(_t("Warning! \nYou won't be notified of any email or discussion on this document. Do you really want to unfollow this document ?"))) {
_(this.$('.oe_msg_subtype_check')).each(function (record) {
$(record).attr('checked',false);
});
var action_unsubscribe = 'message_unsubscribe_users';
this.$('.oe_subtype_list > .dropdown-toggle').attr('disabled', true);
var follower_ids = [this.session.uid];
if (user_pid) {
action_unsubscribe = 'message_unsubscribe';
@ -332,6 +347,8 @@ openerp_mail_followers = function(session, mail) {
if (!checklist.length) {
if (!this.do_unfollow(user_pid)) {
$(event.target).attr("checked", "checked");
} else {
self.$('.oe_subtype_list ul').empty();
}
} else {
var context = new session.web.CompoundContext(this.build_context(), {});

View File

@ -5,17 +5,23 @@
followers main template
Template used to display the followers, the actions and the subtypes in a record.
-->
<div t-name="mail.followers" class="oe_followers">
<div t-name="mail.followers" class="oe_followers">
<div class="oe_actions">
<button type="button" class="oe_follower oe_notfollow">
<span class="oe_follow">Follow</span>
<span class="oe_unfollow">Unfollow</span>
<span class="oe_following">Following</span>
</button>
<div t-attf-class="btn-group oe_subtype_list">
<button class="btn oe_follower oe_notfollow">
<span class="oe_follow">Follow</span>
<span class="oe_unfollow">Unfollow</span>
<span class="oe_following">Following</span>
</button>
<button type="button" t-attf-class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu"></ul>
</div>
<t t-if="widget.comment">
<h5 class="oe_comment"><t t-raw="widget.comment"/></h5>
</t>
<div class="oe_subtype_list"></div>
</div>
<div class='oe_follower_title_box'>
<h4 class='oe_follower_title'>Followers</h4>
@ -42,8 +48,12 @@
<t t-name="mail.followers.subtype">
<table class='oe_subtype'>
<tr>
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-esc="record.name"/></label></td>
<td width="10%">
<input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id+(dialog ? '_in_dialog': '')" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/>
</td>
<td>
<label t-att-for="'input_mail_followers_subtype_'+record.id+(dialog ? '_in_dialog': '')"><t t-raw="record.name"/></label>
</td>
</tr>
</table>
</t>

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

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

@ -285,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;
}
@ -400,6 +396,10 @@ section, .carousel, .parallax, .row, .hr, .blockquote {
padding-bottom: 16px;
}
.hr {
padding: 4px 0;
}
/* Parallax Theme */
div.carousel .carousel-indicators li {
border: 1px solid grey;

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
@ -326,6 +324,9 @@ section, .carousel, .parallax, .row, .hr, .blockquote
.quotecarousel
padding-bottom: 16px
.hr
padding: 4px 0
/* Parallax Theme */
div.carousel

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

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

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

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

@ -27,14 +27,14 @@ class event_track_tag(osv.osv):
_name = "event.track.tag"
_order = 'name'
_columns = {
'name': fields.char('Event Track Tag')
'name': fields.char('Event Track Tag', translate=True)
}
class event_tag(osv.osv):
_name = "event.tag"
_order = 'name'
_columns = {
'name': fields.char('Event Tag')
'name': fields.char('Event Tag', translate=True)
}
#
@ -45,7 +45,7 @@ class event_track_stage(osv.osv):
_name = "event.track.stage"
_order = 'sequence'
_columns = {
'name': fields.char('Track Stage'),
'name': fields.char('Track Stage', translate=True),
'sequence': fields.integer('Sequence')
}
_defaults = {
@ -160,7 +160,7 @@ class event_sponsors_type(osv.osv):
_name = "event.sponsor.type"
_order = "sequence"
_columns = {
"name": fields.char('Sponsor Type', required=True),
"name": fields.char('Sponsor Type', required=True, translate=True),
"sequence": fields.integer('Sequence')
}

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>