diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index f71d4ea7d8b..8af974bfa6b 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -42,7 +42,7 @@ import os from distutils.version import LooseVersion -from pyPdf import PdfFileWriter, PdfFileReader +#from pyPdf import PdfFileWriter, PdfFileReader from werkzeug import exceptions from werkzeug.test import Client from werkzeug.wrappers import BaseResponse diff --git a/addons/website_blog/views/website_blog_views.xml b/addons/website_blog/views/website_blog_views.xml index 114c9d6961f..cd62070b255 100644 --- a/addons/website_blog/views/website_blog_views.xml +++ b/addons/website_blog/views/website_blog_views.xml @@ -104,7 +104,7 @@ - + Blogs blog.blog diff --git a/addons/website_forum/__init__.py b/addons/website_forum/__init__.py new file mode 100644 index 00000000000..708d6ce4669 --- /dev/null +++ b/addons/website_forum/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import controllers +import models diff --git a/addons/website_forum/__openerp__.py b/addons/website_forum/__openerp__.py new file mode 100644 index 00000000000..c71ac9a126f --- /dev/null +++ b/addons/website_forum/__openerp__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +{ + 'name': 'Forum', + 'category': 'Website', + 'summary': 'Ask Questions and give Answers', + 'version': '1.0', + 'description': """ +Ask questions, get answers, no distractions + """, + 'author': 'OpenERP SA', + 'depends': ['website', 'website_mail'], + 'data': [ + 'data/forum_data.xml', + 'views/website_forum.xml', + 'security/ir.model.access.csv', + 'security/website_forum.xml', + ], + 'qweb': [ + 'static/src/xml/*.xml' + ], + 'demo': [ + 'data/forum_demo.xml' + ], + 'css': ['static/src/css/website_forum.css'], + 'installable': True, + 'application': True, +} diff --git a/addons/website_forum/controllers/__init__.py b/addons/website_forum/controllers/__init__.py new file mode 100644 index 00000000000..46217e33563 --- /dev/null +++ b/addons/website_forum/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import main diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py new file mode 100644 index 00000000000..e034735f0c9 --- /dev/null +++ b/addons/website_forum/controllers/main.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import werkzeug.urls + +from openerp import tools +from openerp import SUPERUSER_ID +from openerp.addons.web import http + +from openerp.tools.translate import _ +from datetime import datetime, timedelta +from openerp.addons.web.http import request + +from dateutil.relativedelta import relativedelta +from openerp.addons.website.controllers.main import Website as controllers + +controllers = controllers() + +class website_forum(http.Controller): + + @http.route(['/forum/', '/forum/page/'], type='http', auth="public", website=True, multilang=True) + def questions(self, page=1, **searches): + cr, uid, context = request.cr, request.uid, request.context + forum_obj = request.registry['website.forum.post'] + user_obj = request.registry['res.users'] + domain = [('parent_id', '=', False)] + search = searches.get('search',False) + type = searches.get('type',False) + if not type: + searches['type'] = 'all' + if search: + domain += ['|', + ('name', 'ilike', search), + ('content', 'ilike', search)] + if type == 'unanswered': + domain += [ ('child_ids', '=', False) ] + #TODO: update domain to show followed questions of user + if type == 'followed': + user = user_obj.browse(cr, uid, uid, context=context) + domain += [ ('id', 'in', [que.id for que in user.question_ids]) ] + + step = 10 + question_count = forum_obj.search(cr, uid, domain, count=True, context=context) + pager = request.website.pager(url="/forum/", total=question_count, page=page, step=step, scope=10) + + obj_ids = forum_obj.search(cr, uid, domain, limit=step, offset=pager['offset'], context=context) + question_ids = forum_obj.browse(cr, uid, obj_ids, context=context) + + #If dose not get any related question then redirect to ask question form. + if search and not question_ids: + values = { + 'question_name': search, + } + return request.website.render("website_forum.ask_question", values) + + values = { + 'total_questions': question_count, + 'question_ids': question_ids, + 'pager': pager, + 'searches': searches, + } + + return request.website.render("website_forum.index", values) + + @http.route(['/forum/question//page/'], type='http', auth="public", website=True, multilang=True) + def question(self, question, page, **post): + values = { + 'question': question, + 'main_object': question + } + return request.website.render(page, values) + + @http.route(['/forum/question/'], type='http', auth="public", website=True, multilang=True) + def open_question(self, question, **post): + answer_done = False + for answer in question.child_ids: + if answer.create_uid.id == request.uid: + answer_done = True + values = { + 'question': question, + 'main_object': question, + 'searches': post, + 'answer_done': answer_done + } + return request.website.render("website_forum.post_description_full", values) + + @http.route(['/forum/user/'], type='http', auth="public", website=True, multilang=True) + def open_user(self, user, **post): + values = { + 'user': user, + 'main_object': user, + 'searches': post + } + return request.website.render("website_forum.user_detail_full", values) + + @http.route('/forum/question/ask/', type='http', auth="user", multilang=True, methods=['POST'], website=True) + def register_question(self, forum_id=1, **question): + cr, uid, context = request.cr, request.uid, request.context + create_context = dict(context) + new_question_id = request.registry['website.forum.post'].create( + request.cr, request.uid, { + #'forum_id': forum_id, + 'name': question.get('question_name'), + 'content': question.get('question_content'), + #'tags' : question.get('question_tags'), + 'state': 'active', + 'active': True, + }, context=create_context) + return werkzeug.utils.redirect("/forum/question/%s" % new_question_id) + + @http.route('/forum/question/postanswer/', type='http', auth="user", multilang=True, methods=['POST'], website=True) + def post_answer(self, post_id, forum_id=1, **question): + cr, uid, context = request.cr, request.uid, request.context + create_context = dict(context) + new_question_id = request.registry['website.forum.post'].create( + request.cr, request.uid, { + #'forum_id': forum_id, + 'parent_id': post_id, + 'content': question.get('answer_content'), + 'state': 'active', + 'active': True, + }, context=create_context) + return werkzeug.utils.redirect("/forum/question/%s" % post_id) + + @http.route(['/forum/question/editanswer'], type='http', auth="user", website=True, multilang=True) + def edit_answer(self, post_id, **kwargs): + cr, uid, context = request.cr, request.uid, request.context + post = request.registry['website.forum.post'].browse(cr, uid, int(post_id), context=context) + for answer in post.child_ids: + if answer.create_uid.id == request.uid: + post_answer = answer + values = { + 'post': post, + 'post_answer': post_answer, + 'searches': kwargs + } + return request.website.render("website_forum.edit_answer", values) + + @http.route('/forum/question/saveanswer/', type='http', auth="user", multilang=True, methods=['POST'], website=True) + def save_edited_answer(self, forum_id=1, **post): + cr, uid, context = request.cr, request.uid, request.context + answer_id = int(post.get('answer_id')) + + new_question_id = request.registry['website.forum.post'].write( cr, uid, [answer_id], { + 'content': post.get('answer_content'), + }, context=context) + return werkzeug.utils.redirect("/forum/question/%s" % post.get('post_id')) + + @http.route(['/forum/tag/'], type='http', auth="public", website=True, multilang=True) + def tag_questions(self, tag, page=1, **kwargs): + cr, uid, context = request.cr, request.uid, request.context + step = 10 + pager = request.website.pager(url="/forum/", total=len(tag.post_ids), page=page, step=step, scope=10) + + values = { + 'question_ids': tag.post_ids, + 'pager': pager, + 'searches': kwargs + } + + return request.website.render("website_forum.index", values) + + @http.route(['/forum/tags', '/forum/tags/page/'], type='http', auth="public", website=True, multilang=True) + def tags(self, page=1, **searches): + cr, uid, context = request.cr, request.uid, request.context + tag_obj = request.registry['website.forum.tag'] + + step = 30 + tag_count = tag_obj.search(cr, uid, [], count=True, context=context) + pager = request.website.pager(url="/forum/tags/", total=tag_count, page=page, step=step, scope=30) + + obj_ids = tag_obj.search(cr, uid, [], limit=step, offset=pager['offset'], context=context) + tags = tag_obj.browse(cr, uid, obj_ids, context=context) + searches['tags'] = 'True' + + values = { + 'tags': tags, + 'pager': pager, + 'searches': searches, + } + + return request.website.render("website_forum.tag", values) + + @http.route(['/forum/users', '/forum/users/page/'], type='http', auth="public", website=True, multilang=True) + def users(self, page=1, **searches): + cr, uid, context = request.cr, request.uid, request.context + user_obj = request.registry['res.users'] + + step = 30 + tag_count = user_obj.search(cr, uid, [], count=True, context=context) + pager = request.website.pager(url="/forum/users/", total=tag_count, page=page, step=step, scope=30) + + obj_ids = user_obj.search(cr, uid, [], limit=step, offset=pager['offset'], context=context) + users = user_obj.browse(cr, uid, obj_ids, context=context) + searches['users'] = 'True' + + values = { + 'users': users, + 'pager': pager, + 'searches': searches, + } + + return request.website.render("website_forum.users", values) diff --git a/addons/website_forum/data/forum_data.xml b/addons/website_forum/data/forum_data.xml new file mode 100644 index 00000000000..3935f5dda89 --- /dev/null +++ b/addons/website_forum/data/forum_data.xml @@ -0,0 +1,23 @@ + + + + + + Support + /forum + + 40 + + + + + + diff --git a/addons/website_forum/data/forum_demo.xml b/addons/website_forum/data/forum_demo.xml new file mode 100644 index 00000000000..c1e8b8fc458 --- /dev/null +++ b/addons/website_forum/data/forum_demo.xml @@ -0,0 +1,290 @@ + + + + + + + Contract + + + + e-mail + + + + Csv + + + + Action + + + + Alert + + + + POS + + + + Menu + + + + How to configure alerts for employee contract expiration. + 45 + + + + + How to get translated state through browse ? + Hello everybody, + +I use a XML-RPC to access to invoices, I want to get the states of invoices, but always I get paid instead of PayƩ (French) + 45 + + + + Restrain the user from confirming the Purchase requisition + 45 + Can anybody tell me how to restrict the user from confirming the purchase requisition? In my case when a user is creating a purchase requisition than the user after saving it is able to send it to the supplier rather I want the user to just generate the purchase requisition and let the manager do the remaining works like sending the purchase requisition to the supplier and than receiving the goods. Please help me out. Thanks in advance. + + + + + Offline installing for add-ons + 45 + How can I install web based addons without Internet connections (I'm using OpenERP over LAN)? + +P.S. + +AFAIK current version (7.x) supports web interface while the desktop clients became deprecated. +BTW, I left OpenERP since ver. 6.x to use PostBooks instead but I recently decided to use OpenERP once again after recent new features. + + + + CMS replacement for ERP and eCommerce + 45 + I use Wordpress as a CMS and eCommerce platform. The developing in Wordpress is too easy and solid but it missing ERP feature (there is single plugin to integrate with Frontaccounting) so I wonder: + +Can I use OpenERP as a replacement CMS of Wordpress + eCommerce plugin? + +In simple words does OpenERP became CMS+ERP platform? + + + + Register payment using XMLRPC + 45 + Hi there, + +I have been trying to register an invoice payment through OpenERP. I have been able to manually create and approve the invoice, the voucher and its line + +I'm currently creating the voucher using this information: + + + + Create one field on two modules + 45 + Hello, + +I shall like creating a field "x" on an order form ( stock.picking.form ) and to reveal it also on the delivery slip ( stock.picking.form ). + +Thank you in advance + + + + access rights to one2many field + 45 + Now i create new group , assign user and access right as READ & CREATE to object stock.picking.ads but nothing is reflected , iam able to delete and write which should not happen. Very Strange!!! + + + + how to import csv into customer database + 45 + hi, how to import csv into customer database? Thanks + + + + + send mails not receiving properly + 45 + Hi, The options for sending mails have been enabled in openerp. + There are options to send the invoice,sales order, + quotations etc by the email.But the issue is even after the button 'Send By Email' is clicked, + the email is not received to the recipients .Those emails are stored in the Archives under the Messaging window.How to configure incoming and outgoing mails in openerp + + + + + How to refresh weight of stock move lines after product weight update? + 45 + + + + Update new module v7.0 Ubuntu 12.04 + 45 + + + + Can view top menu In openerp 7.0. (black taskbar at top of window???) + 45 + + + + + + + Just for posterity so other can see. Here are the steps to set automatic alerts on any contract.. i.e. HR Employee, or Fleet for example. I will use fleet as an example. + Step 1. As a user who has access rights to Technical Features, go to Settings --> Automated Actions. Create A new Automated Action. For the Related Document Model choose.. Contract information on a vehicle (you can also type in the actual model name.. fleet.vehicle.log.contract ) . Set the trigger date to ... Contract Expiration Date. + The Next Field (Delay After Trigger Date) is a bit ridiculous. Who wants to be reminded of a contract expiration AFTER the fact? The field should say Days Before Date to Fire Action and the number should be converted to a negative. IMHO. Any way... to get a workable solution you must enter in the number in the negative. So for instance like me if you want to be warned 35 days BEFORE the expiration... put in Delay After Trigger Date.. the number -35 But the sake of testing, right now just put in -1 for 1 day before. Save the Action. + Step 2. Go to Server Actions and create new Action. Call it Fleet Contract Expiration Warning. The Object will be the same as above .. Contract information on a vehicle. The Action Type is Email. For email address I just put my email. Under subject put in... [[object.name]]. This will tell you the name of the car. Message you can put any text message you like. Now save the Server Action. + Step 3. Now go back to the Automated Action you created and go to the Action tab next to the conditions tab. Click Add and add the server action you created . In this case Fleet Contract Expiration Warning. Then Save. + Step 4. To test, set a contract to expire tomorrow under one of your fleets vehicles. Then Save it. + 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. + + + + + + Hello everybody, + I use a XML-RPC to access to invoices, I want to get the states of invoices, but always I get paid instead of PayƩ (French) + I use administrator to connect to the databse, he has as lang = 'fr_FR' + def _get_invoice_states(self, cr, uid, ids, field_name, args, context=None): + if not context : + context = {} + user = self.pool.get('res.users').read(cr, uid, uid, ['lang','tz']) + context.update({'lang' : str(user.get('lang',u'fr_FR'))}) + context.update({'tz' : str(user.get('tz',u'GMT'))}) + context.update({'uid' : uid}) + result = {} + for invoice in self.browse(cr, uid, ids, context=context): + res = _('Name : ') + (invoice.number or '') + res += _('Etat : ') + (invoice.state or '') + + + + + + + Yes you could download the module from internet on another + computer and unzip the file on the ERP server. + Then you have to activate the module, see the tutorial on acespritechblog.wordpress.com (sorry I cannot post the full link) + + + + + + OpenERP v8 (next release) provides a web module and an e-commerce module: www.openerp.com/website_cms + The CMS editor in OpernERP web is nice but I prefer drupal for customization and there is a drupal module for OpenERP. I think WP is better than OpenERP web too. + + + + + + + no need to inherit account.voucher. after creating the account.voucher record, you just add this in your XMLRPC script. + + self.sock.execute(self.dbname, self.uid, self.pwd, 'account.voucher', + 'button_proforma_voucher', voucher_id, {}) + + voucher_id should be the ID of the created voucher. + + + + + + + I had the same issue and in my case I needed to remove the field "move_id" from account.voucher and adding the field "move_line_id" to account.voucher.line, have you tried that? + + move_line_id should match the ID of the account.move.line whose "move_id" field matches the "move_id" field of the invoice you created + + I also set 'type'='cr' for account.voucher.line + + After that, executing workflow('account.voucher', 'proforma_voucher', voucher_id) worked ok, + + Hope this helps! + + + + + + + Step 1: First make sure the CRM module is installed. If it isnt then you can click on the CRM module in the applications view. + + Step 2: Go to Settings -> Configuration -> General Settings and check the "Allow users to import data from CSV files" checkbox + + Step 3: Go to Sales -> Customers then switch to the List view by clicking the List view button in the upper right corner + + Step 4: Click on the "Import" link next to the red Create button + + Step 5: Click the "Choose File" button and find your csv file to import + + Step 6: Match the fields from the csv file to the fields in the drop down menus. + + Step 7: Click the "Validate" button to see if any errors occurred. Correct any errors on the csv file, save it, then refresh the file by clicking the refresh button next to the "Choose File" button + + Step 8: Click "Validate" again. If there are still errors, then repeat step 7, otherwise click the "Import" button. Then youre done + + + + + + + + Hello, + Email outgoing server can be configured in: Settings >> General Settings >> Email >> Outgoing mail server. + SMTP Server: Your SMTP server address + Connection Security: SSL or none + Username: Your E-Mail user name + Password: Your E-Mail password + + + + + + + also settings-> update moduel list? can find this anywhere?? + + + + + + + Give your admin user the technical features right and you will be able to see more options in the settings menu. (use a refresh) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/website_forum/doc/changelog.rst b/addons/website_forum/doc/changelog.rst new file mode 100644 index 00000000000..51036d22b70 --- /dev/null +++ b/addons/website_forum/doc/changelog.rst @@ -0,0 +1,9 @@ +.. _changelog: + +Changelog +========= + +`trunk (saas-3)` +---------------- + + - created ``website_forum`` diff --git a/addons/website_forum/doc/index.rst b/addons/website_forum/doc/index.rst new file mode 100644 index 00000000000..104e0473263 --- /dev/null +++ b/addons/website_forum/doc/index.rst @@ -0,0 +1,10 @@ +Website Forum Module documentation topics +''''''''''''''''''''''''''''''''''''''''' + +Changelog +''''''''' + +.. toctree:: + :maxdepth: 1 + + changelog.rst diff --git a/addons/website_forum/models/__init__.py b/addons/website_forum/models/__init__.py new file mode 100644 index 00000000000..510e6b63aa0 --- /dev/null +++ b/addons/website_forum/models/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import website_forum diff --git a/addons/website_forum/models/website_forum.py b/addons/website_forum/models/website_forum.py new file mode 100644 index 00000000000..dbf8e4072dc --- /dev/null +++ b/addons/website_forum/models/website_forum.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import re + +from openerp import SUPERUSER_ID +from openerp.osv import osv, fields +from openerp.tools.translate import _ + +from openerp.addons.website.models.website import slug + +#TODO: Do we need a forum object like blog object ? Need to check with BE team +class Forum(osv.Model): + _name = 'website.forum' + _description = 'Blogs' + _inherit = ['mail.thread', 'website.seo.metadata'] + _order = 'name' + + _columnss = { + 'name': fields.char('Name', required=True), + 'description': fields.text('Description'), + 'forum_post_ids': fields.one2many( + 'website.forum.post', 'forum_id', + 'Posts', + ), + } + +class Post(osv.Model): + _name = 'website.forum.post' + _description = "Question" + _inherit = ['mail.thread', 'website.seo.metadata'] + + _columns = { + 'forum_id': fields.many2one('website.forum', 'Forum'), + + 'name': fields.char('Topic', size=64), + 'content': fields.text('Contents', help='contents'), + + 'create_date': fields.datetime('Asked on', select=True, readonly=True), + 'create_uid': fields.many2one('res.users', 'Asked by', select=True, readonly=True ), + 'write_date': fields.datetime('Update on', select=True, readonly=True ), + 'write_uid': fields.many2one('res.users', 'Update by', select=True, readonly=True), + + 'tags': fields.many2many('website.forum.tag', 'forum_tag_rel', 'forum_id', 'forum_tag_id', 'Tag'), + 'vote_ids':fields.one2many('website.forum.post.vote', 'post_id', 'Vote'), + + 'favourite_ids': fields.many2many('res.users', 'forum_favourite_rel', 'forum_id', 'user_id', 'Favourite'), + + 'state': fields.selection([('active', 'Active'),('close', 'Close'),('offensive', 'Offensive')], 'Status'), + 'active': fields.boolean('Active'), + 'views': fields.integer('Views'), + + 'parent_id': fields.many2one('website.forum.post', 'Parent'), + 'child_ids': fields.one2many('website.forum.post', 'parent_id', 'Child'), + + # TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary + 'website_message_ids': fields.one2many( + 'mail.message', 'res_id', + domain=lambda self: [ + '&', ('model', '=', self._name), ('type', '=', 'comment') + ], + string='Post Messages', + help="Comments on forum post", + ), + } + _defaults = { + 'state': 'active', + 'active': True + } + + def create_history(self, cr, uid, ids, vals, context=None): + for post in ids: + history = self.pool.get('website.forum.post.history') + if vals.get('content'): + create_date = vals.get('create_date') + res = { + 'name': 'Update %s - %s' % (create_date, vals.get('name')), + 'content': vals.get('content', ''), + 'post_id': post + } + if vals.get('version'): + res.update({'version':vals.get('version')}) + + if vals.get('tags'): + res.update({'tags':vals.get('tags')}) + + history.create(cr, uid, res) + + def create(self, cr, uid, vals, context=None): + if context is None: + context = {} + create_context = dict(context, mail_create_nolog=True) + post_id = super(Post, self).create(cr, uid, vals, context=create_context) + self.create_history(cr, uid, [post_id], vals, context) + return post_id + + def write(self, cr, uid, ids, vals, context=None): + result = super(Post, self).write(cr, uid, ids, vals, context) + self.create_history(cr, uid, ids, vals, context) + return result + +class Users(osv.Model): + _inherit = 'res.users' + + _columns = { + 'question_ids':fields.one2many('website.forum.post', 'create_uid', 'Questions', domain=[('parent_id', '=', False)]), + 'answer_ids':fields.one2many('website.forum.post', 'create_uid', 'Answers', domain=[('parent_id', '!=', False)]), + 'vote_ids': fields.one2many('website.forum.post.vote', 'user_id', 'Votes'), + 'tags': fields.many2many('website.forum.tag', 'forum_tag_rel', 'forum_id', 'forum_tag_id', 'Tag'), + 'create_date': fields.datetime('Create Date', select=True, readonly=True), + + 'karma': fields.integer('Karma') + + # TODO: 'tag_ids':fields.function() + # Badges : we will use the groups to define badges, two purpose will get solved + # - Define Groups as Badges with Forum as an Application + # - Access control + } + +class PostHistory(osv.Model): + _name = 'website.forum.post.history' + _description = 'Post History' + _inherit = ['website.seo.metadata'] + _columns = { + 'post_id': fields.many2one('website.forum.post', 'Post'), + 'create_date': fields.datetime('Created on', select=True, readonly=True), + 'create_uid': fields.many2one('res.users', 'Created by', select=True, readonly=True), + 'version': fields.integer('Version'), + 'name': fields.char('Update Notes', size=64, required=True), + 'content': fields.html('Contents', help='Automatically sanitized HTML contents'), + 'tags': fields.many2many('website.forum.tag', 'forum_tag_rel', 'forum_id', 'forum_tag_id', 'Tag'), + } + +class Vote(osv.Model): + _name = 'website.forum.post.vote' + _description = 'Vote' + _columns = { + 'post_id': fields.many2one('website.forum.post', 'Post'), + 'user_id': fields.many2one('res.users', 'User'), + 'vote': fields.integer('rate'), + } + +class ForumActivity(osv.Model): + _name = "website.forum.activity" + _description = "Activity" + + _columns = { + 'name': fields.char('Order Reference', size=64, required=True), + 'post_id': fields.many2one('website.forum.post', 'Post'), + 'user_id': fields.many2one('res.users', 'User'), + 'create_date': fields.datetime('Created on', select=True, readonly=True), + 'create_uid': fields.many2one('res.users', 'Created by', select=True, readonly=True), + 'badge_id': fields.many2one('res.groups', 'Badge'), + 'karma_add': fields.integer('Added Karma'), + 'karma_sub': fields.integer('Karma Removed') + } + +class Tags(osv.Model): + _name = "website.forum.tag" + _description = "Tag" + _inherit = ['website.seo.metadata'] + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'post_ids': fields.many2many('website.forum.post', 'forum_tag_que_rel', 'tag_id', 'forum_id', 'Questions', readonly=True), + } + \ No newline at end of file diff --git a/addons/website_forum/security/ir.model.access.csv b/addons/website_forum/security/ir.model.access.csv new file mode 100644 index 00000000000..d67152a1979 --- /dev/null +++ b/addons/website_forum/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + +access_website_forum_post,website.forum.post,model_website_forum_post,base.group_public,1,0,0,0 +access_website_forum_post_history,website.forum.post.history,model_website_forum_post_history,base.group_public,1,0,0,0 +access_website_forum_post_vote,website.forum.post.vote,model_website_forum_post_vote,base.group_public,1,0,0,0 +access_website_forum_activity,website.forum.activity,model_website_forum_activity,base.group_public,1,0,0,0 +access_website_forum_tag,website.forum.tag,model_website_forum_tag,base.group_public,1,0,0,0 diff --git a/addons/website_forum/security/website_forum.xml b/addons/website_forum/security/website_forum.xml new file mode 100644 index 00000000000..5dd82f1d3ef --- /dev/null +++ b/addons/website_forum/security/website_forum.xml @@ -0,0 +1,15 @@ + + + + + + res_partner: public + + [] + + + + + + + diff --git a/addons/website_forum/static/src/css/Makefile b/addons/website_forum/static/src/css/Makefile new file mode 100644 index 00000000000..8d2edfb2ed2 --- /dev/null +++ b/addons/website_forum/static/src/css/Makefile @@ -0,0 +1,2 @@ +sass: + sass --compass --unix-newlines -t expanded website_forum.sass:website_forum.css diff --git a/addons/website_forum/static/src/css/website_forum.css b/addons/website_forum/static/src/css/website_forum.css new file mode 100644 index 00000000000..360e5f5b61d --- /dev/null +++ b/addons/website_forum/static/src/css/website_forum.css @@ -0,0 +1,20 @@ +.box { + padding-left: 8px; + padding-right: 8px; +} +.box span { + font-size: 200%; + font-weight: bold; +} + +.question .pull-left { + margin-right: 14px; + min-width: 80px; +} +.question .question-name { + font-size: 150%; +} + +.oe_grey { + background-color: #eeeeee; +} diff --git a/addons/website_forum/static/src/css/website_forum.sass b/addons/website_forum/static/src/css/website_forum.sass new file mode 100644 index 00000000000..495da57f01f --- /dev/null +++ b/addons/website_forum/static/src/css/website_forum.sass @@ -0,0 +1,21 @@ +@charset "utf-8" +@import "compass/css3" + + +.box + padding-left: 8px + padding-right: 8px + span + font-size: 200% + font-weight: bold + +.question + .pull-left + margin-right: 14px + min-width: 80px + .question-name + font-size: 150% + +.oe_grey + background-color: #eeeeee + diff --git a/addons/website_forum/static/src/js/website.tour.forum.js b/addons/website_forum/static/src/js/website.tour.forum.js new file mode 100644 index 00000000000..d03530e5596 --- /dev/null +++ b/addons/website_forum/static/src/js/website.tour.forum.js @@ -0,0 +1,31 @@ +(function () { + 'use strict'; + + var website = openerp.website; + var _t = openerp._t; + + website.EditorBar.include({ + start: function () { + this.registerTour(new website.QuestionTour(this)); + return this._super(); + }, + }); + + website.QuestionTour = website.Tour.extend({ + id: 'question', + name: "Create a question", + testPath: '/forum(/[0-9]+/register)?', + init: function (editor) { + var self = this; + self.steps = [ + { + title: _t("Create a question"), + content: _t("Let's go through the first steps to create a new question."), + popover: { next: _("Start Tutorial"), end: _("Skip It") }, + }, + ]; + return this._super(); + } + }); + +}()); diff --git a/addons/website_forum/static/src/js/website_forum.editor.js b/addons/website_forum/static/src/js/website_forum.editor.js new file mode 100644 index 00000000000..fc4dd863739 --- /dev/null +++ b/addons/website_forum/static/src/js/website_forum.editor.js @@ -0,0 +1,31 @@ +(function() { + "use strict"; + + var website = openerp.website; + var _t = openerp._t; + website.add_template_file('/website_forum/static/src/xml/website_forum.xml'); + + website.is_editable = true; + website.EditorBar.include({ + start: function() { + website.is_editable_button = website.is_editable_button || !!$("#wrap.js_event").size(); + var res = this._super(); + this.$(".dropdown:has(.oe_content_menu)").removeClass("hidden"); + return res; + }, + events: _.extend({}, website.EditorBar.prototype.events, { + 'click a[data-action=new_question]': function (ev) { + ev.preventDefault(); + website.prompt({ + id: "editor_new_question", + window_title: _t("New Question"), + input: "Question Name", + }).then(function (question_name) { + website.form('/forum/add_question', 'POST', { + question_name: question_name + }); + }); + } + }), + }); +})(); diff --git a/addons/website_forum/static/src/xml/website_forum.xml b/addons/website_forum/static/src/xml/website_forum.xml new file mode 100644 index 00000000000..06728f9e5ef --- /dev/null +++ b/addons/website_forum/static/src/xml/website_forum.xml @@ -0,0 +1,7 @@ + + + +
  • New Question
  • +
    +
    +
    \ No newline at end of file diff --git a/addons/website_forum/tests/__init__.py b/addons/website_forum/tests/__init__.py new file mode 100644 index 00000000000..3dcbf31d714 --- /dev/null +++ b/addons/website_forum/tests/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 20123TODAY OpenERP S.A. +# +# 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 . +# +############################################################################## + +#import test_ui + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/website_forum/tests/test_ui.py b/addons/website_forum/tests/test_ui.py new file mode 100644 index 00000000000..8337c357c40 --- /dev/null +++ b/addons/website_forum/tests/test_ui.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 . +# +############################################################################## + +import openerp.tests + +class TestUi(openerp.tests.HttpCase): + def test_admin(self): + self.phantom_js("/", "openerp.website.Tour.run_test('question')", "openerp.website.Tour") + diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml new file mode 100644 index 00000000000..ca71dbd3039 --- /dev/null +++ b/addons/website_forum/views/website_forum.xml @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +