--- /dev/null
+# -*- 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 controllers
+import document_page
+import res_config
--- /dev/null
+# -*- 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/>.
+#
+##############################################################################
+
+{
+ 'name': 'Blogs',
+ 'category': 'Website',
+ 'summary': 'News, Blogs, Announces, Discussions',
+ 'version': '1.0',
+ 'description': """
+OpenERP Blog
+============
+
+ """,
+ 'author': 'OpenERP SA',
+ 'depends': ['website_mail', 'document_page'],
+ 'data': [
+ 'website_mail_data.xml',
+ 'views/website_mail.xml',
+ 'views/res_config.xml',
+ 'security/website_mail.xml',
+ ],
+ 'qweb': [
+ 'static/src/xml/*.xml'
+ ],
+ 'installable': True,
+}
--- /dev/null
+import main
--- /dev/null
+# -*- 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/>.
+#
+##############################################################################
+
+from openerp import SUPERUSER_ID
+from openerp.addons.web import http
+from openerp.addons.web.http import request
+from openerp.addons.website import website
+from openerp.tools.translate import _
+from openerp.tools.safe_eval import safe_eval
+
+import simplejson
+import werkzeug
+
+
+class website_mail(http.Controller):
+ _category_post_per_page = 2
+ _post_comment_per_page = 2
+
+ @website.route([
+ '/blog/',
+ '/blog/<int:category_id>/',
+ '/blog/<int:category_id>/<int:blog_post_id>/',
+ '/blog/<int:category_id>/page/<int:page>/',
+ '/blog/<int:category_id>/<int:blog_post_id>/page/<int:page>/'
+ ], type='http', auth="public")
+ def blog(self, category_id=None, blog_post_id=None, page=1, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ blog_post_obj = request.registry['blog.post']
+ category_obj = request.registry['blog.category']
+
+ values = {
+ 'blog_ids': None,
+ 'blog_id': None,
+ 'nav_list': dict(),
+ 'unable_editor': post.get('unable_editor')
+ }
+
+ # no category chosen: display categories
+ categories = None
+ category = None
+ blog_post = None
+ blog_posts = None
+ pager = None
+ if not category_id:
+ category_ids = category_obj.search(cr, uid, [], context=context)
+ categories = category_obj.browse(cr, uid, category_ids, context=context)
+ # category but no post chosen: display the last ones, create pager
+ elif category_id and not blog_post_id:
+ pager_begin = (page - 1) * self._category_post_per_page
+ pager_end = page * self._category_post_per_page
+ category = category_obj.browse(cr, uid, category_id, context=context)
+ blog_posts = category.blog_ids[pager_begin:pager_end]
+ pager = request.website.pager(url="/blog/%s/" % category_id, total=len(category.blog_ids), page=page, step=self._category_post_per_page, scope=7)
+ elif category_id and blog_post_id:
+ category = category_obj.browse(cr, uid, category_id, context=context)
+ blog_post = blog_post_obj.browse(cr, uid, blog_post_id, context=context)
+
+ values.update({
+ 'blog_post': blog_post,
+ 'blog_posts': blog_posts,
+ 'categories': categories,
+ 'category': category,
+ 'pager': pager,
+ })
+
+ for group in blog_post_obj.read_group(cr, uid, [], ['name', 'create_date'], groupby="create_date", orderby="create_date asc", context=context):
+ print 'group', group
+ year = group['create_date'].split(" ")[1]
+ if not values['nav_list'].get(year):
+ values['nav_list'][year] = {'name': year, 'create_date_count': 0, 'months': []}
+ values['nav_list'][year]['create_date_count'] += group['create_date_count']
+ values['nav_list'][year]['months'].append(group)
+
+ print values
+ return request.website.render("website_blog.index", values)
+
+ # @website.route(['/blog/nav'], type='http', auth="public")
+ # def nav(self, **post):
+ # comment_ids = request.registry['mail.group'].get_public_message_ids(request.cr, request.uid, domain=safe_eval(post.get('domain')), order="create_date asc", limit=None, context=request.context)
+ # return simplejson.dumps(request.registry['mail.message'].read(request.cr, request.uid, comment_ids, ['website_published', 'subject', 'res_id'], request.context))
+
+ @website.route(['/blog/<int:category_id>/<int:blog_post_id>/post'], type='http', auth="public")
+ def blog_comment(self, category_id=None, blog_post_id=None, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ url = request.httprequest.host_url
+ request.session.body = post.get('body')
+ print category_id, blog_post_id, post
+ if request.context['is_public_user']: # purpose of this ?
+ return '%s/admin#action=redirect&url=%s/blog/%s/%s/post' % (url, url, category_id, blog_post_id)
+
+ if request.session.get('body') and blog_post_id:
+ request.registry['blog.post'].message_post(
+ cr, uid, blog_post_id,
+ body=request.session.body,
+ type='comment',
+ subtype='mt_comment',
+ context=dict(context, mail_create_nosubcribe=True))
+ request.session.body = False
+
+ return self.blog(category_id=category_id, blog_post_id=blog_post_id)
+
+ @website.route(['/blog/<int:category_id>/new'], type='http', auth="public")
+ def create_blog_post(self, category_id=None, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ create_context = dict(context, mail_create_nosubscribe=True)
+ blog_id = request.registry['blog.post'].create(
+ request.cr, request.uid, {
+ 'category_id': category_id,
+ 'name': _("Blog title"),
+ 'content': '',
+ 'website_published': False,
+ }, context=create_context)
+ return werkzeug.utils.redirect("/blog/%s/%s/?unable_editor=1" % (category_id, blog_id))
+
+ def _find_or_create_partner(self, email, context=None):
+ partner_obj = request.registry['res.partner']
+ user_obj = request.registry['res.users']
+ partner_ids = []
+ if request.context['is_public_user'] and email:
+ partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", email)], context=request.context)
+ if not partner_ids:
+ partner_ids = [partner_obj.create(request.cr, SUPERUSER_ID, {"email": email, "name": email}, request.context)]
+ else:
+ partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
+ return partner_ids
+
+ @website.route(['/blog/<int:category_id>/subscribe'], type='http', auth="public")
+ def category_subscribe(self, category_id=None, **post):
+ partner_ids = self._find_or_create_partner(post.get('email'), request.context)
+ category_obj = request.registry['blog.category']
+
+ category_obj.check_access_rule(request.cr, request.uid, [category_id], 'read', request.context)
+ category_obj.message_subscribe(request.cr, SUPERUSER_ID, [category_id], partner_ids, context=request.context)
+
+ return self.blog(category_id=category_id)
+
+ @website.route(['/blog/<int:category_id>/unsubscribe'], type='http', auth="public")
+ def category_unsubscribe(self, category_id=None, **post):
+ partner_ids = self._find_or_create_partner(post.get('email'), request.context)
+ category_obj = request.registry['blog.category']
+
+ category_obj.check_access_rule(request.cr, request.uid, [category_id], 'read', request.context)
+ category_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [category_id], partner_ids, context=request.context)
+
+ return self.blog(category_id=category_id)
+
+ @website.route(['/blog/<int:category_id>/<int:blog_post_id>/subscribe'], type='http', auth="public")
+ def blog_post_subscribe(self, category_id=None, blog_post_id=None, **post):
+ partner_ids = self._find_or_create_partner(post.get('email'), request.context)
+ blog_post_obj = request.registry['blog.post']
+
+ blog_post_obj.check_access_rule(request.cr, request.uid, [blog_post_id], 'read', request.context)
+ blog_post_obj.message_subscribe(request.cr, SUPERUSER_ID, [blog_post_id], partner_ids, context=request.context)
+
+ return self.blog(category_id=category_id, blog_post_id=blog_post_id)
+
+ @website.route(['/blog/<int:category_id>/<int:blog_post_id>/unsubscribe'], type='http', auth="public")
+ def blog_post_unsubscribe(self, category_id=None, blog_post_id=None, **post):
+ partner_ids = self._find_or_create_partner(post.get('email'), request.context)
+ blog_post_obj = request.registry['blog.post']
+
+ blog_post_obj.check_access_rule(request.cr, request.uid, [blog_post_id], 'read', request.context)
+ blog_post_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [blog_post_id], partner_ids, context=request.context)
+
+ return self.blog(category_id=category_id, blog_post_id=blog_post_id)
--- /dev/null
+# -*- 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/>.
+#
+##############################################################################
+
+from openerp import tools
+from openerp import SUPERUSER_ID
+from openerp.osv import osv, fields
+
+
+class WebsiteDocumentPage(osv.Model):
+ """ Inherit document.page to add website-related columns
+
+ - website_published: standard publish column
+ - website_message_ids: messages to display on website, aka comments
+
+ """
+ _inherit = "blog.post"
+ # maximum number of characters to display in summary
+ _shorten_max_char = 10
+
+ def get_shortened_content(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)
+ except Exception:
+ body_short = False
+ res[page.id] = body_short
+ return res
+
+ _columns = {
+ 'website_published': fields.boolean(
+ 'Publish', help="Publish on the website"
+ ),
+ 'website_published_datetime': fields.datetime(
+ 'Publish Date'
+ ),
+ 'website_message_ids': fields.one2many(
+ 'mail.message', 'res_id',
+ domain=lambda self: [
+ '&', ('model', '=', self._name), ('type', '=', 'comment')
+ ],
+ auto_join=True,
+ string='Website Messages',
+ help="Website communication history",
+ ),
+ 'shortened_content': fields.function(
+ get_shortened_content,
+ type='text',
+ string='Shortened Content',
+ help="Shortened content of the page that serves as a summary"
+ ),
+ }
+
+ 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)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from openerp.osv import fields, osv
+
+
+class website_config_settings(osv.osv_memory):
+ _inherit = 'website.config.settings'
+ _columns = {
+ 'group_website_mail_reply': fields.boolean('Visitors can reply on blogs',
+ implied_group='website_mail.group_website_mail_reply'),
+ }
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+ <record id="group_website_mail_reply" model="res.groups">
+ <field name="name">Visitors can reply on blogs</field>
+ <field name="category_id" ref="base.module_category_hidden"/>
+ </record>
+
+<!-- <record id="website_mail_public" model="ir.rule">
+ <field name="name">website_mail: Public message</field>
+ <field name="model_id" ref="model_mail_message"/>
+ <field name="domain_force">[('website_published', '=', True), ("model", "=", 'mail.group')]</field>
+ <field name="groups" eval="[(4, ref('base.group_public'))]"/>
+ <field name="perm_read" eval="True"/>
+ <field name="perm_write" eval="False"/>
+ <field name="perm_create" eval="False"/>
+ <field name="perm_unlink" eval="False"/>
+ </record> -->
+</data>
+</openerp>
--- /dev/null
+<section class="oe_container">
+ <div class="oe_row oe_spaced">
+ <div class="oe_span12">
+ <h2 class="oe_slogan">Awesome Open Source Blogging Systems</h2>
+ <h3 class="oe_slogan">Express yourself with beautiful blogs</h3>
+ </div>
+ <div class="oe_span6">
+ <div class="oe_demo oe_picture oe_screenshot">
+ <a href="https://www.openerp.com/saas_master/demo?lang=en_US&module=website_mail">
+ <img src="">
+ </a>
+ <div class="oe_demo_footer oe_centeralign">Online Demo</div>
+ </div>
+ </div>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+
+ </p>
+ <div class="oe_centeralign oe_websiteonly">
+ <a href="http://www.openerp.com/start?app=website_sale" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> online store</a>
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container oe_dark">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Write Beautiful Blog Posts</h2>
+ <h3 class="oe_slogan">Get rid of old WYSIWYG editors</h3>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Drag & Drop well designed <em>'Building Blocks'</em> to
+ create beautifull blog posts that perfectly integrates images,
+ videos, call-to-actions, quotes, banners, etc.
+ </p><p>
+ With our unique <em>'edit inline'</em> approach, you don't need
+ to be a designer to create awsome, good-looking, content. Each blog
+ post will look like it's designed by a professional designer.
+ </p>
+ </div>
+ <div class="oe_span6">
+ <div class="oe_bg_img oe_centered">
+ <img class="oe_picture" src="">
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Automated Translation by Professionals</h2>
+ <h3 class="oe_slogan">Reach multi-languages audiences without Pain</h3>
+ <div class="oe_span6">
+ <div class="oe_bg_img">
+ <img class="oe_picture" src="">
+ </div>
+ </div>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Get your blog posts translated in multiple languages with no effort.
+ Our translation "on demand" feature allows you to benefit from
+ professional translators to translate all your changes
+ automatically. ($0.05 per word) Translated versions are updated
+ automatically once translated by professionals (around 32 hours).
+ </p>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container oe_dark">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Engage With Your Visitors</h2>
+ <h3 class="oe_slogan">Live chat with your visitors in one click</h3>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ The integrated live chat features allows you to start chatting in
+ real time with your visitors to get feedback on your recent posts
+ or get ideas to write new posts.
+ </p><p>
+ Engaging with your visitors is also a great way to convert visitors
+ into customers.
+ </p>
+ </div>
+ <div class="oe_span6">
+ <div class="oe_bg_img">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Build Visitors Loyalty</h2>
+ <h3 class="oe_slogan">Social Media Integration and Easy Following</h3>
+ <div class="oe_span6">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ The one click <em>follow</em> button will allow visitors
+ to receive your blog posts by email with no effort, without
+ having to register. Social media icons allows visitors to share
+ your best blog posts easily.
+ </p>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container oe_dark">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Google Analytics Integration</h2>
+ <h3 class="oe_slogan">Get visibility on the impact of each blog post</h3>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Get a clear visibility of your sales funnel. OpenERP's Google
+ Analytics trackers are configured by default to track all kind of
+ events related to shopping carts, call-to-actions, etc.
+ </p><p>
+ As OpenERP marketing tools (mass mailing, campaigns, etc) are also
+ linked with Google Analytics, you get a 360° view of your business.
+ </p>
+ </div>
+ <div class="oe_span6">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </div>
+</section>
+
+<section class="oe_container oe_dark">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Designer-Friendly Themes</h2>
+ <h3 class="oe_slogan">Designers love working on OpenERP</h3>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Themes are awesome and easy to design. You don't need to develop
+ to create new pages, themes or building blocks. We use a clean
+ HTML structure, a <a href="http://getbootstrap.com/">bootstrap</a>
+ CSS and our modularity allows to distribute your themes easily.
+ </p><p>
+ The building block approach allows the website to remain clean
+ after the end-users start creating new contents.
+ </p>
+ </div>
+ <div class="oe_span6">
+ <div class="oe_row_img oe_centered">
+ <img class="oe_picture" src="">
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container">
+ <div class="oe_row">
+ <h2 class="oe_slogan">SEO Optimized Blog Posts</h2>
+ <h3 class="oe_slogan">Attract new visitors with a good referencing</h3>
+ <div class="oe_span6">
+ <div class="oe_bg_img">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </div>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ SEO tools are ready to use, with no configuration required.
+ OpenERP suggests keywords for your titles according to Google
+ most searched terms, Google Analytics tracks interrests of your
+ visitors, sitemap are created automatically for Google
+ indexation, etc.
+ </p><p>
+ We even do structured content automatically to promote your
+ product and events efficiently in Google.
+ </p>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Easy Access Rights</h2>
+ <h3 class="oe_slogan">Adapt your publishing process to your own need</h3>
+ <div class="oe_span6">
+ <div class="oe_bg_img">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </div>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Not everyone requires the same access to your website.
+ Designers manage the layout of the site, editors approves content and
+ authors write that content. This lets you organize your publishing
+ process according to your needs.
+ </p><p>
+ Others access rights related to business objects (products, people,
+ events, etc) directly follows OpenERP's standard access right allowing
+ you to not have to configure them twice.
+ </p>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container oe_dark">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Built-in Comments</h2>
+ <h3 class="oe_slogan">Engage with your readers</h3>
+ <div class="oe_span6">
+ <p class='oe_mt32'>
+ Your blog is your home, and comments provide a space for your
+ friends and followers to engage with your content. OpenERP's
+ comment tools give you everything you need to be a forum for
+ discussion and to moderate that discussion.
+ </p>
+ </div>
+ <div class="oe_span6">
+ <div class="oe_bg_img oe_centered">
+ <img class="oe_picture" src="">
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="oe_container">
+ <div class="oe_row">
+ <h2 class="oe_slogan">Integrated With Others OpenERP Apps</h2>
+ <h3 class="oe_slogan">Get hundreds of open source apps for free</h3>
+ <div class="oe_span4">
+ <a href="/apps/website">
+ <h3>CMS</h3>
+ <div class="oe_row_img oe_centered">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </a>
+ <p>
+ Easily create awesome websites with no technical knowledge required.
+ </p>
+ </div>
+ <div class="oe_span4">
+ <a href="/apps/website_sale">
+ <h3>eCommerce</h3>
+ <div class="oe_row_img oe_centered">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </a>
+ <p>
+ Promote products, sell online, optimize visitors' shopping experiences.
+ </p>
+ </div>
+ <div class="oe_span4">
+ <a href="/apps/website_event">
+ <h3>Online Events</h3>
+ <div class="oe_row_img oe_centered">
+ <img class="oe_picture oe_screenshot" src="">
+ </div>
+ </a>
+ <p>
+ Schedule, organize, promote or sell events online; conferences,
+ webinars, trainings, etc.
+ </p>
+ </div>
+ <div class="oe_span12 oe_centered oe_muted">
+ <a href="/apps">
+ and hundreds more applications...
+ </a>
+ </div>
+ </div>
+</section>
+
--- /dev/null
+sass:
+ sass --compass --trace -t expanded blog.sass blog.css
--- /dev/null
+.css_website_mail .has-error {
+ border-color: red;
+}
+.css_website_mail .css_nav_month {
+ display: none;
+}
+.css_website_mail .css_nav_month:first-of-type {
+ display: block;
+}
--- /dev/null
+@import "compass/css3"
+
+.css_website_mail
+ .has-error
+ border-color: red
+ .css_nav_month
+ display: none
+ &:first-of-type
+ display: block
+
+/* ---- PUBLISH ---- */
+/ a[data-publish]
+/ text-decoration: none !important
+/ z-index: 2
+/ .label
+/ padding: 5px 8px
+/ .css_unpublish, .css_publish, .css_unpublished, .css_published
+/ display: none
+/ &[data-publish='off']
+/ .css_unpublished, &:hover .css_publish
+/ display: inline
+/ &:hover .css_unpublished
+/ display: none
+/ &[data-publish='on']
+/ .css_published, &:hover .css_unpublish
+/ display: inline
+/ &:hover .css_published
+/ display: none
+
+/ [data-publish='off']:not(a)
+/ >:not([data-publish])
+/ opacity: 0.5
+/ [data-publish]:not(a)
+/ position: relative
+/ overflow: visible
+/ >[data-publish]
+/ position: absolute
+/ right: -6px
+/ top: -10px
+/ display: none
+ /*&:hover > [data-publish]*/
+ /* display: block*/
--- /dev/null
+$(document).ready(function () {
+ $form = $('.js_nav_year a:first').on('click', function (e) {
+ e.preventDefault();
+ $(this).next("ul").toggle();
+ });
+
+ $form = $('.js_nav_month a:first').on('click', function (e) {
+ e.preventDefault();
+ var $ul = $(this).next("ul");
+ if (!$ul.find('li').length) {
+ $.post('/blog/nav', {'domain': $(this).data("domain")}, function (result) {
+ var result = JSON.parse(result);
+ var blog_id = +window.location.pathname.split("/").pop();
+ $(result).each(function () {
+ var $a = $('<a href="/blog/'+this.res_id+'/'+this.id+'"/>').text(this.subject);
+ var $li = $("<li/>").append($a);
+ if (blog_id == this.id)
+ $li.addClass("active");
+ if (!this.website_published)
+ $a.css("color", "red");
+ $ul.append($li);
+ });
+
+ });
+ } else {
+ $ul.toggle();
+ }
+ });
+
+ $form = $('.js_website_mail form#post');
+ $form.submit(function (e) {
+ e.preventDefault();
+ var error = $form.find("textarea").val().length < 3;
+ $form.find("textarea").toggleClass("has-error", error);
+ if (!error) {
+ $form.css("visibility", "hidden");
+ $.post(window.location.pathname + '/post', {'body': $form.find("textarea").val()}, function (url) {
+ window.location.href = url
+ });
+ }
+ });
+});
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_website_mail_config_settings" model="ir.ui.view">
+ <field name="name">website_mail settings</field>
+ <field name="model">website.config.settings</field>
+ <field name="inherit_id" ref="website.view_website_config_settings"/>
+ <field name="arch" type="xml">
+ <form position="inside">
+ <separator string="Blog"/>
+ <group>
+ <field name="group_website_mail_reply"/>
+ </group>
+ </form>
+ </field>
+ </record>
+ </data>
+</openerp>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+
+ <!-- Layout add nav and footer -->
+ <template id="header_footer_custom" inherit_id="website.layout">
+ <xpath expr="//body/header//ul[@id='top_menu']/li[last()]" position="before">
+ <li><a href="/blog/%(document_page.blog_category_1)d/">News</a></li>
+ </xpath>
+ <xpath expr="//body/footer//div[@name='info']/ul" position="inside">
+ <li><a href="/blog/%(document_page.blog_category_1)d/">News</a></li>
+ </xpath>
+ </template>
+
+ <!-- Follow button -->
+ <template id="blog_post_unfollow" name="Follow">
+ <form t-attf-action="/blog/#{object.category_id.id}/#{object.id}/unsubscribe" method="POST" class="form-inline" t-if="object.message_is_follower">
+ <input type="hidden" name="email" t-att-value="subscribe" style="display: none"/>
+ <button type="submit" class="btn btn-sm btn-default pull-right" name="unsubscribe">Unfollow</button>
+ </form>
+ </template>
+ <template id="blog_post_follow" name="Follow">
+ <form t-attf-action="/blog/#{object.category_id.id}/#{object.id}/subscribe" method="POST" class="form-inline" t-if="not object.message_is_follower">
+ <div class="col-lg-7">
+ <input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
+ </div>
+ <button type="submit" class="btn btn-sm btn-primary pull-right" name="subscribe">Follow</button>
+ </form>
+ </template>
+
+ <!-- Post -->
+ <template id="blog_post_short" name="Blog Post">
+ <div class="media-body">
+ <t t-call="website_blog.blog_post_follow"><t t-set="object" t-value="blog_post"/></t>
+ <t t-call="website_blog.blog_post_unfollow"><t t-set="object" t-value="blog_post"/></t>
+ <t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
+ <small class="text-muted">
+ <t t-field="blog_post.create_uid"/> on <t t-field="blog_post.create_date"/>
+ </small>
+ <h4 class="media-heading" ><a t-attf-href="/blog_post/#{blog_post.category_id.id}/#{blog_post.id}#comment" t-field="blog_post.name"></a></h4>
+ <div class="media">
+ <div t-field="blog_post.shortened_content"/>
+ <small class="pull-left muted text-right">
+ <a t-if="len(blog_post.message_ids) <= 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comment"><t t-esc="len(blog_post.message_ids)"/> Comment</a>
+ <a t-if="len(blog_post.message_ids) > 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comment"><t t-esc="len(blog_post.message_ids)"/> Comments</a>
+ </small>
+ </div>
+ </div>
+ </template>
+
+ <template id="blog_post" name="Blog Post">
+ <div class="media">
+ <div class="media-body">
+ <t t-call="website_blog.blog_post_follow"><t t-set="object" t-value="blog_post"/></t>
+ <t t-call="website_blog.blog_post_unfollow"><t t-set="object" t-value="blog_post"/></t>
+ <t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
+ <small class="text-muted">
+ <t t-field="blog_post.create_uid"/> on <t t-field="blog_post.create_date"/>
+ </small>
+ <h3 t-field="blog_post.name"/>
+ <div t-field="blog_post.content"/>
+ </div>
+ <ul class="media-list" id="comment">
+ <li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
+ <div class="media-body">
+ <t t-call="website.publish"><t t-set="object" t-value="message"/></t>
+ <t t-raw="message.body"/>
+ <small class="pull-right muted text-right">
+ <div t-field="message.author_id"/>
+ <div t-field="message.date"/>
+ </small>
+ </div>
+ </li>
+ </ul>
+ <form id="blog" t-attf-action="/blog/#{blog_post.category_id.id}/#{blog_post.id}/post#post"
+ method="POST" class="form-horizontal text-center"
+ groups="group_website_mail_reply">
+ <textarea rows="4" placeholder="Your comment" class="col-md-7 form-control"></textarea>
+ <button type="submit" class="btn btn-default">Post your comment</button>
+ </form>
+ </div>
+ </template>
+
+
+ <!-- Page -->
+ <template id="index" name="Blogs" page="True">
+ <t t-call="website.layout">
+ <t t-set="head">
+ <script type="text/javascript" src="/website_blog/static/src/js/blog.js"></script>
+ <link rel='stylesheet' href='/website_blog/static/src/css/blog.css'/>
+ <t t-raw="head or ''"/>
+ </t>
+ <t t-set="title">Blog</t>
+ <div id="wrap">
+ <div class="container mt48 js_website_mail">
+ <div class="row">
+ <div class="col-md-3" id="left_column">
+ <t t-if="category">
+ Latests posts on <t t-field="category.name"/>
+ <a t-if="editable" t-attf-href="/blog/new" class="btn btn-default">New Blog Post</a>
+ <div>
+ <form t-attf-action="/blog/#{category.id}/subscribe"
+ method="POST" class="form-inline"
+ t-if="not category.message_is_follower">
+ <div class="col-lg-7">
+ <input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
+ </div>
+ <button type="submit" class="btn btn-primary" name="subscribe">Follow</button>
+ </form>
+ <form t-attf-action="/blog/#{category.id}/unsubscribe"
+ method="POST" class="form-inline"
+ t-if="category.message_is_follower">
+ <input type="hidden" name="email" t-att-value="subscribe" style="display: none"/>
+ <button type="submit" class="btn btn-default" name="unsubscribe">Unfollow</button>
+ </form>
+ </div>
+ </t>
+ </div>
+ <div class="col-md-9" t-if="blog_post">
+ <t t-call="website_blog.blog_post"><t t-set="blog_post" t-value="blog_post"/></t>
+ </div>
+
+ <div class="col-md-8" t-if="not blog_post and blog_posts">
+ <ul class="media-list">
+ <li t-foreach="blog_posts" t-as="blog_post" data-publish="">
+ <t t-call="website_blog.blog_post_short"><t t-set="blog_post" t-value="blog_post"/></t>
+ </li>
+ </ul>
+ <div class="text-center">
+ <t t-call="website.pager"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </t>
+ </template>
+
+ <template id="blog_author" inherit_id="website_blog.index" inherit_option_id="website_blog.index" name="Author">
+<!-- <xpath expr="//div[@t-field='document_page.create_date']" position="before">
+ <div t-field="document_page.author_id"/>
+ </xpath>
+ <xpath expr="//div[@t-field='blog.create_date']" position="before">
+ <div t-field="blog.author_id"/>
+ </xpath> -->
+ </template>
+
+
+ <template id="blog_history" inherit_id="website_blog.index" inherit_option_id="website_blog.index" name="History">
+ <xpath expr="//div[@id='left_column']" position="inside">
+ <ul class="nav nav-pills nav-stacked">
+ <li class="nav-header"><a t-attf-href="/blog/#{category_id}/">Archives</a></li>
+ <!-- TODO: check qweb iteration -->
+ <li t-foreach="nav_list" t-as="year" class="js_nav_year">
+ <t t-set="year" t-value="nav_list[year]"/>
+ <a href="#"><t t-esc="year['name']"/> <small>(<t t-esc="year['create_date_count']"/>)</small></a>
+ <ul class="nav list-group css_nav_month nav-stacked nav-hierarchy">
+ <t t-foreach="year['months']">
+ <li class="js_nav_month"><a href="#" t-att-data-domain="__domain"><t t-esc="create_date"/> <small>(<t t-esc="create_date_count"/>)</small></a>
+ <ul class="nav list-group nav-stacked nav-hierarchy"/>
+ </li>
+ </t>
+ </ul>
+ </li>
+ </ul>
+ </xpath>
+ </template>
+ </data>
+</openerp>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data noupdate="1">
+
+ <record id="action_open_website" model="ir.actions.act_url">
+ <field name="name">Website Blogs</field>
+ <field name="target">self</field>
+ <field name="url">/blog</field>
+ </record>
+ <record id="base.open_menu" model="ir.actions.todo">
+ <field name="action_id" ref="action_open_website"/>
+ <field name="state">open</field>
+ </record>
+
+ </data>
+</openerp>
-import controllers
+# -*- 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 mail_message
-import res_config
\ No newline at end of file
+# -*- 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/>.
+#
+##############################################################################
+
{
- 'name': 'Blogs',
+ 'name': 'Website Mail',
'category': 'Website',
- 'summary': 'News, Blogs, Announces, Discussions',
- 'version': '1.0',
- 'description': """
-OpenERP Blog
-============
-
+ 'summary': 'Glue Module',
+ 'version': '0.1',
+ 'description': """Glue module holding mail improvements for website.
""",
'author': 'OpenERP SA',
'depends': ['website', 'mail'],
- 'data': [
- 'website_mail_data.xml',
- 'views/website_mail.xml',
- 'views/res_config.xml',
- 'security/website_mail.xml',
- ],
- 'qweb': ['static/src/xml/*.xml'],
+ 'data': [],
+ 'qweb': [],
'installable': True,
+ 'auto_install': True,
}
+++ /dev/null
-import main
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-from openerp import SUPERUSER_ID
-from openerp.addons.web import http
-from openerp.addons.web.http import request
-from openerp.addons.website import website
-import werkzeug
-from openerp.tools.translate import _
-from openerp.tools.safe_eval import safe_eval
-import simplejson
-
-_months = {1:_("January"), 2:_("February"), 3:_("March"), 4:_("April"), 5:_("May"), 6:_("June"), 7:_("July"), 8:_("August"), 9:_("September"), 10:_("October"), 11:_("November"), 12:_("December")}
-
-
-class website_mail(http.Controller):
-
- @website.route(['/blog/', '/blog/<int:mail_group_id>/', '/blog/<int:mail_group_id>/<int:blog_id>/',
- '/blog/page/<int:page>/', '/blog/<int:mail_group_id>/page/<int:page>/', '/blog/<int:mail_group_id>/<int:blog_id>/page/<int:page>/'], type='http', auth="public")
- def blog(self, mail_group_id=None, blog_id=None, page=0, **post):
- group_obj = request.registry['mail.group']
- message_obj = request.registry['mail.message']
- user_obj = request.registry['res.users']
-
- values = {
- 'blog_ids': None,
- 'blog_id': None,
- 'nav_list': dict(),
- 'mail_group_id': mail_group_id,
- 'subscribe': post.get('subscribe'),
- 'unable_editor': post.get('unable_editor')
- }
-
- if not request.context['is_public_user'] and mail_group_id:
- message_follower_ids = group_obj.read(request.cr, request.uid, [mail_group_id], ['message_follower_ids'], request.context)[0]['message_follower_ids']
- parent_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid, request.context).partner_id.id
- values['subscribe'] = parent_id in message_follower_ids
-
- domain = mail_group_id and [("res_id", "=", mail_group_id)] or []
-
- for group in message_obj.read_group(request.cr, request.uid, domain + group_obj.get_domain_public_blog(request.cr, request.uid), ['subject', 'date'], groupby="date", orderby="create_date asc", context=request.context):
- year = group['date'].split(" ")[1]
- if not values['nav_list'].get(year):
- values['nav_list'][year] = {'name': year, 'date_count': 0, 'months': []}
- values['nav_list'][year]['date_count'] += group['date_count']
- values['nav_list'][year]['months'].append(group)
-
- if blog_id:
- values['blog_id'] = message_obj.browse(request.cr, request.uid, blog_id, request.context)
- else:
- step = 20
- message_count = len(group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc", context=request.context))
- pager = request.website.pager(url="/blog/%s/" % mail_group_id, total=message_count, page=page, step=step, scope=7)
- message_ids = group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc", limit=step, offset=pager['offset'], context=request.context)
- values['pager'] = pager
- values['blog_ids'] = message_obj.browse(request.cr, request.uid, message_ids, request.context)
-
- return request.website.render("website_mail.index", values)
-
- @website.route(['/blog/nav'], type='http', auth="public")
- def nav(self, **post):
- comment_ids = request.registry['mail.group'].get_public_message_ids(request.cr, request.uid, domain=safe_eval(post.get('domain')), order="create_date asc", limit=None, context=request.context)
- return simplejson.dumps(request.registry['mail.message'].read(request.cr, request.uid, comment_ids, ['website_published', 'subject', 'res_id'], request.context))
-
- @website.route(['/blog/<int:mail_group_id>/<int:blog_id>/post'], type='http', auth="public")
- def blog_post(self, mail_group_id=None, blog_id=None, **post):
- url = request.httprequest.host_url
- if post.get('body'):
- request.session.body = post.get('body')
- if request.context['is_public_user']:
- return '%s/admin#action=redirect&url=%s/blog/%s/%s/post' % (url, url, mail_group_id, blog_id)
-
- if 'body' in request.session and request.session.body:
- request.registry['mail.group'].message_post(request.cr, request.uid, mail_group_id,
- body=request.session.body,
- parent_id=blog_id,
- website_published=blog_id and True or False,
- type='comment',
- subtype='mt_comment',
- context=dict(request.context, mail_create_nosubcribe=True))
- request.session.body = False
-
- if post.get('body'):
- return '%s/blog/%s/%s/' % (url, mail_group_id, blog_id)
- else:
- return werkzeug.utils.redirect("/blog/%s/%s/" % (mail_group_id, blog_id))
-
- @website.route(['/blog/<int:mail_group_id>/new'], type='http', auth="public")
- def new_blog_post(self, mail_group_id=None, **post):
- blog_id = request.registry['mail.group'].message_post(request.cr, request.uid, mail_group_id,
- body="",
- subject=_("Blog title"),
- website_published=False,
- type='comment',
- subtype='mt_comment',
- context=dict(request.context, mail_create_nosubscribe=True))
- return werkzeug.utils.redirect("/blog/%s/%s/?unable_editor=1" % (mail_group_id, blog_id))
-
- @website.route(['/blog/<int:mail_group_id>/subscribe', '/blog/<int:mail_group_id>/<int:blog_id>/subscribe'], type='http', auth="public")
- def subscribe(self, mail_group_id=None, blog_id=None, **post):
- partner_obj = request.registry['res.partner']
- group_obj = request.registry['mail.group']
- user_obj = request.registry['res.users']
-
- if mail_group_id and 'subscribe' in post and (post.get('email') or not request.context['is_public_user']):
- if request.context['is_public_user']:
- partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))], context=request.context)
- if not partner_ids:
- partner_ids = [partner_obj.create(request.cr, SUPERUSER_ID, {"email": post.get('email'), "name": "Subscribe: %s" % post.get('email')}, request.context)]
- else:
- partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
- group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read', request.context)
- group_obj.message_subscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids, request.context)
-
- return self.blog(mail_group_id=mail_group_id, blog_id=blog_id, subscribe=post.get('email'))
-
- @website.route(['/blog/<int:mail_group_id>/unsubscribe', '/blog/<int:mail_group_id>/<int:blog_id>/unsubscribe'], type='http', auth="public")
- def unsubscribe(self, mail_group_id=None, blog_id=None, **post):
- partner_obj = request.registry['res.partner']
- group_obj = request.registry['mail.group']
- user_obj = request.registry['res.users']
-
- if mail_group_id and 'unsubscribe' in post and (post.get('email') or not request.context['is_public_user']):
- if request.context['is_public_user']:
- partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))], context=request.context)
- else:
- partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
- group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read', request.context)
- group_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids, request.context)
-
- return self.blog(mail_group_id=mail_group_id, blog_id=blog_id, subscribe=None)
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+# 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
from openerp.osv import osv, fields
-class mail_message(osv.osv):
- _inherit = "mail.message"
+class MailMessage(osv.Model):
+ _inherit = 'mail.message'
+
_columns = {
- 'website_published': fields.boolean('Publish', help="Publish on the website as a blog"),
+ 'website_published': fields.boolean(
+ 'Publish', help="Publish on the website as a blog"
+ ),
}
-
-
-class mail_group(osv.Model):
- _inherit = 'mail.group'
-
- def get_domain_public_blog(self, cr, uid, context=None):
- mail_group_ids = self.search(cr, uid, [('public', '=', 'public')], context=context)
- return [ ("type", "in", ['comment']),
- ("parent_id", "=", False),
- ("model", "=", 'mail.group'), ("res_id", "in", mail_group_ids)]
-
- def get_public_message_ids(self, cr, uid, domain=None, order="create_date desc", limit=None, offset=0, context=None):
- domain += self.get_domain_public_blog(cr, uid, context=context)
- return self.pool.get('mail.message').search(cr, uid, domain, order=order, limit=limit, offset=offset, context=context)
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-from openerp.osv import fields, osv
-
-
-class website_config_settings(osv.osv_memory):
- _inherit = 'website.config.settings'
- _columns = {
- 'group_website_mail_reply': fields.boolean('Visitors can reply on blogs',
- implied_group='website_mail.group_website_mail_reply'),
- }
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-<data>
- <record id="group_website_mail_reply" model="res.groups">
- <field name="name">Visitors can reply on blogs</field>
- <field name="category_id" ref="base.module_category_hidden"/>
- </record>
-
- <record id="website_mail_public" model="ir.rule">
- <field name="name">website_mail: Public message</field>
- <field name="model_id" ref="model_mail_message"/>
- <field name="domain_force">[('website_published', '=', True), ("model", "=", 'mail.group')]</field>
- <field name="groups" eval="[(4, ref('base.group_public'))]"/>
- <field name="perm_read" eval="True"/>
- <field name="perm_write" eval="False"/>
- <field name="perm_create" eval="False"/>
- <field name="perm_unlink" eval="False"/>
- </record>
-</data>
-</openerp>
+++ /dev/null
-<section class="oe_container">
- <div class="oe_row oe_spaced">
- <div class="oe_span12">
- <h2 class="oe_slogan">Awesome Open Source Blogging Systems</h2>
- <h3 class="oe_slogan">Express yourself with beautiful blogs</h3>
- </div>
- <div class="oe_span6">
- <div class="oe_demo oe_picture oe_screenshot">
- <a href="https://www.openerp.com/saas_master/demo?lang=en_US&module=website_mail">
- <img src="">
- </a>
- <div class="oe_demo_footer oe_centeralign">Online Demo</div>
- </div>
- </div>
- <div class="oe_span6">
- <p class='oe_mt32'>
-
- </p>
- <div class="oe_centeralign oe_websiteonly">
- <a href="http://www.openerp.com/start?app=website_sale" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> online store</a>
- </div>
- </div>
- </div>
-</section>
-
-<section class="oe_container oe_dark">
- <div class="oe_row">
- <h2 class="oe_slogan">Write Beautiful Blog Posts</h2>
- <h3 class="oe_slogan">Get rid of old WYSIWYG editors</h3>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Drag & Drop well designed <em>'Building Blocks'</em> to
- create beautifull blog posts that perfectly integrates images,
- videos, call-to-actions, quotes, banners, etc.
- </p><p>
- With our unique <em>'edit inline'</em> approach, you don't need
- to be a designer to create awsome, good-looking, content. Each blog
- post will look like it's designed by a professional designer.
- </p>
- </div>
- <div class="oe_span6">
- <div class="oe_bg_img oe_centered">
- <img class="oe_picture" src="">
- </div>
- </div>
- </div>
-</section>
-
-<section class="oe_container">
- <div class="oe_row">
- <h2 class="oe_slogan">Automated Translation by Professionals</h2>
- <h3 class="oe_slogan">Reach multi-languages audiences without Pain</h3>
- <div class="oe_span6">
- <div class="oe_bg_img">
- <img class="oe_picture" src="">
- </div>
- </div>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Get your blog posts translated in multiple languages with no effort.
- Our translation "on demand" feature allows you to benefit from
- professional translators to translate all your changes
- automatically. ($0.05 per word) Translated versions are updated
- automatically once translated by professionals (around 32 hours).
- </p>
- </div>
- </div>
-</section>
-
-<section class="oe_container oe_dark">
- <div class="oe_row">
- <h2 class="oe_slogan">Engage With Your Visitors</h2>
- <h3 class="oe_slogan">Live chat with your visitors in one click</h3>
- <div class="oe_span6">
- <p class='oe_mt32'>
- The integrated live chat features allows you to start chatting in
- real time with your visitors to get feedback on your recent posts
- or get ideas to write new posts.
- </p><p>
- Engaging with your visitors is also a great way to convert visitors
- into customers.
- </p>
- </div>
- <div class="oe_span6">
- <div class="oe_bg_img">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </div>
- </div>
-</section>
-
-<section class="oe_container">
- <div class="oe_row">
- <h2 class="oe_slogan">Build Visitors Loyalty</h2>
- <h3 class="oe_slogan">Social Media Integration and Easy Following</h3>
- <div class="oe_span6">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- <div class="oe_span6">
- <p class='oe_mt32'>
- The one click <em>follow</em> button will allow visitors
- to receive your blog posts by email with no effort, without
- having to register. Social media icons allows visitors to share
- your best blog posts easily.
- </p>
- </div>
- </div>
-</section>
-
-<section class="oe_container oe_dark">
- <div class="oe_row">
- <h2 class="oe_slogan">Google Analytics Integration</h2>
- <h3 class="oe_slogan">Get visibility on the impact of each blog post</h3>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Get a clear visibility of your sales funnel. OpenERP's Google
- Analytics trackers are configured by default to track all kind of
- events related to shopping carts, call-to-actions, etc.
- </p><p>
- As OpenERP marketing tools (mass mailing, campaigns, etc) are also
- linked with Google Analytics, you get a 360° view of your business.
- </p>
- </div>
- <div class="oe_span6">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </div>
-</section>
-
-<section class="oe_container oe_dark">
- <div class="oe_row">
- <h2 class="oe_slogan">Designer-Friendly Themes</h2>
- <h3 class="oe_slogan">Designers love working on OpenERP</h3>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Themes are awesome and easy to design. You don't need to develop
- to create new pages, themes or building blocks. We use a clean
- HTML structure, a <a href="http://getbootstrap.com/">bootstrap</a>
- CSS and our modularity allows to distribute your themes easily.
- </p><p>
- The building block approach allows the website to remain clean
- after the end-users start creating new contents.
- </p>
- </div>
- <div class="oe_span6">
- <div class="oe_row_img oe_centered">
- <img class="oe_picture" src="">
- </div>
- </div>
- </div>
-</section>
-
-<section class="oe_container">
- <div class="oe_row">
- <h2 class="oe_slogan">SEO Optimized Blog Posts</h2>
- <h3 class="oe_slogan">Attract new visitors with a good referencing</h3>
- <div class="oe_span6">
- <div class="oe_bg_img">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </div>
- <div class="oe_span6">
- <p class='oe_mt32'>
- SEO tools are ready to use, with no configuration required.
- OpenERP suggests keywords for your titles according to Google
- most searched terms, Google Analytics tracks interrests of your
- visitors, sitemap are created automatically for Google
- indexation, etc.
- </p><p>
- We even do structured content automatically to promote your
- product and events efficiently in Google.
- </p>
- </div>
- </div>
-</section>
-
-<section class="oe_container">
- <div class="oe_row">
- <h2 class="oe_slogan">Easy Access Rights</h2>
- <h3 class="oe_slogan">Adapt your publishing process to your own need</h3>
- <div class="oe_span6">
- <div class="oe_bg_img">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </div>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Not everyone requires the same access to your website.
- Designers manage the layout of the site, editors approves content and
- authors write that content. This lets you organize your publishing
- process according to your needs.
- </p><p>
- Others access rights related to business objects (products, people,
- events, etc) directly follows OpenERP's standard access right allowing
- you to not have to configure them twice.
- </p>
- </div>
- </div>
-</section>
-
-<section class="oe_container oe_dark">
- <div class="oe_row">
- <h2 class="oe_slogan">Built-in Comments</h2>
- <h3 class="oe_slogan">Engage with your readers</h3>
- <div class="oe_span6">
- <p class='oe_mt32'>
- Your blog is your home, and comments provide a space for your
- friends and followers to engage with your content. OpenERP's
- comment tools give you everything you need to be a forum for
- discussion and to moderate that discussion.
- </p>
- </div>
- <div class="oe_span6">
- <div class="oe_bg_img oe_centered">
- <img class="oe_picture" src="">
- </div>
- </div>
- </div>
-</section>
-
-<section class="oe_container">
- <div class="oe_row">
- <h2 class="oe_slogan">Integrated With Others OpenERP Apps</h2>
- <h3 class="oe_slogan">Get hundreds of open source apps for free</h3>
- <div class="oe_span4">
- <a href="/apps/website">
- <h3>CMS</h3>
- <div class="oe_row_img oe_centered">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </a>
- <p>
- Easily create awesome websites with no technical knowledge required.
- </p>
- </div>
- <div class="oe_span4">
- <a href="/apps/website_sale">
- <h3>eCommerce</h3>
- <div class="oe_row_img oe_centered">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </a>
- <p>
- Promote products, sell online, optimize visitors' shopping experiences.
- </p>
- </div>
- <div class="oe_span4">
- <a href="/apps/website_event">
- <h3>Online Events</h3>
- <div class="oe_row_img oe_centered">
- <img class="oe_picture oe_screenshot" src="">
- </div>
- </a>
- <p>
- Schedule, organize, promote or sell events online; conferences,
- webinars, trainings, etc.
- </p>
- </div>
- <div class="oe_span12 oe_centered oe_muted">
- <a href="/apps">
- and hundreds more applications...
- </a>
- </div>
- </div>
-</section>
-
+++ /dev/null
-sass:
- sass --compass --trace -t expanded blog.sass blog.css
+++ /dev/null
-.css_website_mail .has-error {
- border-color: red;
-}
-.css_website_mail .css_nav_month {
- display: none;
-}
-.css_website_mail .css_nav_month:first-of-type {
- display: block;
-}
+++ /dev/null
-@import "compass/css3"
-
-.css_website_mail
- .has-error
- border-color: red
- .css_nav_month
- display: none
- &:first-of-type
- display: block
\ No newline at end of file
+++ /dev/null
-$(document).ready(function () {
- $form = $('.js_nav_year a:first').on('click', function (e) {
- e.preventDefault();
- $(this).next("ul").toggle();
- });
-
- $form = $('.js_nav_month a:first').on('click', function (e) {
- e.preventDefault();
- var $ul = $(this).next("ul");
- if (!$ul.find('li').length) {
- $.post('/blog/nav', {'domain': $(this).data("domain")}, function (result) {
- var result = JSON.parse(result);
- var blog_id = +window.location.pathname.split("/").pop();
- $(result).each(function () {
- var $a = $('<a href="/blog/'+this.res_id+'/'+this.id+'"/>').text(this.subject);
- var $li = $("<li/>").append($a);
- if (blog_id == this.id)
- $li.addClass("active");
- if (!this.website_published)
- $a.css("color", "red");
- $ul.append($li);
- });
-
- });
- } else {
- $ul.toggle();
- }
- });
-
- $form = $('.js_website_mail form#post');
- $form.submit(function (e) {
- e.preventDefault();
- var error = $form.find("textarea").val().length < 3;
- $form.find("textarea").toggleClass("has-error", error);
- if (!error) {
- $form.css("visibility", "hidden");
- $.post(window.location.pathname + '/post', {'body': $form.find("textarea").val()}, function (url) {
- window.location.href = url
- });
- }
- });
-});
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
- <data>
- <record id="view_website_mail_config_settings" model="ir.ui.view">
- <field name="name">website_mail settings</field>
- <field name="model">website.config.settings</field>
- <field name="inherit_id" ref="website.view_website_config_settings"/>
- <field name="arch" type="xml">
- <form position="inside">
- <separator string="Blog"/>
- <group>
- <field name="group_website_mail_reply"/>
- </group>
- </form>
- </field>
- </record>
- </data>
-</openerp>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-<data>
-
- <!-- Mail group for the company's news -->
-
- <record id="website_mail_blog" model="mail.group">
- <field name="name">News</field>
- <field name="public">public</field>
- <field name="description">News displayed on your website</field>
- </record>
-
- <!-- Layout add nav and footer -->
-
- <template id="header_footer_custom" inherit_id="website.layout">
- <xpath expr="//body/header//ul[@id='top_menu']/li[last()]" position="before">
- <li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
- </xpath>
- <xpath expr="//body/footer//div[@name='info']/ul" position="inside">
- <li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
- </xpath>
- </template>
-
- <!-- Page -->
-
- <template id="index" name="Blogs" page="True">
- <t t-call="website.layout">
- <t t-set="head">
- <script type="text/javascript" src="/website_mail/static/src/js/blog.js"></script>
- <link rel='stylesheet' href='/website_mail/static/src/css/blog.css'/>
- <t t-raw="head or ''"/>
- </t>
- <t t-set="title">Blog</t>
- <div id="wrap">
- <div class="container mt48 js_website_mail">
- <div class="row">
- <div class="col-md-3" id="left_column">
- <a t-if="mail_group_id and editable" t-attf-href="/blog/#{ mail_group_id }/new" class="btn btn-default">New Blog Post</a>
- <div>
- <form action="./subscribe" method="POST" class="form-inline" t-if="not subscribe">
- <div class="col-lg-7">
- <input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
- </div>
- <button type="submit" class="btn btn-primary" name="subscribe">Subscribe</button>
- </form>
- <form action="./unsubscribe" method="POST" class="form-inline" t-if="subscribe">
- <input type="hidden" name="email" t-att-value="subscribe" style="display: none"/>
- <button type="submit" class="btn btn-default" name="unsubscribe">Unsubscribe</button>
- </form>
- </div>
- </div>
- <div class="col-md-9" t-if="blog_id">
- <div class="media">
- <div class="media-body">
- <t t-call="website.publish"><t t-set="object" t-value="blog_id"/></t>
- <h3 t-field="blog_id.subject"/>
- <div t-field="blog_id.body"/>
- <small class="pull-right muted text-right">
- <div t-field="blog_id.date"/>
- </small>
- </div>
- <ul class="media-list" id="comment" t-if="not unable_editor">
- <li t-foreach="blog_id.child_ids" t-as="comment" class="media">
- <div class="media-body">
- <t t-call="website.publish"><t t-set="object" t-value="comment"/></t>
- <t t-raw="comment.body"/>
- <small class="pull-right muted text-right">
- <div t-field="comment.author_id"/>
- <div t-field="comment.date"/>
- </small>
- </div>
- </li>
- </ul>
- <form id="post" t-if="not unable_editor" t-attf-action="/blog/#{ blog_id.res_id }/#{ blog_id.id }/post#post" method="POST" class="form-horizontal text-center" groups="group_website_mail_reply">
- <textarea rows="4" placeholder="Your comment" class="col-md-7 form-control"></textarea>
- <button type="submit" class="btn btn-default">Post your comment</button>
- </form>
- </div>
- </div>
-
- <div class="col-md-8" t-if="not blog_id and blog_ids">
- <ul class="media-list">
- <li t-foreach="blog_ids" t-as="blog" class="media well thumbnail" data-publish="">
- <div class="media-body">
- <t t-call="website.publish"><t t-set="object" t-value="blog"/></t>
- <h4 class="media-heading" ><a t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment" t-field="blog.subject"></a></h4>
- <div class="media">
- <div t-field="blog.body"/>
- <small class="pull-left muted text-right" t-if="len(blog.child_ids)">
- <a t-if="len(blog.child_ids) <= 1" t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment"><t t-esc="len(blog.child_ids)"/> Comment</a>
- <a t-if="len(blog.child_ids) > 1" t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment"><t t-esc="len(blog.child_ids)"/> Comments</a>
- </small>
- <small class="pull-right muted text-right">
- <div t-field="blog.date"/>
- </small>
- </div>
- </div>
- </li>
- </ul>
- <div class="text-center">
- <t t-call="website.pager" />
- </div>
- </div>
- </div>
- </div>
- </div>
- </t>
- </template>
-
- <template id="blog_author" inherit_id="website_mail.index" inherit_option_id="website_mail.index" name="Author">
- <xpath expr="//div[@t-field='blog_id.date']" position="before">
- <div t-field="blog_id.author_id"/>
- </xpath>
- <xpath expr="//div[@t-field='blog.date']" position="before">
- <div t-field="blog.author_id"/>
- </xpath>
- </template>
-
- <template id="blog_history" inherit_id="website_mail.index" inherit_option_id="website_mail.index" name="History">
- <xpath expr="//div[@id='left_column']" position="inside">
- <ul class="nav nav-pills nav-stacked">
- <li class="nav-header"><a t-attf-href="/blog/#{ mail_group_id }/">BLOG ARCHIVE</a></li>
- <!-- TODO: check qweb iteration -->
- <li t-foreach="nav_list" t-as="year" class="js_nav_year">
- <t t-set="year" t-value="nav_list[year]"/>
- <a href="#"><t t-esc="year['name']"/> <small>(<t t-esc="year['date_count']"/>)</small></a>
- <ul class="nav list-group css_nav_month nav-stacked nav-hierarchy">
- <t t-foreach="year['months']">
- <li class="js_nav_month"><a href="#" t-att-data-domain="__domain"><t t-esc="date"/> <small>(<t t-esc="date_count"/>)</small></a>
- <ul class="nav list-group nav-stacked nav-hierarchy"/>
- </li>
- </t>
- </ul>
- </li>
- </ul>
- </xpath>
- </template>
- </data>
-</openerp>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
- <data noupdate="1">
-
- <record id="action_open_website" model="ir.actions.act_url">
- <field name="name">Website Blogs</field>
- <field name="target">self</field>
- <field name="url">/blog</field>
- </record>
- <record id="base.open_menu" model="ir.actions.todo">
- <field name="action_id" ref="action_open_website"/>
- <field name="state">open</field>
- </record>
-
- </data>
-</openerp>