Merge branch 'master' into master-forum-extension-tpa
authorFabien Pinckaers <fp@openerp.com>
Sun, 5 Oct 2014 13:35:49 +0000 (15:35 +0200)
committerFabien Pinckaers <fp@openerp.com>
Sun, 5 Oct 2014 13:35:49 +0000 (15:35 +0200)
addons/website_forum/controllers/main.py
addons/website_forum/data/forum_data.xml
addons/website_forum/data/forum_demo.xml
addons/website_forum/models/forum.py
addons/website_forum/static/src/js/website_forum.js
addons/website_forum/views/forum.xml
addons/website_forum/views/website_forum.xml

index ea68250..68fae56 100644 (file)
@@ -5,6 +5,8 @@ import werkzeug.urls
 import werkzeug.wrappers
 import re
 import simplejson
+import lxml
+from urllib2 import urlopen
 
 from openerp import tools
 from openerp import SUPERUSER_ID
@@ -40,6 +42,7 @@ class WebsiteForum(http.Controller):
                   'notifications': self._get_notifications(),
                   'header': kwargs.get('header', dict()),
                   'searches': kwargs.get('searches', dict()),
+                  'no_introduction_message': request.httprequest.cookies.get('no_introduction_message', False),
                   }
         if forum:
             values['forum'] = forum
@@ -76,7 +79,7 @@ class WebsiteForum(http.Controller):
                  '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions''',
                  '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions/page/<int:page>''',
                  ], type='http', auth="public", website=True)
-    def questions(self, forum, tag=None, page=1, filters='all', sorting='date', search='', **post):
+    def questions(self, forum, tag=None, page=1, filters='all', sorting='relevancy', search='', post_type='all', **post):
         cr, uid, context = request.cr, request.uid, request.context
         Post = request.registry['forum.post']
         user = request.registry['res.users'].browse(cr, uid, uid, context=context)
@@ -93,15 +96,24 @@ class WebsiteForum(http.Controller):
         else:
             filters = 'all'
 
+        if post_type == 'link':
+            domain += [('type', '=', 'link')]
+        elif post_type == 'question':
+            domain += [('type', '=', 'question')]
+        elif post_type == 'discussion':
+            domain += [('type', '=', 'discussion')]
+
         if sorting == 'answered':
             order = 'child_count desc'
         elif sorting == 'vote':
             order = 'vote_count desc'
         elif sorting == 'date':
             order = 'write_date desc'
-        else:
-            sorting = 'creation'
+        elif sorting == 'creation':
             order = 'create_date desc'
+        else:
+            sorting == 'relevancy'
+            order = 'relevancy'
 
         question_count = Post.search(cr, uid, domain, count=True, context=context)
         if tag:
@@ -133,6 +145,7 @@ class WebsiteForum(http.Controller):
             'filters': filters,
             'sorting': sorting,
             'search': search,
+            'post_type': post_type,
         })
         return request.website.render("website_forum.forum_index", values)
 
@@ -163,35 +176,10 @@ class WebsiteForum(http.Controller):
     # Questions
     # --------------------------------------------------
 
-    @http.route(['/forum/<model("forum.forum"):forum>/ask'], type='http', auth="public", website=True)
-    def question_ask(self, forum, **post):
-        if not request.session.uid:
-            return login_redirect()
-        values = self._prepare_forum_values(forum=forum, searches={},  header={'ask_hide': True})
-        return request.website.render("website_forum.ask_question", values)
-
-    @http.route('/forum/<model("forum.forum"):forum>/question/new', type='http', auth="user", methods=['POST'], website=True)
-    def question_create(self, forum, **post):
-        cr, uid, context = request.cr, request.uid, request.context
-        Tag = request.registry['forum.tag']
-        question_tag_ids = []
-        if post.get('question_tags').strip('[]'):
-            tags = post.get('question_tags').strip('[]').replace('"', '').split(",")
-            for tag in tags:
-                tag_ids = Tag.search(cr, uid, [('name', '=', tag)], context=context)
-                if tag_ids:
-                    question_tag_ids.append((4, tag_ids[0]))
-                else:
-                    question_tag_ids.append((0, 0, {'name': tag, 'forum_id': forum.id}))
-
-        new_question_id = request.registry['forum.post'].create(
-            request.cr, request.uid, {
-                'forum_id': forum.id,
-                'name': post.get('question_name'),
-                'content': post.get('content'),
-                'tag_ids': question_tag_ids,
-            }, context=context)
-        return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), new_question_id))
+    @http.route('/forum/get_url_title', type='json', auth="user", methods=['POST'], website=True)
+    def get_url_title(self, **kwargs):
+        arch = lxml.html.parse(urlopen(kwargs.get('url')))
+        return arch.find(".//title").text
 
     @http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0]),('parent_id','=',False)]"):question>'''], type='http', auth="public", website=True)
     def question(self, forum, question, **post):
@@ -241,7 +229,7 @@ class WebsiteForum(http.Controller):
             'forum': forum,
             'reasons': reasons,
         })
-        return request.website.render("website_forum.close_question", values)
+        return request.website.render("website_forum.close_post", values)
 
     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/edit_answer', type='http', auth="user", website=True)
     def question_edit_answer(self, forum, question, **kwargs):
@@ -274,21 +262,45 @@ class WebsiteForum(http.Controller):
     # Post
     # --------------------------------------------------
 
-    @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/new', type='http', auth="public", methods=['POST'], website=True)
-    def post_new(self, forum, post, **kwargs):
+    @http.route(['/forum/<model("forum.forum"):forum>/<post_type>'], type='http', auth="public", website=True)
+    def forum_post(self, forum, post_type, **post):
         if not request.session.uid:
             return login_redirect()
         cr, uid, context = request.cr, request.uid, request.context
         user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
         if not user.email or not tools.single_email_re.match(user.email):
             return werkzeug.utils.redirect("/forum/%s/user/%s/edit?email_required=1" % (slug(forum), uid))
-        request.registry['forum.post'].create(
-            request.cr, request.uid, {
+        values = self._prepare_forum_values(forum=forum, searches={},  header={'ask_hide': True})
+        return request.website.render("website_forum.%s" % post_type, values)
+
+    @http.route(['/forum/<model("forum.forum"):forum>/<post_type>/new',
+                 '/forum/<model("forum.forum"):forum>/<model("forum.post"):post_parent>/reply']
+                , type='http', auth="public", methods=['POST'], website=True)
+    def post_create(self, forum, post_parent='', post_type='', **post):
+        cr, uid, context = request.cr, request.uid, request.context
+        if not request.session.uid:
+            return login_redirect()
+
+        post_tag_ids = []
+        Tag = request.registry['forum.tag']
+        if post.get('post_tags', False) and post.get('post_tags').strip('[]'):
+            tags = post.get('post_tags').strip('[]').replace('"', '').split(",")
+            for tag in tags:
+                tag_ids = Tag.search(cr, uid, [('name', '=', tag)], context=context)
+                if tag_ids:
+                    post_tag_ids.append((4, tag_ids[0]))
+                else:
+                    post_tag_ids.append((0, 0, {'name': tag, 'forum_id': forum.id}))
+
+        new_question_id = request.registry['forum.post'].create(cr, uid, {
                 'forum_id': forum.id,
-                'parent_id': post.id,
-                'content': kwargs.get('content'),
-            }, context=request.context)
-        return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(post)))
+                'name': post.get('post_name', ''),
+                'content': post.get('content'),
+                'parent_id': post_parent and post_parent.id or False,
+                'tag_ids': post_tag_ids,
+                'type': post_parent and post_parent.type or post_type,
+            }, context=context)
+        return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), post_parent and slug(post_parent) or new_question_id))
 
     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment', type='http', auth="public", methods=['POST'], website=True)
     def post_comment(self, forum, post, **kwargs):
@@ -344,20 +356,20 @@ class WebsiteForum(http.Controller):
     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/save', type='http', auth="user", methods=['POST'], website=True)
     def post_save(self, forum, post, **kwargs):
         cr, uid, context = request.cr, request.uid, request.context
-        question_tags = []
-        if kwargs.get('question_tag') and kwargs.get('question_tag').strip('[]'):
+        post_tags = []
+        if kwargs.get('post_tag') and kwargs.get('post_tag').strip('[]'):
             Tag = request.registry['forum.tag']
-            tags = kwargs.get('question_tag').strip('[]').replace('"', '').split(",")
+            tags = kwargs.get('post_tag').strip('[]').replace('"', '').split(",")
             for tag in tags:
                 tag_ids = Tag.search(cr, uid, [('name', '=', tag)], context=context)
                 if tag_ids:
-                    question_tags += tag_ids
+                    post_tags += tag_ids
                 else:
                     new_tag = Tag.create(cr, uid, {'name': tag, 'forum_id': forum.id}, context=context)
-                    question_tags.append(new_tag)
+                    post_tags.append(new_tag)
         vals = {
-            'tag_ids': [(6, 0, question_tags)],
-            'name': kwargs.get('question_name'),
+            'tag_ids': [(6, 0, post_tags)],
+            'name': kwargs.get('post_name'),
             'content': kwargs.get('content'),
         }
         request.registry['forum.post'].write(cr, uid, [post.id], vals, context=context)
index 61bb7ee..1b08846 100644 (file)
@@ -76,7 +76,7 @@
 
         <!-- Reasons for closing Post -->
         <record id="reason_1" model="forum.post.reason">
-            <field name="name">duplicate question</field>
+            <field name="name">duplicate post</field>
         </record>
         <record id="reason_2" model="forum.post.reason">
             <field name="name">off-topic or not relevant</field>
@@ -85,7 +85,7 @@
             <field name="name">too subjective and argumentative</field>
         </record>
         <record id="reason_4" model="forum.post.reason">
-            <field name="name">not a real question</field>
+            <field name="name">not a real post</field>
         </record>
         <record id="reason_6" model="forum.post.reason">
             <field name="name">not relevant or out dated</field>
index 1223c95..a5a5540 100644 (file)
             <field name="name">ecommerce</field>
             <field name="forum_id" ref="website_forum.forum_help"/>
         </record>
+        <record id="tags_3" model="forum.tag">
+            <field name="name">Development</field>
+            <field name="forum_id" ref="website_forum.forum_help"/>
+        </record>
 
         <!-- Questions -->
         <record id="question_0" model="forum.post">
             <field name="name">How to configure alerts for employee contract expiration</field>
             <field name="forum_id" ref="website_forum.forum_help"/>
+            <field name="type">question</field>
             <field name="views">3</field>
             <field name="tag_ids" eval="[(4,ref('website_forum.tags_0')), (4,ref('website_forum.tags_1'))]"/>
         </record>
@@ -32,6 +37,7 @@
 Can I use Odoo as a replacement CMS of Wordpress + eCommerce plugin?
 
 In simple words does Odoo became CMS+ERP platform?</p>]]></field>
+            <field name="type">question</field>
             <field name="tag_ids" eval="[(4,ref('website_forum.tags_2'))]"/>
         </record>
 
@@ -46,6 +52,7 @@ In simple words does Odoo became CMS+ERP platform?</p>]]></field>
     <li>Step 4. To test, set a contract to expire tomorrow under one of your fleets vehicles. Then Save it.</li>
     <li>Step 5. Go to Scheduled Actions.. Set interval number to 1. Interval Unit to Minutes. Then Set the Next Execution date to 2 minutes from now. If your SMTP is configured correctly you will start to get a mail every minute with the reminder.</li></ul>]]></field>
             <field name="parent_id" ref="question_0" />
+            <field name="type">question</field>
         </record>
         <record id="answer_1" model="forum.post">
             <field name="forum_id" ref="website_forum.forum_help"/>
@@ -53,6 +60,47 @@ In simple words does Odoo became CMS+ERP platform?</p>]]></field>
                             The CMS editor in Odoo web is nice but I prefer Drupal for customization and there is a Drupal module for Odoo. I think WP is better than Odoo web too.
             </field>
             <field name="parent_id" ref="question_1"/>
+            <field name="type">question</field>
+        </record>
+
+        <!-- Article-->
+        <record id="article_0" model="forum.post">
+            <field name="name">https://www.odoo.com/forum/how-to/developers-13/the-different-openerp-model-inheritance-mechanisms-whats-the-difference-between-them-and-when-should-they-be-used-46</field>
+            <field name="content">OpenERP S.A. - The different "openerp model inheritance" mechanisms: what's the difference between them, and when should they be used ?</field>
+            <field name="forum_id" ref="website_forum.forum_help"/>
+            <field name="type">link</field>
+            <field name="views">6</field>
+            <field name="tag_ids" eval="[(4,ref('website_forum.tags_3'))]"/>
+        </record>
+
+        <!-- Discussion-->
+        <record id="discussion_0" model="forum.post">
+            <field name="name">How to setup a regular PostgreSQL database backup</field>
+            <field name="content">I'm not familiar with PostgreSQL database administration , so I figured I might ask for help from someone more knowledgeable on the subject.
+
+What would you recommend in order to setup a regular (daily) OpenERP database backup?
+
+Ideally, this would be done without stopping the database (hot backup). Is it possible to setup incremental backups, e.g., one weekly reference backup and daily incremental backups?
+
+Are there any automation scripts available I might use?
+
+(I'm using Ubuntu Server.)</field>
+            <field name="forum_id" ref="website_forum.forum_help"/>
+            <field name="type">discussion</field>
+            <field name="views">5</field>
+        </record>
+
+        <record id="reply_1" model="forum.post">
+            <field name="forum_id" ref="website_forum.forum_help"/>
+            <field name="content"><![CDATA[<p>I use the following setup:</p>
+                <ol>
+                    <li>Backup-Script</li>
+                    <li>Housekeeping script (deletes backups which are older than 30 days)</li>
+                    <li>Create daily cronjobs in to run Backup and Housekeeping Script. The backup runs daily at 1am and the housekeeping job runs daily at 5am.</li>
+                </ol>]]>
+            </field>
+            <field name="parent_id" ref="discussion_0"/>
+            <field name="type">discussion</field>
         </record>
 
         <!-- Post Vote  -->
@@ -66,6 +114,11 @@ In simple words does Odoo became CMS+ERP platform?</p>]]></field>
             <field name="user_id" ref="base.user_demo"/>
             <field name="vote">1</field>
         </record>
+        <record id="post_vote_3" model="forum.post.vote">
+            <field name="post_id" ref="article_0"/>
+            <field name="user_id" ref="base.user_demo"/>
+            <field name="vote">1</field>
+        </record>
 
         <!-- Run Scheduler -->
         <function model="gamification.challenge" name="_cron_update"/>
index 92fd1f6..cb89609 100644 (file)
@@ -23,41 +23,54 @@ class Forum(osv.Model):
     _inherit = ['mail.thread', 'website.seo.metadata']
 
     _columns = {
-        'name': fields.char('Name', required=True, translate=True),
+        'name': fields.char('Forum Name', required=True, translate=True),
         'faq': fields.html('Guidelines'),
         'description': fields.html('Description'),
+        'introduction_message': fields.html('Introduction Message'),
+        'relevancy_option_first': fields.float('First Relevancy Parameter'),
+        'relevancy_option_second': fields.float('Second Relevancy Parameter'),
+        'default_order': fields.selection([
+            ('creation','Newest'),
+            ('date','Last Updated'),
+            ('vote','Most Voted'),
+            ('relevancy','Relevancy'),
+            ], 'Default Order', required=True),
+        'default_allow': fields.selection([('post_link','Link'),('ask_question','Question'),('post_discussion','Discussion')], 'Default Post', required=True),
+        'allow_link': fields.boolean('Links', help="When clicking on the post, it redirects to an external link"),
+        'allow_question': fields.boolean('Questions', help="Users can answer only once per question. Contributors can edit answers and mark the right ones."),
+        'allow_discussion': fields.boolean('Discussions'),
         # karma generation
-        'karma_gen_question_new': fields.integer('Karma earned for new questions'),
-        'karma_gen_question_upvote': fields.integer('Karma earned for upvoting a question'),
-        'karma_gen_question_downvote': fields.integer('Karma earned for downvoting a question'),
-        'karma_gen_answer_upvote': fields.integer('Karma earned for upvoting an answer'),
-        'karma_gen_answer_downvote': fields.integer('Karma earned for downvoting an answer'),
-        'karma_gen_answer_accept': fields.integer('Karma earned for accepting an anwer'),
-        'karma_gen_answer_accepted': fields.integer('Karma earned for having an answer accepted'),
-        'karma_gen_answer_flagged': fields.integer('Karma earned for having an answer flagged'),
+        'karma_gen_question_new': fields.integer('Post a Questions'),
+        'karma_gen_question_upvote': fields.integer('Upvote a Question'),
+        'karma_gen_question_downvote': fields.integer('Downvote a Question'),
+        'karma_gen_answer_upvote': fields.integer('Upvote an Answer'),
+        'karma_gen_answer_downvote': fields.integer('Downvote an answer'),
+        'karma_gen_answer_accept': fields.integer('Accept an Answer'),
+        'karma_gen_answer_accepted': fields.integer('Have Your Answer Accepted'),
+        'karma_gen_answer_flagged': fields.integer('Have Your Answer Flagged'),
         # karma-based actions
-        'karma_ask': fields.integer('Karma to ask a new question'),
-        'karma_answer': fields.integer('Karma to answer a question'),
-        'karma_edit_own': fields.integer('Karma to edit its own posts'),
-        'karma_edit_all': fields.integer('Karma to edit all posts'),
-        'karma_close_own': fields.integer('Karma to close its own posts'),
-        'karma_close_all': fields.integer('Karma to close all posts'),
-        'karma_unlink_own': fields.integer('Karma to delete its own posts'),
-        'karma_unlink_all': fields.integer('Karma to delete all posts'),
-        'karma_upvote': fields.integer('Karma to upvote'),
-        'karma_downvote': fields.integer('Karma to downvote'),
-        'karma_answer_accept_own': fields.integer('Karma to accept an answer on its own questions'),
-        'karma_answer_accept_all': fields.integer('Karma to accept an answers to all questions'),
-        'karma_editor_link_files': fields.integer('Karma for linking files (Editor)'),
-        'karma_editor_clickable_link': fields.integer('Karma for clickable links (Editor)'),
-        'karma_comment_own': fields.integer('Karma to comment its own posts'),
-        'karma_comment_all': fields.integer('Karma to comment all posts'),
-        'karma_comment_convert_own': fields.integer('Karma to convert its own answers to comments and vice versa'),
-        'karma_comment_convert_all': fields.integer('Karma to convert all answers to answers and vice versa'),
-        'karma_comment_unlink_own': fields.integer('Karma to unlink its own comments'),
-        'karma_comment_unlink_all': fields.integer('Karma to unlinnk all comments'),
-        'karma_retag': fields.integer('Karma to change question tags'),
-        'karma_flag': fields.integer('Karma to flag a post as offensive'),
+        'karma_ask': fields.integer('Ask a new question'),
+        'karma_answer': fields.integer('Answer a question'),
+        'karma_edit_own': fields.integer('Edit its own posts'),
+        'karma_edit_all': fields.integer('Edit all posts'),
+        'karma_close_own': fields.integer('Close its own posts'),
+        'karma_close_all': fields.integer('Close all posts'),
+        'karma_unlink_own': fields.integer('Delete its own posts'),
+        'karma_unlink_all': fields.integer('Delete all posts'),
+        'karma_upvote': fields.integer('Upvote'),
+        'karma_downvote': fields.integer('Downvote'),
+        'karma_answer_accept_own': fields.integer('Accept an answer on its own questions'),
+        'karma_answer_accept_all': fields.integer('Accept an answers to all questions'),
+        'karma_editor_link_files': fields.integer('Linking files (Editor)'),
+        'karma_editor_clickable_link': fields.integer('Add clickable links (Editor)'),
+        'karma_comment_own': fields.integer('Comment its own posts'),
+        'karma_comment_all': fields.integer('Comment all posts'),
+        'karma_comment_convert_own': fields.integer('Convert its own answers to comments and vice versa'),
+        'karma_comment_convert_all': fields.integer('Convert all answers to answers and vice versa'),
+        'karma_comment_unlink_own': fields.integer('Unlink its own comments'),
+        'karma_comment_unlink_all': fields.integer('Unlink all comments'),
+        'karma_retag': fields.integer('Change question tags'),
+        'karma_flag': fields.integer('Flag a post as offensive'),
     }
 
     def _get_default_faq(self, cr, uid, context=None):
@@ -67,8 +80,20 @@ class Forum(osv.Model):
         return False
 
     _defaults = {
+        'default_order': 'date',
+        'allow_question': True,
+        'default_allow': 'ask_question',
+        'allow_link': False,
+        'allow_discussion': False,
         'description': 'This community is for professionals and enthusiasts of our products and services.',
         'faq': _get_default_faq,
+        'introduction_message': """<h1>Welcome to Odoo Forum</h1>
+                  <p> This community is for professionals and enthusiasts of our products and services.
+                      Share and discuss the best content and new marketing ideas,
+                      build your professional profile and become a better marketer together.
+                  </p>""",
+        'relevancy_option_first': 0.8,
+        'relevancy_option_second': 1.8,
         'karma_gen_question_new': 2,
         'karma_gen_question_upvote': 5,
         'karma_gen_question_downvote': -2,
@@ -114,6 +139,14 @@ class Post(osv.Model):
     _inherit = ['mail.thread', 'website.seo.metadata']
     _order = "is_correct DESC, vote_count DESC, write_date DESC"
 
+    def _get_post_relevancy(self, cr, uid, ids, field_name, arg, context):
+        res = dict.fromkeys(ids, 0)
+        for post in self.browse(cr, uid, ids, context=context):
+            days = (datetime.today() - datetime.strptime(post.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)).days
+            relavency = abs(post.vote_count - 1) ** post.forum_id.relevancy_option_first / ( days + 2) ** post.forum_id.relevancy_option_second
+            res[post.id] = relavency if (post.vote_count - 1) >= 0 else -relavency
+        return res
+
     def _get_user_vote(self, cr, uid, ids, field_name, arg, context):
         res = dict.fromkeys(ids, 0)
         vote_ids = self.pool['forum.post.vote'].search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], context=context)
@@ -220,6 +253,13 @@ class Post(osv.Model):
         'state': fields.selection([('active', 'Active'), ('close', 'Close'), ('offensive', 'Offensive')], 'Status'),
         'views': fields.integer('Number of Views'),
         'active': fields.boolean('Active'),
+        'type': fields.selection([('question', 'Question'), ('link', 'Article'), ('discussion', 'Discussion')], 'Type'),
+        'relevancy': fields.function(
+            _get_post_relevancy, string="Relevancy", type='float',
+            store={
+                'forum.post': (lambda self, cr, uid, ids, c={}: ids, ['vote_ids'], 10),
+                'forum.post.vote': (_get_post_from_vote, [], 10),
+            }),
         'is_correct': fields.boolean('Valid Answer', help='Correct Answer or Answer on this question accepted.'),
         'website_message_ids': fields.one2many(
             'mail.message', 'res_id',
@@ -304,6 +344,7 @@ class Post(osv.Model):
         'state': 'active',
         'views': 0,
         'active': True,
+        'type': 'question',
         'vote_ids': list(),
         'favourite_ids': list(),
         'child_ids': list(),
index de4e1b2..cf93590 100644 (file)
@@ -109,6 +109,32 @@ $(document).ready(function () {
             return true;
         });
 
+        $('.js_close_intro').on('click', function (ev) {
+            ev.preventDefault();
+            document.cookie = "no_introduction_message = false";
+            return true;
+        });
+
+        $('.link_url').on('change', function (ev) {
+            ev.preventDefault();
+            var $link = $(ev.currentTarget);
+            if ($link.attr("value").search("^http(s?)://.*")) {
+                var $warning = $('<div class="alert alert-danger alert-dismissable" style="position:absolute; margin-top: -180px; margin-left: 90px;">'+
+                    '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
+                    'Please enter valid URl.'+
+                    '</div>');
+                $link.parent().append($warning);
+                $link.parent().find("button#btn_post_your_article")[0].disabled = true;
+                $link.parent().find("input[name='content']")[0].value = '';
+            } else {
+                openerp.jsonRpc("/forum/get_url_title", 'call', {'url': $link.attr("value")}).then(function (data) {
+                    $link.parent().find("input[name='content']")[0].value = data;
+                    $('button').prop('disabled', false);
+                    $('input').prop('readonly', false);
+                });
+            }
+        });
+
         if($('input.load_tags').length){
             var tags = $("input.load_tags").val();
             $("input.load_tags").val("");
@@ -174,8 +200,11 @@ $(document).ready(function () {
         }
 
         if ($('textarea.load_editor').length) {
-            var editor = CKEDITOR.instances['content'];
-            editor.on('instanceReady', CKEDITORLoadComplete);
+            $('textarea.load_editor').each(function () {
+                if (this['id']) {
+                    CKEDITOR.replace(this['id']).on('instanceReady', CKEDITORLoadComplete);
+                }
+            });
         }
     }
 });
index a2d7c09..b19d701 100644 (file)
@@ -12,6 +12,9 @@
             <field name="arch" type="xml">
                 <tree string="Forums">
                     <field name="name"/>
+                    <field name="allow_question"/>
+                    <field name="allow_link"/>
+                    <field name="allow_discussion"/>
                 </tree>
             </field>
         </record>
             <field name="arch" type="xml">
                 <form string="Forum">
                     <sheet>
-                        <group>
+                        <label for="name" class="oe_edit_only"/>
+                        <h1>
                             <field name="name"/>
-                            <field name="karma_ask"/>
-                            <field name="karma_edit_own"/>
-                            <field name="karma_edit_all"/>
-                            <field name="karma_close_own"/>
-                            <field name="karma_close_all"/>
-                            <field name="karma_unlink_own"/>
-                            <field name="karma_unlink_all"/>
+                        </h1>
+                        <group>
+                            <group string="Post Types">
+                                <field name="allow_question"/>
+                                <field name="allow_link"/>
+                                <field name="allow_discussion"/>
+                                <field name="default_allow"/>
+                            </group>
+                            <group string="Orders">
+                                <field name="default_order"/>
+                                <label for="relevancy_option_first" string="Relevancy Computation"/>
+                                <div>
+                                    (votes - 1) ** <field name="relevancy_option_first" class="oe_inline"/> / (days + 2) ** <field name="relevancy_option_second" class="oe_inline"/>
+                                </div>
+                            </group>
                         </group>
                         <group>
-                            <field name="karma_upvote"/>
-                            <field name="karma_downvote"/>
-                            <field name="karma_answer_accept_own"/>
-                            <field name="karma_answer_accept_all"/>
-                            <field name="karma_editor_link_files"/>
-                            <field name="karma_editor_clickable_link"/>
-                            <field name="karma_comment_own"/>
-                            <field name="karma_comment_all"/>
-                            <field name="karma_comment_convert_own"/>
-                            <field name="karma_comment_convert_all"/>
-                            <field name="karma_comment_unlink_own"/>
-                            <field name="karma_comment_unlink_all"/>
+                            <group string="Earn Karma">
+                                <field name="karma_gen_question_new"/>
+                                <field name="karma_gen_question_upvote"/>
+                                <field name="karma_gen_question_downvote"/>
+                                <field name="karma_gen_answer_upvote"/>
+                                <field name="karma_gen_answer_downvote"/>
+                                <field name="karma_gen_answer_accept"/>
+                                <field name="karma_gen_answer_accepted"/>
+                                <field name="karma_gen_answer_flagged"/>
+                            </group>
+                            <group string="Karma Related Rights">
+                                <field name="karma_ask"/>
+                                <field name="karma_edit_own"/>
+                                <field name="karma_edit_all"/>
+                                <field name="karma_close_own"/>
+                                <field name="karma_close_all"/>
+                                <field name="karma_unlink_own"/>
+                                <field name="karma_unlink_all"/>
+                                <field name="karma_upvote"/>
+                                <field name="karma_downvote"/>
+                                <field name="karma_answer_accept_own"/>
+                                <field name="karma_answer_accept_all"/>
+                                <field name="karma_editor_link_files"/>
+                                <field name="karma_editor_clickable_link"/>
+                                <field name="karma_comment_own"/>
+                                <field name="karma_comment_all"/>
+                                <field name="karma_comment_convert_own"/>
+                                <field name="karma_comment_convert_all"/>
+                                <field name="karma_comment_unlink_own"/>
+                                <field name="karma_comment_unlink_all"/>
+                            </group>
                         </group>
                     </sheet>
                     <div class="oe_chatter">
                                 <field name="vote_count"/>
                                 <field name="favourite_count"/>
                                 <field name="child_count"/>
+                                <field name="relevancy"/>
                             </group>
                         </group>
                     </sheet>
