[MERGE] merge with latest trunk
This commit is contained in:
commit
91af4e99ae
|
@ -50,11 +50,11 @@
|
|||
<filter string="No Responsible" domain="[('payment_responsible_id', '=', False)]"/>
|
||||
<filter string="My Follow-ups" domain="[('payment_responsible_id','=', uid)]" name="my"/>
|
||||
</group>
|
||||
<group expand="1" string="Group By...">
|
||||
<filter string="Follow-up Responsible" context="{'group_by':'payment_responsible_id'}"/>
|
||||
<filter string="Followup Level" context="{'group_by':'latest_followup_level_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
<group string="Group By..." position="inside">
|
||||
<filter string="Follow-up Responsible" context="{'group_by':'payment_responsible_id'}"/>
|
||||
<filter string="Followup Level" context="{'group_by':'latest_followup_level_id'}"/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@
|
|||
</record>
|
||||
|
||||
<!-- Case Sections Action -->
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_crm_tag_kanban_view_salesteams_oppor11">
|
||||
<field name="sequence" eval="0"/>
|
||||
<field name="view_mode">kanban</field>
|
||||
|
|
|
@ -361,8 +361,8 @@
|
|||
<filter string="Last Post (weekly)" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
|
||||
</group>
|
||||
<group string="Display">
|
||||
<filter string="Show Countries" context="{'invisible_country': False}" help="Show Countries"/>
|
||||
<filter string="Show Sales Team" context="{'invisible_section': False}" domain="[]" help="Show Sales Team" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Countries" context="{'invisible_country': False}" help="Countries"/>
|
||||
<filter string="Sales Team" context="{'invisible_section': False}" domain="[]" help="Sales Team" groups="base.group_multi_salesteams"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
@ -581,8 +581,8 @@
|
|||
<filter string="Last Post (weekly)" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
|
||||
</group>
|
||||
<group string="Display">
|
||||
<filter string="Show Sales Team" context="{'invisible_section': False}" domain="[]" help="Show Sales Team" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Show Countries" context="{'invisible_country': False}" help="Show Countries"/>
|
||||
<filter string="Sales Team" context="{'invisible_section': False}" domain="[]" help="Sales Team" groups="base.group_multi_salesteams"/>
|
||||
<filter string="Countries" context="{'invisible_country': False}" help="Countries"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -216,7 +216,7 @@ class delivery_grid(osv.osv):
|
|||
ok = True
|
||||
break
|
||||
if not ok:
|
||||
raise osv.except_osv(_('No price available!'), _('No line matched this product or order in the chosen delivery grid.'))
|
||||
raise osv.except_osv(_("Unable to fetch delivery method!"), _("Selected product in the delivery method doesn't fulfill any of the delivery grid(s) criteria."))
|
||||
|
||||
return price
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
<field name="period">once</field>
|
||||
<field name="visibility_mode">personal</field>
|
||||
<field name="report_message_frequency">never</field>
|
||||
<field name="user_domain">[('groups_id', 'in', ref('base.group_user'))]</field>
|
||||
<field name="user_domain" eval="[('groups_id', '=', ref('base.group_user'))]" />
|
||||
<field name="state">inprogress</field>
|
||||
<field name="category">other</field>
|
||||
</record>
|
||||
|
@ -174,7 +174,7 @@
|
|||
<field name="period">once</field>
|
||||
<field name="visibility_mode">personal</field>
|
||||
<field name="report_message_frequency">never</field>
|
||||
<field name="user_domain">[('groups_id', 'in', ref('base.user_root'))]</field>
|
||||
<field name="user_domain" eval="[('groups_id', '=', ref('base.group_erp_manager'))]" />
|
||||
<field name="state">inprogress</field>
|
||||
<field name="category">other</field>
|
||||
</record>
|
||||
|
|
|
@ -21,14 +21,13 @@
|
|||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF
|
||||
from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT as DF
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
import calendar
|
||||
import logging
|
||||
import functools
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# display top 3 in ranking, could be db variable
|
||||
|
@ -117,12 +116,6 @@ class gamification_challenge(osv.Model):
|
|||
except ValueError:
|
||||
return False
|
||||
|
||||
def _get_challenger_users(self, cr, uid, domain, context=None):
|
||||
ref = functools.partial(self.pool['ir.model.data'].xmlid_to_res_id, cr, uid)
|
||||
user_domain = eval(domain, {'ref': ref})
|
||||
return self.pool['res.users'].search(cr, uid, user_domain, context=context)
|
||||
|
||||
|
||||
_order = 'end_date, start_date, name, id'
|
||||
_columns = {
|
||||
'name': fields.char('Challenge Name', required=True, translate=True),
|
||||
|
@ -218,7 +211,6 @@ class gamification_challenge(osv.Model):
|
|||
def create(self, cr, uid, vals, context=None):
|
||||
"""Overwrite the create method to add the user of groups"""
|
||||
|
||||
# add users when change the group auto-subscription
|
||||
if vals.get('user_domain'):
|
||||
user_ids = self._get_challenger_users(cr, uid, vals.get('user_domain'), context=context)
|
||||
|
||||
|
@ -240,14 +232,18 @@ class gamification_challenge(osv.Model):
|
|||
if isinstance(ids, (int,long)):
|
||||
ids = [ids]
|
||||
|
||||
if vals.get('state') == 'inprogress':
|
||||
for challenge in self.browse(cr, uid, ids, context=context):
|
||||
user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context)
|
||||
write_op = [(4, user_id) for user_id in user_ids]
|
||||
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
|
||||
self.message_subscribe_users(cr, uid, [challenge.id], user_ids, context=context)
|
||||
if vals.get('user_domain'):
|
||||
user_ids = self._get_challenger_users(cr, uid, vals.get('user_domain'), context=context)
|
||||
|
||||
self.generate_goals_from_challenge(cr, uid, ids, context=context)
|
||||
if not vals.get('user_ids'):
|
||||
vals['user_ids'] = []
|
||||
vals['user_ids'] += [(4, user_id) for user_id in user_ids]
|
||||
|
||||
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
if vals.get('state') == 'inprogress':
|
||||
self._recompute_challenge_users(cr, uid, ids, context=context)
|
||||
self._generate_goals_from_challenge(cr, uid, ids, context=context)
|
||||
|
||||
elif vals.get('state') == 'done':
|
||||
self.check_challenge_reward(cr, uid, ids, force=True, context=context)
|
||||
|
@ -256,9 +252,6 @@ class gamification_challenge(osv.Model):
|
|||
# resetting progress
|
||||
if self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids), ('state', '=', 'inprogress')], context=context):
|
||||
raise osv.except_osv("Error", "You can not reset a challenge with unfinished goals.")
|
||||
|
||||
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
|
||||
return write_res
|
||||
|
||||
|
@ -314,18 +307,10 @@ class gamification_challenge(osv.Model):
|
|||
# update every running goal already generated linked to selected challenges
|
||||
goal_obj.update(cr, uid, goal_ids, context=context)
|
||||
|
||||
self._recompute_challenge_users(cr, uid, ids, context=context)
|
||||
self._generate_goals_from_challenge(cr, uid, ids, context=context)
|
||||
|
||||
for challenge in self.browse(cr, uid, ids, context=context):
|
||||
# in case of new users matching the domain
|
||||
old_user_ids = [user.id for user in challenge.user_ids]
|
||||
new_user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context)
|
||||
to_remove_ids = list(set(old_user_ids) - set(new_user_ids))
|
||||
to_add_ids = list(set(new_user_ids) - set(old_user_ids))
|
||||
|
||||
write_op = [(3, user_id) for user_id in to_remove_ids]
|
||||
write_op += [(4, user_id) for user_id in to_add_ids]
|
||||
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
|
||||
|
||||
self.generate_goals_from_challenge(cr, uid, [challenge.id], context=context)
|
||||
|
||||
# goals closed but still opened at the last report date
|
||||
closed_goals_to_report = goal_obj.search(cr, uid, [
|
||||
|
@ -345,11 +330,37 @@ class gamification_challenge(osv.Model):
|
|||
return True
|
||||
|
||||
def quick_update(self, cr, uid, challenge_id, context=None):
|
||||
"""Update all the goals of a challenge, no generation of new goals"""
|
||||
"""Update all the goals of a specific challenge, no generation of new goals"""
|
||||
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', '=', challenge_id)], context=context)
|
||||
self.pool.get('gamification.goal').update(cr, uid, goal_ids, context=context)
|
||||
return True
|
||||
|
||||
def _get_challenger_users(self, cr, uid, domain, context=None):
|
||||
user_domain = eval(ustr(domain))
|
||||
return self.pool['res.users'].search(cr, uid, user_domain, context=context)
|
||||
|
||||
def _recompute_challenge_users(self, cr, uid, challenge_ids, context=None):
|
||||
"""Recompute the domain to add new users and remove the one no longer matching the domain"""
|
||||
for challenge in self.browse(cr, uid, challenge_ids, context=context):
|
||||
if challenge.user_domain:
|
||||
|
||||
old_user_ids = [user.id for user in challenge.user_ids]
|
||||
new_user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context)
|
||||
to_remove_ids = list(set(old_user_ids) - set(new_user_ids))
|
||||
to_add_ids = list(set(new_user_ids) - set(old_user_ids))
|
||||
|
||||
write_op = [(3, user_id) for user_id in to_remove_ids]
|
||||
write_op += [(4, user_id) for user_id in to_add_ids]
|
||||
if write_op:
|
||||
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
|
||||
|
||||
if to_remove_ids:
|
||||
self.message_unsubscribe_users(cr, uid, [challenge.id], to_remove_ids, context=None)
|
||||
if to_add_ids:
|
||||
self.message_subscribe_users(cr, uid, [challenge.id], to_add_ids, context=context)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def action_check(self, cr, uid, ids, context=None):
|
||||
"""Check a challenge
|
||||
|
@ -370,12 +381,17 @@ class gamification_challenge(osv.Model):
|
|||
##### Automatic actions #####
|
||||
|
||||
def generate_goals_from_challenge(self, cr, uid, ids, context=None):
|
||||
_logger.warning("Deprecated, use private method _generate_goals_from_challenge(...) instead.")
|
||||
return self._generate_goals_from_challenge(cr, uid, ids, context=context)
|
||||
|
||||
def _generate_goals_from_challenge(self, cr, uid, ids, context=None):
|
||||
"""Generate the goals for each line and user.
|
||||
|
||||
If goals already exist for this line and user, the line is skipped. This
|
||||
can be called after each change in the list of users or lines.
|
||||
:param list(int) ids: the list of challenge concerned"""
|
||||
|
||||
goal_obj = self.pool.get('gamification.goal')
|
||||
to_update = []
|
||||
for challenge in self.browse(cr, uid, ids, context=context):
|
||||
(start_date, end_date) = start_end_date_for_period(challenge.period)
|
||||
|
@ -387,45 +403,49 @@ class gamification_challenge(osv.Model):
|
|||
end_date = challenge.end_date
|
||||
|
||||
for line in challenge.line_ids:
|
||||
# FIXME: allow to restrict to a subset of users
|
||||
for user in challenge.user_ids:
|
||||
|
||||
goal_obj = self.pool.get('gamification.goal')
|
||||
domain = [('line_id', '=', line.id), ('user_id', '=', user.id)]
|
||||
if start_date:
|
||||
domain.append(('start_date', '=', start_date))
|
||||
# there is potentially a lot of users
|
||||
# detect the ones with no goal linked to this line
|
||||
date_clause = ""
|
||||
query_params = [line.id]
|
||||
if start_date:
|
||||
date_clause += "AND g.start_date = %s"
|
||||
query_params.append(start_date)
|
||||
if end_date:
|
||||
date_clause += "AND g.end_date = %s"
|
||||
query_params.append(end_date)
|
||||
|
||||
query = """SELECT u.id AS user_id
|
||||
FROM res_users u
|
||||
LEFT JOIN gamification_goal g
|
||||
ON (u.id = g.user_id)
|
||||
WHERE line_id = %s
|
||||
{date_clause}
|
||||
""".format(date_clause=date_clause)
|
||||
|
||||
# goal already existing for this line ?
|
||||
if len(goal_obj.search(cr, uid, domain, context=context)) > 0:
|
||||
cr.execute(query, query_params)
|
||||
user_with_goal_ids = cr.dictfetchall()
|
||||
user_without_goal_ids = list(set([user.id for user in challenge.user_ids]) - set([user['user_id'] for user in user_with_goal_ids]))
|
||||
|
||||
# resume canceled goals
|
||||
domain.append(('state', '=', 'canceled'))
|
||||
canceled_goal_ids = goal_obj.search(cr, uid, domain, context=context)
|
||||
if canceled_goal_ids:
|
||||
goal_obj.write(cr, uid, canceled_goal_ids, {'state': 'inprogress'}, context=context)
|
||||
to_update.extend(canceled_goal_ids)
|
||||
values = {
|
||||
'definition_id': line.definition_id.id,
|
||||
'line_id': line.id,
|
||||
'target_goal': line.target_goal,
|
||||
'state': 'inprogress',
|
||||
}
|
||||
|
||||
# skip to next user
|
||||
continue
|
||||
if start_date:
|
||||
values['start_date'] = start_date
|
||||
if end_date:
|
||||
values['end_date'] = end_date
|
||||
|
||||
values = {
|
||||
'definition_id': line.definition_id.id,
|
||||
'line_id': line.id,
|
||||
'user_id': user.id,
|
||||
'target_goal': line.target_goal,
|
||||
'state': 'inprogress',
|
||||
}
|
||||
if challenge.remind_update_delay:
|
||||
values['remind_update_delay'] = challenge.remind_update_delay
|
||||
|
||||
if start_date:
|
||||
values['start_date'] = start_date
|
||||
if end_date:
|
||||
values['end_date'] = end_date
|
||||
|
||||
if challenge.remind_update_delay:
|
||||
values['remind_update_delay'] = challenge.remind_update_delay
|
||||
|
||||
new_goal_id = goal_obj.create(cr, uid, values, context=context)
|
||||
to_update.append(new_goal_id)
|
||||
for user_id in user_without_goal_ids:
|
||||
values.update({'user_id': user_id})
|
||||
goal_id = goal_obj.create(cr, uid, values, context=context)
|
||||
to_update.append(goal_id)
|
||||
|
||||
goal_obj.update(cr, uid, to_update, context=context)
|
||||
|
||||
|
@ -638,7 +658,7 @@ class gamification_challenge(osv.Model):
|
|||
message = "%s has joined the challenge" % user.name
|
||||
self.message_post(cr, SUPERUSER_ID, challenge_ids, body=message, context=context)
|
||||
self.write(cr, SUPERUSER_ID, challenge_ids, {'invited_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context)
|
||||
return self.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||
return self._generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||
|
||||
# TODO in trunk, remove unused parameter user_id
|
||||
def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv
|
||||
from challenge import MAX_VISIBILITY_RANKING
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
<label for="user_domain" class="oe_edit_only" string="Assign Challenge To"/>
|
||||
<div>
|
||||
<field name="user_domain" widget="char_domain" options="{'model': 'res.users'}" />
|
||||
<field name="user_ids" groups="base.group_no_one" widget="many2many_tags" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
<field name="name">Monthly Sales Targets</field>
|
||||
<field name="period">monthly</field>
|
||||
<field name="visibility_mode">ranking</field>
|
||||
<field name="user_domain">[('groups_id', 'in', ref('base.group_sale_salesman'))]</field>
|
||||
<field name="user_domain" eval="[('groups_id', 'in', ref('base.group_sale_salesman'))]" />
|
||||
<field name="report_message_frequency">weekly</field>
|
||||
</record>
|
||||
|
||||
|
@ -138,7 +138,7 @@
|
|||
<field name="name">Lead Acquisition</field>
|
||||
<field name="period">monthly</field>
|
||||
<field name="visibility_mode">ranking</field>
|
||||
<field name="user_domain">[('groups_id', 'in', ref('base.group_sale_salesman'))]</field>
|
||||
<field name="user_domain" eval="[('groups_id', 'in', ref('base.group_sale_salesman'))]" />
|
||||
<field name="report_message_frequency">weekly</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ class hr_timesheet_sheet(osv.osv):
|
|||
def create(self, cr, uid, vals, *args, **argv):
|
||||
if 'employee_id' in vals:
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).user_id:
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must assign it to a user.'))
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must link him/her to a user.'))
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).product_id:
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must link the employee to a product, like \'Consultant\'.'))
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).journal_id:
|
||||
|
@ -76,7 +76,7 @@ class hr_timesheet_sheet(osv.osv):
|
|||
if 'employee_id' in vals:
|
||||
new_user_id = self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).user_id.id or False
|
||||
if not new_user_id:
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must assign it to a user.'))
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must link him/her to a user.'))
|
||||
if not self._sheet_date(cr, uid, ids, forced_user_id=new_user_id):
|
||||
raise osv.except_osv(_('Error!'), _('You cannot have 2 timesheets that overlap!\nYou should use the menu \'My Timesheet\' to avoid this problem.'))
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).product_id:
|
||||
|
|
|
@ -25,16 +25,14 @@ class TestMassMailing(osv.TransientModel):
|
|||
test_emails = tools.email_split(wizard.email_to)
|
||||
mail_ids = []
|
||||
for test_mail in test_emails:
|
||||
body = mailing.body_html
|
||||
unsubscribe_url = self.pool['mail.mass_mailing'].get_unsubscribe_url(cr, uid, mailing.id, 0, email=test_mail, context=context)
|
||||
body = tools.append_content_to_html(body, unsubscribe_url, plaintext=False, container_tag='p')
|
||||
mail_values = {
|
||||
'email_from': mailing.email_from,
|
||||
'reply_to': mailing.reply_to,
|
||||
'email_to': test_mail,
|
||||
'subject': mailing.name,
|
||||
'body_html': body,
|
||||
'body_html': mailing.body_html,
|
||||
'auto_delete': True,
|
||||
'mailing_id': wizard.mass_mailing_id.id,
|
||||
}
|
||||
mail_ids.append(Mail.create(cr, uid, mail_values, context=context))
|
||||
Mail.send(cr, uid, mail_ids, context=context)
|
||||
|
|
|
@ -106,8 +106,8 @@
|
|||
<separator/>
|
||||
<filter string="No Timebox" domain="[('timebox_id', '=', False)]" help="Tasks having no timebox assigned yet"/>
|
||||
<group expand="0" string="Display">
|
||||
<filter string="Show Context" name="context_show" context="{'context_show': True}" domain="[]" icon="terp-camera_test" help="Show the context field"/>
|
||||
<filter string="Show Deadlines" context="{'deadline_visible': False}" domain="[]" help="Show only tasks having a deadline" icon="terp-gnome-cpu-frequency-applet+"/>
|
||||
<filter string="Context" name="context_show" context="{'context_show': True}" domain="[]" icon="terp-camera_test" help="Show the context field"/>
|
||||
<filter string="Deadlines" context="{'deadline_visible': False}" domain="[]" help="Show only tasks having a deadline" icon="terp-gnome-cpu-frequency-applet+"/>
|
||||
</group>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Stage" name="group_stage_id" context="{'group_by':'stage_id'}"/>
|
||||
|
|
|
@ -468,7 +468,7 @@ class purchase_order(osv.osv):
|
|||
if not acc_id:
|
||||
acc_id = po_line.product_id.categ_id.property_account_expense_categ.id
|
||||
if not acc_id:
|
||||
raise osv.except_osv(_('Error!'), _('Define expense account for this company: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,))
|
||||
raise osv.except_osv(_('Error!'), _('Define an expense account for this product: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,))
|
||||
else:
|
||||
acc_id = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context=context).id
|
||||
fpos = po_line.order_id.fiscal_position or False
|
||||
|
@ -590,7 +590,7 @@ class purchase_order(osv.osv):
|
|||
if inv and inv.state not in ('cancel','draft'):
|
||||
raise osv.except_osv(
|
||||
_('Unable to cancel this purchase order.'),
|
||||
_('You must first cancel all receptions related to this purchase order.'))
|
||||
_('You must first cancel all invoices related to this purchase order.'))
|
||||
self.pool.get('account.invoice') \
|
||||
.signal_invoice_cancel(cr, uid, map(attrgetter('id'), purchase.invoice_ids))
|
||||
self.write(cr,uid,ids,{'state':'cancel'})
|
||||
|
|
|
@ -36,7 +36,7 @@ class purchase_requisition_partner(osv.osv_memory):
|
|||
record_id = context and context.get('active_id', False) or False
|
||||
tender = self.pool.get('purchase.requisition').browse(cr, uid, record_id, context=context)
|
||||
if not tender.line_ids:
|
||||
raise osv.except_osv(_('Error!'), _('No Product in Tender.'))
|
||||
raise osv.except_osv(_('Error!'), _('Define product(s) you want to include in the call for bids.'))
|
||||
return res
|
||||
|
||||
def create_order(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
|
||||
import openerp
|
||||
import openerp.tests
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -159,7 +159,7 @@ class sale_order(osv.osv):
|
|||
if s['state'] in ['draft', 'cancel']:
|
||||
unlink_ids.append(s['id'])
|
||||
else:
|
||||
raise osv.except_osv(_('Invalid Action!'), _('In order to delete a confirmed sales order, you must cancel it.\nTo do so, you must first cancel related picking for delivery orders.'))
|
||||
raise osv.except_osv(_('Invalid Action!'), _('In order to delete a confirmed sales order, you must cancel it. \nTo do so, you must first cancel all related delivery order(s).'))
|
||||
|
||||
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
|
||||
|
||||
|
|
|
@ -1195,7 +1195,7 @@ class stock_picking(osv.osv):
|
|||
return True
|
||||
for move in pick.move_lines:
|
||||
if move.state == 'done':
|
||||
raise osv.except_osv(_('Error!'), _('You cannot cancel the picking as some moves have been done. You should cancel the picking lines.'))
|
||||
raise osv.except_osv(_('Error!'), _('You cannot cancel the picking as some moves have been done. You should cancel remaining moves of this picking.'))
|
||||
return True
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -34,6 +34,27 @@ import uuid
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class survey_stage(osv.Model):
|
||||
"""Stages for Kanban view of surveys"""
|
||||
|
||||
_name = 'survey.stage'
|
||||
_description = 'Survey Stage'
|
||||
_order = 'sequence asc'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(string="Name", required=True, translate=True),
|
||||
'sequence': fields.integer(string="Sequence"),
|
||||
'closed': fields.boolean(string="Closed", help="If closed, people won't be able to answer to surveys in this column."),
|
||||
'fold': fields.boolean(string="Folded in kanban view")
|
||||
}
|
||||
_defaults = {
|
||||
'sequence': 1,
|
||||
'closed': False
|
||||
}
|
||||
_sql_constraints = [
|
||||
('positive_sequence', 'CHECK(sequence >= 0)', 'Sequence number MUST be a natural')
|
||||
]
|
||||
|
||||
|
||||
class survey_survey(osv.Model):
|
||||
'''Settings for a multi-page/multi-question survey.
|
||||
|
@ -175,9 +196,15 @@ class survey_survey(osv.Model):
|
|||
'quizz_mode': fields.boolean(string='Quizz mode')
|
||||
}
|
||||
|
||||
def _default_stage(self, cr, uid, context=None):
|
||||
ids = self.pool['survey.stage'].search(cr, uid, [], limit=1, context=context)
|
||||
if ids:
|
||||
return ids[0]
|
||||
return False
|
||||
|
||||
_defaults = {
|
||||
'color': 0,
|
||||
'stage_id': lambda self, cr, uid, context: self.pool.get('survey.stage').search_read(cr, uid, fields=['id'], order='sequence asc', limit=1, context=context)[0]['id']
|
||||
'stage_id': lambda self, *a, **kw: self._default_stage(*a, **kw)
|
||||
}
|
||||
|
||||
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
||||
|
@ -456,26 +483,6 @@ class survey_survey(osv.Model):
|
|||
}
|
||||
|
||||
|
||||
class survey_stage(osv.Model):
|
||||
"""Stages for Kanban view of surveys"""
|
||||
|
||||
_name = 'survey.stage'
|
||||
_description = 'Survey Stage'
|
||||
_order = 'sequence asc'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(string="Name", required=True, translate=True),
|
||||
'sequence': fields.integer(string="Sequence"),
|
||||
'closed': fields.boolean(string="Closed", help="If closed, people won't be able to answer to surveys in this column."),
|
||||
'fold': fields.boolean(string="Folded in kanban view")
|
||||
}
|
||||
_defaults = {
|
||||
'sequence': 1,
|
||||
'closed': False
|
||||
}
|
||||
_sql_constraints = [
|
||||
('positive_sequence', 'CHECK(sequence >= 0)', 'Sequence number MUST be a natural')
|
||||
]
|
||||
|
||||
|
||||
class survey_page(osv.Model):
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
|
||||
*/
|
||||
|
||||
var dummy = function () {};
|
||||
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website/static/src/xml/website.snippets.xml');
|
||||
|
@ -361,15 +362,21 @@
|
|||
},
|
||||
clean_for_save: function () {
|
||||
var self = this;
|
||||
$(website.snippet.globalSelector).each(function () {
|
||||
var $snippet = $(this);
|
||||
self.make_active($snippet);
|
||||
self.make_active(false);
|
||||
var editor = $snippet.data("snippet-editor");
|
||||
if (editor) {
|
||||
editor.clean_for_save();
|
||||
|
||||
$("*[contentEditable], *[attributeEditable]")
|
||||
.removeAttr('contentEditable')
|
||||
.removeAttr('attributeEditable');
|
||||
|
||||
var options = website.snippet.options;
|
||||
var template = website.snippet.templateOptions;
|
||||
for (var k in options) {
|
||||
if (template[k] && options[k].prototype.clean_for_save !== dummy) {
|
||||
var $snippet = this.dom_filter(template[k].selector);
|
||||
$snippet.each(function () {
|
||||
new options[k](self, null, $(this), k).clean_for_save();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
make_active: function ($snippet) {
|
||||
if ($snippet && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snippet.get(0)) {
|
||||
|
@ -853,8 +860,7 @@
|
|||
}
|
||||
},
|
||||
|
||||
clean_for_save: function () {
|
||||
}
|
||||
clean_for_save: dummy
|
||||
});
|
||||
|
||||
website.snippet.options.background = website.snippet.Option.extend({
|
||||
|
@ -942,8 +948,7 @@
|
|||
this.id = this.unique_id();
|
||||
this.$target.attr("id", this.id);
|
||||
this.$target.find("[data-slide]").attr("data-cke-saved-href", "#" + this.id);
|
||||
this.$target.find("[data-slide-to]").attr("data-target", "#" + this.id);
|
||||
|
||||
this.$target.find("[data-target]").attr("data-target", "#" + this.id);
|
||||
this.rebind_event();
|
||||
},
|
||||
on_clone: function ($clone) {
|
||||
|
@ -963,7 +968,7 @@
|
|||
},
|
||||
clean_for_save: function () {
|
||||
this._super();
|
||||
this.$target.find(".item").removeClass("next prev left right");
|
||||
$(".carousel").find(".item").removeClass("next prev left right active");
|
||||
if(!this.$target.find(".item.active").length) {
|
||||
this.$target.find(".item:first").addClass("active");
|
||||
}
|
||||
|
@ -1757,19 +1762,6 @@
|
|||
}
|
||||
this.$overlay.removeClass('oe_active');
|
||||
},
|
||||
|
||||
/* clean_for_save
|
||||
* function called just before save vue
|
||||
*/
|
||||
clean_for_save: function () {
|
||||
for (var i in this.styles){
|
||||
this.styles[i].clean_for_save();
|
||||
}
|
||||
this.$target.removeAttr('contentEditable')
|
||||
.find('*').removeAttr('contentEditable');
|
||||
this.$target.removeAttr('attributeEditable')
|
||||
.find('*').removeAttr('attributeEditable');
|
||||
},
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -52,7 +52,7 @@ class BlogPost(osv.Model):
|
|||
'name': fields.char('Title', required=True, translate=True),
|
||||
'subtitle': fields.char('Sub Title', translate=True),
|
||||
'author_id': fields.many2one('res.partner', 'Author'),
|
||||
'background_image': fields.binary('Background Image'),
|
||||
'background_image': fields.binary('Background Image', oldname='content_image'),
|
||||
'blog_id': fields.many2one(
|
||||
'blog.blog', 'Blog',
|
||||
required=True, ondelete='cascade',
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import certification
|
||||
import controllers
|
|
@ -0,0 +1,38 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'Certified People',
|
||||
'category': 'Website',
|
||||
'summary': 'Display your network of certified people on your website',
|
||||
'version': '1.0',
|
||||
'author': 'OpenERP S.A.',
|
||||
'depends': ['marketing', 'website'],
|
||||
'description': """
|
||||
Display your network of certified people on your website
|
||||
""",
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/website_certification_views.xml',
|
||||
'views/website_certification_templates.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class certification_type(osv.Model):
|
||||
_name = 'certification.type'
|
||||
_order = 'name ASC'
|
||||
_columns = {
|
||||
'name': fields.char("Certification Type", required=True)
|
||||
}
|
||||
|
||||
|
||||
class certification_certification(osv.Model):
|
||||
_name = 'certification.certification'
|
||||
_order = 'certification_date DESC'
|
||||
_columns = {
|
||||
'partner_id': fields.many2one('res.partner', string="Partner", required=True),
|
||||
'type_id': fields.many2one('certification.type', string="Certification", required=True),
|
||||
'certification_date': fields.date("Certification Date", required=True),
|
||||
'certification_score': fields.char("Certification Score", required=True),
|
||||
'certification_hidden_score': fields.boolean("Hide score on website?")
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import main
|
|
@ -0,0 +1,49 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
|
||||
|
||||
class WebsiteCertifiedPartners(http.Controller):
|
||||
|
||||
@http.route(['/certifications',
|
||||
'/certifications/<model("certification.type"):cert_type>'], type='http', auth='public',
|
||||
website=True, multilang=True)
|
||||
def certified_partners(self, cert_type=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
certification_obj = request.registry['certification.certification']
|
||||
cert_type_obj = request.registry['certification.type']
|
||||
|
||||
domain = []
|
||||
if cert_type:
|
||||
domain.append(('type_id', '=', cert_type.id))
|
||||
|
||||
certifications_ids = certification_obj.search(cr, uid, domain, context=context)
|
||||
certifications = certification_obj.browse(cr, uid, certifications_ids, context=context)
|
||||
types = cert_type_obj.browse(cr, uid, cert_type_obj.search(cr, uid, [], context=context), context=context)
|
||||
data = {
|
||||
'certifications': certifications,
|
||||
'types': types
|
||||
}
|
||||
|
||||
return request.website.render("website_certification.certified_partners", data)
|
|
@ -0,0 +1,7 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_certifications_public,certification.certification public,model_certification_certification,base.group_public,1,0,0,0
|
||||
access_certifications_types_public,certification.type public,model_certification_type,base.group_public,1,0,0,0
|
||||
access_certifications_users,certification.certification users,model_certification_certification,base.group_user,1,0,0,0
|
||||
access_certifications_types_users,certification.type users,model_certification_type,base.group_user,1,0,0,0
|
||||
access_certifications_marketing,certification.certification marketing,model_certification_certification,marketing.group_marketing_user,1,1,1,1
|
||||
access_certifications_types_marketing,certification.type marketing,model_certification_type,marketing.group_marketing_user,1,1,1,1
|
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="certified_partners" name="Certified People">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<h1>Certified People</h1>
|
||||
<div class="well">Filter by certification type:
|
||||
<a href="/certifications"><span class="badge">all</span></a>
|
||||
<t t-foreach="types" t-as="t">
|
||||
<a t-att-href="'/certifications/%s' % slug(t)"><span class="badge" t-field="t.name" /></a>
|
||||
</t>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead><tr>
|
||||
<th>Name</th>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
<th>Score</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr t-foreach="certifications" t-as="c">
|
||||
<td><span t-field="c.partner_id"/></td>
|
||||
<td><span t-field="c.certification_date"/></td>
|
||||
<td><span t-field="c.type_id.name"/></td>
|
||||
<td><t t-if="c.certification_hidden_score == False"><span t-field="c.certification_score"/></t></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="certification_certification_tree">
|
||||
<field name="name">view.certification.certification.tree</field>
|
||||
<field name="model">certification.certification</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Granted Certifications">
|
||||
<field name="partner_id" />
|
||||
<field name="type_id" />
|
||||
<field name="certification_date" />
|
||||
<field name="certification_score" />
|
||||
<field name="certification_hidden_score" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="certification_certification_form">
|
||||
<field name="name">view.certification.certification.form</field>
|
||||
<field name="model">certification.certification</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Granted Certification" version="7.0">
|
||||
<sheet>
|
||||
<group nolabel="1">
|
||||
<field name="partner_id" />
|
||||
<field name="type_id" />
|
||||
<field name="certification_date" />
|
||||
<field name="certification_score" />
|
||||
<field name="certification_hidden_score" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="certification_certification_search">
|
||||
<field name="name">view.certification.certification.search</field>
|
||||
<field name="model">certification.certification</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Certification">
|
||||
<field name="partner_id" />
|
||||
<field name="type_id" />
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Partner" name="group_by_partner" domain="[]" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Type" name="group_by_type" domain="[]" context="{'group_by': 'type_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_certifications_list">
|
||||
<field name="name">Certifications</field>
|
||||
<field name="res_model">certification.certification</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="certification_certification_search"/>
|
||||
</record>
|
||||
<menuitem name="Certifications" id="menu_certifications" parent="base.marketing_menu" sequence="35" />
|
||||
<menuitem name="Certifications" id="menu_certifications_list" action="action_certifications_list" parent="menu_certifications" sequence="1"/>
|
||||
</data>
|
||||
</openerp>
|
|
@ -32,12 +32,21 @@ class WebsiteForum(http.Controller):
|
|||
return msg
|
||||
|
||||
def _prepare_forum_values(self, forum=None, **kwargs):
|
||||
Forum = request.registry['forum.forum']
|
||||
user = request.registry['res.users'].browse(request.cr, request.uid, request.uid, context=request.context)
|
||||
public_uid = request.registry['website'].get_public_user(request.cr, request.uid, request.context)
|
||||
values = {'user': user, 'is_public_user': user.id == public_uid,
|
||||
'notifications': self._get_notifications(),
|
||||
'header': kwargs.get('header', dict()),
|
||||
'searches': kwargs.get('searches', dict())}
|
||||
'searches': kwargs.get('searches', dict()),
|
||||
'can_edit_own': True,
|
||||
'can_edit_all': user.karma > Forum._karma_modo_edit_all,
|
||||
'can_close_own': user.karma > Forum._karma_modo_close_own,
|
||||
'can_close_all': user.karma > Forum._karma_modo_close_all,
|
||||
'can_unlink_own': user.karma > Forum._karma_modo_unlink_own,
|
||||
'can_unlink_all': user.karma > Forum._karma_modo_unlink_all,
|
||||
'can_unlink_comment': user.karma > Forum._karma_modo_unlink_comment,
|
||||
}
|
||||
if forum:
|
||||
values['forum'] = forum
|
||||
elif kwargs.get('forum_id'):
|
||||
|
@ -45,6 +54,14 @@ class WebsiteForum(http.Controller):
|
|||
values.update(kwargs)
|
||||
return values
|
||||
|
||||
def _has_enough_karma(self, karma_name, uid=None):
|
||||
Forum = request.registry['forum.forum']
|
||||
karma = hasattr(Forum, karma_name) and getattr(Forum, karma_name) or 0
|
||||
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, uid or request.uid, context=request.context)
|
||||
if user.karma < karma:
|
||||
return False, {'error': 'not_enough_karma', 'karma': karma}
|
||||
return True, {}
|
||||
|
||||
# Forum
|
||||
# --------------------------------------------------
|
||||
|
||||
|
@ -204,6 +221,10 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", multilang=True, website=True)
|
||||
def question_ask_for_close(self, forum, question, **post):
|
||||
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
Reason = request.registry['forum.post.reason']
|
||||
reason_ids = Reason.search(cr, uid, [], context=context)
|
||||
|
@ -211,7 +232,7 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
values = self._prepare_forum_values(**post)
|
||||
values.update({
|
||||
'post': question,
|
||||
'question': question,
|
||||
'question': question,
|
||||
'forum': forum,
|
||||
'reasons': reasons,
|
||||
|
@ -228,6 +249,10 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/close', type='http', auth="user", multilang=True, methods=['POST'], website=True)
|
||||
def question_close(self, forum, question, **post):
|
||||
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {
|
||||
'state': 'close',
|
||||
'closed_uid': request.uid,
|
||||
|
@ -238,17 +263,28 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", multilang=True, website=True)
|
||||
def question_reopen(self, forum, question, **kwarg):
|
||||
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'state': 'active'}, context=request.context)
|
||||
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", multilang=True, website=True)
|
||||
def question_delete(self, forum, question, **kwarg):
|
||||
#instead of unlink record just change 'active' to false so user can undelete it.
|
||||
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': False}, context=request.context)
|
||||
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", multilang=True, website=True)
|
||||
def question_undelete(self, forum, question, **kwarg):
|
||||
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': True}, context=request.context)
|
||||
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
|
||||
|
||||
|
@ -287,19 +323,27 @@ class WebsiteForum(http.Controller):
|
|||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/toggle_correct', type='json', auth="public", website=True)
|
||||
def post_toggle_correct(self, forum, post, **kwargs):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
if post.parent_id is False:
|
||||
return request.redirect('/')
|
||||
if not request.session.uid:
|
||||
return {'error': 'anonymous_user'}
|
||||
# if user have not access to accept answer then reise warning
|
||||
if post.parent_id is False or post.parent_id.create_uid.id != uid:
|
||||
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, request.uid, context=request.context)
|
||||
if post.parent_id.create_uid.id != uid:
|
||||
return {'error': 'own_post'}
|
||||
if post.create_uid.id == user.id and user.karma < request.registry['forum.forum']._karma_answer_accept_own:
|
||||
return {'error': 'not_enough_karma', 'karma': 20}
|
||||
|
||||
# set all answers to False, only one can be accepted
|
||||
request.registry['forum.post'].write(cr, uid, [c.id for c in post.parent_id.child_ids], {'is_correct': False}, context=context)
|
||||
request.registry['forum.post'].write(cr, uid, [post.id, post.parent_id.id], {'is_correct': not post.is_correct}, context=context)
|
||||
request.registry['forum.post'].write(cr, uid, [post.id], {'is_correct': not post.is_correct}, context=context)
|
||||
return not post.is_correct
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", multilang=True, website=True)
|
||||
def post_delete(self, forum, post, **kwargs):
|
||||
check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
question = post.parent_id
|
||||
request.registry['forum.post'].unlink(request.cr, request.uid, [post.id], context=request.context)
|
||||
if question:
|
||||
|
@ -308,6 +352,10 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/edit', type='http', auth="user", website=True, multilang=True)
|
||||
def post_edit(self, forum, post, **kwargs):
|
||||
check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_edit_own' or '_karma_modo_edit_all')
|
||||
if not check_res[0]:
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
tags = ""
|
||||
for tag_name in post.tag_ids:
|
||||
tags += tag_name.name + ","
|
||||
|
@ -345,15 +393,15 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/upvote', type='json', auth="public", multilang=True, website=True)
|
||||
def post_upvote(self, forum, post, **kwargs):
|
||||
# check for karma and not self vote
|
||||
if not request.session.uid:
|
||||
return {'error': 'anonymous_user'}
|
||||
if request.uid == post.create_uid.id:
|
||||
return {'error': 'own_post'}
|
||||
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, request.uid, context=request.context)
|
||||
if user.karma <= 5:
|
||||
return {'error': 'not_enough_karma', 'karma': 1}
|
||||
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=True, context=request.context)
|
||||
check_res = self._has_enough_karma('_karma_upvote')
|
||||
if not check_res[0]:
|
||||
return check_res[1]
|
||||
upvote = True if not post.user_vote > 0 else False
|
||||
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context)
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/downvote', type='json', auth="public", multilang=True, website=True)
|
||||
def post_downvote(self, forum, post, **kwargs):
|
||||
|
@ -361,10 +409,11 @@ class WebsiteForum(http.Controller):
|
|||
return {'error': 'anonymous_user'}
|
||||
if request.uid == post.create_uid.id:
|
||||
return {'error': 'own_post'}
|
||||
user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, request.uid, context=request.context)
|
||||
if user.karma <= 50:
|
||||
return {'error': 'not_enough_karma', 'karma': 50}
|
||||
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=False, context=request.context)
|
||||
check_res = self._has_enough_karma('_karma_downvote')
|
||||
if not check_res[0]:
|
||||
return check_res[1]
|
||||
upvote = True if post.user_vote < 0 else False
|
||||
return request.registry['forum.post'].vote(request.cr, request.uid, [post.id], upvote=upvote, context=request.context)
|
||||
|
||||
# User
|
||||
# --------------------------------------------------
|
||||
|
@ -392,6 +441,16 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
return request.website.render("website_forum.users", values)
|
||||
|
||||
@http.route(['/forum/<model("forum.forum"):forum>/partner/<int:partner_id>'], type='http', auth="public", website=True, multilang=True)
|
||||
def open_partner(self, forum, partner_id=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
pids = request.registry['res.partner'].search(cr, SUPERUSER_ID, [('id', '=', partner_id)], context=context)
|
||||
if pids:
|
||||
partner = request.registry['res.partner'].browse(cr, SUPERUSER_ID, pids[0], context=context)
|
||||
if partner.user_ids:
|
||||
return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), partner.user_ids[0].id))
|
||||
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
|
||||
|
||||
@http.route(['/forum/<model("forum.forum"):forum>/user/<int:user_id>'], type='http', auth="public", website=True, multilang=True)
|
||||
def open_user(self, forum, user_id=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- QUALITY (VOTES) -->
|
||||
<!-- Teacher: at least 3 upvotes -->
|
||||
|
@ -8,6 +8,7 @@
|
|||
<field name="name">Teacher</field>
|
||||
<field name="description">Received at least 3 upvote for an answer for the first time</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_teacher">
|
||||
<field name="name">Teacher</field>
|
||||
|
@ -42,6 +43,7 @@
|
|||
<field name="name">Nice Answer</field>
|
||||
<field name="description">Answer voted up 4 times</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_nice_answer">
|
||||
<field name="name">Nice Answer (4)</field>
|
||||
|
@ -76,6 +78,7 @@
|
|||
<field name="name">Good Answer</field>
|
||||
<field name="description">Answer voted up 6 times</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_good_answer">
|
||||
<field name="name">Good Answer (6)</field>
|
||||
|
@ -110,6 +113,7 @@
|
|||
<field name="name">Great Answer</field>
|
||||
<field name="description">Answer voted up 15 times</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_great_answer">
|
||||
<field name="name">Great Answer (15)</field>
|
||||
|
@ -146,6 +150,7 @@
|
|||
<field name="name">Enlightened</field>
|
||||
<field name="description">Answer was accepted with 3 or more votes</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_enlightened">
|
||||
<field name="name">Enlightened</field>
|
||||
|
@ -180,6 +185,7 @@
|
|||
<field name="name">Guru</field>
|
||||
<field name="description">Answer accepted with 15 or more votes</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_guru">
|
||||
<field name="name">Guru (15)</field>
|
||||
|
@ -256,6 +262,7 @@ for post in Post.browse(cr, uid, user_posts, context=context):
|
|||
<field name="name">Self-Learner</field>
|
||||
<field name="description">Answered own question with at least 4 up votes</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_self_learner">
|
||||
<field name="name">Self-Learner</field>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Cleanup: answer or question edition -->
|
||||
<!-- Not rollback feature in forum -->
|
||||
|
@ -15,6 +15,7 @@
|
|||
<field name="name">Critic</field>
|
||||
<field name="description">First downvote</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_critic">
|
||||
<field name="name">Critic</field>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<field name="name">Disciplined</field>
|
||||
<field name="description">Deleted own post with 3 or more upvotes</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_disciplined">
|
||||
<field name="name">Disciplined</field>
|
||||
|
@ -85,6 +87,7 @@
|
|||
<field name="name">Editor</field>
|
||||
<field name="description">First edit</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_editor">
|
||||
<field name="name">Editor</field>
|
||||
|
@ -223,6 +226,7 @@ result = int(len(data) >= 15)</field>
|
|||
<field name="name">Supporter</field>
|
||||
<field name="description">First upvote</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_supporter">
|
||||
<field name="name">Supporter</field>
|
||||
|
@ -258,6 +262,7 @@ result = int(len(data) >= 15)</field>
|
|||
<field name="name">Peer Pressure</field>
|
||||
<field name="description">Deleted own post with 3 or more downvotes</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_peer_pressure">
|
||||
<field name="name">Peer Pressure</field>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Biography: complet your profile -->
|
||||
<record id="badge_p_1" model="gamification.badge">
|
||||
<field name="name">Autobiographer</field>
|
||||
<field name="description">Completed own biography</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_configure_profile">
|
||||
<field name="name">Completed own biography</field>
|
||||
|
@ -46,6 +47,7 @@
|
|||
<field name="name">Commentator</field>
|
||||
<field name="description">Posted 10 comments</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_commentator">
|
||||
<field name="name">Commentator</field>
|
||||
|
@ -81,6 +83,7 @@
|
|||
<field name="name">Pundit</field>
|
||||
<field name="description">Left comments with score of 10 or more</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_pundit">
|
||||
<field name="name">Pundit</field>
|
||||
|
@ -115,6 +118,7 @@
|
|||
<field name="name">Chief Commentator</field>
|
||||
<field name="description">Posted 100 comments</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.challenge" id="challenge_chief_commentator">
|
||||
<field name="name">Chief Commentator</field>
|
||||
|
@ -177,6 +181,7 @@ result = get_counter(cr, uid, context=context)
|
|||
<field name="name">Taxonomist</field>
|
||||
<field name="description">Created a tag used by 15 questions</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_taxonomist">
|
||||
<field name="name">Taxonomist</field>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!-- <?xml version="1.0" encoding="utf-8"?> -->
|
||||
<openerp>
|
||||
<data>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- POPULARITY (VIEWS) -->
|
||||
<!-- Popular: 150 views -->
|
||||
|
@ -8,6 +8,7 @@
|
|||
<field name="name">Popular Question</field>
|
||||
<field name="description">Asked a question with at least 150 views</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_popular_question">
|
||||
<field name="name">Popular Question (150)</field>
|
||||
|
@ -43,6 +44,7 @@
|
|||
<field name="name">Notable Question</field>
|
||||
<field name="description">Asked a question with at least 250 views</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_notable_question">
|
||||
<field name="name">Popular Question (250)</field>
|
||||
|
@ -77,6 +79,7 @@
|
|||
<field name="name">Famous Question</field>
|
||||
<field name="description">Asked a question with at least 500 views</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_famous_question">
|
||||
<field name="name">Popular Question (500)</field>
|
||||
|
@ -113,6 +116,7 @@
|
|||
<field name="name">Credible Question</field>
|
||||
<field name="description">Question set as favorite by 1 user</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_favorite_question_1">
|
||||
<field name="name">Favourite Question (1)</field>
|
||||
|
@ -147,6 +151,7 @@
|
|||
<field name="name">Favorite Question</field>
|
||||
<field name="description">Question set as favorite by 5 users</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_favorite_question_5">
|
||||
<field name="name">Favourite Question (5)</field>
|
||||
|
@ -181,6 +186,7 @@
|
|||
<field name="name">Stellar Question</field>
|
||||
<field name="description">Question set as favorite by 25 users</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_stellar_question_25">
|
||||
<field name="name">Favourite Question (25)</field>
|
||||
|
@ -217,6 +223,7 @@
|
|||
<field name="name">Student</field>
|
||||
<field name="description">Asked first question with at least one up vote</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_student">
|
||||
<field name="name">Upvoted question (1)</field>
|
||||
|
@ -251,6 +258,7 @@
|
|||
<field name="name">Nice Quesiotn</field>
|
||||
<field name="description">Question voted up 4 times</field>
|
||||
<field name="level">bronze</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_nice_question">
|
||||
<field name="name">Upvoted question (4)</field>
|
||||
|
@ -285,6 +293,7 @@
|
|||
<field name="name">Good Question</field>
|
||||
<field name="description">Question voted up 6 times</field>
|
||||
<field name="level">silver</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_good_question">
|
||||
<field name="name">Upvoted question (6)</field>
|
||||
|
@ -319,6 +328,7 @@
|
|||
<field name="name">Great Question</field>
|
||||
<field name="description">Question voted up 15 times</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_great_question">
|
||||
<field name="name">Upvoted question (15)</field>
|
||||
|
@ -354,6 +364,7 @@
|
|||
<field name="name">Scholar</field>
|
||||
<field name="description">Asked a question and accepted an answer</field>
|
||||
<field name="level">gold</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
</record>
|
||||
<record model="gamification.goal.definition" id="definition_scholar">
|
||||
<field name="name">Scholar</field>
|
||||
|
|
|
@ -9,9 +9,38 @@ from openerp.tools.translate import _
|
|||
|
||||
|
||||
class Forum(osv.Model):
|
||||
"""TDE TODO: set karma values for actions dynamic for a given forum"""
|
||||
_name = 'forum.forum'
|
||||
_description = 'Forums'
|
||||
_inherit = ['website.seo.metadata']
|
||||
# karma values
|
||||
_karma_upvote = 5 # done
|
||||
_karma_downvote = 50 # done
|
||||
_karma_answer_accept_own = 20 # done
|
||||
_karma_answer_accept_own_now = 50
|
||||
_karma_answer_accept_all = 500
|
||||
_karma_editor_link_files = 30 # done
|
||||
_karma_editor_clickable_link = 50
|
||||
_karma_comment = 1
|
||||
_karma_modo_retag = 75
|
||||
_karma_modo_flag = 100
|
||||
_karma_modo_flag_see_all = 300
|
||||
_karma_modo_unlink_comment = 750
|
||||
_karma_modo_edit_own = 1 # done
|
||||
_karma_modo_edit_all = 300 # done
|
||||
_karma_modo_close_own = 100 # done
|
||||
_karma_modo_close_all = 900 # done
|
||||
_karma_modo_unlink_own = 500 # done
|
||||
_karma_modo_unlink_all = 1000 # done
|
||||
# karma generation
|
||||
_karma_gen_quest_new = 2 # done
|
||||
_karma_gen_upvote_quest = 5 # done
|
||||
_karma_gen_downvote_quest = -2 # done
|
||||
_karma_gen_upvote_ans = 10 # done
|
||||
_karma_gen_downvote_ans = -2 # done
|
||||
_karma_gen_ans_accept = 2 # done
|
||||
_karma_gen_ans_accepted = 15 # done
|
||||
_karma_gen_ans_flagged = -100
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True, translate=True),
|
||||
|
@ -97,6 +126,13 @@ class Post(osv.Model):
|
|||
res[post.id] = any(answer.create_uid.id == uid for answer in post.child_ids)
|
||||
return res
|
||||
|
||||
def _get_has_validated_answer(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = dict.fromkeys(ids, False)
|
||||
ans_ids = self.search(cr, uid, [('parent_id', 'in', ids), ('is_correct', '=', True)], context=context)
|
||||
for answer in self.browse(cr, uid, ans_ids, context=context):
|
||||
res[answer.parent_id.id] = True
|
||||
return res
|
||||
|
||||
def _is_self_reply(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = dict.fromkeys(ids, False)
|
||||
for post in self.browse(cr, uid, ids, context=context):
|
||||
|
@ -143,7 +179,8 @@ class Post(osv.Model):
|
|||
}),
|
||||
# hierarchy
|
||||
'parent_id': fields.many2one('forum.post', 'Question', ondelete='cascade'),
|
||||
'self_reply': fields.function(_is_self_reply, 'Reply to own question', type='boolean',
|
||||
'self_reply': fields.function(
|
||||
_is_self_reply, 'Reply to own question', type='boolean',
|
||||
store={
|
||||
'forum.post': (lambda self, cr, uid, ids, c={}: ids, ['parent_id', 'create_uid'], 10),
|
||||
}),
|
||||
|
@ -156,6 +193,12 @@ class Post(osv.Model):
|
|||
'uid_has_answered': fields.function(
|
||||
_get_uid_answered, string='Has Answered', type='boolean',
|
||||
),
|
||||
'has_validated_answer': fields.function(
|
||||
_get_has_validated_answer, string='Has a Validated Answered', type='boolean',
|
||||
store={
|
||||
'forum.post': (_get_post_from_hierarchy, ['parent_id', 'child_ids', 'is_correct'], 10),
|
||||
}
|
||||
),
|
||||
# closing
|
||||
'closed_reason_id': fields.many2one('forum.post.reason', 'Reason'),
|
||||
'closed_uid': fields.many2one('res.users', 'Closed by', select=1),
|
||||
|
@ -183,10 +226,18 @@ class Post(osv.Model):
|
|||
self.message_post(cr, uid, parent.id, subject=_('Re: %s') % parent.name, body=body, subtype='website_forum.mt_answer_new', context=context)
|
||||
else:
|
||||
self.message_post(cr, uid, post_id, subject=vals.get('name', ''), body=_('New Question Created'), subtype='website_forum.mt_question_new', context=context)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], 2, context=context)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], self.pool['forum.forum']._karma_gen_quest_new, context=context)
|
||||
return post_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
Forum = self.pool['forum.forum']
|
||||
# update karma when accepting/rejecting answers
|
||||
if 'is_correct' in vals:
|
||||
mult = 1 if vals['is_correct'] else -1
|
||||
for post in self.browse(cr, uid, ids, context=context):
|
||||
if vals['is_correct'] != post.is_correct:
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], Forum._karma_gen_ans_accepted * mult, context=context)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], Forum._karma_gen_ans_accept * mult, context=context)
|
||||
res = super(Post, self).write(cr, uid, ids, vals, context=context)
|
||||
# if post content modify, notify followers
|
||||
if 'content' in vals or 'name' in vals:
|
||||
|
@ -198,11 +249,6 @@ class Post(osv.Model):
|
|||
body, subtype = _('Question Edited'), 'website_forum.mt_question_edit'
|
||||
obj_id = post.id
|
||||
self.message_post(cr, uid, obj_id, body=_(body), subtype=subtype, context=context)
|
||||
# update karma of related user when any answer accepted
|
||||
if 'correct' in vals:
|
||||
for post in self.browse(cr, uid, ids, context=context):
|
||||
karma_value = 15 if vals.get('correct') else -15
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], {'karma': karma_value}, context=context)
|
||||
return res
|
||||
|
||||
def vote(self, cr, uid, ids, upvote=True, context=None):
|
||||
|
@ -252,16 +298,30 @@ class Vote(osv.Model):
|
|||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
vote_id = super(Vote, self).create(cr, uid, vals, context=context)
|
||||
karma_value = int(vals.get('vote', '1')) * 10
|
||||
post = self.pool['forum.post'].browse(cr, uid, vals.get('post_id'), context=context)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, post.create_uid.id, karma_value, context=context)
|
||||
if vals.get('vote', '1') == '1':
|
||||
karma = self.pool['forum.forum']._karma_upvote
|
||||
elif vals.get('vote', '1') == '-1':
|
||||
karma = self.pool['forum.forum']._karma_downvote
|
||||
post = self.pool['forum.post'].browse(cr, uid, vals['post_id'], context=context)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], karma, context=context)
|
||||
return vote_id
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
def _get_karma_value(old_vote, new_vote, up_karma, down_karma):
|
||||
_karma_upd = {
|
||||
'-1': {'-1': 0, '0': -1 * down_karma, '1': -1 * down_karma + up_karma},
|
||||
'0': {'-1': 1 * down_karma, '0': 0, '1': up_karma},
|
||||
'1': {'-1': -1 * up_karma + down_karma, '0': -1 * up_karma, '1': 0}
|
||||
}
|
||||
return _karma_upd[old_vote][new_vote]
|
||||
if 'vote' in values:
|
||||
Forum = self.pool['forum.forum']
|
||||
for vote in self.browse(cr, uid, ids, context=context):
|
||||
karma_value = (int(values.get('vote')) - int(vote.vote)) * 10
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, vote.post_id.create_uid.id, karma_value, context=context)
|
||||
if vote.post_id.parent_id:
|
||||
karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_ans, Forum._karma_gen_downvote_ans)
|
||||
else:
|
||||
karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_quest, Forum._karma_gen_downvote_quest)
|
||||
self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
|
||||
res = super(Vote, self).write(cr, uid, ids, values, context=context)
|
||||
return res
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@ class Users(osv.Model):
|
|||
}
|
||||
|
||||
def add_karma(self, cr, uid, ids, karma, context=None):
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
self.write(cr, uid, [user.id], {'karma': user.karma + karma}, context=context)
|
||||
return True
|
||||
|
@ -44,4 +42,4 @@ class Users(osv.Model):
|
|||
excluded_categories.append('forum')
|
||||
else:
|
||||
excluded_categories = ['forum']
|
||||
return super(Users, self).get_serialised_gamification_summary(cr, uid, excluded_categories=excluded_categories, context=context)
|
||||
return super(Users, self).get_serialised_gamification_summary(cr, uid, excluded_categories=excluded_categories, context=context)
|
||||
|
|
|
@ -18,9 +18,9 @@ $(document).ready(function () {
|
|||
'</div>');
|
||||
}
|
||||
else if (data['error'] == 'not_enough_karma') {
|
||||
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+
|
||||
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="max-width: 500px; position:absolute; margin-top: -30px; margin-left: 90px;">'+
|
||||
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>'+
|
||||
'Sorry, at least ' + data['karma'] + ' karma is required to vote'+
|
||||
'Sorry, at least ' + data['karma'] + ' karma is required to vote. You can gain karma by answering questions and receiving votes.'+
|
||||
'</div>');
|
||||
}
|
||||
vote_alert = $link.parent().find("#vote_alert");
|
||||
|
@ -57,7 +57,12 @@ $(document).ready(function () {
|
|||
} else if (data['error'] == 'own_post'){
|
||||
var $warning = $('<div class="alert alert-danger alert-dismissable" id="correct_answer_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+
|
||||
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>'+
|
||||
'Sorry, the user who asked this question can only accept the answer as correct.'+
|
||||
'Sorry, only the user who asked this question can accept the answer as correct.'+
|
||||
'</div>');
|
||||
} else if (data['error'] == 'not_enough_karma') {
|
||||
var $warning = $('<div class="alert alert-danger alert-dismissable" id="vote_alert" style="max-width: 500px; position:absolute; margin-top: -30px; margin-left: 90px;">'+
|
||||
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>'+
|
||||
'Sorry, at least ' + data['karma'] + ' karma is required to accept your own answers. You can gain karma by answering questions and receiving votes.'+
|
||||
'</div>');
|
||||
}
|
||||
correct_answer_alert = $link.parent().find("#correct_answer_alert");
|
||||
|
|
|
@ -333,7 +333,7 @@
|
|||
them.
|
||||
</p>
|
||||
<form t-attf-action="/forum/#{ slug(forum) }/question/#{slug(post)}/close" method="post" role="form" class="form-horizontal mt32 mb64">
|
||||
<input name="post_id" t-att-value="post.id" type="hidden"/>
|
||||
<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>
|
||||
<div class="col-md-8 mt8">
|
||||
|
@ -345,7 +345,7 @@
|
|||
<div class="col-md-8">
|
||||
<select class="form-control" name="reason_id">
|
||||
<t t-foreach="reasons or []" t-as="reason">
|
||||
<option t-att-value="reason.id" t-att-selected="reason.id == post.closed_reason_id.id"><t t-esc="reason.name"/></option>
|
||||
<option t-att-value="reason.id" t-att-selected="reason.id == question.closed_reason_id.id"><t t-esc="reason.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -447,16 +447,19 @@
|
|||
Comment
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="question.state != 'close' and (user.id == question.create_uid.id or user.karma>=100)">
|
||||
<li t-if="question.state != 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
|
||||
<a class="text-muted fa fa-times" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/ask_for_close"> Close</a>
|
||||
</li>
|
||||
<li t-if="user.id == question.create_uid.id or user.karma>=300">
|
||||
<li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
|
||||
<a class="text-muted fa fa-undo" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)/reopen"> Reopen</a>
|
||||
</li>
|
||||
<li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all">
|
||||
<a class="text-muted fa fa-edit" t-attf-href="/forum/#{ slug(forum) }/post/#{slug(question)}/edit"> Edit</a>
|
||||
</li>
|
||||
<li t-if="question.active and user.id == question.create_uid.id or user.karma>=1000">
|
||||
<li t-if="question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
|
||||
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/delete"> Delete</a>
|
||||
</li>
|
||||
<li t-if="uid == question.create_uid.id and not question.active">
|
||||
<li t-if="not question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
|
||||
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/undelete"> Undelete</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -500,10 +503,10 @@
|
|||
t-attf-data-target="#comment#{ answer._name.replace('.','') + '-' + str(answer.id) }"> Comment
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="user.id == answer.create_uid.id or user.karma>=300">
|
||||
<li t-if="(user.id == answer.create_uid.id and can_edit_own) or can_edit_all">
|
||||
<a class="text-muted fa fa-edit" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/edit"> Edit</a>
|
||||
</li>
|
||||
<li t-if="user.id == answer.create_uid.id or user.karma>=1000">
|
||||
<li t-if="(user.id == answer.create_uid.id and can_unlink_own) or can_unlink_all">
|
||||
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/delete"> Delete</a>
|
||||
</li>
|
||||
<li t-if="user.id == answer.create_uid.id">
|
||||
|
@ -546,7 +549,7 @@
|
|||
t-attf-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
|
||||
class="close comment_delete">&times;</button>
|
||||
<span t-field="message.body"/>
|
||||
<a t-attf-href="/forum/#{slug(forum)}/user/#{message.id}"
|
||||
<a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
|
||||
t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
|
||||
style="display: inline-block;"/>
|
||||
on <span t-field="message.date" t-field-options='{"format":"short"}'/>
|
||||
|
|
Loading…
Reference in New Issue