<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 time
from openerp.osv import fields,osv
from openerp.tools.translate import _

WARNING_MESSAGE = [
                   ('no-message','No Message'),
                   ('warning','Warning'),
                   ('block','Blocking Message')
                   ]

WARNING_HELP = _('Selecting the "Warning" option will notify user with the message, Selecting "Blocking Message" will throw an exception with the message and block the flow. The Message has to be written in the next field.')

class res_partner(osv.osv):
    _inherit = 'res.partner'
    _columns = {
        'sale_warn' : fields.selection(WARNING_MESSAGE, 'Sales Order', help=WARNING_HELP, required=True),
        'sale_warn_msg' : fields.text('Message for Sales Order'),
        'purchase_warn' : fields.selection(WARNING_MESSAGE, 'Purchase Order', help=WARNING_HELP, required=True),
        'purchase_warn_msg' : fields.text('Message for Purchase Order'),
        'picking_warn' : fields.selection(WARNING_MESSAGE, 'Stock Picking', help=WARNING_HELP, required=True),
        'picking_warn_msg' : fields.text('Message for Stock Picking'),
        'invoice_warn' : fields.selection(WARNING_MESSAGE, 'Invoice', help=WARNING_HELP, required=True),
        'invoice_warn_msg' : fields.text('Message for Invoice'),
    }
    _defaults = {
         'sale_warn' : 'no-message',
         'purchase_warn' : 'no-message',
         'picking_warn' : 'no-message',
         'invoice_warn' : 'no-message',
    }

res_partner()

class sale_order(osv.osv):
    _inherit = 'sale.order'
    def onchange_partner_id(self, cr, uid, ids, part, context=None):
        if not part:
            return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'payment_term' : False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part, context=context)
        if partner.sale_warn != 'no-message':
            if partner.sale_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.sale_warn_msg)
            title =  _("Warning for %s") % partner.name
            message = partner.sale_warn_msg
            warning = {
                    'title': title,
                    'message': message,
            }

        result =  super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}
sale_order()

class purchase_order(osv.osv):
    _inherit = 'purchase.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_address_id': False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.purchase_warn != 'no-message':
            if partner.purchase_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.purchase_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.purchase_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order()

class account_invoice(osv.osv):
    _inherit = 'account.invoice'
    def onchange_partner_id(self, cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
        if not partner_id:
            return {'value': {
            'account_id': False,
            'payment_term': False,
            }
        }
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
        if partner.invoice_warn != 'no-message':
            if partner.invoice_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.invoice_warn_msg)

            title = _("Warning for %s") % partner.name
            message = partner.invoice_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
            date_invoice=date_invoice, payment_term=payment_term,
            partner_bank_id=partner_bank_id, company_id=company_id)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

account_invoice()

class stock_picking(osv.osv):
    _inherit = 'stock.picking'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            if partner.picking_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
        result =  super(stock_picking, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

stock_picking()

# FIXME:(class stock_picking_in and stock_picking_out) this is a temporary workaround because of a framework bug (ref: lp:996816).
# It should be removed as soon as the bug is fixed
class stock_picking_in(osv.osv):
    _inherit = 'stock.picking.in'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            if partner.picking_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
        result =  super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

class stock_picking_out(osv.osv):
    _inherit = 'stock.picking.out'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            if partner.picking_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
        result =  super(stock_picking_out, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

class product_product(osv.osv):
    _inherit = 'product.product'
    _columns = {
         'sale_line_warn' : fields.selection(WARNING_MESSAGE,'Sales Order Line', help=WARNING_HELP, required=True),
         'sale_line_warn_msg' : fields.text('Message for Sales Order Line'),
         'purchase_line_warn' : fields.selection(WARNING_MESSAGE,'Purchase Order Line', help=WARNING_HELP, required=True),
         'purchase_line_warn_msg' : fields.text('Message for Purchase Order Line'),
     }

    _defaults = {
         'sale_line_warn' : 'no-message',
         'purchase_line_warn' : 'no-message',
    }

product_product()

class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False,
            fiscal_position=False, flag=False, context=None):
        warning = {}
        if not product:
            return {'value': {'th_weight' : 0, 'product_packaging': False,
                'product_uos_qty': qty}, 'domain': {'product_uom': [],
                   'product_uos': []}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.sale_line_warn != 'no-message':
            if product_info.sale_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.sale_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.sale_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
            uom, qty_uos, uos, name, partner_id,
            lang, update_tax, date_order, packaging, fiscal_position, flag, context=context)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

sale_order_line()

class purchase_order_line(osv.osv):
    _inherit = 'purchase.order.line'
    def onchange_product_id(self,cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
            name=False, price_unit=False, notes=False, context=None):
        warning = {}
        if not product:
            return {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'notes': notes or '', 'product_uom' : uom or False}, 'domain':{'product_uom':[]}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.purchase_line_warn != 'no-message':
            if product_info.purchase_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.purchase_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.purchase_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(purchase_order_line, self).onchange_product_id(cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order, fiscal_position_id)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order_line()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] warning: make blocking warning actually doing something (opw 607380)<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 time
from openerp.osv import fields,osv
from openerp.tools.translate import _

WARNING_MESSAGE = [
                   ('no-message','No Message'),
                   ('warning','Warning'),
                   ('block','Blocking Message')
                   ]

WARNING_HELP = _('Selecting the "Warning" option will notify user with the message, Selecting "Blocking Message" will throw an exception with the message and block the flow. The Message has to be written in the next field.')

class res_partner(osv.osv):
    _inherit = 'res.partner'
    _columns = {
        'sale_warn' : fields.selection(WARNING_MESSAGE, 'Sales Order', help=WARNING_HELP, required=True),
        'sale_warn_msg' : fields.text('Message for Sales Order'),
        'purchase_warn' : fields.selection(WARNING_MESSAGE, 'Purchase Order', help=WARNING_HELP, required=True),
        'purchase_warn_msg' : fields.text('Message for Purchase Order'),
        'picking_warn' : fields.selection(WARNING_MESSAGE, 'Stock Picking', help=WARNING_HELP, required=True),
        'picking_warn_msg' : fields.text('Message for Stock Picking'),
        'invoice_warn' : fields.selection(WARNING_MESSAGE, 'Invoice', help=WARNING_HELP, required=True),
        'invoice_warn_msg' : fields.text('Message for Invoice'),
    }
    _defaults = {
         'sale_warn' : 'no-message',
         'purchase_warn' : 'no-message',
         'picking_warn' : 'no-message',
         'invoice_warn' : 'no-message',
    }

res_partner()

class sale_order(osv.osv):
    _inherit = 'sale.order'
    def onchange_partner_id(self, cr, uid, ids, part, context=None):
        if not part:
            return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'payment_term' : False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part, context=context)
        if partner.sale_warn != 'no-message':
            title =  _("Warning for %s") % partner.name
            message = partner.sale_warn_msg
            warning = {
                    'title': title,
                    'message': message,
            }
            if partner.sale_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}
sale_order()

class purchase_order(osv.osv):
    _inherit = 'purchase.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_address_id': False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.purchase_warn != 'no-message':
            title = _("Warning for %s") % partner.name
            message = partner.purchase_warn_msg
            warning = {
                'title': title,
                'message': message
                }
            if partner.purchase_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order()

class account_invoice(osv.osv):
    _inherit = 'account.invoice'
    def onchange_partner_id(self, cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
        if not partner_id:
            return {'value': {
            'account_id': False,
            'payment_term': False,
            }
        }
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
        if partner.invoice_warn != 'no-message':
            title = _("Warning for %s") % partner.name
            message = partner.invoice_warn_msg
            warning = {
                'title': title,
                'message': message
                }

            if partner.invoice_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
            date_invoice=date_invoice, payment_term=payment_term,
            partner_bank_id=partner_bank_id, company_id=company_id)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

account_invoice()

class stock_picking(osv.osv):
    _inherit = 'stock.picking'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
            if partner.picking_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(stock_picking, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

stock_picking()

# FIXME:(class stock_picking_in and stock_picking_out) this is a temporary workaround because of a framework bug (ref: lp:996816).
# It should be removed as soon as the bug is fixed
class stock_picking_in(osv.osv):
    _inherit = 'stock.picking.in'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
            if partner.picking_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

class stock_picking_out(osv.osv):
    _inherit = 'stock.picking.out'

    def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
            if partner.picking_warn == 'block':
                return {'value': {'partner_id': False}, 'warning': warning}

        result =  super(stock_picking_out, self).onchange_partner_in(cr, uid, ids, partner_id, context)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

class product_product(osv.osv):
    _inherit = 'product.product'
    _columns = {
         'sale_line_warn' : fields.selection(WARNING_MESSAGE,'Sales Order Line', help=WARNING_HELP, required=True),
         'sale_line_warn_msg' : fields.text('Message for Sales Order Line'),
         'purchase_line_warn' : fields.selection(WARNING_MESSAGE,'Purchase Order Line', help=WARNING_HELP, required=True),
         'purchase_line_warn_msg' : fields.text('Message for Purchase Order Line'),
     }

    _defaults = {
         'sale_line_warn' : 'no-message',
         'purchase_line_warn' : 'no-message',
    }

product_product()

class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False,
            fiscal_position=False, flag=False, context=None):
        warning = {}
        if not product:
            return {'value': {'th_weight' : 0, 'product_packaging': False,
                'product_uos_qty': qty}, 'domain': {'product_uom': [],
                   'product_uos': []}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.sale_line_warn != 'no-message':
            title = _("Warning for %s") % product_info.name
            message = product_info.sale_line_warn_msg
            warning['title'] = title
            warning['message'] = message
            if product_info.sale_line_warn == 'block':
                return {'value': {'product_id': False}, 'warning': warning}

        result =  super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
            uom, qty_uos, uos, name, partner_id,
            lang, update_tax, date_order, packaging, fiscal_position, flag, context=context)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

sale_order_line()

class purchase_order_line(osv.osv):
    _inherit = 'purchase.order.line'
    def onchange_product_id(self,cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
            name=False, price_unit=False, notes=False, context=None):
        warning = {}
        if not product:
            return {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'notes': notes or '', 'product_uom' : uom or False}, 'domain':{'product_uom':[]}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.purchase_line_warn != 'no-message':
            title = _("Warning for %s") % product_info.name
            message = product_info.purchase_line_warn_msg
            warning['title'] = title
            warning['message'] = message
            if product_info.purchase_line_warn == 'block':
                return {'value': {'product_id': False}, 'warning': warning}

        result =  super(purchase_order_line, self).onchange_product_id(cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order, fiscal_position_id)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order_line()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before>{
    "name" : "web",
    "category": "Hidden",
    "description":
        """
        OpenERP Web core module.
        This module provides the core of the OpenERP web client.
        """,
    "depends" : [],
    'auto_install': True,
    'post_load' : 'wsgi_postload',
    'js' : [
        "static/lib/cleditor/jquery.cleditor.min.js",
        "static/lib/datejs/globalization/en-US.js",
        "static/lib/datejs/core.js",
        "static/lib/datejs/parser.js",
        "static/lib/datejs/sugarpak.js",
        "static/lib/datejs/extras.js",
        "static/lib/jquery/jquery-1.7.2b1.js",
        "static/lib/jquery.MD5/jquery.md5.js",
        "static/lib/jquery.form/jquery.form.js",
        "static/lib/jquery.validate/jquery.validate.js",
        "static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
        "static/lib/jquery.blockUI/jquery.blockUI.js",
        "static/lib/jquery.superfish/js/hoverIntent.js",
        "static/lib/jquery.superfish/js/superfish.js",
        "static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js",
        "static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js",
        "static/lib/jquery.ui.notify/js/jquery.notify.js",
        "static/lib/jquery.deferred-queue/jquery.deferred-queue.js",
        "static/lib/jquery.scrollTo/jquery.scrollTo-min.js",
        "static/lib/jquery.tipsy/jquery.tipsy.js",
        "static/lib/jquery.textext/jquery.textext.js",
        "static/lib/json/json2.js",
        "static/lib/qweb/qweb2.js",
        "static/lib/underscore/underscore.js",
        "static/lib/underscore/underscore.string.js",
        "static/lib/backbone/backbone.js",

        "static/lib/labjs/LAB.src.js",
        "static/lib/py.js/lib/py.js",
        "static/src/js/boot.js",
        "static/src/js/corelib.js",
        "static/src/js/coresetup.js",
        "static/src/js/dates.js",
        "static/src/js/formats.js",
        "static/src/js/chrome.js",
        "static/src/js/views.js",
        "static/src/js/data.js",
        "static/src/js/data_export.js",
        "static/src/js/data_import.js",
        "static/src/js/search.js",
        "static/src/js/view_form.js",
        "static/src/js/view_list.js",
        "static/src/js/view_list_editable.js",
        "static/src/js/view_tree.js",
        "static/src/js/view_editor.js"
    ],
    'css' : [
        "static/lib/cleditor/jquery.cleditor.css",
        "static/lib/jquery.superfish/css/superfish.css",
        "static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css",
        "static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
        "static/lib/jquery.ui.notify/css/ui.notify.css",
        "static/lib/jquery.tipsy/tipsy.css",
#        "static/src/css/base_old.css",
        "static/src/css/base.css",
        "static/src/css/data_export.css",
        "static/src/css/data_import.css",
    ],
    'qweb' : [
        "static/src/xml/*.xml",
    ],
}
<commit_msg>[FIX] Fixed __openerp__ js file order, to prevent a bug with cleditor not knowing about jquery.<commit_after>{
    "name" : "web",
    "category": "Hidden",
    "description":
        """
        OpenERP Web core module.
        This module provides the core of the OpenERP web client.
        """,
    "depends" : [],
    'auto_install': True,
    'post_load' : 'wsgi_postload',
    'js' : [
        "static/lib/datejs/globalization/en-US.js",
        "static/lib/datejs/core.js",
        "static/lib/datejs/parser.js",
        "static/lib/datejs/sugarpak.js",
        "static/lib/datejs/extras.js",
        "static/lib/jquery/jquery-1.7.2b1.js",
        "static/lib/jquery.MD5/jquery.md5.js",
        "static/lib/jquery.form/jquery.form.js",
        "static/lib/jquery.validate/jquery.validate.js",
        "static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
        "static/lib/jquery.blockUI/jquery.blockUI.js",
        "static/lib/jquery.superfish/js/hoverIntent.js",
        "static/lib/jquery.superfish/js/superfish.js",
        "static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js",
        "static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js",
        "static/lib/jquery.ui.notify/js/jquery.notify.js",
        "static/lib/jquery.deferred-queue/jquery.deferred-queue.js",
        "static/lib/jquery.scrollTo/jquery.scrollTo-min.js",
        "static/lib/jquery.tipsy/jquery.tipsy.js",
        "static/lib/jquery.textext/jquery.textext.js",
        "static/lib/json/json2.js",
        "static/lib/qweb/qweb2.js",
        "static/lib/underscore/underscore.js",
        "static/lib/underscore/underscore.string.js",
        "static/lib/backbone/backbone.js",
        "static/lib/cleditor/jquery.cleditor.min.js",

        "static/lib/labjs/LAB.src.js",
        "static/lib/py.js/lib/py.js",
        "static/src/js/boot.js",
        "static/src/js/corelib.js",
        "static/src/js/coresetup.js",
        "static/src/js/dates.js",
        "static/src/js/formats.js",
        "static/src/js/chrome.js",
        "static/src/js/views.js",
        "static/src/js/data.js",
        "static/src/js/data_export.js",
        "static/src/js/data_import.js",
        "static/src/js/search.js",
        "static/src/js/view_form.js",
        "static/src/js/view_list.js",
        "static/src/js/view_list_editable.js",
        "static/src/js/view_tree.js",
        "static/src/js/view_editor.js"
    ],
    'css' : [
        "static/lib/jquery.superfish/css/superfish.css",
        "static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css",
        "static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
        "static/lib/jquery.ui.notify/css/ui.notify.css",
        "static/lib/jquery.tipsy/tipsy.css",
#        "static/src/css/base_old.css",
        "static/src/css/base.css",
        "static/src/css/data_export.css",
        "static/src/css/data_import.css",
        "static/lib/cleditor/jquery.cleditor.css",
    ],
    'qweb' : [
        "static/src/xml/*.xml",
    ],
}
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenUpgrade module for Odoo
#    @copyright 2014-Today: Odoo Community Association
#    @author: Sylvain LE GAL <https://twitter.com/legalsylvain>
#
#    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 logging
from openerp import pooler, SUPERUSER_ID
from openerp.openupgrade import openupgrade
logger = logging.getLogger('OpenUpgrade')