index 995c127..47e0b90 100644 (file)
         <t t-set="head">
             <t t-call-assets="website_forum.assets_forum"/>
         </t>
+        <div t-if="is_public_user and not no_introduction_message" class="alert alert-success alert-dismissable">
+            <div class="container">
+                <div t-field="forum.introduction_message"/>
+                <a class='btn btn-primary' t-attf-href="/web?redirect=#{ request.httprequest.url }">Register</a>
+                <button type="button" class="btn btn-link js_close_intro" data-dismiss="alert" aria-hidden="true">Hide Intro</button>
+            </div>
+        </div>
         <div class="container mt16 website_forum">
             <div class="navbar navbar-default">
                 <div class="navbar-header">
                 </div>
                 <div class="collapse navbar-collapse" id="oe-help-navbar-collapse">
                     <ul class="nav navbar-nav">
-                        <li t-att-class="filters in ('all', 'unanswered','followed','question','tag') and 'active' or '' ">
-                            <a t-attf-href="/forum/#{ slug(forum) }">Questions</a>
+                        <li t-att-class="sorting == 'relevancy' and 'active' or '' ">
+                            <a t-attf-href="/forum/#{ slug(forum) }?{{ keep_query( 'search', 'post_type', 'filters', sorting='relevancy') }}">Trending</a>
+                        </li>
+                        <li t-att-class="sorting == 'creation' and 'active' or '' ">
+                            <a t-attf-href="/forum/#{ slug(forum) }?{{ keep_query( 'search', 'post_type', 'filters', sorting='creation') }}">Newest</a>
                         </li>
                         <li t-att-class="searches.get('users') and 'active' or '' ">
                             <a t-attf-href="/forum/#{ slug(forum) }/users">People</a>
                         </li>
                     </ul>
                     <form class="navbar-form navbar-right" role="search" t-attf-action="/forum/#{ slug(forum) }" method="get">
-                        <div class="form-group">
-                            <input type="search" class="form-control"
-                                name="search" placeholder="Search a question..."
-                                t-att-value="search or ''"/>
-                            <button type="submit" class="btn btn-default">Search</button>
+                        <div class="input-group">
+                            <input type="search" class="form-control" name="search" t-att-value="search or ''"/>
+                            <span class="input-group-btn">
+                                <button type="submit" class="btn btn-default">Search</button>
+                            </span>
                         </div>
                     </form>
                 </div>
                     <t t-raw="0"/>
                 </div>
                 <div class="col-sm-3" id="right-column">
-                    <a t-if="not header.get('ask_hide')" class="btn btn-primary btn-lg btn-block mb16" t-attf-href="/forum/#{slug(forum)}/ask">Ask a Question</a>
+                    <div t-if="not header.get('ask_hide')" class="btn-group btn-block mb16">
+                        <a type="button" class="btn btn-primary btn-lg col-sm-10" t-attf-href="/forum/#{slug(forum)}/#{forum.default_allow}">
+                            <t t-if="forum.default_allow == 'ask_question'">Ask a Question</t>
+                            <t t-if="forum.default_allow == 'post_link'">Submit a Post</t>
+                            <t t-if="forum.default_allow == 'post_discussion'">New Discussion</t>
+                        </a>
+                        <button type="button" class="btn btn-primary btn-lg col-sm-2 dropdown-toggle" data-toggle="dropdown">
+                            <span class="caret"></span>
+                            <span class="sr-only">Select Post</span>
+                        </button>
+                        <ul class="dropdown-menu" role="menu">
+                            <li t-if="forum.allow_link"><a t-attf-href="/forum/#{slug(forum)}/post_link">Submit a Post</a></li>
+                            <li t-if="forum.allow_question"><a t-attf-href="/forum/#{slug(forum)}/ask_question">Ask a Question</a></li>
+                            <li t-if="forum.allow_discussion"><a t-attf-href="/forum/#{slug(forum)}/post_discussion">Launch a Discussion</a></li>
+                        </ul>
+                    </div>
                     <div class="panel panel-default">
                         <div class="panel-heading">
                             <h3 class="panel-title">Keep Informed</h3>
                     </div>
                     <div class="panel panel-default" id="about_forum">
                         <div class="panel-heading">
-                            <h3 class="panel-title">About This Forum</h3>
+                            <h3 class="panel-title">About This Community</h3>
                         </div>
                         <div class="panel-body">
                             <t t-raw="forum.description"/>
 <template id="display_post">
     <div class="question row">
         <div class="col-md-2 hidden-xs text-center">
-            <div t-attf-class="box #{question.is_correct and 'oe_green' or 'oe_grey'} #{(question.child_count == 0) and 'text-muted' or ''}">
-                <span t-esc="question.child_count"/>
-                <div t-if="question.child_count&gt;1" class="subtitle">Answers</div>
-                <div t-if="question.child_count&lt;=1" class="subtitle">Answer</div>
-            </div>
+            <t t-call="website_forum.vote">
+                <t t-set="post" t-value="question"/>
+            </t>
         </div>
         <div class="col-md-10 clearfix">
             <div class="question-name">
-                <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
-                    <span t-if="not question.active"><b> [Deleted]</b></span>
-                    <span t-if="question.state == 'close'"><b> [Closed]</b></span>
+                <t t-if="question.type == 'link'">
+                    <a t-att-href="question.name" t-raw="question.content"/>
+                </t>
+                <t t-if="question.type in ('question', 'discussion')">
+                    <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
+                </t>
+                <span t-if="not question.active"><b> [Deleted]</b></span>
+                <span t-if="question.state == 'close'"><b> [Closed]</b></span>
             </div>
             <t t-foreach="question.tag_ids" t-as="question_tag">
                 <a t-attf-href="/forum/#{ slug(forum) }/tag/#{slug(question_tag)}/questions">
-                    <span t-attf-class="pull-right badge #{tag and tag.name == question_tag.name and 'badge-active' ''}" t-field="question_tag.name"
+                    <span t-attf-class="pull-right label #{tag and tag.name == question_tag.name and 'label-primary' or 'label-default'}" t-field="question_tag.name"
                         style="margin-right: 4px;"/>
                 </a>
             </t>
-            <div class="text-muted">
-                by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
-                    t-field="question.create_uid" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
-                    style="display: inline-block;"/>
-                on <span t-field="question.write_date" t-field-options='{"format":"short"}'/>
-                <span class="visible-xs">
-                    <b t-esc="question.child_count or 0"/>
-                    <t t-if="question.child_count&gt;1">answers</t>
-                    <t t-if="question.child_count==1">answers</t>
-                </span>
-                with <b t-field="question.views"/> views
-                <span t-if="question.vote_count&gt;0"> and
-                    <b t-esc="question.vote_count or 0"/>
-                    <t t-if="question.vote_count&gt;1">votes</t>
-                    <t t-if="question.vote_count==1">vote</t>
-                </span>
-            </div>
+            <small class="text-muted">
+                By <span t-field="question.create_uid" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}' style="display: inline-block;"/>
+                â€¢ <span t-field="question.write_date" t-field-options='{"format":"short"}'/>
+                â€¢ <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }">
+                    <t t-esc="question.child_count"/>
+                    <t t-if="question.child_count&gt;1">comments</t>
+                    <t t-if="question.child_count&lt;=1">comment</t>
+                </a>
+            </small>
         </div>
     </div>
 </template>
 <!-- Specific Forum Layout -->
 <template id="forum_index" name="Forum">
     <t t-call="website_forum.header">
-        <h1 class="page-header mt0">
-            <t t-esc="question_count"/> <span>Questions</span>
+        <h2 class="page-header mt0">
+            <t t-esc="question_count"/>
+                <span t-if="post_type == 'all'">Posts</span>
+                <span t-if="post_type == 'question'">Questions</span>
+                <span t-if="post_type == 'link'">Posts</span>
+                <span t-if="post_type == 'discussion'">Discussions</span>
             <t t-esc="search"/>
             <small class="dropdown" t-if="filters in ('all', 'unanswered','followed', 'tag')">
               <a href="#" class="dropdown-toggle" data-toggle="dropdown">
-                  <t t-if="filters == 'all'">All</t>
                   <t t-if="filters == 'unanswered'">Unanswered</t>
                   <t t-if="filters == 'followed'">Followed</t>
                   <t t-if="tag"><span t-field="tag.name"/></t>
+                  <t t-if="sorting == 'relevancy'"> by relevancy</t>
                   <t t-if="sorting == 'date'"> by activity date</t>
-                  <t t-if="sorting == 'creation'"> by creation date</t>
+                  <t t-if="sorting == 'creation'"> by newest</t>
                   <t t-if="sorting == 'answered'"> by most answered</t>
                   <t t-if="sorting == 'vote'"> by most voted</t>
                   <b class="caret"/>
                       <a href=""><t t-esc="tag.name"/></a>
                   </li>
                   <li class="dropdown-header">Sort by</li>
+                  <li t-att-class="sorting == 'relevancy' and 'active' or '' ">
+                      <a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='relevancy')">Relevancy</a>
+                  </li>
                   <li t-att-class="sorting == 'date' and 'active' or '' ">
                       <a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='date')">Last activity date</a>
                   </li>
                   </li>
               </ul>
             </small>
-        </h1>
+        </h2>
         <div t-foreach="question_ids" t-as="question" class="mb16">
             <t t-call="website_forum.display_post"/>
         </div>
     </t>
 </template>
 
+<!-- Edition: Post Article -->
+<template id="post_link">
+    <t t-call="website_forum.header">
+        <h1 class="mt0">Submit a Link</h1>
+        <p class="mb32">
+            Share an awesome link. Your post will appear in the 'Newest' top-menu.
+            If the community vote on your post, it will get traction by being promoted
+            in the homepage.
+        </p><p>
+            We keep a high level of quality in showcased posts, only 20% of the submited
+            posts will be featured.
+        </p>
+        <form t-attf-action="/forum/#{ slug(forum) }/link/new" method="post" role="form" class="tag_text form-horizontal">
+            <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
+            <div class="form-group">
+                <label class="col-sm-2 control-label" for="post_name">URL to Share</label>
+                <div class="col-sm-8">
+                    <input type="text" name="post_name" required="True" t-attf-value="#{post_name}"
+                        class="form-control mb16 link_url" placeholder="e.g. https://www.odoo.com"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-sm-2 control-label" for="post_name">Post Title</label>
+                <div class="col-sm-8">
+                    <input type="text" name="content" readonly="True" required="True" t-attf-value="#{content}"
+                        class="form-control"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-sm-2 control-label" for="post_name">Tags</label>
+                <div class="col-sm-8">
+                    <input type="text" name="post_tags" readonly="True" class="form-control load_tags"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="col-sm-offset-2 col-sm-8">
+                    <button class="btn btn-primary" disabled="True" id="btn_post_your_article">Post Your Article</button>
+                </div>
+            </div>
+        </form>
+    </t>
+</template>
+
+<!-- Edition: Post your Discussion Topic -->
+<template id="post_discussion">
+    <t t-call="website_forum.header">
+        <h1 class="mt0">Post Your Discussion Topic</h1>
+        <p>
+            <b>Share</b> Start Something Awesome
+        </p>
+        <form t-attf-action="/forum/#{ slug(forum) }/discussion/new" method="post" role="form" class="tag_text">
+            <input type="text" name="post_name" required="True" t-attf-value="#{post_name}"
+                class="form-control mb16" placeholder="Your Discussion Title..."/>
+            <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
+            <textarea name="content" id="content" required="True" class="form-control load_editor">
+                <t t-esc="question_content"/>
+            </textarea>
+            <br/>
+            <input type="text" name="post_tags" placeholder="Tags" class="form-control load_tags"/>
+            <br/>
+            <button class="btn btn-primary" id="btn_ask_your_question">Post Your Topic</button>
+        </form>
+    </t>
+</template>
+
 <!-- Edition: ask your question -->
 <template id="ask_question">
     <t t-call="website_forum.header">
             });
         </script>
         <form t-attf-action="/forum/#{ slug(forum) }/question/new" method="post" role="form" class="tag_text">
-            <input type="text" name="question_name" required="True" t-attf-value="#{question_name}"
+            <input type="text" name="post_name" required="True" t-attf-value="#{post_name}"
                 class="form-control mb16" placeholder="Your Question Title..."/>
             <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
-            <textarea name="content" required="True" class="form-control load_editor">
+            <textarea name="content" required="True" id="content" class="form-control load_editor">
                 <t t-esc="question_content"/>
             </textarea>
             <br/>
-            <input type="text" name="question_tags" placeholder="Tags" class="form-control load_tags"/>
+            <input type="text" name="post_tags" placeholder="Tags" class="form-control load_tags"/>
             <br/>
             <button class="btn btn-primary" id="btn_ask_your_question">Post Your Question</button>
         </form>
-        <script type="text/javascript">
-            CKEDITOR.replace("content");
-        </script>
     </t>
 </template>
 
 <!-- Edition: edit a post -->
 <template id="edit_post">
     <t t-call="website_forum.header">
-        <h3 t-if="not is_answer">Edit question</h3>
-        <h3 t-if="is_answer">Edit answer</h3>
+        <h3 t-if="not is_answer">Edit <span t-field="post.type"/></h3>
+        <h3 t-if="is_answer">Edit reply</h3>
         <form t-attf-action="/forum/#{slug(forum)}/post/#{slug(post)}/save" method="post" role="form" class="tag_text">
             <div t-if="not is_answer">
-                <input type="text" name="question_name" id="question_name" required="True"
-                    t-attf-value="#{post.name}" class="form-control" placeholder="Edit your Question"/>
-                <h5 class="mt20">Please enter a descriptive question (should finish by a '?')</h5>
+                <input type="text" name="post_name" required="True"
+                    t-attf-value="#{post.name}" class="form-control mb8" placeholder="Edit your Post"/>
+                <h5 t-if="post.type == 'question'" class="mt20">Please enter a descriptive question (should finish by a '?')</h5>
             </div>
             <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
-            <textarea name="content" required="True" class="form-control load_editor">
+            <textarea name="content" id="content" required="True" class="form-control load_editor">
                 <t t-esc="post.content"/>
             </textarea>
             <div t-if="not is_answer">
                 <br/>
-                <input type="text" name="question_tag" class="form-control col-md-9 load_tags" placeholder="Tags" t-attf-value="#{tags}"/>
+                <input type="text" name="post_tag" class="form-control col-md-9 load_tags" placeholder="Tags" t-attf-value="#{tags}"/>
                 <br/>
             </div>
             <button class="btn btn-primary btn-lg">Save</button>
         </form>