def check_ir_actions_server_state(cr, pool):
    """Test if 'state' values are correct.
    If not, log an error to indicate that the user has to overload _get_state
    function in his custom modules."""
    ias_obj = pool['ir.actions.server']
    valid_selection = ias_obj._get_states(cr, SUPERUSER_ID)
    valid_list = [x[0] for x in valid_selection]
    ias_ids = ias_obj.search(
        cr, SUPERUSER_ID, [('state', 'not in', valid_list)])
    for ias in ias_obj.browse(cr, SUPERUSER_ID, ias_ids):
        logger.error(
            "Invalid value '%s' in the model 'ir_actions_server' "
            "for the field 'state'. (id %s).Please overload the new "
            "ir_actions_server._get_state function." % (
                ias.state, ias.id))

@openupgrade.migrate()
def migrate(cr, version):
    pool = pooler.get_pool(cr.dbname)
    check_ir_actions_server_state(cr, pool)
<commit_msg>[ADD] [8.0] 'base' add remove_account_report_company_record and check_commercial_partner function;<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenUpgrade module for Odoo
#    @copyright 2014-Today: Odoo Community Association
#    @author: Sylvain LE GAL <https://twitter.com/legalsylvain>
#
#    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 logging
from openerp import pooler, SUPERUSER_ID
from openerp.openupgrade import openupgrade
logger = logging.getLogger('OpenUpgrade')

def check_ir_actions_server_state(cr, pool):
    """Test if 'state' values are correct.
    If not, log an error to indicate that the user has to overload _get_state
    function in his custom modules."""
    ias_obj = pool['ir.actions.server']
    valid_selection = ias_obj._get_states(cr, SUPERUSER_ID)
    valid_list = [x[0] for x in valid_selection]
    ias_ids = ias_obj.search(
        cr, SUPERUSER_ID, [('state', 'not in', valid_list)])
    for ias in ias_obj.browse(cr, SUPERUSER_ID, ias_ids):
        logger.error(
            "Invalid value '%s' in the model 'ir_actions_server' "
            "for the field 'state'. (id %s).Please overload the new "
            "ir_actions_server._get_state function." % (
                ias.state, ias.id))

def check_commercial_partner(cr, pool):
    """ Test if the column is present (iow: if there exists a partner with a parent"""
    if not openupgrade.column_exists(cr, "res_partner", "commercial_partner_id"):
        sql = """ALTER TABLE res_partner ADD COLUMN commercial_partner_id int"""
        cr.execute(sql)

def remove_account_report_company_record(cr, pool):
    """An ir_ui_view record from the discontinued module account_report_company is not removed and causes an
    AttributeError, remove it here to fix that error. """
    view_obj = pool.get('ir.ui.view')
    view_ids = view_obj.search(cr, SUPERUSER_ID,
                               [('arch', 'like', '//templates//'), ('name', '=', 'res.partner kanban')])
    view_obj.unlink(cr, SUPERUSER_ID, view_ids)

@openupgrade.migrate()
def migrate(cr, version):
    pool = pooler.get_pool(cr.dbname)
    check_ir_actions_server_state(cr, pool)
    check_commercial_partner(cr, pool)
    remove_account_report_company_record(cr, pool)
<|endoftext|>


<commit_before>import csv
from datetime import datetime
import re
from StringIO import StringIO

import formencode
from pylons.i18n import _
from webhelpers.html import literal

FORBIDDEN_NAMES = ["www", "static", "mail", "edit", "create", "settings",
                   "join", "leave", "control", "test", "support", "page",
                   "proposal", "wiki", "blog", "proposals", "admin", "dl",
                   "downloads", "stats", "branch", "merge", "pull", "push",
                   "hg", "git", "adhocracy", "user", "openid", "auth", "watch",
                   "poll", "delegation", "event", "comment", "root", "search",
                   "tag", "svn", "trac", "lists", "list", "new", "update",
                   "variant", "provision", "untag"]

VALIDUSER = re.compile(r"^[a-zA-Z0-9_\-]{3,255}$")
VALIDVARIANT = re.compile(r"^[\w\-_ ]{1,255}$", re.U)
TIME = re.compile(r"\d{1,2}.\d{1,2}.\d{4}")