-        <script type="text/javascript">
-            CKEDITOR.replace("content");
-        </script>
     </t>
 </template>
 
-<!-- Moderation: close a question -->
-<template id="close_question">
+<!-- Moderation: close a post -->
+<template id="close_post">
     <t t-call="website_forum.header">
-        <h1 class="mt0">Close question</h1>
+        <h1 class="mt0">Close Post</h1>
         <p class="text-muted">
-            If you close this question, it will be hidden for most users. Only
-            users having a high karma can see closed questions to moderate
+            If you close this post, it will be hidden for most users. Only
+            users having a high karma can see closed posts to moderate
             them.
         </p>
         <form t-attf-action="/forum/#{ slug(forum) }/question/#{slug(question)}/close" method="post" role="form" class="form-horizontal mt32 mb64">
             <input name="post_id" t-att-value="question.id" type="hidden"/>
             <div class="form-group">
-                <label class="col-md-3 control-label" for="reason">Question:</label>
+                <label class="col-md-3 control-label" for="reason">Post:</label>
                 <div class="col-md-8 mt8">
                     <span t-field="question.name"/>
                 </div>
             </div>
             <div class="form-group">
                 <div class="col-md-offset-3 col-md-8">
-                    <button class="btn btn-primary">Close question</button>
+                    <button class="btn btn-primary">Close post</button>
                     <span class="text-muted">or</span>
-                    <a class="btn btn-link" t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }">back to question</a>
+                    <a class="btn btn-link" t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }">back to post</a>
                 </div>
             </div>
         </form>
     </t>
 </template>
 
+<!-- Edition: post a reply -->
+<template id="post_reply">
+    <div class="css_editable_mode_hidden">
+        <form t-attf-id="reply#{ object._name.replace('.','') + '-' + str(object.id) }" class="collapse oe_comment_grey"
+            t-attf-action="/forum/#{ slug(forum) }/#{slug(object)}/reply" method="post" role="form">
+            <h3 class="mt10">Your Reply</h3>
+            <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
+            <textarea name="content" t-attf-id="content-#{str(object.id)}" class="form-control load_editor" required="True"/>
+            <button class="btn btn-primary" id="btn_ask_your_question">Post Your Reply</button>
+        </form>
+    </div>
+</template>
+
 <!-- Edition: post an answer -->
 <template id="post_answer">
-    <h3 class="mt10">Your answer</h3>
-    <p>
+    <h3 class="mt10">Your Reply</h3>
+    <p t-if="question.type == 'question'">
         <b>Please try to give a substantial answer.</b> If you wanted to comment on the question or answer, just
         <b>use the commenting tool.</b> Please remember that you can always <b>revise your answers</b>
         - no need to answer the same question twice. Also, please <b>don't forget to vote</b>
         - it really helps to select the best questions and answers!
     </p>
-    <form t-attf-action="/forum/#{ slug(forum) }/post/#{slug(question)}/new" method="post" role="form">
+    <form t-attf-action="/forum/#{ slug(forum) }/#{slug(question)}/reply" method="post" role="form">
         <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
-        <textarea name="content" class="form-control load_editor" required="True"/>
-        <button class="btn btn-primary mt16" id="btn_ask_your_question">Post Your Answer</button>
+        <textarea name="content" t-attf-id="content-#{str(question.id)}" class="form-control load_editor" required="True"/>
+        <button class="btn btn-primary mt16" id="btn_ask_your_question">Post Your Reply</button>
     </form>
-    <script type="text/javascript">
-        CKEDITOR.replace("content");
-    </script>
 </template>
 
 <template id="vote">
             </div>
             <div t-attf-class="col-md-10 #{not question.active and 'alert alert-danger' or ''}">
                 <h1 class="mt0">
-                    <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
+                    <t t-if="question.type == 'link'">
+                        <a t-att-href="question.name" t-raw="question.content"/>
+                    </t>
+                    <t t-if="question.type in ('question', 'discussion')">
+                        <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
+                    </t>
                     <span t-if="not question.active"><b> [Deleted]</b></span>
                     <span t-if="question.state == 'close'"><b> [Closed]</b></span>
                 </h1>
                 <div class="alert alert-info text-center" t-if="question.state == 'close'">
                     <p class="mt16">
-                        <b>The question has been closed<t t-if="question.closed_reason_id"> for reason: <i t-esc="question.closed_reason_id.name"/></t></b>
+                        <b>The <i t-field="question.type"/> has been closed<t t-if="question.closed_reason_id"> for reason: <i t-esc="question.closed_reason_id.name"/></t></b>
                     </p>
                     <t t-if="question.closed_uid">
                         <b>by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.closed_uid.id }"
                         </t>
                     </div>
                 </div>
-                <t t-raw="question.content"/>
+                <div t-if="question.type != 'link'"><t t-raw="question.content"/></div>
                 <div class="mt16 clearfix">
                     <div class="pull-right">
                         <div class="text-right">
                             <t t-foreach="question.tag_ids" t-as="tag">
-                                <a t-attf-href="/forum/#{ slug(forum) }/tag/#{ tag.id }/questions" class="badge" t-field="tag.name"/>
+                                <a t-attf-href="/forum/#{ slug(forum) }/tag/#{ tag.id }/questions" class="label lebel-default" t-field="tag.name"/>
                             </t>
                         </div>
                         <ul class="list-inline" id="options">
-                            <li>
+                            <li t-if="question.type == 'question'">
                                 <a style="cursor: pointer" data-toggle="collapse"
                                     t-attf-class="fa fa-comment-o #{not question.can_comment and 'karma_required text-muted' or ''}"
                                     t-attf-data-karma="#{not question.can_comment and question.karma_comment or 0}"
             </div>
         </div>
         <hr/>
+        <div t-foreach="question.child_ids" t-as="post_answer" class="mt16 mb32">
+            <t t-call="website_forum.post_answers">
+                <t t-set="answer" t-value="post_answer"/>
+            </t>
+        </div>
+        <div t-if="question.type != 'question' or question.type == 'question' and not question.uid_has_answered and question.state != 'close' and question.active != False">
+            <t t-call="website_forum.post_answer"/>
+        </div>
+        <div t-if="question.type == 'question' and question.uid_has_answered" class="mb16">
+            <a class="btn btn-primary" t-attf-href="/forum/#{slug(forum)}/question/#{slug(question)}/edit_answer">Edit Your Previous Answer</a>
+            <span class="text-muted">(only one answer per question is allowed)</span>
+        </div>
+    </t>
+</template>
 
-        <div t-foreach="question.child_ids" t-as="answer" class="mt16 mb32">
-            <a t-attf-id="answer-#{str(answer.id)}"/>
-            <div t-attf-class="forum_answer row" t-attf-id="answer_#{answer.id}" >
-                <div class="col-md-2 hidden-xs text-center">
+<template id="post_answers">
+    <a t-attf-id="answer-#{str(answer.id)}"/>
+    <div t-attf-class="forum_answer row" t-attf-id="answer_#{answer.id}" >
+        <div class="col-md-2 hidden-xs text-center">
+            <t t-call="website_forum.vote">
+                <t t-set="post" t-value="answer"/>
+            </t>
+            <div t-if="question.type == 'question'" class="text-muted mt8">
+                <a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'} #{not answer.can_accept and 'karma_required' or ''}"
+                    t-attf-data-karma="#{answer.karma_accept}"
+                    t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
+            </div>
+        </div>
+        <div class="col-md-10 clearfix">
+            <t t-raw="answer.content"/>
+            <div class="mt16">
+                <ul class="list-inline pull-right">
+                    <li t-if="question.type == 'question'">
+                        <a t-attf-class="fa fa-comment-o #{not answer.can_comment and 'karma_required text-muted' or ''}"
+                            t-attf-data-karma="#{not answer.can_comment and answer.karma_comment or 0}"
+                            style="cursor: pointer" data-toggle="collapse"
+                            t-attf-data-target="#comment#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Comment
+                        </a>
+                    </li>
+                    <li t-if="question.type != 'question' and not answer.parent_id or answer.parent_id and not answer.parent_id.parent_id">
+                        <a t-attf-class="fa fa-comment-o #{not answer.can_comment and 'karma_required text-muted' or ''}"
+                            t-attf-data-karma="#{not answer.can_comment and answer.karma_comment or 0}"
+                            style="cursor: pointer" data-toggle="collapse"
+                            t-attf-data-target="#reply#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Reply
+                        </a>
+                    </li>
+                    <li>
+                        <t t-call="website_forum.link_button">
+                            <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/edit'"/>
+                            <t t-set="label" t-value="'Edit'"/>
+                            <t t-set="classes" t-value="'fa fa-edit'"/>
+                            <t t-set="karma" t-value="not answer.can_edit and answer.karma_edit or 0"/>
+                        </t>
+                    </li>
+                    <li>
+                        <t t-call="website_forum.link_button">
+                            <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/delete'"/>
+                            <t t-set="label" t-value="'Delete'"/>
+                            <t t-set="classes" t-value="'fa-trash-o'"/>
+                            <t t-set="karma" t-value="not answer.can_unlink and answer.karma_unlink or 0"/>
+                        </t>
+                    </li>
+                    <li t-if="question.type == 'question'">
+                        <t t-call="website_forum.link_button">
+                            <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/convert_to_comment'"/>
+                            <t t-set="label" t-value="'Convert as a comment'"/>
+                            <t t-set="classes" t-value="'fa-magic'"/>
+                            <t t-set="karma" t-value="not answer.can_comment_convert and answer.karma_comment_convert or 0"/>
+                        </t>
+                    </li>
+                </ul>
+                <img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{answer.create_uid.id}/avatar"/>
+                <div>
+                    <a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }"
+                        t-field="answer.create_uid"
+                        t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
+                        style="display: inline-block;"/>
+                    <div t-field="answer.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/>
+                    <span class="text-muted">Answered on <span t-field="answer.create_date" t-field-options='{"format":"short"}'/></span>
+                </div>
+                <div class="visible-xs text-center">
                     <t t-call="website_forum.vote">
                         <t t-set="post" t-value="answer"/>
                     </t>
                             t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
                     </div>
                 </div>
-                <div class="col-md-10 clearfix">
-                    <t t-raw="answer.content"/>
-                    <div class="mt16">
-                        <ul class="list-inline pull-right">
-                            <li>
-                                <a t-attf-class="fa fa-comment-o #{not answer.can_comment and 'karma_required text-muted' or ''}"
-                                    t-attf-data-karma="#{not answer.can_comment and answer.karma_comment or 0}"
-                                    style="cursor: pointer" data-toggle="collapse"
-                                    t-attf-data-target="#comment#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Comment
-                                </a>
-                            </li>
-                            <li>
-                                <t t-call="website_forum.link_button">
-                                    <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/edit'"/>
-                                    <t t-set="label" t-value="'Edit'"/>
-                                    <t t-set="classes" t-value="'fa fa-edit'"/>
-                                    <t t-set="karma" t-value="not answer.can_edit and answer.karma_edit or 0"/>
-                                </t>
-                            </li>
-                            <li>
-                                <t t-call="website_forum.link_button">
-                                    <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/delete'"/>
-                                    <t t-set="label" t-value="'Delete'"/>
-                                    <t t-set="classes" t-value="'fa-trash-o'"/>
-                                    <t t-set="karma" t-value="not answer.can_unlink and answer.karma_unlink or 0"/>
-                                </t>
-                            </li>
-                            <li>
-                                <t t-call="website_forum.link_button">
-                                    <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/convert_to_comment'"/>
-                                    <t t-set="label" t-value="'Convert as a comment'"/>
-                                    <t t-set="classes" t-value="'fa-magic'"/>
-                                    <t t-set="karma" t-value="not answer.can_comment_convert and answer.karma_comment_convert or 0"/>
-                                </t>
-                            </li>
-                        </ul>
-                        <img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{answer.create_uid.id}/avatar"/>
-                        <div>
-                            <a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }"
-                                t-field="answer.create_uid"
-                                t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
-                                style="display: inline-block;"/>
-                            <div t-field="answer.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/>
-                            <span class="text-muted">Answered on <span t-field="answer.create_date" t-field-options='{"format":"short"}'/></span>
-                        </div>
-                        <div class="visible-xs text-center">
-                            <t t-call="website_forum.vote">
-                                <t t-set="post" t-value="answer"/>
-                            </t>
-                            <div class="text-muted mt8">
-                                <a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'} #{not answer.can_accept and 'karma_required' or ''}"
-                                    t-attf-data-karma="#{answer.karma_accept}"
-                                    t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
-                            </div>
-                        </div>
-                    </div>
-                    <t t-call="website_forum.post_comment">
-                        <t t-set="object" t-value="answer"/>
-                    </t>
-                </div>
+            </div>
+            <t t-if="answer.type == 'question'" t-call="website_forum.post_comment">
+                <t t-set="object" t-value="answer"/>
+            </t>
+            <div t-if="answer.type != 'question' and question.state != 'close' and question.active != False">
+                <t t-call="website_forum.post_reply">
+                    <t t-set="object" t-value="answer"/>
+                </t>
+            </div>
+            <div t-foreach="answer.child_ids" t-as="child_answer" class="mt16 mb16">
+                <t t-call="website_forum.post_answers">
+                    <t t-set="answer" t-value="child_answer"/>
+                </t>
             </div>
         </div>
-        <div t-if="not question.uid_has_answered and question.state != 'close' and question.active != False">
-            <t t-call="website_forum.post_answer"/>
-        </div>
-        <div t-if="question.uid_has_answered" class="mb16">
-            <a class="btn btn-primary" t-attf-href="/forum/#{slug(forum)}/question/#{slug(question)}/edit_answer">Edit Your Previous Answer</a>
-            <span class="text-muted">(only one answer per question is allowed)</span>
-        </div>
-    </t>
+    </div>
 </template>
 
 <!-- Utility template: Post a Comment -->
         </p>
         <div class="row">
             <div class="col-sm-3 mt16" t-foreach="tags" t-as="tag">
-                <a t-attf-href="/forum/#{ slug(forum) }/tag/#{ slug(tag) }/questions?{{ keep_query( filters='tag') }}" class="badge">
+                <a t-attf-href="/forum/#{ slug(forum) }/tag/#{ slug(tag) }/questions?{{ keep_query( filters='tag') }}" class="label label-default">
                     <span t-field="tag.name"/>
                 </a>
                 <span>