class UniqueUsername(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import meta, User
        if not value or not isinstance(value, basestring):
            raise formencode.Invalid(
                _('No username is given'),
                value, state)
        if len(value.strip()) < 3:
            raise formencode.Invalid(
                _('Username is too short'),
                value, state)
        if not VALIDUSER.match(value) or value in FORBIDDEN_NAMES:
            raise formencode.Invalid(
                _('The username is invalid'),
                value, state)
        if meta.Session.query(User.user_name).filter(User.user_name ==
                                                     value).count():
            raise formencode.Invalid(
                _('That username already exists'),
                value, state)
        return value

class UniqueEmail(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import meta, User
        email = value.lower()
        if meta.Session.query(User.email).filter(User.email == email).all():
            raise formencode.Invalid(
                _('That email is already registered'),
                value, state)
        return value

class ValidDate(formencode.FancyValidator):
    def _to_python(self, value, state):
        if not TIME.match(value):
            raise formencode.Invalid(
                _('Invalid date, expecting DD.MM.YYYY'),
                    value, state)
        try:
            return datetime.strptime(value, "%d.%m.%Y")
        except ValueError:
            raise formencode.Invalid(
                _('Invalid date, expecting DD.MM.YYYY'),
                   value, state)
        return value

class ValidHTMLColor(formencode.validators.Regex):

    regex = r'^#[0-9a-fA-F]{1,6}'

    def to_python(self, value, state):
        try:
            super(ValidHTMLColor, self).to_python(value, state)
        except formencode.Invalid:
            raise formencode.Invalid(
                _("Please enter a html color code like '#f0f0f0'. "
                  "'%(value)' is not a valid color code."), value, state)
        return value

class UniqueInstanceKey(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Instance
        if not value:
            raise formencode.Invalid(
                _('No instance key is given'),
                value, state)
        if not Instance.INSTANCE_KEY.match(value) or value in FORBIDDEN_NAMES:
            raise formencode.Invalid(
                _('The instance key is invalid'),
                value, state)
        if Instance.find(value):
            raise formencode.Invalid(
                _('An instance with that key already exists'),
                value, state)
        return value

class ValidDelegateable(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Delegateable
        delegateable = Delegateable.find(value)
        if not delegateable:
            raise formencode.Invalid(
                _("No entity with ID '%s' exists") % value,
                value, state)
        return delegateable

class ValidProposal(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Proposal
        proposal = Proposal.find(value)
        if not proposal:
            raise formencode.Invalid(
                _("No proposal with ID '%s' exists") % value,
                value, state)
        return proposal

class ValidGroup(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Group
        group = Group.by_code(value)
        if not group:
            raise formencode.Invalid(
                _("No group with ID '%s' exists") % value,
                value, state)
        return group

class ContainsChar(formencode.validators.Regex):

    regex = r"[a-zA-Z]"

    def to_python(self, value, state):
        try:
            super(ContainsChar, self).to_python(value, state)
        except formencode.Invalid:
            raise formencode.Invalid(_("At least on character is required"),
                                     value, state)
        return value

class ValidBadge(formencode.FancyValidator):

    def _to_python(self, value, state):
        from adhocracy.model import Badge
        badge = Badge.by_id(value)
        if not badge:
            raise formencode.Invalid(
                _("No Badge ID '%s' exists") % value,
                value, state)
        return badge

class MaybeMilestone(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Milestone
        try:
            return Milestone.find(value)
        except Exception:
            return None

class ValidRevision(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Revision
        revision = Revision.find(value)
        if not revision:
            raise formencode.Invalid(
                _("No revision with ID '%s' exists") % value,
                value, state)
        return revision

class ValidComment(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Comment
        comment = Comment.find(value)
        if not comment:
            raise formencode.Invalid(
                _("No comment with ID '%s' exists") % value,
                value, state)
        return comment

class ValidWatch(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Watch
        watch = Watch.by_id(value)
        if not watch:
            raise formencode.Invalid(
                _("No watchlist entry with ID '%s' exists") % value,
                value, state)
        return watch

class ValidRef(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import refs
        try:
            entity = refs.from_url(value)
            if not entity:
                raise TypeError()
            return entity
        except:
            raise formencode.Invalid(_("Invalid reference"), value, state)

class ExistingUserName(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import User
        user = User.find(value)
        if not user:
            raise formencode.Invalid(
                _("No user with the user name '%s' exists") % value,
                value, state)
        return user

class ValidTagging(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Tagging
        tagging = Tagging.find(value)
        if not tagging:
            raise formencode.Invalid(
                _("No tagging with ID '%s' exists") % value,
                value, state)
        return tagging

class ValidTag(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Tag
        tag = Tag.find(value)
        if not tag:
            raise formencode.Invalid(
                _("No tag with ID '%s' exists") % value,
                value, state)
        return tag

class ValidText(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Text
        text = Text.find(value)
        if not text:
            raise formencode.Invalid(
                _("No text with ID '%s' exists") % value,
                         value, state)
        return text

class ValidPage(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Page
        page = Page.find(value)
        if not page:
            raise formencode.Invalid(_("No page '%s' exists") % value,
                                     value, state)
        return page

class ValidPageFunction(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Page
        function = value.lower().strip()
        if function not in Page.FUNCTIONS:
            raise formencode.Invalid(_("Invalid page function: %s") % value,
                                     value, state)
        return function

class VariantName(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.lib.text import variant_normalize
        var = variant_normalize(value)
        if not var or len(var) < 2:
            raise formencode.Invalid(_("No variant name is given."),
                                     value, state)

        if (var.lower() in FORBIDDEN_NAMES or not
            VALIDVARIANT.match(var.lower())):
            raise formencode.Invalid(_("Invalid variant name: %s") % value,
                                     value, state)
        try:
            int(var)
            raise formencode.Invalid(
                _("Variant name cannot be purely numeric: %s") % value,
                value, state)
        except:
            return var

class UnusedTitle(formencode.validators.String):
    def __init__(self):
        super(UnusedTitle, self).__init__(min=3, max=254, not_empty=True)

    def _to_python(self, value, state):
        from adhocracy.model import Page
        value = super(UnusedTitle, self)._to_python(value, state)
        page = Page.find_fuzzy(value)
        if hasattr(state, 'page') and state.page == page:
            return value

        if page is not None:
            raise formencode.Invalid(
                _("An entry with this title already exists"), value, state)

        if not value or len(value) < 2:
            raise formencode.Invalid(_("No page name is given."), value, state)

        if value.lower() in FORBIDDEN_NAMES:
            raise formencode.Invalid(_("Invalid page name: %s") % value,
                                     value, state)

        try:
            int(value)
            raise formencode.Invalid(
                _("Variant name cannot be purely numeric: %s") % value,
                value, state)
        except:
            return value

USER_NAME = 'user_name'
DISPLAY_NAME = 'display_name'
EMAIL = 'email'
USERNAME_VALIDATOR = UniqueUsername()
EMAIL_VALIDATOR = formencode.All(formencode.validators.Email(),
                                 UniqueEmail())

class UsersCSV(formencode.FancyValidator):

    def to_python(self, value, state):
        fieldnames = [USER_NAME, DISPLAY_NAME, EMAIL]
        errors = []
        items = []
        self.usernames = {}
        self.emails = {}
        self.duplicates = False
        reader = csv.DictReader(StringIO(value), fieldnames=fieldnames)
        try:
            for item in reader:
                error_list = self._check_item(item, reader.line_num)
                if error_list:
                    errors.append((reader.line_num, error_list))
                if not errors:
                    items.append(item)
        except csv.Error, E:
            line_content = value.split('\n')[reader.line_num]
            msg = _('Error "%(error)s" while reading line '
                    '
%(line_content)s
') % dict(
                line_content=line_content,
                error=str(E))
            errors.append((reader.line_num + 1, [msg]))
        if errors or self.duplicates:
            error_msg = _('The following errors occured while reading '
                          'the list of users:
%s')
            line_error_messages = []
            for (line, messages) in errors:
                line_error_messages.append(
                    _('Line %s: %s') % (line, ', '.join(messages)))

            # Insert messages for duplicate emails and usernames
            self._insert_duplicate_messages(
                line_error_messages,
                self.emails,
                _('Email %s is used multiple times'))
            self._insert_duplicate_messages(
                line_error_messages,
                self.usernames,
                _('Username %s is used multiple times'))
            error_msg = error_msg % ('
'.join(line_error_messages))
            raise formencode.Invalid(literal(error_msg), value, state)
        else:
            return items

    def _insert_duplicate_messages(self, line_error_messages, duplicate_dict,
                                   msg_template):
        for (value, lines) in duplicate_dict.items():
            if len(lines) > 1:
                lines = [str(line) for line in lines]
                line_error_messages.append(
                    _('Lines %s: %s') % (
                        ', '.join(lines),
                        msg_template % value))

    def _check_item(self, item, line):
        error_list = []
        user_name = item.get(USER_NAME, '')
        email = item.get(EMAIL, '')
        for (validator, value) in ((USERNAME_VALIDATOR, user_name),
                                   (EMAIL_VALIDATOR, email)):
            try:
                validator.to_python(value, None)
            except formencode.Invalid, E:
                error_list.append(u'%s (%s)' % (E.msg, value))
        emails = self.emails.setdefault(email.strip(), [])
        emails.append(line)
        usernames = self.usernames.setdefault(user_name.strip(), [])
        usernames.append(line)
        if len(emails) > 1 or len(usernames) > 1:
            self.duplicates = True
        return error_list

class ContainsUrlPlaceholder(formencode.FancyValidator):

    def _to_python(self, value, state):
        if not '{url}' in value:
            raise formencode.Invalid(_('You need to insert "{url}" into the '
                                       'email text so we can insert a url for '
                                       'the user where he can set a password'),
                                     value, state)
        return value
<commit_msg>Fix UnicodeDecodeError during user import<commit_after>import csv
from datetime import datetime
import re
from StringIO import StringIO

import formencode
from pylons.i18n import _
from webhelpers.html import literal

FORBIDDEN_NAMES = ["www", "static", "mail", "edit", "create", "settings",
                   "join", "leave", "control", "test", "support", "page",
                   "proposal", "wiki", "blog", "proposals", "admin", "dl",
                   "downloads", "stats", "branch", "merge", "pull", "push",
                   "hg", "git", "adhocracy", "user", "openid", "auth", "watch",
                   "poll", "delegation", "event", "comment", "root", "search",
                   "tag", "svn", "trac", "lists", "list", "new", "update",
                   "variant", "provision", "untag"]

VALIDUSER = re.compile(r"^[a-zA-Z0-9_\-]{3,255}$")
VALIDVARIANT = re.compile(r"^[\w\-_ ]{1,255}$", re.U)
TIME = re.compile(r"\d{1,2}.\d{1,2}.\d{4}")

class UniqueUsername(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import meta, User
        if not value or not isinstance(value, basestring):
            raise formencode.Invalid(
                _('No username is given'),
                value, state)
        if len(value.strip()) < 3:
            raise formencode.Invalid(
                _('Username is too short'),
                value, state)
        if not VALIDUSER.match(value) or value in FORBIDDEN_NAMES:
            raise formencode.Invalid(
                _('The username is invalid'),
                value, state)
        if meta.Session.query(User.user_name).filter(User.user_name ==
                                                     value).count():
            raise formencode.Invalid(
                _('That username already exists'),
                value, state)
        return value

class UniqueEmail(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import meta, User
        email = value.lower()
        if meta.Session.query(User.email).filter(User.email == email).all():
            raise formencode.Invalid(
                _('That email is already registered'),
                value, state)
        return value

class ValidDate(formencode.FancyValidator):
    def _to_python(self, value, state):
        if not TIME.match(value):
            raise formencode.Invalid(
                _('Invalid date, expecting DD.MM.YYYY'),
                    value, state)
        try:
            return datetime.strptime(value, "%d.%m.%Y")
        except ValueError:
            raise formencode.Invalid(
                _('Invalid date, expecting DD.MM.YYYY'),
                   value, state)
        return value

class ValidHTMLColor(formencode.validators.Regex):

    regex = r'^#[0-9a-fA-F]{1,6}'

    def to_python(self, value, state):
        try:
            super(ValidHTMLColor, self).to_python(value, state)
        except formencode.Invalid:
            raise formencode.Invalid(
                _("Please enter a html color code like '#f0f0f0'. "
                  "'%(value)' is not a valid color code."), value, state)
        return value

class UniqueInstanceKey(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Instance
        if not value:
            raise formencode.Invalid(
                _('No instance key is given'),
                value, state)
        if not Instance.INSTANCE_KEY.match(value) or value in FORBIDDEN_NAMES:
            raise formencode.Invalid(
                _('The instance key is invalid'),
                value, state)
        if Instance.find(value):
            raise formencode.Invalid(
                _('An instance with that key already exists'),
                value, state)
        return value

class ValidDelegateable(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Delegateable
        delegateable = Delegateable.find(value)
        if not delegateable:
            raise formencode.Invalid(
                _("No entity with ID '%s' exists") % value,
                value, state)
        return delegateable

class ValidProposal(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Proposal
        proposal = Proposal.find(value)
        if not proposal:
            raise formencode.Invalid(
                _("No proposal with ID '%s' exists") % value,
                value, state)
        return proposal

class ValidGroup(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Group
        group = Group.by_code(value)
        if not group:
            raise formencode.Invalid(
                _("No group with ID '%s' exists") % value,
                value, state)
        return group

class ContainsChar(formencode.validators.Regex):

    regex = r"[a-zA-Z]"

    def to_python(self, value, state):
        try:
            super(ContainsChar, self).to_python(value, state)
        except formencode.Invalid:
            raise formencode.Invalid(_("At least on character is required"),
                                     value, state)
        return value

class ValidBadge(formencode.FancyValidator):

    def _to_python(self, value, state):
        from adhocracy.model import Badge
        badge = Badge.by_id(value)
        if not badge:
            raise formencode.Invalid(
                _("No Badge ID '%s' exists") % value,
                value, state)
        return badge

class MaybeMilestone(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Milestone
        try:
            return Milestone.find(value)
        except Exception:
            return None

class ValidRevision(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Revision
        revision = Revision.find(value)
        if not revision:
            raise formencode.Invalid(
                _("No revision with ID '%s' exists") % value,
                value, state)
        return revision

class ValidComment(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Comment
        comment = Comment.find(value)
        if not comment:
            raise formencode.Invalid(
                _("No comment with ID '%s' exists") % value,
                value, state)
        return comment

class ValidWatch(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Watch
        watch = Watch.by_id(value)
        if not watch:
            raise formencode.Invalid(
                _("No watchlist entry with ID '%s' exists") % value,
                value, state)
        return watch

class ValidRef(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import refs
        try:
            entity = refs.from_url(value)
            if not entity:
                raise TypeError()
            return entity
        except:
            raise formencode.Invalid(_("Invalid reference"), value, state)

class ExistingUserName(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import User
        user = User.find(value)
        if not user:
            raise formencode.Invalid(
                _("No user with the user name '%s' exists") % value,
                value, state)
        return user

class ValidTagging(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Tagging
        tagging = Tagging.find(value)
        if not tagging:
            raise formencode.Invalid(
                _("No tagging with ID '%s' exists") % value,
                value, state)
        return tagging

class ValidTag(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Tag
        tag = Tag.find(value)
        if not tag:
            raise formencode.Invalid(
                _("No tag with ID '%s' exists") % value,
                value, state)
        return tag

class ValidText(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Text
        text = Text.find(value)
        if not text:
            raise formencode.Invalid(
                _("No text with ID '%s' exists") % value,
                         value, state)
        return text

class ValidPage(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Page
        page = Page.find(value)
        if not page:
            raise formencode.Invalid(_("No page '%s' exists") % value,
                                     value, state)
        return page

class ValidPageFunction(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.model import Page
        function = value.lower().strip()
        if function not in Page.FUNCTIONS:
            raise formencode.Invalid(_("Invalid page function: %s") % value,
                                     value, state)
        return function

class VariantName(formencode.FancyValidator):
    def _to_python(self, value, state):
        from adhocracy.lib.text import variant_normalize
        var = variant_normalize(value)
        if not var or len(var) < 2:
            raise formencode.Invalid(_("No variant name is given."),
                                     value, state)

        if (var.lower() in FORBIDDEN_NAMES or not
            VALIDVARIANT.match(var.lower())):
            raise formencode.Invalid(_("Invalid variant name: %s") % value,
                                     value, state)
        try:
            int(var)
            raise formencode.Invalid(
                _("Variant name cannot be purely numeric: %s") % value,
                value, state)
        except:
            return var

class UnusedTitle(formencode.validators.String):
    def __init__(self):
        super(UnusedTitle, self).__init__(min=3, max=254, not_empty=True)

    def _to_python(self, value, state):
        from adhocracy.model import Page
        value = super(UnusedTitle, self)._to_python(value, state)
        page = Page.find_fuzzy(value)
        if hasattr(state, 'page') and state.page == page:
            return value

        if page is not None:
            raise formencode.Invalid(
                _("An entry with this title already exists"), value, state)

        if not value or len(value) < 2:
            raise formencode.Invalid(_("No page name is given."), value, state)

        if value.lower() in FORBIDDEN_NAMES:
            raise formencode.Invalid(_("Invalid page name: %s") % value,
                                     value, state)

        try:
            int(value)
            raise formencode.Invalid(
                _("Variant name cannot be purely numeric: %s") % value,
                value, state)
        except:
            return value

USER_NAME = 'user_name'
DISPLAY_NAME = 'display_name'
EMAIL = 'email'
USERNAME_VALIDATOR = UniqueUsername()
EMAIL_VALIDATOR = formencode.All(formencode.validators.Email(),
                                 UniqueEmail())

class UsersCSV(formencode.FancyValidator):

    def to_python(self, value, state):
        fieldnames = [USER_NAME, DISPLAY_NAME, EMAIL]
        errors = []
        items = []
        self.usernames = {}
        self.emails = {}
        self.duplicates = False
        value = value.encode('utf-8')
        reader = csv.DictReader(StringIO(value), fieldnames=fieldnames)
        try:
            for item in reader:
                error_list = self._check_item(item, reader.line_num)
                if error_list:
                    errors.append((reader.line_num, error_list))
                if not errors:
                    items.append(item)
        except csv.Error, E:
            line_content = value.split('\n')[reader.line_num]
            msg = _('Error "%(error)s" while reading line '
                    '
%(line_content)s
') % dict(
                line_content=line_content,
                error=str(E))
            errors.append((reader.line_num + 1, [msg]))
        if errors or self.duplicates:
            error_msg = _('The following errors occured while reading '
                          'the list of users:
%s')
            line_error_messages = []
            for (line, messages) in errors:
                line_error_messages.append(
                    _('Line %s: %s') % (line, ', '.join(messages)))

            # Insert messages for duplicate emails and usernames
            self._insert_duplicate_messages(
                line_error_messages,
                self.emails,
                _('Email %s is used multiple times'))
            self._insert_duplicate_messages(
                line_error_messages,
                self.usernames,
                _('Username %s is used multiple times'))
            error_msg = error_msg % ('
'.join(line_error_messages))
            raise formencode.Invalid(literal(error_msg), value, state)
        else:
            return items

    def _insert_duplicate_messages(self, line_error_messages, duplicate_dict,
                                   msg_template):
        for (value, lines) in duplicate_dict.items():
            if len(lines) > 1:
                lines = [str(line) for line in lines]
                line_error_messages.append(
                    _('Lines %s: %s') % (
                        ', '.join(lines),
                        msg_template % value))

    def _check_item(self, item, line):
        error_list = []
        user_name = item.get(USER_NAME, '')
        email = item.get(EMAIL, '')
        for (validator, value) in ((USERNAME_VALIDATOR, user_name),
                                   (EMAIL_VALIDATOR, email)):
            try:
                validator.to_python(value, None)
            except formencode.Invalid, E:
                error_list.append(u'%s (%s)' % (E.msg, value))
        emails = self.emails.setdefault(email.strip(), [])
        emails.append(line)
        usernames = self.usernames.setdefault(user_name.strip(), [])
        usernames.append(line)
        if len(emails) > 1 or len(usernames) > 1:
            self.duplicates = True
        return error_list

class ContainsUrlPlaceholder(formencode.FancyValidator):

    def _to_python(self, value, state):
        if not '{url}' in value:
            raise formencode.Invalid(_('You need to insert "{url}" into the '
                                       'email text so we can insert a url for '
                                       'the user where he can set a password'),
                                     value, state)
        return value
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# smv documentation build configuration file, created by
# sphinx-quickstart on Fri Apr 14 16:47:39 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
# import sys
# sys.path.insert(0, u'/Users/khorkhe/Desktop/twine/SMV/src/main/python/smv')

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Spark Modularized View'
copyright = u'2017, TresAmigosSD'
author = u'TresAmigosSD'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
# The location of this file is not reliable so we cannot read .smv_version directly
# Instead the version is exported from the script that creates the sphinx build
release = os.environ["SMV_VERSION"]
# The short X.Y version.
version = release[:2]

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
<commit_msg>fix string index typo<commit_after># -*- coding: utf-8 -*-
#
# smv documentation build configuration file, created by
# sphinx-quickstart on Fri Apr 14 16:47:39 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
# import sys
# sys.path.insert(0, u'/Users/khorkhe/Desktop/twine/SMV/src/main/python/smv')

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Spark Modularized View'
copyright = u'2017, TresAmigosSD'
author = u'TresAmigosSD'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
# The location of this file is not reliable so we cannot read .smv_version directly
# Instead the version is exported from the script that creates the sphinx build
release = os.environ["SMV_VERSION"]
# The short X.Y version.
version = release[:3]

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
<|endoftext|>


<commit_before>import sublime
import sublime_plugin
import os
import re
import functools
import subprocess

PHANTOM_KEY_ALL = 'git-blame-all'
SETTING_PHANTOM_ALL_DISPLAYED = 'git-blame-all-displayed'

stylesheet_one = '''

'''

template_one = '''

        {stylesheet}

                Git Blame ({user})
                {date} {time} |
                {sha}
                [Copy]
                [Show]
                ''' + chr(0x00D7) + '''

'''

stylesheet_all = '''

'''

template_all = '''

        {stylesheet}

                {sha} ({user} {date} {time})
                ''' + chr(0x00D7) + '''

'''

# Sometimes this fails on other OS, just error silently
try:
    si = subprocess.STARTUPINFO()
    si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
except Exception:
    si = None

class BlameCommand(sublime_plugin.TextCommand):

    def __init__(self, view):
        self.view = view
        self.phantom_set = sublime.PhantomSet(view, 'git-blame')

    # TODO: Can this cache start providing stale results?
    @functools.lru_cache(128, False)
    def get_blame(self, line, path):
        return subprocess.check_output(
            ["git", "blame", "--minimal", "-w", "-L {0},{0}".format(line), os.path.basename(path)],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode("utf-8")

    def parse_blame(self, blame):
        sha, file_path, user, date, time, tz_offset, *_ = blame.split()

        # Was part of the inital commit so no updates
        if file_path[0] == '(':
            user, date, time, tz_offset = file_path, user, date, time
            file_path = None

        # Fix an issue where the username has a space
        # Im going to need to do something better though if people
        # start to have multiple spaces in their names.
        if not date[0].isdigit():
            user = "{0} {1}".format(user, date)
            date, time = time, tz_offset

        return(sha, user[1:], date, time)

    def get_commit(self, sha, path):
        return subprocess.check_output(
            ["git", "show", sha],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode('utf-8')

    def on_phantom_close(self, href):
        href_parts = href.split('-')

        if len(href_parts) > 1:
            intent = href_parts[0]
            sha = href_parts[1]
            # The SHA output by git-blame may have a leading caret to indicate
            # that it is a "boundary commit". That useful information has
            # already been shown in the phantom, so strip it before going on to
            # use the SHA programmatically.
            sha = sha.strip('^')

            if intent == "copy":
                sublime.set_clipboard(sha)
                sublime.status_message('Git SHA copied to clipboard')
            elif intent == "show":
                try:
                    desc = self.get_commit(sha, self.view.file_name())
                except Exception as e:
                    communicate_error(e)
                    return

                buf = self.view.window().new_file()
                buf.run_command('insert_commit_description', {'desc': desc, 'scratch_view_name': 'commit ' + sha})
            else:
                self.view.erase_phantoms('git-blame')
        else:
            self.view.erase_phantoms('git-blame')

    def run(self, edit):
        if not view_is_suitable(self.view):
            return

        phantoms = []
        self.view.erase_phantoms('git-blame')
        # Before adding the phantom, see if the current phantom that is displayed is at the same spot at the selection
        if self.phantom_set.phantoms:
            phantom_exists = self.view.line(self.view.sel()[0]) == self.view.line(self.phantom_set.phantoms[0].region)
            if phantom_exists:
                self.phantom_set.update(phantoms)
                return

        for region in self.view.sel():
            line = self.view.line(region)
            (row, col) = self.view.rowcol(region.begin())
            full_path = self.view.file_name()

            try:
                blame_output = self.get_blame(int(row) + 1, full_path)
            except Exception as e:
                communicate_error(e)
                return

            sha, user, date, time = self.parse_blame(blame_output)

            body = template_one.format(sha=sha, user=user, date=date, time=time, stylesheet=stylesheet_one)

            phantom = sublime.Phantom(line, body, sublime.LAYOUT_BLOCK, self.on_phantom_close)
            phantoms.append(phantom)
        self.phantom_set.update(phantoms)

class BlameShowAllCommand(sublime_plugin.TextCommand):

    # The fixed length for author names
    NAME_LENGTH = 10

    def __init__(self, view):
        super().__init__(view)
        self.phantom_set = sublime.PhantomSet(self.view, PHANTOM_KEY_ALL)
        self.pattern = None

    def run(self, edit):
        if not view_is_suitable(self.view):
            return

        self.view.erase_phantoms(PHANTOM_KEY_ALL)
        phantoms = []

        # If they are currently shown, toggle them off and return.
        if self.view.settings().get(SETTING_PHANTOM_ALL_DISPLAYED, False):
            self.phantom_set.update(phantoms)
            self.view.settings().set(SETTING_PHANTOM_ALL_DISPLAYED, False)
            return

        try:
            blame_output = self.get_blame(self.view.file_name())
        except Exception as e:
            communicate_error(e)
            return

        for l in blame_output.splitlines():
            parsed = self.parse_blame(l)
            if not parsed:
                continue

            sha, author, date, time, line_number = parsed

            body = template_all.format(sha=sha,
                                       user=self.format_name(author),
                                       date=date,
                                       time=time,
                                       stylesheet=stylesheet_all)

            line_point = self.get_line_point(line_number - 1)
            phantom = sublime.Phantom(line_point,
                                      body,
                                      sublime.LAYOUT_INLINE,
                                      self.on_phantom_close)
            phantoms.append(phantom)

        self.phantom_set.update(phantoms)
        self.view.settings().set(SETTING_PHANTOM_ALL_DISPLAYED, True)
        # Bring the phantoms into view without the user needing to manually scroll left.
        self.view.set_viewport_position((0.0, self.view.viewport_position()[1]))

    def get_blame(self, path):
        return subprocess.check_output(
            # The option --show-name is necessary to force file name display.
            ["git", "blame", "--show-name", "--minimal", "-w", os.path.basename(path)],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode("utf-8")

    def parse_blame(self, blame):
        '''Parses git blame output.
        '''
        if not self.pattern:
            self.prepare_pattern()

        m = self.pattern.match(blame)
        if m:
            sha = m.group('sha')
            # Currently file is not used.
            # file = m.group('file')
            author = m.group('author')
            date = m.group('date')
            time = m.group('time')
            line_number = int(m.group('line_number'))
            return sha, author, date, time, line_number
        else:
            return None

    def prepare_pattern(self):
        '''Prepares the regex pattern to parse git blame output.
        '''
        # The SHA output by git-blame may have a leading caret to indicate
        # that it is a "boundary commit".
        p_sha = r'(?P<sha>\^?\w+)'
        p_file = r'((?P<file>[\S ]+)\s+)'
        p_author = r'(?P<author>.+?)'
        p_date = r'(?P<date>\d{4}-\d{2}-\d{2})'
        p_time = r'(?P<time>\d{2}:\d{2}:\d{2})'
        p_timezone = r'(?P<timezone>[\+-]\d+)'
        p_line = r'(?P<line_number>\d+)'
        s = r'\s+'

        self.pattern = re.compile(r'^' + p_sha + s + p_file + r'\(' +
                                  p_author + s + p_date + s + p_time + s +
                                  p_timezone + s + p_line + r'\) ')

    def format_name(self, name):
        '''Formats author names so that widths of phantoms become equal.
        '''
        ellipsis = '...'
        if len(name) > self.NAME_LENGTH:
            return name[:self.NAME_LENGTH] + ellipsis
        else:
            return name + '.' * (self.NAME_LENGTH - len(name)) + ellipsis

    def get_line_point(self, line):
        '''Get the point of specified line in a view.
        '''
        return self.view.line(self.view.text_point(line, 0))

    def on_phantom_close(self, href):
        '''Closes opened phantoms.
        '''
        if href == 'close':
            self.view.run_command('blame_erase_all')

class BlameEraseAllCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        '''Erases the blame results.
        '''
        sublime.status_message("The git blame result is cleared.")
        self.view.erase_phantoms(PHANTOM_KEY_ALL)

class BlameEraseAllListener(sublime_plugin.ViewEventListener):

    @classmethod
    def is_applicable(cls, settings):
        '''Checks if the blame_erase_all command is applicable.
        '''
        return settings.get(SETTING_PHANTOM_ALL_DISPLAYED, False)

    def on_modified_async(self):
        '''Automatically erases the blame results to prevent mismatches.
        '''
        self.view.run_command('blame_erase_all')
        self.view.settings().erase(SETTING_PHANTOM_ALL_DISPLAYED)

class InsertCommitDescriptionCommand(sublime_plugin.TextCommand):

    def run(self, edit, desc, scratch_view_name):
        view = self.view
        view.set_scratch(True)
        view.set_syntax_file('Packages/Diff/Diff.sublime-syntax')
        view.insert(edit, 0, desc)
        view.set_name(scratch_view_name)

def view_is_suitable(view):
    ok = view.file_name() and not view.is_dirty()
    if not ok:
        communicate_error("Please save file changes to disk first.")
    return ok

def communicate_error(e, modal=True):
    user_msg = "st3-gitblame:\n\n{}".format(e)
    if isinstance(e, subprocess.CalledProcessError):
        user_msg += "\n\n{}".format(e.output.decode("utf-8"))

    print()
    if modal:
        sublime.error_message(user_msg)
    else:
        sublime.status_message(user_msg)
        # Unlike with the error dialog, a status message is not automatically
        # persisted in the console too.
        print(user_msg)
<commit_msg>Use the user-facing name in messages<commit_after>import sublime
import sublime_plugin
import os
import re
import functools
import subprocess

PHANTOM_KEY_ALL = 'git-blame-all'
SETTING_PHANTOM_ALL_DISPLAYED = 'git-blame-all-displayed'

stylesheet_one = '''

'''

template_one = '''

        {stylesheet}

                Git Blame ({user})
                {date} {time} |
                {sha}
                [Copy]
                [Show]
                ''' + chr(0x00D7) + '''

'''

stylesheet_all = '''

'''

template_all = '''

        {stylesheet}

                {sha} ({user} {date} {time})
                ''' + chr(0x00D7) + '''

'''

# Sometimes this fails on other OS, just error silently
try:
    si = subprocess.STARTUPINFO()
    si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
except Exception:
    si = None

class BlameCommand(sublime_plugin.TextCommand):

    def __init__(self, view):
        self.view = view
        self.phantom_set = sublime.PhantomSet(view, 'git-blame')

    # TODO: Can this cache start providing stale results?
    @functools.lru_cache(128, False)
    def get_blame(self, line, path):
        return subprocess.check_output(
            ["git", "blame", "--minimal", "-w", "-L {0},{0}".format(line), os.path.basename(path)],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode("utf-8")

    def parse_blame(self, blame):
        sha, file_path, user, date, time, tz_offset, *_ = blame.split()

        # Was part of the inital commit so no updates
        if file_path[0] == '(':
            user, date, time, tz_offset = file_path, user, date, time
            file_path = None

        # Fix an issue where the username has a space
        # Im going to need to do something better though if people
        # start to have multiple spaces in their names.
        if not date[0].isdigit():
            user = "{0} {1}".format(user, date)
            date, time = time, tz_offset

        return(sha, user[1:], date, time)

    def get_commit(self, sha, path):
        return subprocess.check_output(
            ["git", "show", sha],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode('utf-8')

    def on_phantom_close(self, href):
        href_parts = href.split('-')

        if len(href_parts) > 1:
            intent = href_parts[0]
            sha = href_parts[1]
            # The SHA output by git-blame may have a leading caret to indicate
            # that it is a "boundary commit". That useful information has
            # already been shown in the phantom, so strip it before going on to
            # use the SHA programmatically.
            sha = sha.strip('^')

            if intent == "copy":
                sublime.set_clipboard(sha)
                sublime.status_message('Git SHA copied to clipboard')
            elif intent == "show":
                try:
                    desc = self.get_commit(sha, self.view.file_name())
                except Exception as e:
                    communicate_error(e)
                    return

                buf = self.view.window().new_file()
                buf.run_command('insert_commit_description', {'desc': desc, 'scratch_view_name': 'commit ' + sha})
            else:
                self.view.erase_phantoms('git-blame')
        else:
            self.view.erase_phantoms('git-blame')

    def run(self, edit):
        if not view_is_suitable(self.view):
            return

        phantoms = []
        self.view.erase_phantoms('git-blame')
        # Before adding the phantom, see if the current phantom that is displayed is at the same spot at the selection
        if self.phantom_set.phantoms:
            phantom_exists = self.view.line(self.view.sel()[0]) == self.view.line(self.phantom_set.phantoms[0].region)
            if phantom_exists:
                self.phantom_set.update(phantoms)
                return

        for region in self.view.sel():
            line = self.view.line(region)
            (row, col) = self.view.rowcol(region.begin())
            full_path = self.view.file_name()

            try:
                blame_output = self.get_blame(int(row) + 1, full_path)
            except Exception as e:
                communicate_error(e)
                return

            sha, user, date, time = self.parse_blame(blame_output)

            body = template_one.format(sha=sha, user=user, date=date, time=time, stylesheet=stylesheet_one)

            phantom = sublime.Phantom(line, body, sublime.LAYOUT_BLOCK, self.on_phantom_close)
            phantoms.append(phantom)
        self.phantom_set.update(phantoms)

class BlameShowAllCommand(sublime_plugin.TextCommand):

    # The fixed length for author names
    NAME_LENGTH = 10

    def __init__(self, view):
        super().__init__(view)
        self.phantom_set = sublime.PhantomSet(self.view, PHANTOM_KEY_ALL)
        self.pattern = None

    def run(self, edit):
        if not view_is_suitable(self.view):
            return

        self.view.erase_phantoms(PHANTOM_KEY_ALL)
        phantoms = []

        # If they are currently shown, toggle them off and return.
        if self.view.settings().get(SETTING_PHANTOM_ALL_DISPLAYED, False):
            self.phantom_set.update(phantoms)
            self.view.settings().set(SETTING_PHANTOM_ALL_DISPLAYED, False)
            return

        try:
            blame_output = self.get_blame(self.view.file_name())
        except Exception as e:
            communicate_error(e)
            return

        for l in blame_output.splitlines():
            parsed = self.parse_blame(l)
            if not parsed:
                continue

            sha, author, date, time, line_number = parsed

            body = template_all.format(sha=sha,
                                       user=self.format_name(author),
                                       date=date,
                                       time=time,
                                       stylesheet=stylesheet_all)

            line_point = self.get_line_point(line_number - 1)
            phantom = sublime.Phantom(line_point,
                                      body,
                                      sublime.LAYOUT_INLINE,
                                      self.on_phantom_close)
            phantoms.append(phantom)

        self.phantom_set.update(phantoms)
        self.view.settings().set(SETTING_PHANTOM_ALL_DISPLAYED, True)
        # Bring the phantoms into view without the user needing to manually scroll left.
        self.view.set_viewport_position((0.0, self.view.viewport_position()[1]))

    def get_blame(self, path):
        return subprocess.check_output(
            # The option --show-name is necessary to force file name display.
            ["git", "blame", "--show-name", "--minimal", "-w", os.path.basename(path)],
            cwd=os.path.dirname(os.path.realpath(path)),
            startupinfo=si,
            stderr=subprocess.STDOUT
        ).decode("utf-8")

    def parse_blame(self, blame):
        '''Parses git blame output.
        '''
        if not self.pattern:
            self.prepare_pattern()

        m = self.pattern.match(blame)
        if m:
            sha = m.group('sha')
            # Currently file is not used.
            # file = m.group('file')
            author = m.group('author')
            date = m.group('date')
            time = m.group('time')
            line_number = int(m.group('line_number'))
            return sha, author, date, time, line_number
        else:
            return None

    def prepare_pattern(self):
        '''Prepares the regex pattern to parse git blame output.
        '''
        # The SHA output by git-blame may have a leading caret to indicate
        # that it is a "boundary commit".
        p_sha = r'(?P<sha>\^?\w+)'
        p_file = r'((?P<file>[\S ]+)\s+)'
        p_author = r'(?P<author>.+?)'
        p_date = r'(?P<date>\d{4}-\d{2}-\d{2})'
        p_time = r'(?P<time>\d{2}:\d{2}:\d{2})'
        p_timezone = r'(?P<timezone>[\+-]\d+)'
        p_line = r'(?P<line_number>\d+)'
        s = r'\s+'

        self.pattern = re.compile(r'^' + p_sha + s + p_file + r'\(' +
                                  p_author + s + p_date + s + p_time + s +
                                  p_timezone + s + p_line + r'\) ')

    def format_name(self, name):
        '''Formats author names so that widths of phantoms become equal.
        '''
        ellipsis = '...'
        if len(name) > self.NAME_LENGTH:
            return name[:self.NAME_LENGTH] + ellipsis
        else:
            return name + '.' * (self.NAME_LENGTH - len(name)) + ellipsis

    def get_line_point(self, line):
        '''Get the point of specified line in a view.
        '''
        return self.view.line(self.view.text_point(line, 0))

    def on_phantom_close(self, href):
        '''Closes opened phantoms.
        '''
        if href == 'close':
            self.view.run_command('blame_erase_all')

class BlameEraseAllCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        '''Erases the blame results.
        '''
        sublime.status_message("The git blame result is cleared.")
        self.view.erase_phantoms(PHANTOM_KEY_ALL)

class BlameEraseAllListener(sublime_plugin.ViewEventListener):

    @classmethod
    def is_applicable(cls, settings):
        '''Checks if the blame_erase_all command is applicable.
        '''
        return settings.get(SETTING_PHANTOM_ALL_DISPLAYED, False)

    def on_modified_async(self):
        '''Automatically erases the blame results to prevent mismatches.
        '''
        self.view.run_command('blame_erase_all')
        self.view.settings().erase(SETTING_PHANTOM_ALL_DISPLAYED)

class InsertCommitDescriptionCommand(sublime_plugin.TextCommand):

    def run(self, edit, desc, scratch_view_name):
        view = self.view
        view.set_scratch(True)
        view.set_syntax_file('Packages/Diff/Diff.sublime-syntax')
        view.insert(edit, 0, desc)
        view.set_name(scratch_view_name)

def view_is_suitable(view):
    ok = view.file_name() and not view.is_dirty()
    if not ok:
        communicate_error("Please save file changes to disk first.")
    return ok

def communicate_error(e, modal=True):
    user_msg = "Git blame:\n\n{}".format(e)
    if isinstance(e, subprocess.CalledProcessError):
        user_msg += "\n\n{}".format(e.output.decode("utf-8"))

    print()
    if modal:
        sublime.error_message(user_msg)
    else:
        sublime.status_message(user_msg)
        # Unlike with the error dialog, a status message is not automatically
        # persisted in the console too.
        print(user_msg)
<|endoftext|>


<commit_before>from django import forms

from admin_tools.menu.models import Bookmark

class BookmarkForm(forms.ModelForm):
    """
    This form allows the user to edit bookmarks. It doesn't show the user field.
    It expects the user to be passed in from the view.
    """

    def __init__(self, user, *args, **kwargs):
        super(BookmarkForm, self).__init__(*args, **kwargs)
        self.user = user

    def save(self, *args, **kwargs):
        bookmark = super(BookmarkForm, self).save(commit=False, *args, **kwargs)
        bookmark.user = self.user
        bookmark.save()
        return bookmark

    class Meta:
        fields = ('url', 'title')
        model = Bookmark
<commit_msg>Fixed bookmark bugs. The saved url was urlencoded, so we need to decode it before we save it. Added a clean_url method to the BookmarkForm. fixes #25<commit_after>import urllib

from django import forms

from admin_tools.menu.models import Bookmark

class BookmarkForm(forms.ModelForm):
    """
    This form allows the user to edit bookmarks. It doesn't show the user field.
    It expects the user to be passed in from the view.
    """

    def __init__(self, user, *args, **kwargs):
        super(BookmarkForm, self).__init__(*args, **kwargs)
        self.user = user

    def clean_url(self):
        url = self.cleaned_data['url']
        return urllib.unquote(url)

    def save(self, *args, **kwargs):
        bookmark = super(BookmarkForm, self).save(commit=False, *args, **kwargs)
        bookmark.user = self.user
        bookmark.save()
        return bookmark

    class Meta:
        fields = ('url', 'title')
        model = Bookmark
<|endoftext|>