bzr revid: fp@tinyerp.com-20100404185137-ek2knzn1ts1t4mxn
This commit is contained in:
Fabien Pinckaers 2010-04-04 20:51:37 +02:00
commit 9467aca0fa
296 changed files with 15378 additions and 9528 deletions

View File

@ -3,8 +3,7 @@
<data>
<menuitem name="Auction Management" id="auction_menu_root" icon="terp-purchase"/>
<menuitem name="Configuration" parent="auction_menu_root" id="auction_config_menu" sequence="7"/>
<menuitem name="Tools Bar Codes" id="auction_outils_menu" parent="auction_menu_root" sequence="5" />
<menuitem name="Deliveries Management" action="action_auction_taken" id="menu_wizard_emporte" parent="auction_outils_menu"/>
=======================================================
Auction Management/Configuration/artist
=======================================================
@ -204,7 +203,6 @@
<field name="lot_est2"/>
<field name="product_id"/>
<field name="state" readonly="1"/>
</form>
</field>
</record>
@ -279,7 +277,7 @@
<separator string="Description" colspan="4"/>
<field name="obj_desc" colspan="4" nolabel="1" select="2"/>
<field name="state" colspan="1" select="2"/>
<group col="8" colspan="2">
<group col="4" colspan="2">
<button name="button_bought" string="Sold" states="draft" type="object" icon="gtk-jump-to"/>
<button name="button_not_bought" string="Not sold" states="draft" type="object" icon="gtk-undo"/>
<button name="button_taken_away" string="Taken away" states="sold" type="object" icon="gtk-goto-last"/>
@ -332,6 +330,7 @@
<field name="date"/>
</tree>
</field>
</page>
<page string="Bids">
<field name="bid_lines" colspan="4" nolabel="1" editable="top">
@ -341,7 +340,9 @@
<field name="call"/>
</tree>
</field>
</page>
</notebook>
</form>
</field>

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Following wizard is remaining to implement in osv_memory-->
<!--
<!--<delete model="ir.actions.wizard" search="[('wiz_name','like','auction.lots.')]"/>-->
<wizard string="Send to website"
model="auction.lots"
name="auction.lots.send.aie"
@ -20,6 +20,45 @@
multi="1"
id="wizard_map_user"/>
<wizard string="Change Auction Date"
model="auction.lots"
name="auction.lots.auction_move"
multi="1"
id="wizard_change_auction"/>
<!--wizard string="Pay objects of the seller"
model="auction.lots"
name="auction.pay.sel"
id="wizard_lots_pay"/-->
<wizard string="Pay objects of the buyer"
model="auction.lots"
name="auction.pay.buy"
id="wizard_pay"/>
<wizard
string="Gestion emporte"
model="auction.dates"
name="auction.taken"
multi="1"
id="wizard_emporte"/>
<menuitem name="Tools Bar Codes"
id="auction_outils_menu"
parent="auction_menu_root" sequence="5"
/>
<menuitem name="Deliveries Management"
action="wizard_emporte"
type="wizard"
id="menu_wizard_emporte"
parent="auction_outils_menu"
/>
<!--wizard string="Cancel payment"
model="auction.lots"
name="auction.lots.cancel"
multi="1"
id="wizard_cancel_pay"/-->
<wizard string="Numerotation (per lot)"
model="auction.lots"
@ -33,13 +72,56 @@
multi="1"
id="wizard_numerotate_automatic"/>
<wizard string="SMS Send"
model="auction.lots"
name="auction.lots.sms_send"
multi="1"
id="wizard_sms"/>
<!--wizard string="Invoice"
model="auction.lots"
name="auction.lots.invoice"
multi="1"
id="wizard_invoicing"/-->
<wizard string="Invoice Seller objects"
model="auction.lots"
name="auction.lots.make_invoice"
multi="1"
id="wizard_invoice"/>
<wizard string="Invoice Buyer objects"
model="auction.lots"
name="auction.lots.make_invoice_buyer"
id="wizard_invoice_buyer1"/>
<wizard string="Unmark as taken away"
model="auction.lots"
name="auction.lots.enable"
multi="1"
id="auction_wizard_enable_taken"/>
<wizard string="Mark as taken away"
model="auction.lots"
name="auction.lots.able"
multi="1"
id="auction_wizard_able_taken"/>
<wizard
id="auction_catalog_flagy"
string="Auction Catalog Flagey"
model="auction.dates"
name="auction.catalog.flagey"
keyword="client_print_multi"/>
-->
<wizard string="Mark as paid for seller"
model="auction.lots"
name="auction.payer.sel"
multi="1"
id="auction_wizard_payer_sel"/>
</data>
</openerp>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_able" model="ir.ui.view">
<field name="name">auction.lots.able.form</field>
<field name="model">auction.lots.able</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Confirm">
<group colspan="4" >
<separator string="Confirmation set taken away" colspan="4"/>
<newline/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-goto-last" string="Able Taken away" name="confirm_able" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Mark as taken away"
res_model="auction.lots.able"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_able"/>
</data>
</openerp>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_auction_move" model="ir.ui.view">
<field name="name">auction.lots.auction.move.form</field>
<field name="model">auction.lots.auction.move</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Change Auction Date">
<group colspan="4" >
<label string="Warning, this will erase the object adjudication price and its buyer !" colspan="2"/>
<newline/>
<field name="auction_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-redo" string="Move to Auction date" name="auction_move_set" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Change Auction Date"
res_model="auction.lots.auction.move"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_auction_move"/>
</data>
</openerp>

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_enable" model="ir.ui.view">
<field name="name">auction.lots.enable.form</field>
<field name="model">auction.lots.enable</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Confirm">
<group colspan="4" >
<separator string="Confirmation enable taken away" colspan="4"/>
<newline/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-goto-last" string="Enable Taken away" name="confirm_enable" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Unmark as taken away"
res_model="auction.lots.enable"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_enable"/>
</data>
</openerp>

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_make_invoice_buyer" model="ir.ui.view">
<field name="name">auction.lots.make.invoice.buyer.form</field>
<field name="model">auction.lots.make.invoice.buyer</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Paid">
<group colspan="4" >
<field name="amount"/>
<field name="objects"/>
<field name="number"/>
<label string="(Keep empty for automatic number)" colspan="2"/>
<field name="buyer_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-ok" string="Create invoices" name="makeInvoices" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Invoice Buyer objects"
res_model="auction.lots.make.invoice.buyer"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_make_invoice_buyer"/>
</data>
</openerp>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_make_invoice" model="ir.ui.view">
<field name="name">auction.lots.make.invoice.form</field>
<field name="model">auction.lots.make.invoice</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Paid">
<group colspan="4" >
<field name="amount"/>
<field name="objects"/>
<field name="number" colspan="3"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-ok" string="Create invoices" name="makeInvoices" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Invoice Seller objects"
res_model="auction.lots.make.invoice"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_make_invoice"/>
</data>
</openerp>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_lots_sms_send" model="ir.ui.view">
<field name="name">auction.lots.sms.send.form</field>
<field name="model">auction.lots.sms.send</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="SMS - Gateway: clickatell','Bulk SMS send">
<group colspan="4" >
<separator string="SMS - Gateway: clickatell','Bulk SMS send" colspan="4"/>
<field name="app_id"/>
<newline/>
<field name="user"/>
<field name="password"/>
<newline/>
<field name="text" colspan="3"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-go-back" string="Send SMS" name="sms_send" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="SMS Send"
res_model="auction.lots.sms.send"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_lots_sms_send"/>
</data>
</openerp>

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_pay_buy" model="ir.ui.view">
<field name="name">auction.pay.buy.form</field>
<field name="model">auction.pay.buy</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Pay objects">
<group colspan="4" >
<field name="amount"/>
<field name="statement_id1" domain="[('state','=','draft')]"/>
<field name="amount2"/>
<field name="statement_id2" domain="[('state','=','draft')]"/>
<field name="amount3"/>
<field name="statement_id3" domain="[('state','=','draft')]"/>
<newline/>
<field name="buyer_id"/>
<field name="total"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-yes" string="Pay" name="pay_and_reconcile" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Pay objects of the buyer"
res_model="auction.pay.buy"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_pay_buy"/>
</data>
</openerp>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_pay_sel" model="ir.ui.view">
<field name="name">auction.pay.sel.form</field>
<field name="model">auction.pay.sel</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Pay invoice">
<group colspan="4" >
<field name="amount"/>
<field name="dest_account_id"/>
<field name="journal_id"/>
<field name="period_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-yes" string="Pay" name="pay_and_reconcile" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_auction_pay_sel" model="ir.actions.act_window">
<field name="name">Pay objects of the seller</field>
<field name="res_model">auction.pay.sel</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_auction_pay_sel"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_payer" model="ir.ui.view">
<field name="name">auction.payer.form</field>
<field name="model">auction.payer</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Check payment for buyer">
<separator string="" colspan="4" />
<group colspan="4" col="2">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon = "gtk-yes" string="Pay" name="payer" type="object"/>
</group>
</form>
</field>
</record>
<record id="view_auction_payer_sel" model="ir.ui.view">
<field name="name">auction.payer.sel.form</field>
<field name="model">auction.payer.sel</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Check payment for seller">
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-yes" string="Pay" name="payer_sel" type="object"/>
</group>
</form>
</field>
</record>
<act_window name="Mark as paid for seller"
res_model="auction.payer.sel"
src_model="auction.lots"
view_mode="form"
target="new"
key2="client_action_multi"
id="action_auction_payer_sel"/>
</data>
</openerp>

View File

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_auction_taken" model="ir.ui.view">
<field name="name">auction.taken.form</field>
<field name="model">auction.taken</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Mark Lots">
<group colspan="4" >
<label string="Select lots which are Sold" colspan="4"/>
<field name="lot_ids" nolabel="1" colspan="4" domain="[('state','=','sold')]"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-ok" string="OK" name="process" type="object" />
</group>
</form>
</field>
</record>
<record id="action_auction_taken" model="ir.actions.act_window">
<field name="name">Gestion emporte</field>
<field name="res_model">auction.taken</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_auction_taken"/>
<field name="target">new</field>
</record>
<!-- <menuitem name="Tools Bar Codes"
id="auction_outils_menu"
parent="auction_menu_root" sequence="5"
/>
<menuitem name="Deliveries Management"
action="action_auction_taken"
id="menu_wizard_emporte"
parent="auction_outils_menu"
/> -->
</data>
</openerp>

View File

@ -1,8 +1,8 @@
# -*- encoding: utf-8 -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# 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
@ -21,39 +21,40 @@
import wizard
import netsvc
import ir
import pooler
confirm_order_form = """<?xml version="1.0"?>
take_form = """<?xml version="1.0"?>
<form title="Confirm">
<separator string="Orders Confirmation" colspan="4"/>
<field name="confirm_cashbox"/>
<separator string="Confirmation set taken away" colspan="4"/>
<newline/>
</form>
"""
confirm_order_fields = {
'confirm_cashbox': {'string':'Name of box', 'type':'many2one', 'required':True, 'relation':'lunch.cashbox' },
take_fields = {
# 'confirm_en': {'string':'Catalog Number', 'type':'integer'},
}
def _confirm(self,cr,uid,data,context):
pool= pooler.get_pool(cr.dbname)
order_ref = pool.get('lunch.order')
order_ref.confirm(cr,uid,data['ids'],data['form']['confirm_cashbox'],context)
def _confirm_able(self,cr,uid,data,context={}):
res={}
pool = pooler.get_pool(cr.dbname)
pool.get('auction.lots').write(cr,uid,data['ids'],{'ach_emp':True})
return {}
class order_confirm(wizard.interface):
class able_take_away(wizard.interface):
states = {
'init': {
'action':[],
'result':{'type' : 'form', 'arch' : confirm_order_form, 'fields' : confirm_order_fields, 'state' : [('end', 'Cancel'),('go', 'Confirm Order') ]},
'init' : {
'actions' : [],
'result' : {
'type' : 'form',
'arch' : take_form,
'fields' : take_fields,
'state' : [('end', 'Cancel'),('go', 'Able Taken away')]}
},
'go' : {
'actions' : [_confirm],
'go' : {
'actions' : [_confirm_able],
'result' : {'type' : 'state', 'state' : 'end'}
},
}
order_confirm('lunch.order.confirm')
}
able_take_away('auction.lots.able')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -20,44 +20,42 @@
##############################################################################
import wizard
import ir
from mx.DateTime import now
import pooler
import netsvc
import pooler
#field name="confirm_en"/>
vote_form = """<?xml version="1.0" ?>
<form string="Create Tasks">
<field name="vote"/>
</form>"""
take_form = """<?xml version="1.0"?>
<form title="Confirm">
<separator string="Confirmation enable taken away" colspan="4"/>
<newline/>
</form>
"""
vote_fields = {
'vote': {'string': 'Post Vote', 'type': 'selection',
'selection': [('-1','Not Voted'),('0','Very Bad'),('25', 'Bad'),('50','Normal'),('75','Good'),('100','Very Good') ]},
take_fields = {
'confirm_en': {'string':'Catalog Number', 'type':'integer'},
}
def _confirm_enable(self,cr,uid,data,context={}):
pool = pooler.get_pool(cr.dbname)
pool.get('auction.lots').write(cr,uid,data['ids'],{'ach_emp':False})
return {}
class idea_vote(wizard.interface):
def _do_vote(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
vote_obj = pool.get('idea.vote')
score=str(data['form']['vote'])
dic={'idea_id' : data['id'], 'user_id' : uid, 'score' : score }
vote=vote_obj.create(cr,uid,dic)
return True
class enable_take_away(wizard.interface):
states = {
'init':{
'actions': [],
'result': {'type':'form', 'arch':vote_form, 'fields':vote_fields, 'state':[('end', 'Cancel'), ('post', 'Post Vote')] },
'init' : {
'actions' : [],
'result' : {
'type' : 'form',
'arch' : take_form,
'fields' : take_fields,
'state' : [ ('end', 'Cancel'),('go', 'Enable Taken away')]}
},
'post':{
'actions': [],
'result': {'type':'action', 'action': _do_vote, 'state':'end'},
'go' : {
'actions' : [_confirm_enable],
'result' : {'type' : 'state', 'state' : 'end'}
},
}
idea_vote('idea.post.vote')
}
enable_take_away('auction.lots.enable')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,79 @@
# -*- 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 wizard
import netsvc
import netsvc
import osv
import time
import pooler
pay_form = '''<?xml version="1.0"?>
<form string="Check payment for buyer">
</form>'''
pay_fields = {
}
pay_form1 = '''<?xml version="1.0"?>
<form string="Check payment for seller">
</form>'''
pay_fields1 = {
}
def _payer(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
pool.get('auction.lots').write(cr,uid,data['ids'],{'is_ok':True, 'state':'paid'})
return {}
def _payer_sel(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
pool.get('auction.lots').write(cr,uid,data['ids'],{'paid_vnd':True})
return {}
class wiz_auc_pay(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':pay_form, 'fields': pay_fields, 'state':[('end','Cancel'),('pay','Pay')]}
},
'pay': {
'actions': [_payer],
'result': {'type': 'state', 'state':'end'}
}}
wiz_auc_pay('auction.payer')
class wiz_auc_pay_sel(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':pay_form1, 'fields': pay_fields1, 'state':[('end','Cancel'),('pay2','Pay')]}
},
'pay2': {
'actions': [_payer_sel],
'result': {'type': 'state', 'state':'end'}
}}
wiz_auc_pay_sel('auction.payer.sel')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -37,6 +37,7 @@
'depends': ['base'],
'init_xml': [],
'update_xml': [
'wizard/audittrail_view_log_view.xml',
'audittrail_view.xml',
'security/ir.model.access.csv',
'security/audittrail_security.xml'

View File

@ -19,35 +19,39 @@
#
##############################################################################
from osv import fields, osv
from osv.osv import osv_pool
from tools.translate import _
import ir
import netsvc
import pooler
import string
import time, copy
import time
class audittrail_rule(osv.osv):
"""
For Auddittrail Rule
"""
_name = 'audittrail.rule'
_description = "Audittrail Rule"
_columns = {
"name": fields.char("Rule Name", size=32, required=True),
"object_id": fields.many2one('ir.model', 'Object', required=True),
"user_id": fields.many2many('res.users', 'audittail_rules_users', 'user_id', 'rule_id', 'Users'),
"log_read": fields.boolean("Log reads"),
"log_write": fields.boolean("Log writes"),
"log_unlink": fields.boolean("Log deletes"),
"log_create": fields.boolean("Log creates"),
"state": fields.selection((("draft", "Draft"), ("subscribed", "Subscribed")), "State", required=True),
"action_id":fields.many2one('ir.actions.act_window', "Action ID"),
"name": fields.char("Rule Name", size=32, required=True),
"object_id": fields.many2one('ir.model', 'Object', required=True),
"user_id": fields.many2many('res.users', 'audittail_rules_users',
'user_id', 'rule_id', 'Users'),
"log_read": fields.boolean("Log reads"),
"log_write": fields.boolean("Log writes"),
"log_unlink": fields.boolean("Log deletes"),
"log_create": fields.boolean("Log creates"),
"state": fields.selection((("draft", "Draft"),
("subscribed", "Subscribed")),
"State", required=True),
"action_id": fields.many2one('ir.actions.act_window', "Action ID"),
}
_defaults = {
'state': lambda *a: 'draft',
'log_create': lambda *a: 1,
'log_unlink': lambda *a: 1,
'log_write': lambda *a: 1,
'state': lambda *a: 'draft',
'log_create': lambda *a: 1,
'log_unlink': lambda *a: 1,
'log_write': lambda *a: 1,
}
_sql_constraints = [
@ -56,59 +60,86 @@ class audittrail_rule(osv.osv):
__functions = {}
def subscribe(self, cr, uid, ids, *args):
"""
Subscribe Rule for auditing changes on object and apply shortcut for logs on that object.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Auddittrail Rules IDs.
@return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
obj_model = self.pool.get('ir.model.data')
#start Loop
for thisrule in self.browse(cr, uid, ids):
obj = self.pool.get(thisrule.object_id.model)
if not obj:
raise osv.except_osv(
_('WARNING:audittrail is not part of the pool'),
_('WARNING: audittrail is not part of the pool'),
_('Change audittrail depends -- Setting rule as DRAFT'))
self.write(cr, uid, [thisrule.id], {"state": "draft"})
val={
"name":'View Log',
"res_model":'audittrail.log',
"src_model":thisrule.object_id.model,
"domain":"[('object_id','=',"+str(thisrule.object_id.id)+"),('res_id', '=', active_id)]"
val = {
"name": 'View Log',
"res_model": 'audittrail.log',
"src_model": thisrule.object_id.model,
"domain": "[('object_id','=', " + str(thisrule.object_id.id) + "), ('res_id', '=', active_id)]"
}
id=self.pool.get('ir.actions.act_window').create(cr, uid, val)
self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id":id})
id = obj_action.create(cr, uid, val)
self.write(cr, uid, [thisrule.id], {"state": "subscribed", "action_id": id})
keyword = 'client_action_relate'
value = 'ir.actions.act_window,'+str(id)
res=self.pool.get('ir.model.data').ir_set(cr, uid, 'action', keyword, 'View_log_'+thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False)
value = 'ir.actions.act_window,' + str(id)
res = obj_model.ir_set(cr, uid, 'action', keyword, 'View_log_' + thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False)
#End Loop
return True
def unsubscribe(self, cr, uid, ids, *args):
"""
Unsubscribe Auditing Rule on object
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Auddittrail Rules IDs.
@return: True
"""
obj_action = self.pool.get('ir.actions.act_window')
val_obj = self.pool.get('ir.values')
#start Loop
for thisrule in self.browse(cr, uid, ids):
if thisrule.id in self.__functions :
if thisrule.id in self.__functions:
for function in self.__functions[thisrule.id]:
setattr(function[0], function[1], function[2])
w_id=self.pool.get('ir.actions.act_window').search(cr, uid, [('name', '=', 'View Log'), ('res_model', '=', 'audittrail.log'), ('src_model', '=', thisrule.object_id.model)])
self.pool.get('ir.actions.act_window').unlink(cr, uid, w_id)
val_obj=self.pool.get('ir.values')
value="ir.actions.act_window"+','+str(w_id[0])
val_id=val_obj.search(cr, uid, [('model', '=', thisrule.object_id.model), ('value', '=', value)])
w_id = obj_action.search(cr, uid, [('name', '=', 'View Log'), ('res_model', '=', 'audittrail.log'), ('src_model', '=', thisrule.object_id.model)])
obj_action.unlink(cr, uid, w_id)
value = "ir.actions.act_window" + ',' + str(w_id[0])
val_id = val_obj.search(cr, uid, [('model', '=', thisrule.object_id.model), ('value', '=', value)])
if val_id:
res = ir.ir_del(cr, uid, val_id[0])
self.write(cr, uid, [thisrule.id], {"state": "draft"})
#End Loop
return True
audittrail_rule()
class audittrail_log(osv.osv):
"""
For Audittrail Log
"""
_name = 'audittrail.log'
_description = "Audittrail Log"
_columns = {
"name": fields.char("Name", size=32),
"object_id": fields.many2one('ir.model', 'Object'),
"user_id": fields.many2one('res.users', 'User'),
"method": fields.selection((('read', 'Read'), ('write', 'Write'), ('unlink', 'Delete'), ('create', 'Create')), "Method"),
"timestamp": fields.datetime("Date"),
"res_id":fields.integer('Resource Id'),
"line_ids":fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
"name": fields.char("Name", size=32),
"object_id": fields.many2one('ir.model', 'Object'),
"user_id": fields.many2one('res.users', 'User'),
"method": fields.selection((('read', 'Read'),
('write', 'Write'),
('unlink', 'Delete'),
('create', 'Create')), "Method"),
"timestamp": fields.datetime("Date"),
"res_id": fields.integer('Resource Id'),
"line_ids": fields.one2many('audittrail.log.line', 'log_id', 'Log lines'),
}
_defaults = {
"timestamp": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S")
}
@ -116,235 +147,293 @@ class audittrail_log(osv.osv):
audittrail_log()
class audittrail_log_line(osv.osv):
_name='audittrail.log.line'
_columns={
'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
'log_id':fields.many2one('audittrail.log', 'Log'),
'log':fields.integer("Log ID"),
'old_value':fields.text("Old Value"),
'new_value':fields.text("New Value"),
'old_value_text':fields.text('Old value Text'),
'new_value_text':fields.text('New value Text'),
'field_description':fields.char('Field Description' , size=64),
}
"""
Audittrail Log Line.
"""
_name = 'audittrail.log.line'
_description = "Log Line"
_columns = {
'field_id': fields.many2one('ir.model.fields', 'Fields', required=True),
'log_id': fields.many2one('audittrail.log', 'Log'),
'log': fields.integer("Log ID"),
'old_value': fields.text("Old Value"),
'new_value': fields.text("New Value"),
'old_value_text': fields.text('Old value Text'),
'new_value_text': fields.text('New value Text'),
'field_description': fields.char('Field Description', size=64),
}
audittrail_log_line()
class audittrail_objects_proxy(osv_pool):
def get_value_text(self, cr, uid, field_name, values, object, context={}):
pool = pooler.get_pool(cr.dbname)
obj=pool.get(object.model)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object.model)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', field_name), ('model_id', '=', object.id)])
if f_id:
field=pool.get('ir.model.fields').read(cr, uid, f_id)[0]
model=field['relation']
""" Uses Object proxy for auditing changes on object of subscribed Rules"""
def get_value_text(self, cr, uid, field_name, values, object, context=None):
"""
Gets textual values for the fields
e.g.: For field of type many2one it gives its name value instead of id
if field['ttype']=='many2one':
if values:
if type(values)==tuple:
values=values[0]
val=pool.get(model).read(cr, uid, [values], [pool.get(model)._rec_name])
if len(val):
return val[0][pool.get(model)._rec_name]
elif field['ttype'] == 'many2many':
value=[]
if values:
for id in values:
val=pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if len(val):
value.append(val[0][pool.get(model)._rec_name])
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param field_name: List of fields for text values
@param values: Values for field to be converted into textual values
@return: values: List of textual values for given fields
"""
if not context:
context = {}
pool = pooler.get_pool(cr.dbname)
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', field_name), ('model_id', '=', object.id)])
if f_id:
field = pool.get('ir.model.fields').read(cr, uid, f_id)[0]
model = field['relation']
if field['ttype'] == 'many2one':
if values:
if type(values) == tuple:
values = values[0]
val = pool.get(model).read(cr, uid, [values], [pool.get(model)._rec_name])
if val:
return val[0][pool.get(model)._rec_name]
elif field['ttype'] == 'many2many':
value = []
if values:
for id in values:
val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if val:
value.append(val[0][pool.get(model)._rec_name])
return value
elif field['ttype'] == 'one2many':
if values:
value = []
for id in values:
val = pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if val:
value.append(val[0][pool.get(model)._rec_name])
return value
elif field['ttype'] == 'one2many':
if values:
value=[]
for id in values:
val=pool.get(model).read(cr, uid, [id], [pool.get(model)._rec_name])
if len(val):
value.append(val[0][pool.get(model)._rec_name])
return value
return values
def create_log_line(self, cr, uid, id, object, lines=[]):
"""
Creates lines for changed fields with its old and new values
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param lines: List of values for line is to be created
"""
pool = pooler.get_pool(cr.dbname)
obj=pool.get(object.model)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object.model)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
obj = pool.get(object.model)
#start Loop
for line in lines:
if obj._inherits:
inherits_ids= pool.get('ir.model').search(cr, uid, [('model', '=', obj._inherits.keys()[0])])
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (object.id, inherits_ids[0]))])
inherits_ids = pool.get('ir.model').search(cr, uid, [('model', '=', obj._inherits.keys()[0])])
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (object.id, inherits_ids[0]))])
else:
f_id= pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', '=', object.id)])
if len(f_id):
fields=pool.get('ir.model.fields').read(cr, uid, f_id)
old_value='old_value' in line and line['old_value'] or ''
new_value='new_value' in line and line['new_value'] or ''
old_value_text='old_value_text' in line and line['old_value_text'] or ''
new_value_text='new_value_text' in line and line['new_value_text'] or ''
f_id = pool.get('ir.model.fields').search(cr, uid, [('name', '=', line['name']), ('model_id', '=', object.id)])
if f_id:
fields = pool.get('ir.model.fields').read(cr, uid, f_id)
old_value = 'old_value' in line and line['old_value'] or ''
new_value = 'new_value' in line and line['new_value'] or ''
old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
if old_value_text == new_value_text:
continue
if fields[0]['ttype']== 'many2one':
if type(old_value)==tuple:
old_value=old_value[0]
if type(new_value)==tuple:
new_value=new_value[0]
log_line_id = pool.get('audittrail.log.line').create(cr, uid, {"log_id": id, "field_id": f_id[0] , "old_value":old_value , "new_value":new_value, "old_value_text":old_value_text , "new_value_text":new_value_text, "field_description":fields[0]['field_description']})
if fields[0]['ttype'] == 'many2one':
if type(old_value) == tuple:
old_value = old_value[0]
if type(new_value) == tuple:
new_value = new_value[0]
vals = {
"log_id": id,
"field_id": f_id[0],
"old_value": old_value,
"new_value": new_value,
"old_value_text": old_value_text,
"new_value_text": new_value_text,
"field_description": fields[0]['field_description']
}
line_id = pool.get('audittrail.log.line').create(cr, uid, vals)
cr.commit()
#End Loop
return True
def log_fct(self, db, uid, object, method, fct_src, *args):
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
obj=pool.get(object)
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object)])
model_object=pool.get('ir.model').browse(cr, uid, obj_ids)[0]
if method in ('create'):
res_id = fct_src(db, uid, object, method, *args)
"""
Logging function: This function is performs logging oprations according to method
@param db: the current database
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param method: method to log: create, read, write, unlink
@param fct_src: execute method of Object proxy
@return: Returns result as per method of Object proxy
"""
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
model_object = pool.get('ir.model').browse(cr, uid, obj_ids)[0]
if method in ('create'):
res_id = fct_src(db, uid, object, method, *args)
cr.commit()
new_value = pool.get(model_object.model).read(cr, uid, [res_id], args[0].keys())[0]
if 'id' in new_value:
del new_value['id']
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
vals = {
"method": method,
"object_id": model_object.id,
"user_id": uid, "res_id": res_id,
"name": resource_name
}
id = pool.get('audittrail.log').create(cr, uid, vals)
lines = []
for field in new_value:
if new_value[field]:
line = {
'name': field,
'new_value': new_value[field],
'new_value_text': self.get_value_text(cr, uid, field, new_value[field], model_object)
}
lines.append(line)
self.create_log_line(cr, uid, id, model_object, lines)
cr.commit()
cr.close()
return res_id
if method in ('write'):
res_ids = args[0]
for res_id in res_ids:
old_values = pool.get(model_object.model).read(cr, uid, res_id, args[1].keys())
old_values_text = {}
for field in args[1].keys():
old_values_text[field] = self.get_value_text(cr, uid, field, old_values[field], model_object)
res = fct_src(db, uid, object, method, *args)
cr.commit()
new_value=pool.get(model_object.model).read(cr, uid, [res_id], args[0].keys())[0]
if 'id' in new_value:
del new_value['id']
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in new_value:
if new_value[field]:
line={
'name':field,
'new_value':new_value[field],
'new_value_text': self.get_value_text(cr, uid, field, new_value[field], model_object)
}
lines.append(line)
self.create_log_line(cr, uid, id, model_object, lines)
cr.commit()
cr.close()
return res_id
if method in ('write'):
res_ids=args[0]
for res_id in res_ids:
old_values=pool.get(model_object.model).read(cr, uid, res_id, args[1].keys())
old_values_text={}
for field in args[1].keys():
old_values_text[field] = self.get_value_text(cr, uid, field, old_values[field], model_object)
res =fct_src(db, uid, object, method, *args)
cr.commit()
if res:
new_values=pool.get(model_object.model).read(cr, uid, res_ids, args[1].keys())[0]
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in args[1].keys():
if args[1].keys():
line={
'name':field,
'new_value':field in new_values and new_values[field] or '',
'old_value':field in old_values and old_values[field] or '',
'new_value_text': self.get_value_text(cr, uid, field, new_values[field], model_object),
'old_value_text':old_values_text[field]
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('read'):
res_ids=args[0]
old_values={}
res =fct_src(db, uid, object, method, *args)
if type(res)==list:
for v in res:
old_values[v['id']]=v
else:
old_values[res['id']]=res
for res_id in old_values:
if not len(logged_uids) or uid in logged_uids:
if res:
new_values = pool.get(model_object.model).read(cr, uid, res_ids, args[1].keys())[0]
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in old_values[res_id]:
if old_values[res_id][field]:
line={
'name':field,
'old_value':old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('unlink'):
res_ids=args[0]
old_values={}
for res_id in res_ids:
old_values[res_id]=pool.get(model_object.model).read(cr, uid, res_id, [])
for res_id in res_ids:
if not len(logged_uids) or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id=pool.get('audittrail.log').create(cr, uid, {"method": method , "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines=[]
for field in old_values[res_id]:
if old_values[res_id][field]:
line={
'name':field,
'old_value':old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in args[1].keys():
if args[1].keys():
line = {
'name': field,
'new_value': field in new_values and new_values[field] or '',
'old_value': field in old_values and old_values[field] or '',
'new_value_text': self.get_value_text(cr, uid, field, new_values[field], model_object),
'old_value_text': old_values_text[field]
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
res =fct_src(db, uid, object, method, *args)
cr.close()
return res
if method in ('read'):
res_ids = args[0]
old_values = {}
res = fct_src(db, uid, object, method, *args)
if type(res) == list:
for v in res:
old_values[v['id']] = v
else:
old_values[res['id']] = res
for res_id in old_values:
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in old_values[res_id]:
if old_values[res_id][field]:
line = {
'name': field,
'old_value': old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
cr.close()
return res
if method in ('unlink'):
res_ids = args[0]
old_values = {}
for res_id in res_ids:
old_values[res_id] = pool.get(model_object.model).read(cr, uid, res_id, [])
for res_id in res_ids:
if not logged_uids or uid in logged_uids:
resource_name = pool.get(model_object.model).name_get(cr, uid, [res_id])
resource_name = resource_name and resource_name[0][1] or ''
id = pool.get('audittrail.log').create(cr, uid, {"method": method, "object_id": model_object.id, "user_id": uid, "res_id": res_id, "name": resource_name})
lines = []
for field in old_values[res_id]:
if old_values[res_id][field]:
line = {
'name': field,
'old_value': old_values[res_id][field],
'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model_object)
}
lines.append(line)
cr.commit()
self.create_log_line(cr, uid, id, model_object, lines)
res = fct_src(db, uid, object, method, *args)
cr.close()
return res
cr.close()
def execute(self, db, uid, object, method, *args, **kw):
"""
Overrides Object Proxy execute method
@param db: the current database
@param uid: the current users ID for security checks,
@param object: Object who's values are being changed
@param method: method to log: create, read, write, unlink
@return: Returns result as per method of Object proxy
"""
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor()
cr.autocommit(True)
obj=pool.get(object)
logged_uids = []
fct_src = super(audittrail_objects_proxy, self).execute
def my_fct(db, uid, object, method, *args):
field = method
rule = False
obj_ids= pool.get('ir.model').search(cr, uid, [('model', '=', object)])
obj_ids = pool.get('ir.model').search(cr, uid, [('model', '=', object)])
for obj_name in pool.obj_list():
if obj_name == 'audittrail.rule':
rule = True
if not rule:
return fct_src(db, uid, object, method, *args)
if not len(obj_ids):
if not obj_ids:
return fct_src(db, uid, object, method, *args)
rule_ids=pool.get('audittrail.rule').search(cr, uid, [('object_id', '=', obj_ids[0]), ('state', '=', 'subscribed')])
if not len(rule_ids):
rule_ids = pool.get('audittrail.rule').search(cr, uid, [('object_id', '=', obj_ids[0]), ('state', '=', 'subscribed')])
if not rule_ids:
return fct_src(db, uid, object, method, *args)
for thisrule in pool.get('audittrail.rule').browse(cr, uid, rule_ids):
for user in thisrule.user_id:
logged_uids.append(user.id)
if not len(logged_uids) or uid in logged_uids:
if not logged_uids or uid in logged_uids:
if field in ('read', 'write', 'create', 'unlink'):
if getattr(thisrule, 'log_'+field):
if getattr(thisrule, 'log_' + field):
return self.log_fct(db, uid, object, method, fct_src, *args)
return fct_src(db, uid, object, method, *args)
res = my_fct(db, uid, object, method, *args)

View File

@ -1,6 +1,9 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- Audittrail Rule form -->
<record model="ir.ui.view" id="view_audittrail_rule_form">
<field name="name">audittrail.rule.form</field>
<field name="model">audittrail.rule</field>
@ -17,12 +20,15 @@
<field name="user_id" select="1" colspan="4"/>
<field name="state" select="1" readonly="1" />
<group colspan="2" col="2">
<button string="Subscribe" name="subscribe" type="object" states="draft"/>
<button string="UnSubscribe" name="unsubscribe" type="object" states="subscribed"/>
<button string="Subscribe" name="subscribe"
type="object" states="draft" />
<button string="UnSubscribe" name="unsubscribe"
type="object" states="subscribed" />
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_audittrail_rule_tree">
<field name="name">audittrail.rule.tree</field>
<field name="model">audittrail.rule</field>
@ -40,6 +46,9 @@
</tree>
</field>
</record>
<!-- Action for audittrail rule -->
<record model="ir.actions.act_window" id="action_audittrail_rule_tree">
<field name="name">Rules</field>
<field name="res_model">audittrail.rule</field>
@ -48,8 +57,11 @@
<field name="view_mode">tree,form</field>
<!--<field name="view_id" ref="view_audittrail_rule_form" />-->
</record>
<menuitem name="Audittrails" id="menu_action_audittrail" parent="base.menu_administration"/>
<menuitem name="Rules" id="menu_action_audittrail_rule_tree" action="action_audittrail_rule_tree" parent="menu_action_audittrail"/>
<menuitem name="Audittrails" id="menu_action_audittrail"
parent="base.menu_administration" />
<menuitem name="Rules" id="menu_action_audittrail_rule_tree"
action="action_audittrail_rule_tree" parent="menu_action_audittrail" />
<record model="ir.actions.act_window" id="action_audittrail_rule_tree_sub">
@ -60,6 +72,8 @@
<field name="domain">[('state','=','subscribed')]</field>
<field name="filter" eval="True"/>
</record>
<!-- AuditTrail Log form -->
<record model="ir.ui.view" id="view_audittrail_log_form">
<field name="name">audittrail.log.form</field>
@ -73,23 +87,34 @@
<field name="user_id" select="1" readonly="1"/>
<field name="res_id" readonly="1"/>
<field name="name" readonly="1" select="1"/>
<field name="line_ids" colspan="4" mode="tree,form" widget="one2many_list" readonly="1">
<field name="line_ids" colspan="4" mode="tree,form"
widget="one2many_list" readonly="1">
<form string="Log Lines">
<field name="field_id" colspan="4" readonly="1"/>
<newline/>
<field name="field_description" colspan="4" readonly="1"/>
<newline/>
<separator string="Old Value : " colspan="2"/>
<separator string="New Value : " colspan="2"/>
<newline/>
<field name="old_value" nolabel="1" colspan="2" readonly="1"/>
<field name="new_value" nolabel="1" colspan="2" readonly="1"/>
<newline/>
<separator string="Old Value Text : " colspan="2"/>
<separator string="New Value Text: " colspan="2"/>
<newline/>
<field name="old_value_text" nolabel="1" colspan="2" readonly="1"/>
<field name="new_value_text" nolabel="1" colspan="2" readonly="1"/>
<field name="field_id" colspan="4"
readonly="1" />
<newline />
<field name="field_description" colspan="4"
readonly="1" />
<newline />
<separator string="Old Value : "
colspan="2" />
<separator string="New Value : "
colspan="2" />
<newline />
<field name="old_value" nolabel="1"
colspan="2" readonly="1" />
<field name="new_value" nolabel="1"
colspan="2" readonly="1" />
<newline />
<separator string="Old Value Text : "
colspan="2" />
<separator string="New Value Text: "
colspan="2" />
<newline />
<field name="old_value_text" nolabel="1"
colspan="2" readonly="1" />
<field name="new_value_text" nolabel="1"
colspan="2" readonly="1" />
</form>
<tree string="Log Lines">
@ -117,15 +142,19 @@
</field>
</record>
<!-- Action for Audittrail Log -->
<record model="ir.actions.act_window" id="action_audittrail_log_tree">
<field name="name">Logs</field>
<field name="res_model">audittrail.log</field>
<field name="view_type">form</field>
</record>
<menuitem name="Logs" id="menu_action_audittrail_log_tree" action="action_audittrail_log_tree" parent="menu_action_audittrail"/>
<menuitem name="Logs" id="menu_action_audittrail_log_tree"
action="action_audittrail_log_tree" parent="menu_action_audittrail" />
<wizard string="View log" menu="False" model="audittrail.log" name="audittrail.view.log" id="wizard_audittrail_log"/>
<menuitem name="View Logs" id="menu_action_log_tree2" action="wizard_audittrail_log" type="wizard" parent="menu_action_audittrail"/>
<!-- <wizard string="View log" menu="False" model="audittrail.log" name="audittrail.view.log" id="wizard_audittrail_log"/>-->
<menuitem name="View Logs" id="menu_action_log_tree2"
action="action_audittrail_view_log" parent="menu_action_audittrail" />
</data>
</data>
</openerp>

View File

@ -5,3 +5,4 @@
"access_audittrail_rule_all_users","audittrail rule all","model_audittrail_rule","base.group_user",1,0,0,0
"access_audittrail_log_all_users","audittrail log all","model_audittrail_log","base.group_user",1,0,1,0
"access_audittrail_log_line_all_users","audittrail log line all","model_audittrail_log_line","base.group_user",1,0,1,0
"access_audittrail_view_log","audittrail.view.log","model_audittrail_view_log","base.group_user",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_audittrail_rule_all_users audittrail rule all model_audittrail_rule base.group_user 1 0 0 0
6 access_audittrail_log_all_users audittrail log all model_audittrail_log base.group_user 1 0 1 0
7 access_audittrail_log_line_all_users audittrail log line all model_audittrail_log_line base.group_user 1 0 1 0
8 access_audittrail_view_log audittrail.view.log model_audittrail_view_log base.group_user 1 1 1 1

View File

@ -19,6 +19,6 @@
#
##############################################################################
import wizard_view_log
import audittrail_view_log
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,69 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
import time
class audittrail_view_log(osv.osv_memory):
_name = "audittrail.view.log"
_description = "View Log"
_columns = {
'from':fields.datetime('Log From'),
'to':fields.datetime('Log To', required = True)
}
_defaults = {
'to': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
}
def log_open_window(self, cr, uid, ids, context=None):
"""
Open Log form from given date range..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of audittrail view logs IDs.
@return: Dictionary of audittrail log form on given date range.
"""
if not context:
context = {}
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj._get_id(cr, uid, 'audittrail', 'action_audittrail_log_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id])[0]
#log_obj = self.pool.get(result['res_model'])
#log_id = log_obj.search(cr, uid, [])
#log_model = log_obj.read(cr, uid, log_id, ['object_id'])
#start Loop
for datas in self.read(cr, uid, ids):
if not datas.get('from', None):
if datas.get('to') <> time.strftime("%Y-%m-%d %H:%M:%S"):
result['domain'] = str([('timestamp', '<', datas.get('to'))])
else:
pass
else:
result['domain'] = str([('timestamp', '>', datas.get('from', None)), ('timestamp', '<', datas.get('to'))])
#End Loop
return result
audittrail_view_log()

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Audittrail View Log wizard-->
<record id="view_audittrail_view_log" model="ir.ui.view">
<field name="name">audittrail.view.log.form</field>
<field name="model">audittrail.view.log</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Audit Logs">
<group colspan="4" >
<field name="from" colspan="4"/>
<newline/>
<field name="to" colspan="4"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-open" string="Open Logs"
name="log_open_window" type="object" />
</group>
</form>
</field>
</record>
<!-- action for audittrail view log wizard -->
<record id="action_audittrail_view_log" model="ir.actions.act_window">
<field name="name">View log</field>
<field name="res_model">audittrail.view.log</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_audittrail_view_log"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,81 +0,0 @@
# -*- 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 wizard
import pooler
import time
class wizard_view_log(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="Audit Logs">
<field name="from" colspan="4"/>
<newline/>
<field name="to" colspan="4"/>
</form>'''
form1_fields = {
'from': {
'string': 'Log From',
'type': 'datetime',
},
'to': {
'string': 'Log To',
'type': 'datetime',
'default': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
'required':True
},
}
def _log_open_window(self, cr, uid, data, context):
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
result = mod_obj._get_id(cr, uid, 'audittrail', 'action_audittrail_log_tree')
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id])[0]
log_obj= pooler.get_pool(cr.dbname).get(result['res_model'])
log_id = log_obj.search(cr, uid, [])
log_model=log_obj.read(cr, uid,log_id,['object_id'])
if not data['form']['from']:
if data['form']['to'] <> time.strftime("%Y-%m-%d %H:%M:%S"):
result['domain'] = str([('timestamp', '<',data['form']['to'])])
else:
pass
else:
result['domain'] = str([('timestamp', '>',data['form']['from']),('timestamp', '<',data['form']['to'])])
return result
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel'), ('open', 'Open Logs')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action':_log_open_window, 'state':'end'}
}
}
wizard_view_log('audittrail.view.log')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -29,61 +29,86 @@ from osv.orm import except_orm
from tools.translate import _
class base_action_rule(osv.osv):
""" Base Action Rules """
_name = 'base.action.rule'
_description = 'Action Rules'
_description = 'Action Rules'
_columns = {
'name': fields.many2one('ir.model', 'Model', required=True),
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines'),
'create_date': fields.datetime('Create Date', readonly=1),
'active': fields.boolean('Active')
}
_defaults = {
'active': lambda *a: True,
'max_level': lambda *a: 15,
}
def format_body(self, body):
""" Foramat Action rule's body
@param self: The object pointer """
return body and tools.ustr(body) or ''
def format_mail(self, obj, body):
""" Foramat Mail
@param self: The object pointer """
data = {
'object_id': obj.id,
'object_subject': hasattr(obj, 'name') and obj.name or False,
'object_date': hasattr(obj, 'date') and obj.date or False,
'object_description': hasattr(obj, 'description') and obj.description or False,
'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/',
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email) or '/',
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \
obj.user_id.address_id and obj.user_id.address_id.email) or '/',
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and\
obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/',
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and obj.partner_address_id.email) or '/',
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\
obj.partner_address_id.email) or '/',
}
return self.format_body(body % data)
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
""" send email
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param email: pass the emails
@param emailfrom: Pass name the email From else False
@param context: A standard dictionary for contextual values """
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.address_id and\
obj.user_id.address_id.email:
emailfrom = obj.user_id.address_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
emailfrom = tools.ustr(emailfrom)
reply_to = emailfrom
reply_to = emailfrom
if not emailfrom:
raise osv.except_osv(_('Error!'),
_("No E-Mail ID Found for your Company address!"))
return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
def do_check(self, cr, uid, action, obj, context={}):
ok = True
if hasattr(obj, 'user_id'):
""" check Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
ok = True
if hasattr(obj, 'user_id'):
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
if hasattr(obj, 'partner_id'):
ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==obj.partner_id.id)
ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==obj.partner_id.id)
ok = ok and (
not action.trg_partner_categ_id.id or
(
@ -113,14 +138,22 @@ class base_action_rule(osv.osv):
return ok
def do_action(self, cr, uid, action, model_obj, obj, context={}):
""" Do Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param action: pass action
@param model_obj: pass Model object
@param context: A standard dictionary for contextual values """
if action.server_action_id:
context.update({'active_id':obj.id,'active_ids':[obj.id]})
self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context)
write = {}
write = {}
if hasattr(obj, 'user_id') and action.act_user_id:
obj.user_id = action.act_user_id
write['user_id'] = action.act_user_id.id
if hasattr(obj, 'date_action_last'):
if hasattr(obj, 'date_action_last'):
write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
if hasattr(obj, 'state') and action.act_state:
obj.state = action.act_state
@ -135,7 +168,7 @@ class base_action_rule(osv.osv):
write['priority'] = action.act_priority
model_obj.write(cr, uid, [obj.id], write, context)
if hasattr(model_obj, 'remind_user') and action.act_remind_user:
model_obj.remind_user(cr, uid, [obj.id], context, attach=action.act_remind_attach)
if hasattr(model_obj, 'remind_partner') and action.act_remind_partner:
@ -146,7 +179,7 @@ class base_action_rule(osv.osv):
if hasattr(obj, 'user_id') and action.act_mail_to_user:
if obj.user_id and obj.user_id.address_id:
emails.append(obj.user_id.address_id.email)
if action.act_mail_to_watchers:
emails += (action.act_email_cc or '').split(',')
if action.act_mail_to_email:
@ -158,11 +191,19 @@ class base_action_rule(osv.osv):
return True
def _action(self, cr, uid, ids, objects, scrit=None, context={}):
""" Do Action
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Basic Action Rules IDs,
@param objects: pass objects
@param context: A standard dictionary for contextual values """
if not scrit:
scrit = []
rule_line_obj = self.pool.get('base.action.rule.line')
for rule in self.browse(cr, uid, ids):
level = rule.max_level
for rule in self.browse(cr, uid, ids):
level = rule.max_level
if not level:
break
newactions = []
@ -184,7 +225,8 @@ class base_action_rule(osv.osv):
base = mx.DateTime.strptime(obj.date_action_last, '%Y-%m-%d %H:%M:%S')
else:
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' and obj.date_deadline:
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' \
and obj.date_deadline:
base = mx.DateTime.strptime(obj.date_deadline, '%Y-%m-%d %H:%M:%S')
elif hasattr(obj, 'date') and action.trg_date_type=='date' and obj.date:
base = mx.DateTime.strptime(obj.date, '%Y-%m-%d %H:%M:%S')
@ -212,7 +254,7 @@ class base_action_rule(osv.osv):
if ok:
self.do_action(cr, uid, action, model_obj, obj, context)
break
break
level -= 1
return True
base_action_rule()
@ -222,20 +264,46 @@ class base_action_rule_line(osv.osv):
_description = 'Action Rule Lines'
def _state_get(self, cr, uid, context={}):
""" Get State
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return self.state_get(cr, uid, context=context)
def _priority_get(self, cr, uid, context={}):
""" Get Priority
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return self.priority_get(cr, uid, context=context)
def state_get(self, cr, uid, context={}):
""" Get State
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return [('','')]
def priority_get(self, cr, uid, context={}):
""" Get Priority
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
return [('','')]
_columns = {
'name': fields.char('Rule Name',size=64, required=True),
'rule_id': fields.many2one('base.action.rule','Rule'),
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the rule without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of rules."),
'active': fields.boolean('Active', help="If the active field is set to true,\
it will allow you to hide the rule without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when\
displaying a list of rules."),
'trg_date_type': fields.selection([
('none','None'),
@ -243,11 +311,13 @@ class base_action_rule_line(osv.osv):
('action_last','Last Action Date'),
('date','Date'),
], 'Trigger Date', size=16),
'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date, specifies you can put a negative number " \
"if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'),
'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date,\
specifies you can put a negative number " \
"if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),\
('day','Days'),('month','Months')], 'Delay type'),
'trg_user_id': fields.many2one('res.users', 'Responsible'),
'trg_partner_id': fields.many2one('res.partner', 'Partner'),
@ -255,42 +325,59 @@ class base_action_rule_line(osv.osv):
'trg_state_from': fields.selection(_state_get, 'State', size=16),
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
'trg_priority_from': fields.selection(_priority_get, 'Minimum Priority'),
'trg_priority_to': fields.selection(_priority_get, 'Maximum Priority'),
'act_method': fields.char('Call Object Method', size=64),
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
'trg_priority_to': fields.selection(_priority_get, 'Maximum Priority'),
'act_method': fields.char('Call Object Method', size=64),
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
'act_state': fields.selection(_state_get, 'Set state to', size=16),
'act_priority': fields.selection(_priority_get, 'Set priority to'),
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people will receive a copy of the future communication between partner and users by email"),
'act_priority': fields.selection(_priority_get, 'Set priority to'),
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people\
will receive a copy of the future communication between partner and users by email"),
'act_remind_partner': fields.boolean('Remind Partner', help="Check this if you want the rule to send a reminder by email to the partner."),
'act_remind_user': fields.boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user."),
'act_remind_partner': fields.boolean('Remind Partner', help="Check this if\
you want the rule to send a reminder by email to the partner."),
'act_remind_user': fields.boolean('Remind responsible', help="Check this if \
you want the rule to send a reminder by email to the user."),
'act_reply_to': fields.char('Reply-To', size=64),
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."),
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if\
you want that all documents attached to the object be attached \
to the reminder email sent."),
'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if you want the rule to send an email to the responsible person."),
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this if you want the rule to mark CC(mail to any other person defined in actions)."),
'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id of the persons whom mail is to be sent"),
'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if \
you want the rule to send an email to the responsible person."),
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this\
if you want the rule to mark CC(mail to any other person\
defined in actions)."),
'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id \
of the persons whom mail is to be sent"),
'act_mail_body': fields.text('Mail body',help="Content of mail"),
'regex_name': fields.char('Regular Expression on Model Name', size=128),
'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the action name." \
"eg:on which object which action to be taken on basis of which condition"),
'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the\
action name." \
"eg:on which object which action to be taken on basis of which condition"),
}
_defaults = {
'active': lambda *a: 1,
'trg_date_type': lambda *a: 'none',
'trg_date_range_type': lambda *a: 'day',
'act_mail_to_user': lambda *a: 0,
'act_remind_partner': lambda *a: 0,
'act_remind_user': lambda *a: 0,
'act_remind_user': lambda *a: 0,
'act_mail_to_watchers': lambda *a: 0,
}
_order = 'sequence'
_order = 'sequence'
def _check_mail(self, cr, uid, ids, context=None):
""" Check Mail
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Action Rules IDs
@param context: A standard dictionary for contextual values """
empty = orm.browse_null()
rule_obj = self.pool.get('base.action.rule')
for rule in self.browse(cr, uid, ids):
@ -300,10 +387,11 @@ class base_action_rule_line(osv.osv):
except (ValueError, KeyError, TypeError):
return False
return True
_constraints = [
(_check_mail, 'Error: The mail is not well formated', ['act_mail_body']),
]
base_action_rule_line()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem id="menu_base_action_rule" name="Action Rule" parent="base.menu_base_config" sequence="0"/>
<menuitem id="menu_base_action_rule" name="Action Rule"
parent="base.menu_base_config" sequence="0" />
<!--
Action Rule
<!--
Action Rule Form View
-->
<record id="view_base_action_rule_form" model="ir.ui.view">
<field name="name">base.action.rule.form</field>
@ -21,6 +22,8 @@
</field>
</record>
<!-- Action Rule Tree View -->
<record id="view_base_action_rule_tree" model="ir.ui.view">
<field name="name">base.action.rule.tree</field>
<field name="model">base.action.rule</field>
@ -34,6 +37,8 @@
</field>
</record>
<!-- Action Rule Action -->
<record id="base_action_rule_act" model="ir.actions.act_window">
<field name="name">Action Rules</field>
<field name="res_model">base.action.rule</field>
@ -42,10 +47,11 @@
<field name="view_id" ref="view_base_action_rule_tree"/>
</record>
<menuitem id="menu_base_action_rule_form" parent="menu_base_action_rule" action="base_action_rule_act"/>
<menuitem id="menu_base_action_rule_form"
parent="menu_base_action_rule" action="base_action_rule_act" />
<!--
Action Rule Lines
<!--
Action Rule Lines Form View
-->
<record id="view_base_action_rule_line_form" model="ir.ui.view">
<field name="name">base.action.rule.line.form</field>
@ -127,6 +133,9 @@
</form>
</field>
</record>
<!-- Action Rule Lines Tree View -->
<record id="view_base_action_rule_line_tree" model="ir.ui.view">
<field name="name">base.action.rule.line.tree</field>
<field name="model">base.action.rule.line</field>

0
addons/base_calendar/__init__.py Normal file → Executable file
View File

47
addons/base_calendar/__terp__.py Normal file → Executable file
View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,36 +15,33 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : [
"base",
],
'description': """
Full featured calendar system that support:
- Alerts (create requests)
- Recurring events (*)
- Invitations to other people
""",
"author" : "Tiny",
'category': 'Generic Modules/Others',
'website': 'http://www.openerp.com',
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : ["base"],
'description': """Full featured calendar system that support:
- Alerts (create requests)
- Recurring events (*)
- Invitations to others people""",
"author" : "Tiny",
'category': 'Generic Modules/Others',
'website': 'http://www.openerp.com',
"init_xml" : [
'base_calendar_data.xml'
],
"demo_xml" : [],
'base_calendar_data.xml'
],
"demo_xml" : [],
"update_xml" : [
'security/ir.model.access.csv',
'base_calendar_view.xml'
],
"installable" : True,
"active" : False,
'security/ir.model.access.csv',
'wizard/calendar_event_edit_all_view.xml',
'wizard/base_calendar_invite_attendee_view.xml',
'base_calendar_view.xml'
],
"installable" : True,
"active" : False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

File diff suppressed because it is too large Load Diff

489
addons/base_calendar/base_calendar_data.xml Normal file → Executable file
View File

@ -1,376 +1,133 @@
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
<!-- Event attributes-->
<record model="basic.calendar.attributes" id="field_event_comment">
<field name="name">comment</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_uid">
<field name="name">uid</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_seq">
<field name="name">seq</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_recurrence-id">
<field name="name">recurrence-id</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_transp">
<field name="name">transp</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_attendee">
<field name="name">attendee</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_related">
<field name="name">related</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_rrule">
<field name="name">rrule</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_dtend">
<field name="name">dtend</field>
<field name="type">vevent</field>
<record model="res.alarm" id="alarm1">
<field name="name">1 minute before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="1" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm2">
<field name="name">5 minutes before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="5" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="basic.calendar.attributes" id="field_event_valarm">
<field name="name">valarm</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_vtimezone">
<field name="name">vtimezone</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_priority">
<field name="name">priority</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_location">
<field name="name">location</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_exrule">
<field name="name">exrule</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_resources">
<field name="name">resources</field>
<field name="type">vevent</field>
</record>
<record model="res.alarm" id="alarm3">
<field name="name">10 minutes before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="10" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm4">
<field name="name">15 minutes before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="15" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm5">
<field name="name">30 minutes before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="30" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm6">
<field name="name">45 minutes before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="45" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm7">
<field name="name">1 hour before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="1" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm8">
<field name="name">2 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="2" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm9">
<field name="name">3 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="3" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm10">
<field name="name">4 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="4" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm11">
<field name="name">5 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="5" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm12">
<field name="name">18 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="18" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="basic.calendar.attributes" id="field_event_rstatus">
<field name="name">rstatus</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_status">
<field name="name">status</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_exdate">
<field name="name">exdate</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_dtstamp">
<field name="name">dtstamp</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_description">
<field name="name">description</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_rdate">
<field name="name">rdate</field>
<field name="type">vevent</field>
<!-- Scheduler for Event Alarm-->
<record forcecreate="True" id="ir_cron_scheduler_alarm"
model="ir.cron">
<field name="name">Run Event Reminder</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field eval="'calendar.alarm'" name="model" />
<field eval="'do_run_scheduler'" name="function" />
<field eval="'(False,)'" name="args" />
</record>
<record model="basic.calendar.attributes" id="field_event_dtstart">
<field name="name">dtstart</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_class">
<field name="name">class</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_x-openobject-model">
<field name="name">x-openobject-model</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_created">
<field name="name">created</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_url">
<field name="name">url</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_summary">
<field name="name">summary</field>
<field name="type">vevent</field>
</record>
<record model="basic.calendar.attributes" id="field_event_contact">
<field name="name">contact</field>
<field name="type">vevent</field>
</record>
<!-- Todo attributes-->
<record model="basic.calendar.attributes" id="field_todo_status">
<field name="name">status</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_comment">
<field name="name">comment</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_attendee">
<field name="name">attendee</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_valarm">
<field name="name">valarm</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_description">
<field name="name">description</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_seq">
<field name="name">seq</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_vtimezone">
<field name="name">vtimezone</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_url">
<field name="name">url</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_completed">
<field name="name">completed</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_percent">
<field name="name">percent</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_due">
<field name="name">due</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_summary">
<field name="name">summary</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_priority">
<field name="name">priority</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_exdate">
<field name="name">exdate</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_location">
<field name="name">location</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_exrule">
<field name="name">exrule</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_duration">
<field name="name">duration</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_organizer">
<field name="name">organizer</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_dtstart">
<field name="name">dtstart</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_rrule">
<field name="name">rrule</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_class">
<field name="name">class</field>
<field name="type">vtodo</field>
</record>
<record model="basic.calendar.attributes" id="field_todo_uid">
<field name="name">uid</field>
<field name="type">vtodo</field>
</record>
<!-- Attendee's attributes-->
<record model="basic.calendar.attributes" id="field_attendee_cn">
<field name="name">cn</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_sent-by">
<field name="name">sent-by</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_language">
<field name="name">language</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_delegated-from">
<field name="name">delegated-from</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_member">
<field name="name">member</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_cutype">
<field name="name">cutype</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_role">
<field name="name">role</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_partstat">
<field name="name">partstat</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_delegated-to">
<field name="name">delegated-to</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_dir">
<field name="name">dir</field>
<field name="type">attendee</field>
</record>
<record model="basic.calendar.attributes" id="field_attendee_rsvp">
<field name="name">rsvp</field>
<field name="type">attendee</field>
</record>
<!-- Alarm attributes-->
<record model="basic.calendar.attributes" id="field_alarm_attendee">
<field name="name">attendee</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_trigger_duration">
<field name="name">trigger_duration</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_description">
<field name="name">description</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_attach">
<field name="name">attach</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_trigger_occurs">
<field name="name">trigger_occurs</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_trigger_interval">
<field name="name">trigger_interval</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_summary">
<field name="name">summary</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_duration">
<field name="name">duration</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_repeat">
<field name="name">repeat</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_action">
<field name="name">action</field>
<field name="type">alarm</field>
</record>
<record model="basic.calendar.attributes" id="field_alarm_trigger_related">
<field name="name">trigger_related</field>
<field name="type">alarm</field>
</record>
</data>
</openerp>

216
addons/base_calendar/base_calendar_view.xml Normal file → Executable file
View File

@ -1,141 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Attendee invite wizard-->
<record id="view_calendar_invite_attendee_wizard" model="ir.ui.view">
<field name="name">Invite Attendees</field>
<field name="model">base_calendar.invite.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invite People">
<field name="type" />
<field name="send_mail" />
<newline/>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'external')]}">
<field name="email" colspan="4"
attrs="{'required': [('type', '=', 'external')]}" />
</group>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'internal')]}">
<separator string="Users" colspan="4" />
<field name="user_ids" select="1" colspan="4"
nolabel="1" />
<newline />
</group>
<group col="2" colspan="4"
attrs="{'invisible': [('type', '!=', 'partner')]}">
<field name="partner_id" colspan="2"
on_change="onchange_partner_id(partner_id)"
attrs="{'required': [('type', '=', 'partner')]}" />
<newline />
<separator string="Partner Contacts"
colspan="4" />
<field name="contact_ids" select="1" colspan="4"
nolabel="1" domain="[('partner_id', '=', partner_id)]"
attrs="{'readonly': [('type', '!=', 'partner')]}" />
</group>
<newline/>
<separator string="" colspan="6" />
<label string="" colspan="2" />
<button icon='gtk-cancel' special="cancel"
string="Cancel" />
<button name="do_invite" string="Invite"
type="object" icon="gtk-ok" />
</form>
</field>
</record>
<record id="action_view_calendar_invite_attendee_wizard" model="ir.actions.act_window">
<field name="name">Invite Attendees</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">base_calendar.invite.attendee</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="base_calendar_attendee_form_view" model="ir.ui.view">
<field name="name">calendar.attendee.form</field>
<field name="model">calendar.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="sent_by_uid" string="Invitation From"/>
<notebook colspan="4">
<page string="Invitation">
<form string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="sent_by_uid" string="Invitation From" />
<notebook colspan="4">
<page string="Invitation">
<separator string="Invitation Detail" colspan="4" />
<group colspan="4" col="4">
<field name="user_id" string="Invited User"/>
<newline/>
<field name="partner_address_id" string="Partner Contact"/>
<field name="partner_id" string="Partner" readonly="1"/>
<field name="partner_address_id"
string="Partner Contact" />
<field name="partner_id"
string="Partner" readonly="1" />
<field name="role" string="Role" />
<field name="cutype" string="Invitation type" />
<field name="rsvp" />
<field name="rsvp" />
</group>
<separator string="Event Detail" colspan="4" />
<group colspan="4" col="4">
<field name="event_date" />
<field name="event_end_date" />
<field name="language"/>
<field name="ref" colspan="4"/>
<field name="ref" colspan="4" readonly="1"/>
</group>
</page>
<page string="Delegation Info">
<separator string="Delegated From" colspan="4" />
<field name="parent_ids" nolabel="1"
colspan="4" readonly="1" />
<separator string="Delegated To" colspan="4" />
<field name="child_ids" nolabel="1"
colspan="4" readonly="1" />
</page>
</notebook>
<group col="6" colspan="4">
<field name="state" select="2" />
<button name="do_tentative" states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept" states="needs-action,tentative,declined"
type="object" icon="gtk-apply" />
<button name="do_decline" string="Decline" states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel" />
<button
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
string="Delegate" type="action"
icon="gtk-sort-descending" states="needs-action,tentative,declined,accepted"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
</group>
</form>
</page>
<page string="Delegation Info">
<separator string="Delegated From" colspan="4" />
<field name="parent_ids" nolabel="1"
colspan="4" readonly="1" />
<separator string="Delegated To" colspan="4" />
<field name="child_ids" nolabel="1"
colspan="4" readonly="1" />
</page>
</notebook>
<group col="6" colspan="4">
<field name="state" />
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" icon="gtk-apply" />
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel" />
<button
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
string="Delegate" type="action"
icon="gtk-sort-descending"
states="needs-action,tentative,declined,accepted"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
</group>
</form>
</field>
</record>
<record id="base_calendar_attendee_tree_view" model="ir.ui.view">
<record id="base_calendar_attendee_tree_view" model="ir.ui.view">
<field name="name">calendar.attendee.tree</field>
<field name="model">calendar.attendee</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Invitation details">
<field name="email" string="Invitation To"/>
<tree string="Invitation details">
<field name="email" string="Invitation To"/>
<field name="partner_id" string="Partner" />
<field name="partner_address_id" string="Contact" />
<field name="role" />
<field name="state" />
</tree>
<field name="role" />
<field name="state" />
</tree>
</field>
</record>
</record>
<record id="base_calendar_attendee_search_view" model="ir.ui.view">
<field name="name">calendar.attendee.search</field>
<field name="model">calendar.attendee</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Search Invitations">
<filter icon="gtk-apply" string="Accepted"
domain="[('state','=', 'accepted')]"
separator="1" help="Accepted Invitations" />
<filter icon="gtk-close" string="Declined"
domain="[('state','=', 'declined')]"
separator="1" help="Declined Invitations" />
<separator orientation="vertical"/>
<field name="email" select='1'/>
<field name="sent_by_uid" select="1" widget="selection"/>
<separator orientation="vertical"/>
<field name="cutype" string="Invitation type" select="1"/>
<field name="event_date" select="1"/>
</search>
</field>
</record>
<record id="res_alarm_form_view" model="ir.ui.view">
<field name="name">res.alarm.form</field>
<field name="model">res.alarm</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Reminder details">
<form string="Reminder details">
<field name="name" />
<field name="active" />
<separator string="Reminder Details" colspan="4" />
@ -146,7 +117,7 @@
<separator string="" colspan="4" />
<field name="duration" />
<field name="repeat" />
</form>
</form>
</field>
</record>
@ -154,34 +125,31 @@
<field name="name">res.alarm.tree</field>
<field name="model">res.alarm</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Reminder details">
<field name="name" select="1"/>
<field name="trigger_interval" select="1"/>
<field name="trigger_duration" select="1"/>
<field name="trigger_occurs" select="1"/>
<field name="trigger_related" select="1"/>
</tree>
<field name="arch" type="xml">
<tree string="Reminder details">
<field name="name" select="1"/>
<field name="trigger_interval" select="1"/>
<field name="trigger_duration" select="1"/>
<field name="trigger_occurs" select="1"/>
<field name="trigger_related" select="1"/>
</tree>
</field>
</record>
<record id="action_res_alarm_view" model="ir.actions.act_window">
<field name="name">Available Alarms</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.alarm</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="name">Available Alarms</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.alarm</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Available alarms-->
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="10" />
<!--Available alarms-->
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="10" />
<menuitem name="Available Alarms" id="menu_crm_meeting_avail_alarm"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" />
<menuitem name="Available Alarms" id="menu_crm_meeting_avail_alarm"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" />
</data>
</openerp>

1
addons/base_calendar/security/ir.model.access.csv Normal file → Executable file
View File

@ -5,3 +5,4 @@
"access_calendar_event_all","calendar.event","model_calendar_event",,1,1,1,1
"access_calendar_todo","calendar.todo","model_calendar_todo",,1,1,1,1
"access_base_calendar_invite_attendee","base_calendar.invite.attendee","model_base_calendar_invite_attendee",,1,1,1,1
"access_calendar_event_edit_all","calendar_event_edit_all","model_calendar_event_edit_all",,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_calendar_event_all calendar.event model_calendar_event 1 1 1 1
6 access_calendar_todo calendar.todo model_calendar_todo 1 1 1 1
7 access_base_calendar_invite_attendee base_calendar.invite.attendee model_base_calendar_invite_attendee 1 1 1 1
8 access_calendar_event_edit_all calendar_event_edit_all model_calendar_event_edit_all 1 1 1 1

4
addons/base_calendar/wizard/__init__.py Normal file → Executable file
View File

@ -18,7 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard_cal_edit_event
import calendar_event_edit_all
import base_calendar_invite_attendee
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 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/>.
#
##############################################################################
from base_calendar import base_calendar
from osv import fields, osv
from tools.translate import _
import tools
class base_calendar_invite_attendee(osv.osv_memory):
"""
Invite attendee.
"""
_name = "base_calendar.invite.attendee"
_description = "Invite Attendees"
_columns = {
'type': fields.selection([('internal', 'Internal User'), \
('external', 'External Email'), \
('partner', 'Partner Contacts')], 'Type', required=True),
'user_ids': fields.many2many('res.users', 'invite_user_rel',
'invite_id', 'user_id', 'Users'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'email': fields.char('Email', size=124),
'contact_ids': fields.many2many('res.partner.address', 'invite_contact_rel',
'invite_id', 'contact_id', 'Contacts'),
'send_mail': fields.boolean('Send mail?', help='Check this if you want to \
send an Email to Invited Person')
}
_defaults = {
'type': lambda *x: 'internal'
}
def do_invite(self, cr, uid, ids, context=None):
"""
Invites attendee for meeting..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of base calendar invite attendees IDs.
@param context: A standard dictionary for contextual values
@return: Dictionary of {}.
"""
if not context:
context = {}
model = False
model_field = False
context_id = context and context.get('active_id', False) or False
if not context or not context.get('model'):
return {}
else:
model = context.get('model')
model_field = context.get('attendee_field', False)
for datas in self.read(cr, uid, ids, context=context):
obj = self.pool.get(model)
res_obj = obj.browse(cr, uid, context_id)
type = datas.get('type')
att_obj = self.pool.get('calendar.attendee')
vals = []
mail_to = []
attendees = []
ref = {}
if not model == 'calendar.attendee':
if context_id:
ref = {'ref': '%s,%s' % (model, base_calendar.base_calendar_id2real_id(context_id))}
else:
return {}
if type == 'internal':
user_obj = self.pool.get('res.users')
if not datas.get('user_ids'):
raise osv.except_osv(_('Error!'), ("Please select any User"))
for user_id in datas.get('user_ids'):
user = user_obj.browse(cr, uid, user_id)
res = {
'user_id': user_id,
'email': user.address_id.email
}
res.update(ref)
vals.append(res)
if user.address_id.email:
mail_to.append(user.address_id.email)
elif type == 'external' and datas.get('email'):
res = {'email': datas['email']}
res.update(ref)
vals.append(res)
mail_to.append(datas['email'])
elif type == 'partner':
add_obj = self.pool.get('res.partner.address')
for contact in add_obj.browse(cr, uid, datas['contact_ids']):
res = {
'partner_address_id': contact.id,
'email': contact.email
}
res.update(ref)
vals.append(res)
if contact.email:
mail_to.append(contact.email)
att = att_obj.browse(cr, uid, context_id)
for att_val in vals:
if model == 'calendar.attendee':
if ref:
att_val.update({
'parent_ids': [(4, att.id)],
'ref': att.ref._name + ',' +str(att.ref.id)
})
attendees.append(att_obj.create(cr, uid, att_val))
if model_field:
for attendee in attendees:
obj.write(cr, uid, res_obj.id, {model_field: [(4, attendee)]})
if datas.get('send_mail'):
if not mail_to:
name = map(lambda x: x[1], filter(lambda x: type==x[0], \
self._columns['type'].selection))
raise osv.except_osv(_('Error!'), ("%s must have an email Address to send mail") %(name[0]))
att_obj._send_mail(cr, uid, attendees, mail_to, \
email_from= tools.config.get('email_from', False))
return {}
def onchange_partner_id(self, cr, uid, ids, partner_id, *args, **argv):
"""
Make entry on contact_ids on change of partner_id field.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of base calendar invite attendees IDs.
@param partner_id: id of Partner
@return: dictionary of value.
"""
if not partner_id:
return {'value': {'contact_ids': []}}
cr.execute('select id from res_partner_address \
where partner_id=%s' % (partner_id))
contacts = map(lambda x: x[0], cr.fetchall())
return {'value': {'contact_ids': contacts}}
base_calendar_invite_attendee()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Attendee invite wizard-->
<record id="view_calendar_invite_attendee_wizard"
model="ir.ui.view">
<field name="name">Invite Attendees</field>
<field name="model">base_calendar.invite.attendee</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invite People">
<field name="type" />
<field name="send_mail" />
<newline />
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'external')]}">
<field name="email" colspan="4"
attrs="{'required': [('type', '=', 'external')]}" />
</group>
<group col="1" colspan="4"
attrs="{'invisible': [('type', '!=', 'internal')]}">
<separator string="Users" colspan="4" />
<field name="user_ids" select="1" colspan="4"
nolabel="1" />
<newline />
</group>
<group col="2" colspan="4"
attrs="{'invisible': [('type', '!=', 'partner')]}">
<field name="partner_id" colspan="2"
on_change="onchange_partner_id(partner_id)"
attrs="{'required': [('type', '=', 'partner')]}" />
<newline />
<separator string="Partner Contacts"
colspan="4" />
<field name="contact_ids" select="1" colspan="4"
nolabel="1" domain="[('partner_id', '=', partner_id)]"
attrs="{'readonly': [('type', '!=', 'partner')]}" />
</group>
<newline />
<separator string="" colspan="6" />
<label string="" colspan="2" />
<button icon='gtk-cancel' special="cancel"
string="Cancel" />
<button name="do_invite" string="Invite"
type="object" icon="gtk-ok" />
</form>
</field>
</record>
<!-- Attendee invite action-->
<record id="action_view_calendar_invite_attendee_wizard" model="ir.actions.act_window">
<field name="name">Invite Attendees</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">base_calendar.invite.attendee</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 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/>.
#
##############################################################################
from osv import osv
from osv import fields
class calendar_event_edit_all(osv.osv_memory):
def _default_values(self, cr, uid, context={}):
""" Get Default value for Start Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@Return: Get Default value for Start Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date'):
return context.get('date')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date']
def _default_deadline(self, cr, uid, context={}):
""" Get Default value for End Date
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@return: Get Default value for End Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date_deadline'):
return context.get('date_deadline')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date_deadline']
def modify_this(self, cr, uid, ids, context=None):
"""
Modify All event for Crm Meeting.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendar event edit alls IDs
@return: dictionary {}
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
for datas in self.read(cr, uid, ids):
model = context.get('model', False)
model_obj = self.pool.get(model)
model_obj.modify_all(cr, uid, [context_id], datas, context=context)
return {}
_name = "calendar.event.edit.all"
_description = "Calendar Edit all event"
_columns = {
'name': fields.char('Title', size=64, required=True),
'date': fields.datetime('Start Date', required=True),
'date_deadline': fields.datetime('End Date', required=True),
'location': fields.char('Location', size=124),
'alarm_id': fields.many2one('res.alarm', 'Reminder'),
}
_defaults = {
'date': _default_values,
'date_deadline': _default_deadline
}
calendar_event_edit_all()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_edit_all" model="ir.ui.view">
<field name="name">calendar.event.edit.all.form</field>
<field name="model">calendar.event.edit.all</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Edit all Occurrences">
<group colspan="4" >
<separator string="" colspan="4" />
<newline/>
<field name='name' colspan="4" />
<newline />
<field name='location' colspan="4" />
<newline />
<field name='date' />
<field name='date_deadline' />
<newline />
<field name='alarm_id'/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-save" string="_Save" name="modify_this" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_edit_all" model="ir.actions.act_window">
<field name="name">Edit all events</field>
<field name="res_model">calendar.event.edit.all</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_edit_all"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 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 wizard
import pooler
class event_edit_all(wizard.interface):
event_form = """<?xml version="1.0"?>
<form string="Edit all Occurrences">
<separator string="" colspan="4" />
<newline />
<field name='name' colspan="4" />
<newline />
<field name='location' colspan="4" />
<newline />
<field name='date' />
<field name='date_deadline' />
<newline />
<field name='alarm_id'/>
</form>"""
event_fields = {
'name': {'string': 'Title', 'type': 'char', 'size': 64, 'required': True},
'date': {'string': 'Start Date', 'type': 'datetime', 'required': True},
'date_deadline': {'string': 'End Date', 'type': 'datetime', 'required': True},
'location': {'string': 'Location', 'type': 'char', 'size': 124},
'alarm_id': {'string': 'Reminder', 'type': 'many2one', 'relation': 'res.alarm'},
}
def _default_values(self, cr, uid, data, context=None):
model = data.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
event = model_obj.read(cr, uid, data['id'], ['name', 'location', 'alarm_id'])
event.update({
'date': context.get('date'),
'date_deadline': context.get('date_deadline')
})
return event
def _modify_all(self, cr, uid, datas, context=None):
model = datas.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
model_obj.modify_all(cr, uid, datas['id'], datas['form'], context)
return {}
states = {
'init': {
'actions': [_default_values],
'result': {'type': 'form', 'arch': event_form, 'fields': event_fields,
'state': [('end', 'Cancel', 'gtk-cancel'), ('edit', '_Save', 'gtk-save')]}
},
'edit': {
'actions': [],
'result': {'type': 'action', 'action': _modify_all, 'state': 'end'}
}
}
event_edit_all('calendar.event.edit.all')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -23,16 +23,19 @@ import netsvc
from osv import fields, osv
class res_partner_contact(osv.osv):
""" Partner Contact """
_name = "res.partner.contact"
_description = "res.partner.contact"
# def init(self, cr):
# address_obj = self.pool.get('res.partner.address')
# job_obj = self.pool.get('res.partner.job')
# address_ids = address_obj.search(cr, 1, [])
# for address in address_obj.browse(cr, 1, address_ids):
# contact_id = self.create(cr, 1, {'name': address.name or 't'})
def _title_get(self,cr, user, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param context: A standard dictionary for contextual values
"""
obj = self.pool.get('res.partner.title')
ids = obj.search(cr, user, [])
res = obj.read(cr, user, ids, ['shortcut', 'name','domain'], context)
@ -40,6 +43,16 @@ class res_partner_contact(osv.osv):
return res
def _main_job(self, cr, uid, ids, fields, arg, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of partner contacts IDs
@fields: Get Fields
@param context: A standard dictionary for contextual values
@param arg: list of tuples of form [(name_of_the_field, operator, value), ...]. """
res = dict.fromkeys(ids, False)
for contact in self.browse(cr, uid, ids, context):
if contact.job_ids:
@ -49,30 +62,43 @@ class res_partner_contact(osv.osv):
_columns = {
'name': fields.char('Last Name', size=30,required=True),
'first_name': fields.char('First Name', size=30),
'mobile':fields.char('Mobile',size=30),
'mobile': fields.char('Mobile',size=30),
'title': fields.selection(_title_get, 'Title'),
'website':fields.char('Website',size=120),
'lang_id':fields.many2one('res.lang','Language'),
'job_ids':fields.one2many('res.partner.job','contact_id','Functions and Addresses'),
'country_id':fields.many2one('res.country','Nationality'),
'birthdate':fields.date('Birth Date'),
'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the partner contact without removing it."),
'partner_id':fields.related('job_ids','address_id','partner_id',type='many2one', relation='res.partner', string='Main Employer'),
'function_id':fields.related('job_ids','function_id',type='many2one', relation='res.partner.function', string='Main Function'),
'job_id': fields.function(_main_job, method=True, type='many2one', relation='res.partner.job', string='Main Job'),
'website': fields.char('Website',size=120),
'lang_id': fields.many2one('res.lang','Language'),
'job_ids': fields.one2many('res.partner.job','contact_id','Functions and Addresses'),
'country_id': fields.many2one('res.country','Nationality'),
'birthdate': fields.date('Birth Date'),
'active': fields.boolean('Active', help="If the active field is set to true,\
it will allow you to hide the partner contact without removing it."),
'partner_id': fields.related('job_ids','address_id','partner_id',type='many2one',\
relation='res.partner', string='Main Employer'),
'function_id': fields.related('job_ids','function_id',type='many2one', \
relation='res.partner.function', string='Main Function'),
'job_id': fields.function(_main_job, method=True, type='many2one',\
relation='res.partner.job', string='Main Job'),
'email': fields.char('E-Mail', size=240),
'comment' : fields.text('Notes', translate=True),
'photo' : fields.binary('Image'),
'comment': fields.text('Notes', translate=True),
'photo': fields.binary('Image'),
}
_defaults = {
'active' : lambda *a: True,
}
_order = "name,first_name"
def name_get(self, cr, user, ids, context={}):
#will return name and first_name.......
""" will return name and first_name.......
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param ids: List of create menus IDs
@return: name and first_name
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
@ -84,60 +110,93 @@ class res_partner_contact(osv.osv):
addr += (r.get('first_name', '') or '')
res.append((r['id'], addr))
return res
res_partner_contact()
class res_partner_address(osv.osv):
def search(self, cr, user, args, offset=0, limit=None, order=None,
context=None, count=False):
""" search parnter address
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@param offset: The Number of Results to Pass
@param limit: The Number of Results to Return
@param context: A standard dictionary for contextual values
"""
if context and context.has_key('address_partner_id' ) and context['address_partner_id']:
args.append(('partner_id', '=', context['address_partner_id']))
return super(res_partner_address, self).search(cr, user, args, offset, limit, order, context, count)
#overriding of the name_get defined in base in order to remove the old contact name
def name_get(self, cr, user, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param ids: List of partner addresss IDs
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
for r in self.read(cr, user, ids, ['zip','city','partner_id', 'street']):
for r in self.read(cr, user, ids, ['zip', 'city', 'partner_id', 'street']):
if context.get('contact_display', 'contact')=='partner' and r['partner_id']:
res.append((r['id'], r['partner_id'][1]))
else:
addr = str('')
addr += "%s %s %s" % ( r.get('street', '') or '', r.get('zip', '') or '', r.get('city', '') or '' )
addr += "%s %s %s" % (r.get('street', '') or '', r.get('zip', '') \
or '', r.get('city', '') or '')
res.append((r['id'], addr.strip() or '/'))
return res
_name = 'res.partner.address'
_inherit='res.partner.address'
_inherit = 'res.partner.address'
_description ='Partner Address'
_columns = {
'job_id':fields.related('job_ids','contact_id','job_id',type='many2one', relation='res.partner.job', string='Main Job'),
'job_ids':fields.one2many('res.partner.job', 'address_id', 'Contacts'),
'job_id': fields.related('job_ids','contact_id','job_id',type='many2one',\
relation='res.partner.job', string='Main Job'),
'job_ids': fields.one2many('res.partner.job', 'address_id', 'Contacts'),
}
res_partner_address()
class res_partner_job(osv.osv):
# def init(self, cr):
# address_obj = self.pool.get('res.partner.address')
# contact_obj = self.pool.get('res.partner.contact')
# address_ids = address_obj.search(cr, 1, [])
# for address in address_obj.browse(cr, 1, address_ids):
# contact_id = contact_obj.search(cr, 1, [('name','=', address.name)])
# if contact_id:
# contact_id = contact_id[0]
# self.create(cr, 1, {'address_id': address.id, 'contact_id': contact_id})
def name_get(self, cr, uid, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user,
@param ids: List of partner addresss IDs
@param context: A standard dictionary for contextual values
"""
if not len(ids):
return []
res = []
for r in self.browse(cr, uid, ids):
funct = r.function_id and (", " + r.function_id.name) or ""
res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, [r.contact_id.id])[0][1] + funct))
res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, \
[r.contact_id.id])[0][1] + funct))
return res
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
""" search parnter job
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param user: the current user
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@param offset: The Number of Results to Pass
@param limit: The Number of Results to Return
@param context: A standard dictionary for contextual values
"""
job_ids = []
for arg in args:
if arg[0] == 'address_id':
@ -147,12 +206,14 @@ class res_partner_job(osv.osv):
contact_obj = self.pool.get('res.partner.contact')
search_arg = ['|', ('first_name', 'ilike', arg[2]), ('name', 'ilike', arg[2])]
contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, limit=limit, order=order, context=context, count=count)
contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, \
limit=limit, order=order, context=context, count=count)
contacts = contact_obj.browse(cr, user, contact_ids, context=context)
for contact in contacts:
job_ids.extend([item.id for item in contact.job_ids])
res = super(res_partner_job,self).search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count)
res = super(res_partner_job,self).search(cr, user, args, offset=offset,\
limit=limit, order=order, context=context, count=count)
if job_ids:
res = list(set(res + job_ids))
@ -161,27 +222,36 @@ class res_partner_job(osv.osv):
_name = 'res.partner.job'
_description ='Contact Partner Function'
_order = 'sequence_contact'
_columns = {
'name': fields.related('address_id','partner_id', type='many2one', relation='res.partner', string='Partner', help="You may enter Address first,Partner will be linked automatically if any."),
'address_id':fields.many2one('res.partner.address','Address', help='Address which is linked to the Partner'),
'contact_id':fields.many2one('res.partner.contact','Contact', required=True, ondelete='cascade'),
'function_id': fields.many2one('res.partner.function','Partner Function', help="Function of this contact with this partner"),
'sequence_contact':fields.integer('Contact Seq.',help='Order of importance of this address in the list of addresses of the linked contact'),
'sequence_partner':fields.integer('Partner Seq.',help='Order of importance of this job title in the list of job title of the linked partner'),
'name': fields.related('address_id','partner_id', type='many2one',\
relation='res.partner', string='Partner', help="You may\
enter Address first,Partner will be linked automatically if any."),
'address_id': fields.many2one('res.partner.address','Address', \
help='Address which is linked to the Partner'),
'contact_id': fields.many2one('res.partner.contact','Contact', required=True, ondelete='cascade'),
'function_id': fields.many2one('res.partner.function','Partner Function', \
help="Function of this contact with this partner"),
'sequence_contact': fields.integer('Contact Seq.',help='Order of\
importance of this address in the list of addresses of the linked contact'),
'sequence_partner': fields.integer('Partner Seq.',help='Order of importance\
of this job title in the list of job title of the linked partner'),
'email': fields.char('E-Mail', size=240, help="Job E-Mail"),
'phone': fields.char('Phone', size=64, help="Job Phone no."),
'fax': fields.char('Fax', size=64, help="Job FAX no."),
'extension': fields.char('Extension', size=64, help='Internal/External extension phone number'),
'other': fields.char('Other', size=64, help='Additional phone field'),
'date_start' : fields.date('Date Start',help="Start date of job(Joining Date)"),
'date_stop' : fields.date('Date Stop', help="Last date of job"),
'state' : fields.selection([('past', 'Past'),('current', 'Current')], 'State', required=True, help="Status of Address"),
'date_start': fields.date('Date Start',help="Start date of job(Joining Date)"),
'date_stop': fields.date('Date Stop', help="Last date of job"),
'state': fields.selection([('past', 'Past'),('current', 'Current')], \
'State', required=True, help="Status of Address"),
}
_defaults = {
'sequence_contact' : lambda *a: 0,
'state' : lambda *a: 'current',
'state': lambda *a: 'current',
}
res_partner_job()

View File

@ -2,7 +2,8 @@
<openerp>
<data>
<!-- Views for Contacts -->
<!-- Views for Contacts Tree View -->
<record model="ir.ui.view" id="view_partner_contact_tree">
<field name="name">res.partner.contact.tree</field>
<field name="model">res.partner.contact</field>
@ -20,6 +21,8 @@
</field>
</record>
<!-- Views for Contacts Form View -->
<record model="ir.ui.view" id="view_partner_contact_form">
<field name="name">res.partner.contact.form</field>
<field name="model">res.partner.contact</field>
@ -93,6 +96,8 @@
</field>
</record>
<!-- Views for Contacts Search View -->
<record model="ir.ui.view" id="view_partner_contact_search">
<field name="name">res.partner.contact.search</field>
<field name="model">res.partner.contact</field>
@ -106,6 +111,8 @@
</field>
</record>
<!-- Views for Contacts Action -->
<record model="ir.actions.act_window" id="action_partner_contact_form">
<field name="name">Contacts</field>
<field name="res_model">res.partner.contact</field>
@ -116,7 +123,7 @@
</record>
<menuitem name="Contacts" id="menu_partner_contact_form" action="action_partner_contact_form" parent = "base.menu_address_book" sequence="2"/>
<!-- Views for Partners -->
<!-- Views for Partners Form View -->
<record model="ir.ui.view" id="view_partner_form_inherit">
<field name="name">Partner form inherited</field>
@ -186,6 +193,7 @@
<!-- don't display the categories, since it is displayed in an other tab-->
<record model="ir.ui.view" id="view_partner_form_inherit2">
<field name="name">res.partner.form</field>
<field name="type">form</field>
@ -198,6 +206,7 @@
</record>
<!-- don't display the categories label-->
<record model="ir.ui.view" id="view_partner_form_inherit2bis">
<field name="name">res.partner.form</field>
<field name="type">form</field>
@ -225,6 +234,7 @@
<!-- Views for Addresses -->
<record model="ir.ui.view" id="view_partner_address_tree_inherited1">
<field name="name">Partner addresses inherited</field>
<field name="model">res.partner.address</field>
@ -306,39 +316,6 @@
</field>
</record>
<!--
<record id='view_partner_address_form_inherited5' model='ir.ui.view'>
<field name='name'>res.partner.address.form.inherited5</field>
<field name='model'>res.partner.address</field>
<field name="inherit_id" ref="base.view_partner_address_form1"/>
<field name='type'>form</field>
<field name='arch' type='xml'>
<group string="Communication" colspan="2" col="2" position="after">
<newline/>
<field name="job_ids" mode="tree,form" colspan="4">
<tree string="Contacts" editable="top">
<field name="contact_id"/>
<field name="function_id"/>
<field name="phone"/>
<field name="fax"/>
<field name="extension"/>
<field name="email"/>
</tree>
<form string="Contacts">
<field name="contact_id"/>
<field name="function_id"/>
<field name="phone"/>
<field name="fax"/>
<field name="extension"/>
<field name="email" widget="email"/>
</form>
</field>
</group>
</field>
</record>
-->
<record id="view_res_partner_address_filter" model="ir.ui.view">
<field name="name">res.partner.address.select</field>
<field name="model">res.partner.address</field>
@ -351,8 +328,8 @@
</field>
</record>
<!-- Views for partner job Tree view -->
<!-- Views for res.partner.job -->
<record model="ir.ui.view" id="view_partner_job_tree">
<field name="name">res.partner.job.tree</field>
<field name="model">res.partner.job</field>
@ -372,6 +349,8 @@
</field>
</record>
<!-- Views for partner job Form view -->
<record model="ir.ui.view" id="view_partner_job_form">
<field name="name">res.partner.job.form</field>
<field name="model">res.partner.job</field>
@ -400,19 +379,14 @@
</record>
<!-- Menuitem for res.partner.job -->
<record model="ir.actions.act_window" id="action_res_partner_job">
<field name="name">Contact's Jobs</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.job</field>
<field name="view_type">form</field>
</record>
<!-- <menuitem-->
<!-- name="Partners/Contact's Jobs"-->
<!-- action="action_res_partner_job"-->
<!-- id="menu_action_res_partner_job"-->
<!-- groups="base.group_extended"/>-->
<!-- Act window defining a shorcut on partners to open all his contacts -->
<act_window domain="[('name', '=', active_id)]"
id="act_res_partner_jobs" name="Partner Contacts"
res_model="res.partner.job"
@ -420,6 +394,7 @@
/>
<!-- Act window defining a shorcut on partner address to open all his jobs -->
<act_window
id="act_res_partner_jobs" name="Open Jobs"
domain="[('address_id', '=', active_id)]"
@ -427,9 +402,13 @@
src_model="res.partner.address"
/>
<menuitem icon="terp-purchase" id="base.menu_purchase_root" name="Procurement Management" sequence="7"/>
<menuitem id="base.menu_procurement_management_supplier" name="Suppliers"
parent="base.menu_purchase_root" sequence="3"/>
<menuitem name="Contacts" id="menu_partner_contact_supplier_form" action="action_partner_contact_form" parent = "base.menu_procurement_management_supplier" sequence="2"/>
<menuitem icon="terp-purchase" id="base.menu_purchase_root"
name="Procurement Management" sequence="7" />
<menuitem id="base.menu_procurement_management_supplier"
name="Suppliers" parent="base.menu_purchase_root" sequence="3" />
<menuitem name="Contacts" id="menu_partner_contact_supplier_form"
action="action_partner_contact_form"
parent="base.menu_procurement_management_supplier" sequence="2" />
</data>
</openerp>

View File

@ -37,6 +37,8 @@ the "Dashboard" menu.
'init_xml': [],
'update_xml': [
'security/ir.model.access.csv',
'wizard/report_menu_create_view.xml',
'wizard/report_open_view.xml',
'base_report_creator_wizard.xml',
'base_report_creator_view.xml'
],

View File

@ -19,35 +19,13 @@
#
##############################################################################
import string
import time
import tools
from osv import fields,osv,orm
from osv import fields, osv
from tools.translate import _
#class ir_model_fields(osv.osv):
# _inherit = 'ir.model.fields'
# def _get_models(self, cr, uid, model_name, level=1):
# if not level:
# return []
# result = [model_name]
# print model_name
# for field,data in self.pool.get(model_name).fields_get(cr, uid).items():
# if data.get('relation', False):
# result += self._get_models(cr, uid, data['relation'], level-1)
# return result
# def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None):
# if context and ('model_id' in context):
# model_name = self.pool.get("ir.model").browse(cr, uid, context['model_id'], context).model
# models = self._get_models(cr, uid, model_name, context.get('model_level',2))
# models = map(lambda x: self.pool.get('ir.model').search(cr, uid, [('model','=',x)])[0], models)
# args.append(('model_id','in',models))
# print args
# return super(ir_model_fields, self).search(cr, uid, args, offset, limit, order, context)
#ir_model_fields()
class report_creator(osv.osv):
"""
Report Creator
"""
_name = "base_report_creator.report"
_description = "Report"
model_set_id = False
@ -55,45 +33,74 @@ class report_creator(osv.osv):
# Should request only used fields
#
def export_data(self, cr, uid, ids, fields_to_export, context=None):
if not context:
context = {}
data_l = self.read(cr, uid, ids, ['sql_query'], context)
final_datas =[]
final_datas = []
#start Loop
for i in data_l:
datas = []
for key,value in i.items():
for key, value in i.items():
if key not in fields_to_export:
continue
if isinstance(value,tuple):
if isinstance(value, tuple):
datas.append(str(value[1]))
else:
datas.append(str(value))
final_datas += [datas]
return {'datas':final_datas}
#End Loop
return {'datas': final_datas}
def fields_get(self, cr, user, fields=None, context=None):
"""
Get Fields.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param Fields: List of field of customer reports form.
@return: Dictionary of Fields
"""
if not context:
context = {}
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).fields_get(cr, user, fields, context)
report = self.browse(cr, user, context['report_id'])
models = {}
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
fields = {}
i = 0
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i+=1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
return fields
if data:
report = self.browse(cr, user, data)
models = {}
#Start Loop
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
#End Loop
fields = {}
i = 0
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i += 1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
return fields
#
# Should Call self.fields_get !
#
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
"""
Overrides orm field_view_get.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@return: Dictionary of Fields, arch and toolbar.
"""
if not context:
context = {}
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
report = self.browse(cr, user, context['report_id'])
report = self.browse(cr, user, data)
models = {}
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
@ -102,33 +109,33 @@ class report_creator(osv.osv):
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
i+=1
i += 1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
arch = '<?xml version="1.0" encoding="utf-8"?>\n'
if view_type=='graph':
arch +='<graph string="%s" type="%s" orientation="%s">' % (report.name, report.view_graph_type,report.view_graph_orientation)
if view_type == 'graph':
arch += '<graph string="%s" type="%s" orientation="%s">' % (report.name, report.view_graph_type, report.view_graph_orientation)
for val in ('x','y'):
i = 0
for f in report.field_ids:
if f.graph_mode==val:
if f.graph_mode == val:
if f.field_id.model:
arch += '<field name="%s" select="1"/>' % ('field'+str(i),)
i+=1
i += 1
else:
arch += '<field name="%s" select="1"/>' % ('column_count',)
elif view_type=='calendar':
required_types = ['date_start','date_delay','color']
set_dict = {'view_type':view_type,'string':report.name}
elif view_type == 'calendar':
required_types = ['date_start', 'date_delay', 'color']
set_dict = {'view_type':view_type, 'string':report.name}
temp_list = []
i=0
i = 0
for f in report.field_ids:
if f.calendar_mode and f.calendar_mode in required_types:
if f.field_id.model:
field_cal = 'field'+str(i)
i+=1
i += 1
else:
field_cal = 'column_count'
set_dict[f.calendar_mode] = field_cal
@ -136,20 +143,20 @@ class report_creator(osv.osv):
else:
if f.field_id.model:
temp_list.append('''<field name="%(name)s" select="1"/>''' % {'name':'field'+str(i)})
i+=1
temp_list.append('''<field name = "%(name)s" select = "1"/>''' % {'name': 'field' + str(i)})
i += 1
else:
temp_list.append('''<field name="%(name)s" select="1"/>''' % {'name':'column_count'})
arch += '''<%(view_type)s string="%(string)s" date_start="%(date_start)s" ''' %set_dict
if set_dict.get('date_delay',False):
arch +=''' date_delay="%(date_delay)s" '''%set_dict
arch += '''<% (view_type)s string = "%(string)s" date_start = "%(date_start)s" ''' % set_dict
if set_dict.get('date_delay', False):
arch += ''' date_delay = "%(date_delay)s" ''' % set_dict
if set_dict.get('date_stop',False):
arch +=''' date_stop="%(date_stop)s" '''%set_dict
if set_dict.get('date_stop', False):
arch += ''' date_stop="%(date_stop)s" '''%set_dict
if set_dict.get('color',False):
arch +=''' color="%(color)s"'''%set_dict
if set_dict.get('color', False):
arch += ''' color="%(color)s"'''%set_dict
arch += '''>'''
arch += ''.join(temp_list)
else:
@ -157,8 +164,8 @@ class report_creator(osv.osv):
i = 0
for f in report.field_ids:
if f.field_id.model:
arch += '<field name="%s" select="1"/>' % ('field'+str(i),)
i+=1
arch += '<field name="%s" select="1"/>' % ('field' + str(i),)
i += 1
else:
arch += '<field name="%s" select="1"/>' % ('column_count',)
arch += '</%s>' % (view_type,)
@ -171,70 +178,90 @@ class report_creator(osv.osv):
'action': [],
'relate': []
}
return result
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
def read(self, cr, user, ids, fields = None, context = None, load = '_classic_read'):
"""
overrides orm Read method.Read List of fields for report creator.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param ids: List of report creator's id.
@param fields: List of fields.
@return: List of Dictionary of form [{name_of_the_field: value, ...}, ...]
"""
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).read(cr, user, ids, fields, context, load)
ctx = context or {}
wp = ''
if self.model_set_id:
wp = [self._id_get(cr, user, context['report_id'], context)+(' in (%s)' % (','.join(map(lambda x: "'"+str(x)+"'",ids))))]
report = self._sql_query_get(cr, user, [context['report_id']], 'sql_query', None, ctx, where_plus = wp)
sql_query = report[context['report_id']]
cr.execute(sql_query)
res = cr.dictfetchall()
fields_get = self.fields_get(cr,user,None,context)
for r in res:
for k in r:
r[k] = r[k] or False
field_dict = fields_get.get(k)
field_type = field_dict and field_dict.get('type',False) or False
if field_type and field_type == 'many2one':
if r[k]==False:
continue
related_name = self.pool.get(field_dict.get('relation')).name_get(cr,user,[r[k]],context)[0]
r[k] = related_name
return res
if data:
if self.model_set_id:
wp = [self._id_get(cr, user, data, context) + (' in (%s)' % (','.join(map(lambda x: "'" + str(x) + "'", ids))))]
report = self._sql_query_get(cr, user, [data], 'sql_query', None, ctx, where_plus = wp)
sql_query = report[data]
cr.execute(sql_query)
res = cr.dictfetchall()
fields_get = self.fields_get(cr, user, None, context)
for r in res:
for k in r:
r[k] = r[k] or False
field_dict = fields_get.get(k)
field_type = field_dict and field_dict.get('type', False) or False
if field_type and field_type == 'many2one':
if r[k] == False:
continue
related_name = self.pool.get(field_dict.get('relation')).name_get(cr, user, [r[k]], context)[0]
r[k] = related_name
return res
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
"""
overrides orm search method.
@param cr: the current row, from the database cursor,
@param user: the current users ID for security checks,
@param args: list of tuples of form [(name_of_the_field, operator, value), ...].
@return: List of id
"""
context_id = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
return super(report_creator, self).search(cr, user, args, offset, limit, order, context, count)
report = self.browse(cr, user, context['report_id'])
i = 0
fields = {}
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = (f.field_id.model, f.field_id.name)
i+=1
else:
fields['column_count'] = (False, 'Count')
newargs = []
newargs2 = []
for a in args:
if fields[a[0]][0]:
res = self.pool.get(fields[a[0]][0])._where_calc(cr, user, [[fields[a[0]][1],a[1],a[2]]], active_test=False, context=context)
newargs+=res[0]
newargs2+=res[1]
else:
newargs += [("count(*) " + a[1] +" " + str(a[2]))]
ctx = context or {}
ctx['getid'] = True
report = self._sql_query_get(cr, user, [context['report_id']], 'sql_query', None, ctx, where_plus=newargs, limit=limit, offset=offset)
query = report[context['report_id']]
cr.execute(query, newargs2)
result = cr.fetchall()
return map(lambda x: x[0], result)
if context_id:
report = self.browse(cr, user, context_id)
i = 0
fields = {}
for f in report.field_ids:
if f.field_id.model:
fields['field'+str(i)] = (f.field_id.model, f.field_id.name)
i += 1
else:
fields['column_count'] = (False, 'Count')
newargs = []
newargs2 = []
for a in args:
if fields[a[0]][0]:
res = self.pool.get(fields[a[0]][0])._where_calc(cr, user, [[fields[a[0]][1], a[1], a[2]]], active_test = False, context = context)
newargs += res[0]
newargs2 += res[1]
else:
newargs += [("count(*) " + a[1] +" " + str(a[2]))]
ctx = context or {}
ctx['getid'] = True
report = self._sql_query_get(cr, user, [context_id], 'sql_query', None, ctx, where_plus = newargs, limit=limit, offset=offset)
query = report[context_id]
cr.execute(query, newargs2)
result = cr.fetchall()
return map(lambda x: x[0], result)
def _path_get(self,cr, uid, models, filter_ids=[]):
# ret_str = """ sale_order_line
# left join sale_order on (sale_order_line.order_id=sale_order.id)
# left join res_partner on (res_partner.id=sale_order.partner_id)"""
# where_list = []
# for filter_id in filter_ids:
# where_list.append(filter_id.expression)
# if where_list:
# ret_str+="\nwhere\n\t"+" and\n\t".join(where_list)
def _path_get(self, cr, uid, models, filter_ids=[]):
"""
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param models: List of object.
"""
self.model_set_id = False
model_dict = {}
from_list = []
@ -242,7 +269,7 @@ class report_creator(osv.osv):
filter_list = []
for model in models:
model_dict[model.model] = self.pool.get(model.model)._table
model_list = model_dict.keys()
reference_model_dict = {}
for model in model_dict:
@ -250,7 +277,7 @@ class report_creator(osv.osv):
rest_list = model_dict.keys()
rest_list.remove(model)
model_pool = self.pool.get(model)
fields_get = model_pool.fields_get(cr,uid)
fields_get = model_pool.fields_get(cr, uid)
model_columns = {}
def _get_inherit_fields(obj):
@ -259,24 +286,24 @@ class report_creator(osv.osv):
model_columns.update(pool_model._columns)
#Adding the columns of its _inherits
for record in pool_model._inherits.keys():
_get_inherit_fields(record)
_get_inherit_fields(record)
_get_inherit_fields(model)
fields_filter = dict(filter(lambda x:x[1].get('relation',False)
fields_filter = dict(filter(lambda x:x[1].get('relation', False)
and x[1].get('relation') in rest_list
and x[1].get('type')=='many2one'
and not (isinstance(model_columns[x[0]],fields.function) or isinstance(model_columns[x[0]],fields.related)), fields_get.items()))
and x[1].get('type') == 'many2one'
and not (isinstance(model_columns[x[0]], fields.function) or isinstance(model_columns[x[0]], fields.related)), fields_get.items()))
if fields_filter:
model in model_list and model_list.remove(model)
model_count = reference_model_dict.get(model,False)
model_count = reference_model_dict.get(model, False)
if model_count:
reference_model_dict[model] = model_count +1
else:
reference_model_dict[model] = 1
for k,v in fields_filter.items():
for k, v in fields_filter.items():
v.get('relation') in model_list and model_list.remove(v.get('relation'))
relation_count = reference_model_dict.get(v.get('relation'),False)
relation_count = reference_model_dict.get(v.get('relation'), False)
if relation_count:
reference_model_dict[v.get('relation')] = relation_count+1
else:
@ -288,9 +315,9 @@ class report_creator(osv.osv):
if reference_model_dict:
self.model_set_id = model_dict.get(reference_model_dict.keys()[reference_model_dict.values().index(min(reference_model_dict.values()))])
if model_list and not len(model_dict.keys()) == 1:
raise osv.except_osv(_('No Related Models!!'),_('These is/are model(s) (%s) in selection which is/are not related to any other model') % ','.join(model_list))
raise osv.except_osv(_('No Related Models!!'), _('These is/are model(s) (%s) in selection which is/are not related to any other model') % ','.join(model_list))
if filter_ids and where_list<>[]:
if filter_ids and where_list <> []:
filter_list.append(' and ')
filter_list.append(' ')
@ -305,25 +332,32 @@ class report_creator(osv.osv):
else:
ret_str = ",\n".join(from_list)
if where_list:
where_list = list(set(where_list))
ret_str+="\n where \n"+" and\n".join(where_list)
ret_str += "\n where \n"+" and\n".join(where_list)
ret_str = ret_str.strip()
if filter_list:
ret_str +="\n".join(filter_list)
ret_str += "\n".join(filter_list)
if ret_str.endswith('and'):
ret_str = ret_str[0:len(ret_str)-3]
if ret_str.endswith('or'):
ret_str = ret_str[0:len(ret_str)-2]
ret_str = ret_str.strip()
return ret_str % {'uid' : uid}
return ret_str % {'uid': uid}
def _id_get(self, cr, uid, id, context):
"""
Get Model id
"""
# return 'min(sale_order_line.id)'
return self.model_set_id and 'min('+self.model_set_id+'.id)'
def _sql_query_get(self, cr, uid, ids, prop, unknow_none, context, where_plus=[], limit=None, offset=None):
"""
Get sql query which return on sql_query field.
@return: Dictionary of sql query.
"""
result = {}
for obj in self.browse(cr, uid, ids):
fields = []
@ -332,7 +366,7 @@ class report_creator(osv.osv):
for f in obj.field_ids:
# Allowing to use count(*)
if not f.field_id.model and f.group_method == 'count':
fields.insert(0,('count(*) as column_count'))
fields.insert(0, ('count(*) as column_count'))
continue
t = self.pool.get(f.field_id.model_id.model)._table
if f.group_method == 'group':
@ -340,11 +374,11 @@ class report_creator(osv.osv):
else:
fields.append('\t'+f.group_method+'('+t+'.'+f.field_id.name+')'+' as field'+str(i))
groupby.append(t+'.'+f.field_id.name)
i+=1
i += 1
models = self._path_get(cr, uid, obj.model_ids, obj.filter_ids)
check = self._id_get(cr, uid, ids[0], context)
if check<>False:
fields.insert(0,(check+' as id'))
fields.insert(0, (check + ' as id'))
if models:
result[obj.id] = """select
@ -365,36 +399,58 @@ class report_creator(osv.osv):
return result
_columns = {
'name': fields.char('Report Name',size=64, required=True),
'type': fields.selection([('list','Rows And Columns Report'),], 'Report Type',required=True),#('sum','Summation Report')
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the report without removing it."),
'view_type1': fields.selection([('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'First View', required=True),
'view_type2': fields.selection([('','/'),('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'Second View'),
'view_type3': fields.selection([('','/'),('form','Form'),('tree','Tree'),('graph','Graph'),('calendar','Calendar')], 'Third View'),
'view_graph_type': fields.selection([('pie','Pie Chart'),('bar','Bar Chart')], 'Graph Type', required=True),
'view_graph_orientation': fields.selection([('horz','Horizontal'),('vert','Vertical')], 'Graph Orientation', required=True),
'model_ids': fields.many2many('ir.model', 'base_report_creator_report_model_rel', 'report_id','model_id', 'Reported Objects'),
'field_ids': fields.one2many('base_report_creator.report.fields', 'report_id', 'Fields to Display'),
'filter_ids': fields.one2many('base_report_creator.report.filter', 'report_id', 'Filters'),
'name': fields.char('Report Name', size=64, required=True),
'type': fields.selection([('list', 'Rows And Columns Report'), ], 'Report Type', required=True), #('sum','Summation Report')
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the report without removing it."),
'view_type1': fields.selection([('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'First View', required=True),
'view_type2': fields.selection([('', '/'),
('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'Second View'),
'view_type3': fields.selection([('', '/'),
('form', 'Form'),
('tree', 'Tree'),
('graph', 'Graph'),
('calendar', 'Calendar')], 'Third View'),
'view_graph_type': fields.selection([('pie', 'Pie Chart'),
('bar', 'Bar Chart')], 'Graph Type', required=True),
'view_graph_orientation': fields.selection([('horz', 'Horizontal'),
('vert', 'Vertical')], 'Graph Orientation', required=True),
'model_ids': fields.many2many('ir.model', 'base_report_creator_report_model_rel', 'report_id', 'model_id', 'Reported Objects'),
'field_ids': fields.one2many('base_report_creator.report.fields', 'report_id', 'Fields to Display'),
'filter_ids': fields.one2many('base_report_creator.report.filter', 'report_id', 'Filters'),
'state': fields.selection([
('draft','Draft'),
('valid','Valid')],
'State', required=True,
('draft', 'Draft'),
('valid', 'Valid')],
'State', required=True,
help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed custom report. \
\n* The \'Valid\' state is used when user validates the custom report.'),
'sql_query': fields.function(_sql_query_get, method=True, type="text", string='SQL Query', store=True),
'group_ids': fields.many2many('res.groups', 'base_report_creator_group_rel','report_id','group_id','Authorized Groups'),
\n* The \'Valid\' state is used when user validates the custom report.'),
'sql_query': fields.function(_sql_query_get, method=True, type="text", string='SQL Query', store=True),
'group_ids': fields.many2many('res.groups', 'base_report_creator_group_rel', 'report_id', 'group_id', 'Authorized Groups'),
}
_defaults = {
'type': lambda *args: 'list',
'state': lambda *args: 'draft',
'active': lambda *args: True,
'view_type1': lambda *args: 'tree',
'view_type2': lambda *args: 'graph',
'view_graph_type': lambda *args: 'bar',
'view_graph_orientation': lambda *args: 'horz',
'type': lambda *args: 'list',
'state': lambda *args: 'draft',
'active': lambda *args: True,
'view_type1': lambda *args: 'tree',
'view_type2': lambda *args: 'graph',
'view_graph_type': lambda *args: 'bar',
'view_graph_orientation': lambda *args: 'horz',
}
def _function_field(self, cr, uid, ids):
"""
constraints function which specify that
not display field which are not stored in Database.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report creator's id.
@return: True if display field which are stored in database.
or false if display field which are not store in dtabase.
"""
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
for fld in obj.field_ids:
@ -402,13 +458,23 @@ class report_creator(osv.osv):
if not fld.field_id.model and fld.group_method == 'count':
continue
model_column = self.pool.get(fld.field_id.model)._columns[fld.field_id.name]
if (isinstance(model_column,fields.function) or isinstance(model_column,fields.related)) and not model_column.store:
if (isinstance(model_column, fields.function) or isinstance(model_column, fields.related)) and not model_column.store:
return False
return True
def _aggregation_error(self, cr, uid, ids):
aggregate_columns = ('integer','float')
apply_functions = ('sum','min','max','avg','count')
"""
constraints function which specify that
aggregate function to the non calculated field..
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report creator's id.
@return: True if model colume type is in integer or float.
or false model colume type is not in integer or float.
"""
aggregate_columns = ('integer', 'float')
apply_functions = ('sum', 'min', 'max', 'avg', 'count')
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
for fld in obj.field_ids:
@ -425,7 +491,7 @@ class report_creator(osv.osv):
required_types = []
this_objs = self.browse(cr, uid, ids)
for obj in this_objs:
if obj.view_type1=='calendar' or obj.view_type2=='calendar' or obj.view_type3=='calendar':
if obj.view_type1 == 'calendar' or obj.view_type2 == 'calendar' or obj.view_type3 == 'calendar':
for fld in obj.field_ids:
# Allowing to use count(*)
if not fld.field_id.model and fld.group_method == 'count':
@ -442,43 +508,60 @@ class report_creator(osv.osv):
return True
_constraints = [
(_function_field, 'You can not display field which are not stored in Database.', ['field_ids']),
(_aggregation_error, 'You can apply aggregate function to the non calculated field.', ['field_ids']),
(_calander_view_error, "You must have to give calendar view's color,start date and delay.", ['field_ids']),
(_function_field, 'You can not display field which are not stored in Database.', ['field_ids']),
(_aggregation_error, 'You can apply aggregate function to the non calculated field.', ['field_ids']),
(_calander_view_error, "You must have to give calendar view's color,start date and delay.", ['field_ids']),
]
report_creator()
class report_creator_field(osv.osv):
"""
Report Creator Field
"""
_name = "base_report_creator.report.fields"
_description = "Display Fields"
_rec_name = 'field_id'
_order = "sequence,id"
_columns = {
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of fields."),
'field_id': fields.many2one('ir.model.fields', 'Field'),
'report_id': fields.many2one('base_report_creator.report','Report', on_delete='cascade'),
'group_method': fields.selection([('group','Grouped'),('sum','Sum'),('min','Minimum'),('count','Count'),('max','Maximum'),('avg','Average')], 'Grouping Method', required=True),
'graph_mode': fields.selection([('','/'),('x','X Axis'),('y','Y Axis')], 'Graph Mode'),
'calendar_mode': fields.selection([('','/'),('date_start','Starting Date'),('date_end','Ending Date'),('date_delay','Delay'),('date_stop','End Date'),('color','Unique Colors')], 'Calendar Mode'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of fields."),
'field_id': fields.many2one('ir.model.fields', 'Field'),
'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
'group_method': fields.selection([('group', 'Grouped'),
('sum', 'Sum'),
('min', 'Minimum'),
('count', 'Count'),
('max', 'Maximum'),
('avg', 'Average')], 'Grouping Method', required=True),
'graph_mode': fields.selection([('', '/'),
('x', 'X Axis'),
('y', 'Y Axis')], 'Graph Mode'),
'calendar_mode': fields.selection([('', '/'),
('date_start', 'Starting Date'),
('date_end', 'Ending Date'), ('date_delay', 'Delay'), ('date_stop', 'End Date'), ('color', 'Unique Colors')], 'Calendar Mode'),
}
_defaults = {
'group_method': lambda *args: 'group',
'graph_mode': lambda *args: '',
'group_method': lambda *args: 'group',
'graph_mode': lambda *args: '',
}
report_creator_field()
class report_creator_filter(osv.osv):
"""
Report Creator Filter
"""
_name = "base_report_creator.report.filter"
_description = "Report Filters"
_columns = {
'name': fields.char('Filter Name',size=64, required=True),
'expression': fields.text('Value', required=True,help='Provide an expression for the field based on which you want to filter the records.\n e.g. res_partner.id=3'),
'report_id': fields.many2one('base_report_creator.report','Report', on_delete='cascade'),
'condition' : fields.selection([('and','AND'),('or','OR')], 'Condition')
'name': fields.char('Filter Name', size=64, required=True),
'expression': fields.text('Value', required=True, help='Provide an expression for the field based on which you want to filter the records.\n e.g. res_partner.id=3'),
'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
'condition': fields.selection([('and', 'AND'),
('or', 'OR')], 'Condition')
}
_defaults = {
'condition': lambda *args: 'and',
'condition': lambda *args: 'and',
}
report_creator_filter()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,6 +27,9 @@
</tree>
</field>
</record>
<!-- Report Creator form -->
<record model="ir.ui.view" id="base_report_creator_form">
<field name="name">base_report_creator.report.form</field>
<field name="model">base_report_creator.report</field>
@ -38,10 +41,13 @@
<field name="active" select="2"/>
<notebook colspan="4">
<page string="General Configuration">
<field name="model_ids" colspan="4" context="{'model_ids':model_ids}"/>
<field name="model_ids" colspan="4"
context="{'model_ids':model_ids}" />
<separator string="State" colspan="4"/>
<field name="state"/>
<button string="Create Menu" name="%(wizard_menu_create)d" type="action" colspan="2" icon="gtk-justify-fill"/>
<button string="Create Menu"
name="%(action_report_menu_create)d" type="action"
colspan="2" icon="gtk-justify-fill" />
</page><page string="View parameters">
<separator string="Used View" colspan="4"/>
<field name="view_type1"/>
@ -54,12 +60,13 @@
<field name="field_ids" nolabel="1" colspan="4">
<form string="Fields">
<field name="sequence"/>
<field name="field_id" domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []">
<tree string="Field List">
<field name="model_id"/>
<field name="name"/>
<field name="field_description"/>
</tree>
<field name="field_id"
domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []">
<tree string="Field List">
<field name="model_id" />
<field name="name" />
<field name="field_description" />
</tree>
</field>
<field name="group_method"/>
<field name="graph_mode"/>
@ -67,14 +74,18 @@
</form>
<tree editable="bottom" string="Fields to Display">
<field name="sequence"/>
<field name="field_id" domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []" attrs="{'required':[('group_method','!=','count')]}"/>
<field name="field_id"
domain="parent.model_ids and [('model_id','in',parent.model_ids[0][2]),('ttype','&lt;&gt;','many2many'),('ttype','&lt;&gt;','one2many')] or []"
attrs="{'required':[('group_method','!=','count')]}" />
<field name="group_method"/>
<field name="graph_mode"/>
<field name="calendar_mode"/>
</tree>
</field>
</page><page string="Filters on Fields">
<button string="Add filter" name="%(wizard_set_filter_fields)d" type="action" icon="gtk-add"/>
<button string="Add filter"
name="%(wizard_set_filter_fields)d" type="action"
icon="gtk-add" />
<field name="filter_ids" nolabel="1" colspan="4">
<tree string="Filters">
<field name="name"/>
@ -87,8 +98,10 @@
</form>
</field>
<separator string="Legend" colspan="4"/>
<label string="Use %%(uid)s to filter by the connected user" align="0.0"/>
</page><page string="Security">
<label
string="Use %%(uid)s to filter by the connected user"
align="0.0" />
</page><page string="Security">
<separator string="Authorized Groups (empty for all)" colspan="4"/>
<field name="group_ids" colspan="4" nolabel="1"/>
</page><page string="SQL Query">
@ -98,15 +111,20 @@
</form>
</field>
</record>
<!-- Action for Report creator form -->
<record model="ir.actions.act_window" id="base_report_creator_action">
<field name="name">Custom Report</field>
<field name="res_model">base_report_creator.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
name="Custom Reports" id="base.menu_custom_reports"
action="base_report_creator_action" parent="base.reporting_menu" sequence="1"/>
<menuitem
name="Custom Reports" parent="base.next_id_50"
id="menu_base_report_creator_action_config"
@ -122,36 +140,22 @@
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="base_report_creator_action_tree">
<field name="res_model">base_report_creator.report</field>
<field name="view_type">tree</field>
</record>
<menuitem
name="Custom Reports" parent="base.menu_custom_reports"
id="menu_base_report_creator_action"
action="base_report_creator_action_tree"/>
<wizard
id="wizard_report_open"
string="Open Report"
name="base_report_creator.report.open"/>
<record model="ir.values" id="ir_open_report">
<field name="key2" eval="'tree_but_open'"/>
<field name="model" eval="'base_report_creator.report'"/>
<field name="name">Browse Report</field>
<field name="value" eval="'ir.actions.wizard,%d'%wizard_report_open"/>
<field name="object" eval="True"/>
</record>
<record model="ir.values" id="ir_open_report_multi">
<field name="key2" eval="'client_action_multi'"/>
<field name="model" eval="'base_report_creator.report'"/>
<field name="name">Browse Report</field>
<field name="value" eval="'ir.actions.wizard,%d'%wizard_report_open"/>
<field name="object" eval="True"/>
</record>
<act_window id="action_report_open"
key2="client_action_multi" name="Open Report"
res_model="report.open" src_model="base_report_creator.report"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -1,13 +1,8 @@
<?xml version="1.0"?>
<openerp>
<data>
<wizard
id="wizard_set_filter_fields"
string="Set Filter Fields"
name="base_report_creator.report_filter.fields"/>
<wizard
id="wizard_menu_create"
string="Create Menu for Report"
name="base_report_creator.report.menu.create"/>
</data>
<data>
<wizard id="wizard_set_filter_fields" string="Set Filter Fields"
name="base_report_creator.report_filter.fields" />
</data>
</openerp>

View File

@ -1,4 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_base_report_creator_report,base_report_creator.report,model_base_report_creator_report,base.group_system,1,1,1,1
access_base_report_creator_report_fields,base_report_creator.report.fields,model_base_report_creator_report_fields,base.group_system,1,1,1,1
access_base_report_creator_report_filter,base_report_creator.report.filter,model_base_report_creator_report_filter,base.group_system,1,1,1,1
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_base_report_creator_report","base_report_creator.report","model_base_report_creator_report","base.group_system",1,1,1,1
"access_base_report_creator_report_fields","base_report_creator.report.fields","model_base_report_creator_report_fields","base.group_system",1,1,1,1
"access_base_report_creator_report_filter","base_report_creator.report.filter","model_base_report_creator_report_filter","base.group_system",1,1,1,1
"access_report_menu_create","report.menu.create","model_report_menu_create","base.group_system",1,1,1,1
"access_report_open","report.open","model_report_open","base.group_system",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_base_report_creator_report base_report_creator.report model_base_report_creator_report base.group_system 1 1 1 1
3 access_base_report_creator_report_fields base_report_creator.report.fields model_base_report_creator_report_fields base.group_system 1 1 1 1
4 access_base_report_creator_report_filter base_report_creator.report.filter model_base_report_creator_report_filter base.group_system 1 1 1 1
5 access_report_menu_create report.menu.create model_report_menu_create base.group_system 1 1 1 1
6 access_report_open report.open model_report_open base.group_system 1 1 1 1

View File

@ -20,7 +20,7 @@
##############################################################################
import wiz_set_filter_fields
import base_report_creator_open
import report_menu_create
import report_open
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,63 +19,56 @@
#
##############################################################################
from osv import fields, osv
import time
import wizard
import osv
import pooler
section_form = '''<?xml version="1.0"?>
<form string="Create Menu For This Report">
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</form>'''
section_fields = {
'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64},
'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True},
}
def report_menu_create(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('base_report_creator.report').browse(cr, uid, data['id'])
view = board.view_type1
if board.view_type2:
view+=','+board.view_type2
if board.view_type3:
view+=','+board.view_type3
action_id = pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':view,
'context': "{'report_id':%d}" % (board.id,),
'res_model': 'base_report_creator.report'
})
pool.get('ir.ui.menu').create(cr, uid, {
'name': data['form']['menu_name'],
'parent_id': data['form']['menu_parent_id'],
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,'+str(action_id)
}, context)
return {}
class wizard_section_menu_create(wizard.interface):
states = {
'init': {
'actions': [],
'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]}
},
'create_menu': {
'actions': [report_menu_create],
'result': {
'type':'state',
'state':'end'
}
}
}
wizard_section_menu_create('base_report_creator.report.menu.create')
class report_menu_create(osv.osv_memory):
"""
Create Menu
"""
_name = "report.menu.create"
_description = "Menu Create"
_columns = {
'menu_name':fields.char('Menu Name', size=64, required=True),
'menu_parent_id':fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
}
def report_menu_create(self, cr, uid, ids, context=None):
"""
Create Menu.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Report Menu Create's IDs
@return: Dictionary {}.
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
board = self.pool.get('base_report_creator.report').browse(cr, uid, context_id)
view = board.view_type1
if board.view_type2:
view += ',' + board.view_type2
if board.view_type3:
view += ',' + board.view_type3
action_id = self.pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':view,
'context': "{'report_id':%d}" % (board.id,),
'res_model': 'base_report_creator.report'
})
obj_menu = self.pool.get('ir.ui.menu')
#start Loop
for data in self.read(cr, uid, ids):
obj_menu.create(cr, uid, {
'name': data.get('menu_name'),
'parent_id': data.get('menu_parent_id'),
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window, ' + str(action_id)
}, context=context)
return {}
#End Loop
report_menu_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Create Menu For report -->
<record id="view_report_menu_create" model="ir.ui.view">
<field name="name">report.menu.create.form</field>
<field name="model">report.menu.create</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Menu For This Report">
<group colspan="4" >
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-save" string="Create Menu"
name="report_menu_create" type="object" />
</group>
</form>
</field>
</record>
<record id="action_report_menu_create" model="ir.actions.act_window">
<field name="name">Create Menu for Report</field>
<field name="res_model">report.menu.create</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_report_menu_create"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,71 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
class report_open(osv.osv_memory):
"""
Open report
"""
_name = "report.open"
_description = __doc__
def open_report(self, cr, uid, ids, context=None):
"""
This Function opens base creator report view
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of report open's IDs
@param context: A standard dictionary for contextual values
@return : Dictionary value for base creator report form
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context.get('report_id', False):
raise osv.except_osv(_('UserError'), _('No Wizards available for this object!'))
rep = self.pool.get('base_report_creator.report').browse(cr, uid, context_id, context)
view_mode = rep.view_type1
if rep.view_type2:
view_mode += ',' + rep.view_type2
if rep.view_type3:
view_mode += ',' + rep.view_type3
value = {
'name': rep.name,
'view_type': 'form',
'view_mode': view_mode,
'res_model': 'base_report_creator.report',
'context': {'report_id': context_id},
'view_id': False,
'type': 'ir.actions.act_window'
}
return value
report_open()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Report Open wizard -->
<record id="view_report_open" model="ir.ui.view">
<field name="name">report.open.form</field>
<field name="model">report.open</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Report open">
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-open" string="Open Report"
name="open_report" type="object" />
</group>
</form>
</field>
</record>
<record id="action_report_open" model="ir.actions.act_window">
<field name="name">Open Report</field>
<field name="res_model">report.open</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_report_open"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -23,4 +23,3 @@ import board
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,7 +19,6 @@
#
##############################################################################
{
'name': 'Dashboard main module',
'version': '1.0',
@ -27,7 +26,7 @@
'description': """Base module for all dashboards.""",
'author': 'Tiny',
'depends': ['base'],
'update_xml': ['security/ir.model.access.csv', 'board_view.xml'],
'update_xml': ['security/ir.model.access.csv', 'wizard/board_menu_create_view.xml', 'board_view.xml'],
'demo_xml': [],
'installable': True,
'active': False,

View File

@ -19,28 +19,42 @@
#
##############################################################################
from osv import fields, osv
import time
from osv import fields,osv
class board_board(osv.osv):
"""
Board
"""
_name = 'board.board'
_description = "Board"
def create_view(self, cr, uid, ids, context):
board = self.pool.get('board.board').browse(cr, uid, ids, context)
def create_view(self, cr, uid, ids, context=None):
"""
Create view
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board's IDs
@return: arch of xml view.
"""
if not context:
context = {}
board = self.pool.get('board.board').browse(cr, uid, ids, context=context)
left = []
right = []
#start Loop
for line in board.line_ids:
linestr = '<action string="%s" name="%d" colspan="4"' % (line.name, line.action_id.id)
if line.height:
linestr+=(' height="%d"' % (line.height,))
linestr += (' height="%d"' % (line.height, ))
if line.width:
linestr+=(' width="%d"' % (line.width,))
linestr += (' width="%d"' % (line.width, ))
linestr += '/>'
if line.position=='left':
if line.position == 'left':
left.append(linestr)
else:
right.append(linestr)
#End Loop
arch = """<?xml version="1.0"?>
<form string="My Board">
<hpaned>
@ -55,24 +69,45 @@ class board_board(osv.osv):
return arch
def write(self, cr, uid, ids, vals, context={}):
def write(self, cr, uid, ids, vals, context = {}):
"""
Writes values in one or several fields.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board's IDs
@param vals: dictionary with values to update.
dictionary must be with the form: {name_of_the_field: value, ...}.
@return: True
"""
result = super(board_board, self).write(cr, uid, ids, vals, context)
board = self.pool.get('board.board').browse(cr, uid, ids[0])
view = self.create_view(cr, uid, ids[0], context)
id = board.view_id.id
cr.execute("update ir_ui_view set arch=%s where id=%s" , (view, id))
cr.execute("update ir_ui_view set arch=%s where id=%s", (view, id))
return result
def create(self, cr, user, vals, context=None):
"""
create new record.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: dictionary of values for every field.
dictionary must use this form: {name_of_the_field: value, ...}
@return: id of new created record of board.board.
"""
if not context:
context = {}
if not 'name' in vals:
return False
id = super(board_board, self).create(cr, user, vals, context)
view_id = self.pool.get('ir.ui.view').create(cr, user, {
'name': vals['name'],
'model':'board.board',
'priority':16,
'model': 'board.board',
'priority': 16,
'type': 'form',
'arch': self.create_view(cr, user, id, context),
})
@ -81,17 +116,28 @@ class board_board(osv.osv):
return id
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
res = {}
res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None,\
toolbar=False, submenu=False):
"""
Overrides orm field_view_get.
@return: Dictionary of Fields, arch and toolbar.
"""
vids = self.pool.get('ir.ui.view.custom').search(cr, user, [('user_id','=',user), ('ref_id','=',view_id)])
if not context:
context = {}
res = {}
res = super(board_board, self).fields_view_get(cr, user, view_id, view_type,\
context, toolbar=toolbar, submenu=submenu)
vids = self.pool.get('ir.ui.view.custom').search(cr, user,\
[('user_id', '=', user), ('ref_id' ,'=', view_id)])
if vids:
view_id = vids[0]
arch = self.pool.get('ir.ui.view.custom').browse(cr, user, view_id)
res['arch'] = arch.arch
res['toolbar'] = {'print':[],'action':[],'relate':[]}
res['toolbar'] = {'print': [], 'action': [], 'relate': []}
return res
_columns = {
@ -102,44 +148,67 @@ class board_board(osv.osv):
# the following lines added to let the button on dashboard work.
_defaults = {
'name': lambda *args: 'Dashboard'
'name':lambda *args: 'Dashboard'
}
board_board()
class board_line(osv.osv):
"""
Board Line
"""
_name = 'board.board.line'
_description = "Board Line"
_order = 'position,sequence'
_columns = {
'name': fields.char('Title', size=64, required=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of board lines."),
'sequence': fields.integer('Sequence', help="Gives the sequence order\
when displaying a list of board lines."),
'height': fields.integer('Height'),
'width': fields.integer('Width'),
'board_id': fields.many2one('board.board', 'Dashboard', required=True, ondelete='cascade'),
'action_id': fields.many2one('ir.actions.act_window', 'Action', required=True),
'position': fields.selection([('left','Left'),('right','Right')], 'Position', required=True)
'position': fields.selection([('left','Left'),
('right','Right')], 'Position', required=True)
}
_defaults = {
'position': lambda *args: 'left'
}
board_line()
class board_note_type(osv.osv):
"""
Board note Type
"""
_name = 'board.note.type'
_description = "NOte Type"
_columns = {
'name': fields.char('Note Type', size=64, required=True),
}
board_note_type()
def _type_get(self, cr, uid, context={}):
"""
Get by default Note type.
"""
obj = self.pool.get('board.note.type')
ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['name'], context)
res = [(r['name'], r['name']) for r in res]
return res
class board_note(osv.osv):
"""
Board Note
"""
_name = 'board.note'
_description = "Note"
_columns = {
'name': fields.char('Subject', size=128, required=True),
'note': fields.text('Note'),
@ -148,10 +217,10 @@ class board_note(osv.osv):
'type': fields.selection(_type_get, 'Note type', size=64),
}
_defaults = {
'user_id': lambda object,cr,uid,context: uid,
'date': lambda object,cr,uid,context: time.strftime('%Y-%m-%d'),
'user_id': lambda object, cr, uid, context: uid,
'date': lambda object, cr, uid, context: time.strftime('%Y-%m-%d'),
}
board_note()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem icon="terp-graph" id="base.reporting_menu" name="Reporting" sequence="8"/>
<!-- <menuitem icon="terp-graph" id="dashboard" name="Dashboards" sequence="2" parent="base.reporting_menu"/>-->
<menuitem icon="terp-graph" id="base.reporting_menu"
name="Reporting" sequence="8" />
<!--Board Note Tree View -->
<record id="view_board_note_tree" model="ir.ui.view">
<field name="name">board.note.tree</field>
<field name="model">board.note</field>
@ -13,6 +16,9 @@
</tree>
</field>
</record>
<!--Board Note Form View -->
<record id="view_board_note_form" model="ir.ui.view">
<field name="name">board.note.form</field>
<field name="model">board.note</field>
@ -27,6 +33,9 @@
</form>
</field>
</record>
<!-- Action for Publish note Form -->
<record id="action_view_board_note_form" model="ir.actions.act_window">
<field name="name">Publish a note</field>
<field name="res_model">board.note</field>
@ -34,7 +43,7 @@
<field name="view_mode">form,tree</field>
</record>
<wizard id="wizard_board_create_menu" model="board.board" multi="True" name="board.board.menu.create" string="Create Board Menu" />
<!-- Board Tree View -->
<record id="view_board_tree" model="ir.ui.view">
<field name="name">board.board.tree</field>
@ -46,6 +55,9 @@
</tree>
</field>
</record>
<!-- Dashboard Definition Form View -->
<record id="view_board_form" model="ir.ui.view">
<field name="name">board.board.form</field>
<field name="model">board.board</field>
@ -54,7 +66,10 @@
<field name="arch" type="xml">
<form string="Dashboard">
<field name="name" select="1"/>
<button colspan="2" name="%(wizard_board_create_menu)d" string="Create Menu" type="action" icon="gtk-justify-fill"/>
<button colspan="2"
name="%(action_board_menu_create)d"
string="Create Menu" type="action"
icon="gtk-justify-fill" />
<field colspan="4" name="line_ids">
<tree string="Dashboard View">
<field name="name"/>
@ -67,19 +82,24 @@
<field name="sequence"/>
<field name="width"/>
<field name="height"/>
<field name="action_id" domain="[('view_type','!=','tree')]"/>
<field name="action_id"
domain="[('view_type','!=','tree')]" />
<field name="position"/>
</form>
</field>
</form>
</field>
</record>
<!-- Action for DashBoard Definition form -->
<record id="action_view_board_list_form" model="ir.actions.act_window">
<field name="name">Dashboard Definition</field>
<field name="res_model">board.board</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
id="base.next_id_50"
name="Configuration"
@ -90,10 +110,14 @@
action="action_view_board_list_form"
id="menu_view_board_form"
parent="base.next_id_50" sequence="1"/>
<menuitem action="action_view_board_note_form" id="menu_view_board_note_form" parent="base.next_id_50"
sequence="3"
groups="base.group_system" />
<act_window context="{'view': active_id}" id="dashboard_open" multi="True" name="Open Dashboard" res_model="board.board" src_model="board.board"/>
<menuitem action="action_view_board_note_form"
id="menu_view_board_note_form" parent="base.next_id_50"
sequence="3" groups="base.group_system" />
<act_window context="{'view': active_id}" id="dashboard_open"
multi="True" name="Open Dashboard" res_model="board.board"
src_model="board.board" />
</data>
</openerp>

View File

@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_board_board,board.board,model_board_board,base.group_user,1,1,1,1
access_board_board_line,board.board.line,model_board_board_line,base.group_user,1,1,1,1
access_board_note_type,board.note.type,model_board_note_type,base.group_user,1,1,1,1
access_board_note,board.note,model_board_note,base.group_user,1,1,1,1
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_board_board","board.board","model_board_board","base.group_user",1,1,1,1
"access_board_board_line","board.board.line","model_board_board_line","base.group_user",1,1,1,1
"access_board_note_type","board.note.type","model_board_note_type","base.group_user",1,1,1,1
"access_board_note","board.note","model_board_note","base.group_user",1,1,1,1
"access_board_menu_create","board.menu.create","model_board_menu_create","base.group_user",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_board_board board.board model_board_board base.group_user 1 1 1 1
3 access_board_board_line board.board.line model_board_board_line base.group_user 1 1 1 1
4 access_board_note_type board.note.type model_board_note_type base.group_user 1 1 1 1
5 access_board_note board.note model_board_note base.group_user 1 1 1 1
6 access_board_menu_create board.menu.create model_board_menu_create base.group_user 1 1 1 1

View File

@ -19,7 +19,7 @@
#
##############################################################################
import board_wizard
import board_menu_create
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,94 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
class board_menu_create(osv.osv_memory):
"""
Create Menu
"""
def view_init(self, cr, uid, fields, context=None):
"""
This function checks for precondition before wizard executes
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
check dashboard view on menu name field.
@return: False
"""
data = context and context.get('active_id', False) or False
if data:
board = self.pool.get('board.board').browse(cr, uid, data)
if not board.line_ids:
raise osv.except_osv(_('User Error!'),
_('Please Insert Dashboard View(s) !'))
return False
def board_menu_create(self, cr, uid, ids, context=None):
"""
Create Menu.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Board Menu Create's IDs
@return: Dictionary {}.
"""
if not context:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
board = self.pool.get('board.board').browse(cr, uid, context_id)
action_id = self.pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type': 'form',
'view_mode': 'form',
'res_model': 'board.board',
'view_id': board.view_id.id,
})
obj_menu = self.pool.get('ir.ui.menu')
#start Loop
for data in self.read(cr, uid, ids):
obj_menu.create(cr, uid, {
'name': data.get('menu_name'),
'parent_id': data.get('menu_parent_id'),
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,' + str(action_id)
}, context=context)
#End Loop
return {}
_name = "board.menu.create"
_description = "Menu Create"
_columns = {
'menu_name': fields.char('Menu Name', size=64, required=True),
'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
}
board_menu_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--Board menu create wizard -->
<record id="view_board_menu_create" model="ir.ui.view">
<field name="name">board.menu.create.form</field>
<field name="model">board.menu.create</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Menu For Dashboard">
<group colspan="4" >
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel"
string="Cancel" />
<button icon="gtk-save" string="Create Menu"
name="board_menu_create" type="object" />
</group>
</form>
</field>
</record>
<!-- Action for Board Menu create wizard. -->
<record id="action_board_menu_create" model="ir.actions.act_window">
<field name="name">Create Board Menu</field>
<field name="res_model">board.menu.create</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_board_menu_create"/>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -1,86 +0,0 @@
# -*- 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
import wizard
import osv
import pooler
from tools.translate import _
section_form = '''<?xml version="1.0"?>
<form string="Create Menu For Dashboard">
<separator string="Menu Information" colspan="4"/>
<field name="menu_name"/>
<field name="menu_parent_id"/>
</form>'''
section_fields = {
'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64},
'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True},
}
def check_views(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('board.board').browse(cr, uid, data['id'])
if not board.line_ids:
raise wizard.except_wizard(_('User Error!'),_('Please Insert Dashboard View(s) !'))
return data['form']
def board_menu_create(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
board = pool.get('board.board').browse(cr, uid, data['id'])
action_id = pool.get('ir.actions.act_window').create(cr, uid, {
'name': board.name,
'view_type':'form',
'view_mode':'form',
'res_model': 'board.board',
'view_id': board.view_id.id,
})
pool.get('ir.ui.menu').create(cr, uid, {
'name': data['form']['menu_name'],
'parent_id': data['form']['menu_parent_id'],
'icon': 'STOCK_SELECT_COLOR',
'action': 'ir.actions.act_window,'+str(action_id)
}, context)
return {}
class wizard_section_menu_create(wizard.interface):
states = {
'init': {
'actions': [check_views],
'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]}
},
'create_menu': {
'actions': [board_menu_create],
'result': {
'type':'state',
'state':'end'
}
}
}
wizard_section_menu_create('board.board.menu.create')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,6 +27,7 @@ __author__ = AUTHOR
from BaseHTTPServer import BaseHTTPRequestHandler
import os
class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
"""
Buffering HTTP Request Handler
@ -47,12 +48,12 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
If you override the handle() method remember to call
this (see below)
"""
self.__buffer=""
self.__outfp=os.tmpfile()
self.__buffer = ""
self.__outfp = os.tmpfile()
def _append(self,s):
""" append a string to the buffer """
self.__buffer=self.__buffer+s
self.__buffer = self.__buffer+s
def _flush(self):
""" flush the buffer to wfile """
@ -60,7 +61,7 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
self.__outfp.write(self.__buffer)
self.__outfp.flush()
self.wfile.flush()
self.__buffer=""
self.__buffer = ""
def handle(self):
""" Handle a HTTP request """
@ -97,3 +98,4 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
protocol_version="HTTP/1.1"
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
"""
Python WebDAV Server.
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
@ -23,12 +25,20 @@ Subclass this class and specify an IFACE_CLASS. See example.
"""
DEBUG=None
DEBUG = None
from utils import VERSION, AUTHOR
__version__ = VERSION
__author__ = AUTHOR
from propfind import PROPFIND
from delete import DELETE
from davcopy import COPY
from davmove import MOVE
from string import atoi, split
from status import STATUS_CODES
from errors import *
import os
import sys
@ -39,16 +49,6 @@ import posixpath
import base64
import urlparse
import urllib
from propfind import PROPFIND
from delete import DELETE
from davcopy import COPY
from davmove import MOVE
from string import atoi,split
from status import STATUS_CODES
from errors import *
import BaseHTTPServer
class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
@ -74,21 +74,21 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def _log(self, message):
pass
def _append(self,s):
def _append(self, s):
""" write the string to wfile """
self.wfile.write(s)
def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers=None):
def send_body(self, DATA, code, msg, desc, ctype='application/octet-stream', headers=None):
""" send a body in one part """
if not headers:
headers = {}
self.send_response(code,message=msg)
self.send_response(code, message=msg)
self.send_header("Connection", "keep-alive")
self.send_header("Accept-Ranges", "bytes")
for a,v in headers.items():
self.send_header(a,v)
for a, v in headers.items():
self.send_header(a, v)
if DATA:
self.send_header("Content-Length", str(len(DATA)))
@ -100,11 +100,11 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if DATA:
self._append(DATA)
def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
def send_body_chunks(self, DATA, code, msg, desc, ctype='text/xml; encoding="utf-8"'):
""" send a body in chunks """
self.responses[207]=(msg,desc)
self.send_response(code,message=msg)
self.responses[207]=(msg, desc)
self.send_response(code, message=msg)
self.send_header("Content-type", ctype)
self.send_header("Connection", "keep-alive")
self.send_header("Transfer-Encoding", "chunked")
@ -128,12 +128,12 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_PROPFIND(self):
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# read the body
body=None
body = None
if self.headers.has_key("Content-Length"):
l=self.headers['Content-Length']
body=self.rfile.read(atoi(l))
l = self.headers['Content-Length']
body = self.rfile.read(atoi(l))
alt_body = """<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:"><prop>
<getcontentlength xmlns="DAV:"/>
@ -149,26 +149,26 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# which Depth?
if self.headers.has_key('Depth'):
d=self.headers['Depth']
d = self.headers['Depth']
else:
d="infinity"
d = "infinity"
uri=self.geturi()
pf=PROPFIND(uri,dc,d)
uri = self.geturi()
pf = PROPFIND(uri, dc, d)
if body:
pf.read_propfind(body)
try:
DATA=pf.createResponse()
DATA=DATA+"\n"
DATA = pf.createResponse()
DATA = DATA+"\n"
# print "Data:", DATA
except DAV_NotFound,(ec,dd):
except DAV_NotFound, (ec, dd):
return self.send_notFound(dd, uri)
except DAV_Error, (ec,dd):
return self.send_error(ec,dd)
except DAV_Error, (ec, dd):
return self.send_error(ec, dd)
self.send_body_chunks(DATA,207,"Multi-Status","Multiple responses")
self.send_body_chunks(DATA, 207, "Multi-Status", "Multiple responses")
def geturi(self):
buri = self.IFACE_CLASS.baseuri
@ -179,110 +179,110 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
"""Serve a GET request."""
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
# get the last modified date
try:
lm=dc.get_prop(uri,"DAV:","getlastmodified")
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
except:
lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers={"Last-Modified":lm , "Connection": "keep-alive"}
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers = {"Last-Modified":lm , "Connection": "keep-alive"}
# get the content type
try:
ct=dc.get_prop(uri,"DAV:","getcontenttype")
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
except:
ct="application/octet-stream"
ct = "application/octet-stream"
# get the data
try:
data=dc.get_data(uri)
except DAV_Error, (ec,dd):
data = dc.get_data(uri)
except DAV_Error, (ec, dd):
self.send_status(ec)
return
# send the data
self.send_body(data,200,"OK","OK",ct,headers)
self.send_body(data, 200, "OK", "OK", ct, headers)
def do_HEAD(self):
""" Send a HEAD response """
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
# get the last modified date
try:
lm=dc.get_prop(uri,"DAV:","getlastmodified")
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
except:
lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
headers={"Last-Modified":lm, "Connection": "keep-alive"}
headers = {"Last-Modified":lm, "Connection": "keep-alive"}
# get the content type
try:
ct=dc.get_prop(uri,"DAV:","getcontenttype")
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
except:
ct="application/octet-stream"
ct = "application/octet-stream"
try:
data=dc.get_data(uri)
headers["Content-Length"]=str(len(data))
data = dc.get_data(uri)
headers["Content-Length"] = str(len(data))
except DAV_NotFound:
self.send_body(None,404,"Not Found","")
self.send_body(None, 404, "Not Found", "")
return
self.send_body(None,200,"OK","OK",ct,headers)
self.send_body(None, 200, "OK", "OK", ct, headers)
def do_POST(self):
self.send_error(404,"File not found")
self.send_error(404, "File not found")
def do_MKCOL(self):
""" create a new collection """
dc=self.IFACE_CLASS
uri=self.geturi()
dc = self.IFACE_CLASS
uri = self.geturi()
try:
res = dc.mkcol(uri)
if res:
self.send_body(None,201,"Created",'')
self.send_body(None, 201, "Created", '')
else:
self.send_body(None,415,"Cannot create",'')
self.send_body(None, 415, "Cannot create", '')
#self.send_header("Connection", "keep-alive")
# Todo: some content, too
except DAV_Error, (ec,dd):
self.send_body(None,int(ec),dd,dd)
except DAV_Error, (ec, dd):
self.send_body(None, int(ec), dd, dd)
def do_DELETE(self):
""" delete an resource """
dc=self.IFACE_CLASS
uri=self.geturi()
dl=DELETE(uri,dc)
dc = self.IFACE_CLASS
uri = self.geturi()
dl = DELETE(uri, dc)
if dc.is_collection(uri):
res=dl.delcol()
res = dl.delcol()
else:
res=dl.delone()
res = dl.delone()
if res:
self.send_status(207,body=res)
self.send_status(207, body=res)
else:
self.send_status(204)
def do_PUT(self):
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# read the body
body=None
body = None
if self.headers.has_key("Content-Length"):
l=self.headers['Content-Length']
body=self.rfile.read(atoi(l))
uri=self.geturi()
l = self.headers['Content-Length']
body = self.rfile.read(atoi(l))
uri = self.geturi()
ct=None
ct = None
if self.headers.has_key("Content-Type"):
ct=self.headers['Content-Type']
ct = self.headers['Content-Type']
try:
dc.put(uri,body,ct)
except DAV_Error, (ec,dd):
dc.put(uri, body, ct)
except DAV_Error, (ec, dd):
self.send_status(ec)
return
self.send_status(201)
@ -291,84 +291,84 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
""" copy one resource to another """
try:
self.copymove(COPY)
except DAV_Error, (ec,dd):
except DAV_Error, (ec, dd):
self.send_status(ec)
def do_MOVE(self):
""" move one resource to another """
try:
self.copymove(MOVE)
except DAV_Error, (ec,dd):
except DAV_Error, (ec, dd):
self.send_status(ec)
def copymove(self,CLASS):
def copymove(self, CLASS):
""" common method for copying or moving objects """
dc=self.IFACE_CLASS
dc = self.IFACE_CLASS
# get the source URI
source_uri=self.geturi()
source_uri = self.geturi()
# get the destination URI
dest_uri=self.headers['Destination']
dest_uri=urllib.unquote(dest_uri)
dest_uri = self.headers['Destination']
dest_uri = urllib.unquote(dest_uri)
# Overwrite?
overwrite=1
result_code=204
overwrite = 1
result_code = 204
if self.headers.has_key("Overwrite"):
if self.headers['Overwrite']=="F":
overwrite=None
result_code=201
# instanciate ACTION class
cp=CLASS(dc,source_uri,dest_uri,overwrite)
cp = CLASS(dc, source_uri, dest_uri, overwrite)
# Depth?
d="infinity"
d = "infinity"
if self.headers.has_key("Depth"):
d=self.headers['Depth']
d = self.headers['Depth']
if d!="0" and d!="infinity":
self.send_status(400)
return
if d=="0":
res=cp.single_action()
res = cp.single_action()
self.send_status(res)
return
# now it only can be "infinity" but we nevertheless check for a collection
if dc.is_collection(source_uri):
try:
res=cp.tree_action()
except DAV_Error, (ec,dd):
res = cp.tree_action()
except DAV_Error, (ec, dd):
self.send_status(ec)
return
else:
try:
res=cp.single_action()
except DAV_Error, (ec,dd):
res = cp.single_action()
except DAV_Error, (ec, dd):
self.send_status(ec)
return
if res:
self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
self.send_body_chunks(res, 207, STATUS_CODES[207], STATUS_CODES[207],
ctype='text/xml; charset="utf-8"')
else:
self.send_status(result_code)
def get_userinfo(self,user,pw):
def get_userinfo(self, user, pw):
""" Dummy method which lets all users in """
return 1
def send_status(self,code=200,mediatype='text/xml; charset="utf-8"', \
msg=None,body=None):
def send_status(self, code=200, mediatype='text/xml; charset="utf-8"', \
msg=None, body=None):
if not msg: msg=STATUS_CODES[code]
self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
if not msg: msg = STATUS_CODES[code]
self.send_body(body, code, STATUS_CODES[code], msg, mediatype)
def send_notFound(self,descr,uri):
def send_notFound(self, descr, uri):
body = """<?xml version="1.0" encoding="utf-8" ?>
<D:response xmlns:D="DAV:">
<D:href>%s</D:href>
@ -376,4 +376,6 @@ class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
<D:responsedescription>%s</D:responsedescription>
</D:response>
"""
return self.send_status(404,descr, body=body % (uri,descr))
return self.send_status(404, descr, body=body % (uri, descr))
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -14,8 +14,8 @@ import urlparse
from utils import create_treelist, is_prefix
from errors import *
def deltree(dc,uri,exclude={}):
""" delete a tree of resources
def deltree(dc, uri, exclude={}):
""" delete a tree of resources
dc -- dataclass to use
uri -- root uri to delete
@ -30,43 +30,43 @@ def deltree(dc,uri,exclude={}):
"""
tlist=create_treelist(dc,uri)
result={}
tlist = create_treelist(dc,uri)
result = {}
for i in range(len(tlist),0,-1):
problem_uris=result.keys()
element=tlist[i-1]
problem_uris = result.keys()
element = tlist[i-1]
# test here, if an element is a prefix of an uri which
# generated an error before.
# note that we walk here from childs to parents, thus
# we cannot delete a parent if a child made a problem.
# (see example in 8.6.2.1)
ok=1
ok = 1
for p in problem_uris:
if is_prefix(element,p):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# here we test for the exclude list which is the other way round!
for p in exclude.keys():
if is_prefix(p,element):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# now delete stuff
try:
delone(dc,element)
except DAV_Error, (ec,dd):
result[element]=ec
except DAV_Error, (ec,dd):
result[element] = ec
return result
def delone(dc,uri):
def delone(dc, uri):
""" delete a single object """
if dc.is_collection(uri):
dc.rmcol(uri) # should be empty
@ -79,12 +79,12 @@ def delone(dc,uri):
# helper function
def copy(dc,src,dst):
""" only copy the element
def copy(dc, src, dst):
""" only copy the element
This is just a helper method factored out from copy and
copytree. It will not handle the overwrite or depth header.
"""
# destination should have been deleted before
@ -101,28 +101,28 @@ def copy(dc,src,dst):
# the main functions
def copyone(dc,src,dst,overwrite=None):
def copyone(dc, src, dst, overwrite=None):
""" copy one resource to a new destination """
if overwrite and dc.exists(dst):
delres=deltree(dc,dst)
delres = deltree(dc,dst)
else:
delres={}
delres = {}
# if we cannot delete everything, then do not copy!
if delres: return delres
try:
copy(dc,src,dst) # pass thru exceptions
except DAV_Error, (ec,dd):
return ec
def copytree(dc,src,dst,overwrite=None):
def copytree(dc, src, dst, overwrite=None):
""" copy a tree of resources to another location
dc -- dataclass to use
src -- src uri from where to copy
dst -- dst uri
dst -- dst uri
overwrite -- if 1 then delete dst uri before
returns dict of uri:error_code tuples from which
@ -133,59 +133,59 @@ def copytree(dc,src,dst,overwrite=None):
# first delete the destination resource
if overwrite and dc.exists(dst):
delres=deltree(dc,dst)
delres = deltree(dc,dst)
else:
delres={}
delres = {}
# if we cannot delete everything, then do not copy!
if delres: return delres
# get the tree we have to copy
tlist=create_treelist(dc,src)
result={}
tlist = create_treelist(dc,src)
result = {}
# prepare destination URIs (get the prefix)
dpath=urlparse.urlparse(dst)[2]
dpath = urlparse.urlparse(dst)[2]
for element in tlist:
problem_uris=result.keys()
problem_uris = result.keys()
# now URIs get longer and longer thus we have
# to test if we had a parent URI which we were not
# able to copy in problem_uris which is the prefix
# of the actual element. If it is, then we cannot
# copy this as well but do not generate another error.
ok=1
ok = 1
for p in problem_uris:
if is_prefix(p,element):
ok=None
ok = None
break
if not ok: continue
if not ok: continue
# now create the destination URI which corresponds to
# the actual source URI. -> actual_dst
# ("subtract" the base src from the URI and prepend the
# dst prefix to it.)
esrc=replace(element,src,"")
actual_dst=dpath+esrc
esrc = replace(element,src,"")
actual_dst = dpath+esrc
# now copy stuff
try:
copy(dc,element,actual_dst)
except DAV_Error, (ec,dd):
result[element]=ec
except DAV_Error, (ec,dd):
result[element] = ec
return result
###
### MOVE
###
def moveone(dc,src,dst,overwrite=None):
def moveone(dc, src, dst, overwrite=None):
""" move a single resource
This is done by first copying it and then deleting
@ -197,8 +197,8 @@ def moveone(dc,src,dst,overwrite=None):
# then delete it
dc.rm(src)
def movetree(dc,src,dst,overwrite=None):
def movetree(dc, src, dst, overwrite=None):
""" move a collection
This is done by first copying it and then deleting
@ -209,10 +209,11 @@ def movetree(dc,src,dst,overwrite=None):
"""
# first copy it
res=copytree(dc,src,dst,overwrite)
res = copytree(dc,src,dst,overwrite)
# then delete it
res=deltree(dc,src,exclude=res)
res = deltree(dc,src,exclude=res)
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -47,11 +47,11 @@ class COPY:
"""
def __init__(self,dataclass,src_uri,dst_uri,overwrite):
self.__dataclass=dataclass
self.__src=src_uri
self.__dst=dst_uri
self.__overwrite=overwrite
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
self.__dataclass = dataclass
self.__src = src_uri
self.__dst = dst_uri
self.__overwrite = overwrite
def single_action(self):
@ -62,16 +62,16 @@ class COPY:
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
return dc.copyone(self.__src,self.__dst,self.__overwrite)
@ -84,20 +84,20 @@ class COPY:
Here we return a multistatus xml element.
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
result=dc.copytree(self.__src,self.__dst,self.__overwrite)
result = dc.copytree(self.__src,self.__dst,self.__overwrite)
#result=copytree(dc,self.__src,self.__dst,self.__overwrite)
if not result: return None
@ -109,25 +109,26 @@ class COPY:
###
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
for el,ec in result.items():
re=doc.createElement("D:response")
hr=doc.createElement("D:href")
st=doc.createElement("D:status")
huri=doc.createTextNode(quote_uri(el))
t=doc.createTextNode(gen_estring(ec))
re = doc.createElement("D:response")
hr = doc.createElement("D:href")
st = doc.createElement("D:status")
huri = doc.createTextNode(quote_uri(el))
t = doc.createTextNode(gen_estring(ec))
st.appendChild(t)
hr.appendChild(huri)
re.appendChild(hr)
re.appendChild(st)
ms.appendChild(re)
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -39,19 +39,19 @@ class MOVE:
""" move resources and eventually create multistatus responses
This module implements the MOVE class which is responsible for
moving resources.
moving resources.
MOVE is implemented by a COPY followed by a DELETE of the old
resource.
resource.
"""
def __init__(self,dataclass,src_uri,dst_uri,overwrite):
self.__dataclass=dataclass
self.__src=src_uri
self.__dst=dst_uri
self.__overwrite=overwrite
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
self.__dataclass = dataclass
self.__src = src_uri
self.__dst = dst_uri
self.__overwrite = overwrite
def single_action(self):
@ -62,16 +62,16 @@ class MOVE:
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
return dc.moveone(self.__src,self.__dst,self.__overwrite)
@ -82,21 +82,22 @@ class MOVE:
Here we return a multistatus xml element.
"""
dc=self.__dataclass
base=self.__src
dc = self.__dataclass
base = self.__src
### some basic tests
# test if dest exists and overwrite is false
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
# test if src and dst are the same
# (we assume that both uris are on the same server!)
ps=urlparse.urlparse(self.__src)[2]
pd=urlparse.urlparse(self.__dst)[2]
ps = urlparse.urlparse(self.__src)[2]
pd = urlparse.urlparse(self.__dst)[2]
if ps==pd: raise DAV_Error, 403
result=dc.movetree(self.__src,self.__dst,self.__overwrite)
result = dc.movetree(self.__src,self.__dst,self.__overwrite)
if not result: return None
# create the multistatus XML element
return make_xmlresponse(result)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -32,15 +32,15 @@ from davcmd import deltree
class DELETE:
def __init__(self,uri,dataclass):
self.__dataclass=dataclass
self.__uri=uri
def __init__(self, uri, dataclass):
self.__dataclass = dataclass
self.__uri = uri
def delcol(self):
""" delete a collection """
dc=self.__dataclass
result=dc.deltree(self.__uri)
dc = self.__dataclass
result = dc.deltree(self.__uri)
if not len(result.items()):
return None # everything ok
@ -51,9 +51,9 @@ class DELETE:
def delone(self):
""" delete a resource """
dc=self.__dataclass
result=dc.delone(self.__uri)
dc = self.__dataclass
result = dc.delone(self.__uri)
if not result: return None
if not len(result.items()):
return None # everything ok
@ -61,3 +61,4 @@ class DELETE:
# create the result element
return make_xmlresponse(result)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -12,15 +12,15 @@ class DAV_Error(Exception):
2. the error result element, e.g. a <multistatus> element
"""
def __init__(self,*args):
def __init__(self, *args):
if len(args)==1:
self.args=(args[0],"")
self.args = (args[0],"")
else:
self.args=args
self.args = args
class DAV_Secret(DAV_Error):
""" the user is not allowed to know anything about it
returning this for a property value means to exclude it
from the response xml element.
"""
@ -31,10 +31,10 @@ class DAV_Secret(DAV_Error):
class DAV_NotFound(DAV_Error):
""" a requested property was not found for a resource """
def __init__(self,*args):
def __init__(self, *args):
if len(args):
if isinstance(args[0],list):
if isinstance(args[0], list):
stre = "Path %s not found!"%('/'.join(args[0]))
else:
stre = args[0]
@ -46,11 +46,12 @@ class DAV_NotFound(DAV_Error):
class DAV_Forbidden(DAV_Error):
""" a method on a resource is not allowed """
def __init__(self,*args):
def __init__(self, *args):
if len(args):
DAV_Error.__init__(self,403,args[0])
else:
DAV_Error.__init__(self,403)
pass
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -18,16 +18,16 @@ class dav_interface:
### defined properties (modify this but let the DAV stuff there!)
### the format is namespace: [list of properties]
PROPS={"DAV:" : ('creationdate',
'displayname',
'getcontentlanguage',
'getcontentlength',
'getcontenttype',
'getetag',
'getlastmodified',
'lockdiscovery',
'resourcetype',
'source',
PROPS={"DAV:" : ('creationdate',
'displayname',
'getcontentlanguage',
'getcontentlength',
'getcontenttype',
'getetag',
'getlastmodified',
'lockdiscovery',
'resourcetype',
'source',
'supportedlock'),
"NS2" : ("p1","p2")
}
@ -36,28 +36,28 @@ class dav_interface:
# the first item is the namespace URI and the second one
# the method prefix
# e.g. for DAV:getcontenttype we call dav_getcontenttype()
M_NS={"DAV:" : "_get_dav",
M_NS = {"DAV:" : "_get_dav",
"NS2" : "ns2" }
def get_propnames(self,uri):
""" return the property names allowed for the given URI
def get_propnames(self, uri):
""" return the property names allowed for the given URI
In this method we simply return the above defined properties
assuming that they are valid for any resource.
assuming that they are valid for any resource.
You can override this in order to return a different set
of property names for each resource.
"""
return self.PROPS
def get_prop2(self,uri,ns,pname):
""" return the value of a property
def get_prop2(self, uri, ns, pname):
""" return the value of a property
"""
if lower(ns)=="dav:": return self.get_dav(uri,pname)
raise DAV_NotFound
def get_prop(self,uri,ns,propname):
def get_prop(self, uri, ns, propname):
""" return the value of a given property
uri -- uri of the object to get the property of
@ -65,17 +65,17 @@ class dav_interface:
pname -- name of the property
"""
if self.M_NS.has_key(ns):
prefix=self.M_NS[ns]
prefix = self.M_NS[ns]
else:
print "No namespace:",ns, "( for prop:", propname,")"
raise DAV_NotFound
mname=prefix+"_"+propname
mname = prefix+"_"+propname
if not hasattr(self,mname):
raise DAV_NotFound
try:
m=getattr(self,mname)
r=m(uri)
m = getattr(self,mname)
r = m(uri)
return r
except AttributeError, e:
print 'Property %s not supported' % propname
@ -86,16 +86,16 @@ class dav_interface:
### DATA methods (for GET and PUT)
###
def get_data(self,uri):
""" return the content of an object
def get_data(self, uri):
""" return the content of an object
return data or raise an exception
"""
raise DAV_NotFound
def put(self,uri,data):
""" write an object to the repository
def put(self, uri, data):
""" write an object to the repository
return a result code or raise an exception
"""
@ -106,17 +106,17 @@ class dav_interface:
### Methods for DAV properties
###
def _get_dav_creationdate(self,uri):
def _get_dav_creationdate(self, uri):
""" return the creationdate of a resource """
d=self.get_creationdate(uri)
d = self.get_creationdate(uri)
# format it
if isinstance(d, int) or isinstance(d, float):
d = time.localtimetime(d)
return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d)
def _get_dav_getlastmodified(self,uri):
def _get_dav_getlastmodified(self, uri):
""" return the last modified date of a resource """
d=self.get_lastmodified(uri)
d = self.get_lastmodified(uri)
if isinstance(d, int) or isinstance(d, float):
d = time.localtime(d)
# format it
@ -127,37 +127,37 @@ class dav_interface:
### OVERRIDE THESE!
###
def get_creationdate(self,uri):
def get_creationdate(self, uri):
""" return the creationdate of the resource """
return time.time()
def get_lastmodified(self,uri):
def get_lastmodified(self, uri):
""" return the last modification date of the resource """
return time.time()
###
### COPY MOVE DELETE
###
### methods for deleting a resource
def rmcol(self,uri):
""" delete a collection
def rmcol(self, uri):
""" delete a collection
This should not delete any children! This is automatically done
before by the DELETE class in DAV/delete.py
return a success code or raise an exception
"""
raise DAV_NotFound
def rm(self,uri):
""" delete a single resource
def rm(self, uri):
""" delete a single resource
return a success code or raise an exception
"""
raise DAV_NotFound
@ -182,7 +182,7 @@ class dav_interface:
1. to handle the action directly (e.g. cp or mv on filesystems)
2. to let it handle via the copy/move methods in davcmd.
ad 1) The first approach can be used when we know that no error can
ad 1) The first approach can be used when we know that no error can
happen inside a tree or when the action can exactly tell which
element made which error. We have to collect these and return
it in a dict of the form {uri: error_code, ...}
@ -210,28 +210,28 @@ class dav_interface:
### MOVE handlers
def moveone(self,src,dst,overwrite):
def moveone(self, src, dst, overwrite):
""" move one resource with Depth=0 """
return moveone(self,src,dst,overwrite)
return moveone(self, src, dst, overwrite)
def movetree(self,src,dst,overwrite):
def movetree(self, src, dst, overwrite):
""" move a collection with Depth=infinity """
return movetree(self,src,dst,overwrite)
return movetree(self, src, dst, overwrite)
### COPY handlers
def copyone(self,src,dst,overwrite):
def copyone(self, src, dst, overwrite):
""" copy one resource with Depth=0 """
return copyone(self,src,dst,overwrite)
return copyone(self, src, dst, overwrite)
def copytree(self,src,dst,overwrite):
def copytree(self, src, dst, overwrite):
""" copy a collection with Depth=infinity """
return copytree(self,src,dst,overwrite)
return copytree(self, src, dst, overwrite)
### low level copy methods (you only need these for method 2)
def copy(self,src,dst):
""" copy a resource with depth==0
def copy(self, src, dst):
""" copy a resource with depth==0
You don't need to bother about overwrite or not.
This has been done already.
@ -241,8 +241,8 @@ class dav_interface:
return 201
def copycol(self,src,dst):
""" copy a resource with depth==infinity
def copycol(self, src, dst):
""" copy a resource with depth==infinity
You don't need to bother about overwrite or not.
This has been done already.
@ -253,11 +253,12 @@ class dav_interface:
### some utility functions you need to implement
def exists(self,uri):
def exists(self, uri):
""" return 1 or None depending on if a resource exists """
return None # no
def is_collection(self,uri):
def is_collection(self, uri):
""" return 1 or None depending on if a resource is a collection """
return None # no
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -18,8 +18,8 @@
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
"""
from xml.dom import ext
from xml.dom.Document import Document
@ -45,9 +45,9 @@ class PROPFIND:
It will set the following instance vars:
request_class : ALLPROP | PROPNAME | PROP
proplist : list of properties
nsmap : map of namespaces
request_class: ALLPROP | PROPNAME | PROP
proplist: list of properties
nsmap: map of namespaces
The list of properties will contain tuples of the form
(element name, ns_prefix, ns_uri)
@ -55,21 +55,20 @@ class PROPFIND:
"""
def __init__(self, uri, dataclass, depth):
self.request_type = None
self.nsmap = {}
self.proplist = {}
self.default_ns = None
self.__dataclass = dataclass
self.__depth = str(depth)
self.__uri = uri
self.use_full_urls = True
self.__has_body = None # did we parse a body?
def __init__(self,uri,dataclass,depth):
self.request_type=None
self.nsmap={}
self.proplist={}
self.default_ns=None
self.__dataclass=dataclass
self.__depth=str(depth)
self.__uri=uri
self.use_full_urls=True
self.__has_body=None # did we parse a body?
def read_propfind(self, xml_doc):
self.request_type,self.proplist,self.namespaces = utils.parse_propfind(xml_doc)
def read_propfind(self,xml_doc):
self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
# a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype
# but we also have to return the http://groupdav.org/:resourcetype property!
if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']:
@ -91,7 +90,7 @@ class PROPFIND:
If we get an ALLPROP we first get the list of properties and then
we do the same as with a PROP method.
If the uri doesn't exist, return an xml response with a 404 status
"""
@ -114,41 +113,41 @@ class PROPFIND:
def create_propname(self):
""" create a multistatus response for the prop names """
dc=self.__dataclass
dc = self.__dataclass
# create the document generator
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
if self.__depth=="0":
pnames=dc.get_propnames(self.__uri)
re=self.mk_propname_response(self.__uri,pnames,doc)
pnames = dc.get_propnames(self.__uri)
re = self.mk_propname_response(self.__uri,pnames,doc)
ms.appendChild(re)
elif self.__depth=="1":
pnames=dc.get_propnames(self.__uri)
re=self.mk_propname_response(self.__uri,pnames,doc)
pnames = dc.get_propnames(self.__uri)
re = self.mk_propname_response(self.__uri,pnames,doc)
ms.appendChild(re)
for newuri in dc.get_childs(self.__uri):
pnames=dc.get_propnames(newuri)
re=self.mk_propname_response(newuri,pnames,doc)
pnames = dc.get_propnames(newuri)
re = self.mk_propname_response(newuri,pnames,doc)
ms.appendChild(re)
# *** depth=="infinity"
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
def create_allprop(self):
""" return a list of all properties """
self.proplist={}
self.namespaces=[]
self.proplist = {}
self.namespaces = []
for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
self.proplist[ns]=plist
self.proplist[ns] = plist
self.namespaces.append(ns)
return self.create_prop()
@ -178,32 +177,32 @@ class PROPFIND:
# create the document generator
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
doc.appendChild(ms)
if self.__depth=="0":
gp,bp=self.get_propvalues(self.__uri)
res=self.mk_prop_response(self.__uri,gp,bp,doc)
gp,bp = self.get_propvalues(self.__uri)
res = self.mk_prop_response(self.__uri,gp,bp,doc)
ms.appendChild(res)
elif self.__depth=="1":
gp,bp=self.get_propvalues(self.__uri)
res=self.mk_prop_response(self.__uri,gp,bp,doc)
gp,bp = self.get_propvalues(self.__uri)
res = self.mk_prop_response(self.__uri,gp,bp,doc)
ms.appendChild(res)
try:
for newuri in self.__dataclass.get_childs(self.__uri):
gp,bp=self.get_propvalues(newuri)
res=self.mk_prop_response(newuri,gp,bp,doc)
gp,bp = self.get_propvalues(newuri)
res = self.mk_prop_response(newuri,gp,bp,doc)
ms.appendChild(res)
except DAV_NotFound:
# If no children, never mind.
pass
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc,stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
@ -215,32 +214,32 @@ class PROPFIND:
propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
"""
re=doc.createElement("D:response")
re = doc.createElement("D:response")
# write href information
href=doc.createElement("D:href")
href = doc.createElement("D:href")
if self.use_full_urls:
huri=doc.createTextNode(uri)
huri = doc.createTextNode(uri)
else:
uparts=urlparse.urlparse(uri)
fileloc=uparts[2]
huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
uparts = urlparse.urlparse(uri)
fileloc = uparts[2]
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
href.appendChild(huri)
re.appendChild(href)
ps=doc.createElement("D:propstat")
nsnum=0
ps = doc.createElement("D:propstat")
nsnum = 0
for ns,plist in propnames.items():
# write prop element
pr=doc.createElement("D:prop")
nsp="ns"+str(nsnum)
pr = doc.createElement("D:prop")
nsp = "ns"+str(nsnum)
pr.setAttribute("xmlns:"+nsp,ns)
nsnum=nsnum+1
nsnum = nsnum+1
# write propertynames
for p in plist:
pe=doc.createElement(nsp+":"+p)
pe = doc.createElement(nsp+":"+p)
pr.appendChild(pe)
ps.appendChild(pr)
@ -257,33 +256,33 @@ class PROPFIND:
one, that means).
"""
re=doc.createElement("D:response")
re = doc.createElement("D:response")
# append namespaces to response
nsnum=0
nsnum = 0
for nsname in self.namespaces:
re.setAttribute("xmlns:ns"+str(nsnum),nsname)
nsnum=nsnum+1
nsnum = nsnum+1
# write href information
href=doc.createElement("D:href")
href = doc.createElement("D:href")
if self.use_full_urls:
huri=doc.createTextNode(uri)
huri = doc.createTextNode(uri)
else:
uparts=urlparse.urlparse(uri)
fileloc=uparts[2]
huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
uparts = urlparse.urlparse(uri)
fileloc = uparts[2]
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
href.appendChild(huri)
re.appendChild(href)
# write good properties
if good_props and len(good_props.items()):
ps=doc.createElement("D:propstat")
ps = doc.createElement("D:propstat")
gp=doc.createElement("D:prop")
gp = doc.createElement("D:prop")
for ns in good_props.keys():
ns_prefix="ns"+str(self.namespaces.index(ns))+":"
for p,v in good_props[ns].items():
pe=doc.createElement(ns_prefix+str(p))
pe = doc.createElement(ns_prefix+str(p))
if v == None:
pass
elif ns=='DAV:' and p=="resourcetype":
@ -294,15 +293,15 @@ class PROPFIND:
ve=doc.createElement(ns_prefix+v[0])
pe.appendChild(ve)
else:
ve=doc.createTextNode(utf8str(v))
ve = doc.createTextNode(utf8str(v))
pe.appendChild(ve)
gp.appendChild(pe)
if gp.hasChildNodes():
re.appendChild(ps)
ps.appendChild(gp)
s=doc.createElement("D:status")
t=doc.createTextNode("HTTP/1.1 200 OK")
s = doc.createElement("D:status")
t = doc.createTextNode("HTTP/1.1 200 OK")
s.appendChild(t)
ps.appendChild(s)
re.appendChild(ps)
@ -312,20 +311,20 @@ class PROPFIND:
# write a propstat for each error code
for ecode in bad_props.keys():
ps=doc.createElement("D:propstat")
ps = doc.createElement("D:propstat")
re.appendChild(ps)
bp=doc.createElement("D:prop")
bp = doc.createElement("D:prop")
ps.appendChild(bp)
for ns in bad_props[ecode].keys():
ns_prefix="ns"+str(self.namespaces.index(ns))+":"
ns_prefix = "ns"+str(self.namespaces.index(ns))+":"
for p in bad_props[ecode][ns]:
pe=doc.createElement(ns_prefix+str(p))
pe = doc.createElement(ns_prefix+str(p))
bp.appendChild(pe)
s=doc.createElement("D:status")
t=doc.createTextNode(utils.gen_estring(ecode))
s = doc.createElement("D:status")
t = doc.createTextNode(utils.gen_estring(ecode))
s.appendChild(t)
ps.appendChild(s)
re.appendChild(ps)
@ -342,20 +341,20 @@ class PROPFIND:
found or the user is not allowed to read them.
"""
good_props={}
bad_props={}
good_props = {}
bad_props = {}
for (ns,plist) in self.proplist.items():
good_props[ns]={}
bad_props={}
good_props[ns] = {}
bad_props = {}
ec = 0
for prop in plist:
try:
ec = 0
r=self.__dataclass.get_prop(uri,ns,prop)
good_props[ns][prop]=r
r = self.__dataclass.get_prop(uri,ns,prop)
good_props[ns][prop] = r
except DAV_Error, error_code:
ec=error_code[0]
ec = error_code[0]
# ignore props with error_code if 0 (invisible)
if ec==0: continue
@ -364,9 +363,11 @@ class PROPFIND:
if bad_props[ec].has_key(ns):
bad_props[ec][ns].append(prop)
else:
bad_props[ec][ns]=[prop]
bad_props[ec][ns] = [prop]
else:
bad_props[ec]={ns:[prop]}
bad_props[ec] = {ns:[prop]}
return good_props, bad_props
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,7 +27,7 @@ AUTHOR = 'Simon Pamies <s.pamies@banality.de>'
def gen_estring(ecode):
""" generate an error string from the given code """
ec=atoi(str(ecode))
ec = atoi(str(ecode))
if STATUS_CODES.has_key(ec):
return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
else:
@ -46,20 +46,20 @@ def parse_propfind(xml_doc):
doc = PyExpat.Reader().fromString(xml_doc)
snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
request_type=None
props={}
namespaces=[]
request_type = None
props = {}
namespaces = []
while 1:
curr_elem = snit.nextNode()
if not curr_elem: break
ename=fname=lower(curr_elem.nodeName)
ename=fname = lower(curr_elem.nodeName)
if ":" in fname:
ename=split(fname,":")[1]
if ename=="prop": request_type=RT_PROP; continue
ename = split(fname,":")[1]
if ename=="prop": request_type = RT_PROP; continue
if ename=="propfind": continue
if ename=="allprop": request_type=RT_ALLPROP; continue
if ename=="propname": request_type=RT_PROPNAME; continue
if ename=="allprop": request_type = RT_ALLPROP; continue
if ename=="propname": request_type = RT_PROPNAME; continue
# rest should be names of attributes
@ -67,13 +67,13 @@ def parse_propfind(xml_doc):
if props.has_key(ns):
props[ns].append(ename)
else:
props[ns]=[ename]
props[ns] = [ename]
namespaces.append(ns)
return request_type,props,namespaces
def create_treelist(dataclass,uri):
def create_treelist(dataclass, uri):
""" create a list of resources out of a tree
This function is used for the COPY, MOVE and DELETE methods
@ -83,23 +83,23 @@ def create_treelist(dataclass,uri):
It will return the flattened tree as list
"""
queue=[uri]
list=[uri]
queue = [uri]
list = [uri]
while len(queue):
element=queue[-1]
element = queue[-1]
if dataclass.is_collection(element):
childs=dataclass.get_childs(element)
childs = dataclass.get_childs(element)
else:
childs=[]
childs = []
if len(childs):
list=list+childs
list = list+childs
# update queue
del queue[-1]
if len(childs):
queue=queue+childs
queue = queue+childs
return list
def is_prefix(uri1,uri2):
def is_prefix(uri1, uri2):
""" returns 1 of uri1 is a prefix of uri2 """
if uri2[:len(uri1)]==uri1:
return 1
@ -111,50 +111,51 @@ def quote_uri(uri):
import urlparse
import urllib
up=urlparse.urlparse(uri)
np=urllib.quote(up[2])
return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
up = urlparse.urlparse(uri)
np = urllib.quote(up[2])
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
def get_uriparentpath(uri):
""" extract the uri path and remove the last element """
up=urlparse.urlparse(uri)
return joinfields(split(up[2],"/")[:-1],"/")
up = urlparse.urlparse(uri)
return joinfields(split(up[2], "/")[:-1], "/")
def get_urifilename(uri):
""" extract the uri path and return the last element """
up=urlparse.urlparse(uri)
return split(up[2],"/")[-1]
up = urlparse.urlparse(uri)
return split(up[2], "/")[-1]
def get_parenturi(uri):
""" return the parent of the given resource"""
up=urlparse.urlparse(uri)
np=joinfields(split(up[2],"/")[:-1],"/")
return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
up = urlparse.urlparse(uri)
np = joinfields(split(up[2], "/")[:-1], "/")
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
### XML utilities
def make_xmlresponse(result):
""" construct a response from a dict of uri:error_code elements """
doc = Document(None)
ms=doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D","DAV:")
ms = doc.createElement("D:multistatus")
ms.setAttribute("xmlns:D", "DAV:")
doc.appendChild(ms)
for el,ec in result.items():
re=doc.createElement("D:response")
hr=doc.createElement("D:href")
st=doc.createElement("D:status")
huri=doc.createTextNode(quote_uri(el))
t=doc.createTextNode(gen_estring(ec))
for el, ec in result.items():
re = doc.createElement("D:response")
hr = doc.createElement("D:href")
st = doc.createElement("D:status")
huri = doc.createTextNode(quote_uri(el))
t = doc.createTextNode(gen_estring(ec))
st.appendChild(t)
hr.appendChild(huri)
re.appendChild(hr)
re.appendChild(st)
ms.appendChild(re)
sfile=StringIO()
ext.PrettyPrint(doc,stream=sfile)
s=sfile.getvalue()
sfile = StringIO()
ext.PrettyPrint(doc, stream = sfile)
s = sfile.getvalue()
sfile.close()
return s
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

View File

@ -49,7 +49,9 @@
"demo_xml" : [],
"update_xml" : [
'security/ir.model.access.csv',
'caldav_wizard.xml',
'wizard/calendar_event_export_view.xml',
'wizard/calendar_event_import_view.xml',
'wizard/calendar_event_subscribe_view.xml',
'caldav_view.xml'
],
"installable" : True,

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -36,14 +36,21 @@ import time
try:
import vobject
except ImportError:
raise osv.except_osv('vobject Import Error!','Please install python-vobject from http://vobject.skyhouseconsulting.com/')
raise osv.except_osv('vobject Import Error!','Please install python-vobject \
from http://vobject.skyhouseconsulting.com/')
# O-1 Optional and can come only once
# O-n Optional and can come more than once
# R-1 Required and can come only once
# R-n Required and can come more than once
def uid2openobjectid(cr, uidval, oomodel, rdate):
""" UID To Open Object Id
@param cr: the current row, from the database cursor,
@param uidval: Get USerId vale
@oomodel: Open Object ModelName
@param rdate: Get Recurrent Date
"""
__rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
wematch = __rege.match(uidval.encode('utf8'))
if not wematch:
@ -60,17 +67,28 @@ def uid2openobjectid(cr, uidval, oomodel, rdate):
r_id = cr.fetchone()
if r_id:
return (id, r_id[0])
cr.execute(qry)
cr.execute(qry)
ids = map(lambda x: str(x[0]), cr.fetchall())
if id in ids:
return (id, None)
return (False, None)
def openobjectid2uid(cr, uidval, oomodel):
""" Open Object Id To UId
@param cr: the current row, from the database cursor,
@param uidval: Get USerId vale
@oomodel: Open Object ModelName """
value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
return value
def get_attribute_mapping(cr, uid, calname, context={}):
""" Attribute Mapping with Basic calendar fields and lines
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param calname: Get Calendar name
@param context: A standard dictionary for contextual values """
if not context:
context = {}
pool = pooler.get_pool(cr.dbname)
@ -100,6 +118,10 @@ def get_attribute_mapping(cr, uid, calname, context={}):
return res
def map_data(cr, uid, obj):
""" Map Data
@param self: The object pointer
@param cr: the current row, from the database cursor,"""
vals = {}
for map_dict in obj.__attribute__:
map_val = obj.ical_get(map_dict, 'value')
@ -146,14 +168,26 @@ def map_data(cr, uid, obj):
class CalDAV(object):
__attribute__ = {}
def ical_set(self, name, value, type):
""" set calendar Attribute
@param self: The object pointer,
@param name: Get Attribute Name
@param value: Get Attribute Value
@param type: Get Attribute Type
"""
if name in self.__attribute__ and self.__attribute__[name]:
self.__attribute__[name][type] = value
return True
def ical_get(self, name, type):
""" Get calendar Attribute
@param self: The object pointer,
@param name: Get Attribute Name
@param type: Get Attribute Type
"""
if self.__attribute__.get(name):
val = self.__attribute__.get(name).get(type, None)
valtype = self.__attribute__.get(name).get('type', None)
@ -168,12 +202,23 @@ class CalDAV(object):
return self.__attribute__.get(name, None)
def ical_reset(self, type):
""" Reset Calendar Attribute
@param self: The object pointer,
@param type: Get Attribute Type
"""
for name in self.__attribute__:
if self.__attribute__[name]:
self.__attribute__[name][type] = None
return True
def parse_ics(self, cr, uid, child, cal_children=None, context=None):
""" parse calendaring and scheduling information
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
att_data = []
for cal_data in child.getChildren():
if cal_data.name.lower() == 'attendee':
@ -197,7 +242,7 @@ class CalDAV(object):
self.ical_set(cal_data.name.lower(), ','.join(exval), 'value')
continue
if cal_data.name.lower() in self.__attribute__:
if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'):
self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value')
self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
@ -205,8 +250,15 @@ class CalDAV(object):
return vals
def create_ics(self, cr, uid, datas, name, ical, context=None):
""" create calendaring and scheduling information
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
if not datas:
return
timezones = []
for data in datas:
tzval = None
vevent = ical.add(name)
@ -222,10 +274,10 @@ class CalDAV(object):
model_obj = self.pool.get(model)
r_ids = []
if model_obj._columns.get('recurrent_uid', None):
cr.execute('select id from %s where recurrent_uid=%s'
cr.execute('select id from %s where recurrent_uid=%s'
% (model_obj._table, data[map_field]))
r_ids = map(lambda x: x[0], cr.fetchall())
if r_ids:
if r_ids:
rdata = self.pool.get(model).read(cr, uid, r_ids)
event_obj = self.pool.get('basic.calendar.event')
rcal = event_obj.export_cal(cr, uid, rdata, context=context)
@ -246,17 +298,18 @@ class CalDAV(object):
alarm_obj = self.pool.get('basic.calendar.alarm')
vevent = alarm_obj.export_cal(cr, uid, model, \
data[map_field][0], vevent, context=ctx)
elif field == 'vtimezone' and data[map_field]:
elif field == 'vtimezone' and data[map_field] and data[map_field] not in timezones:
tzval = data[map_field]
tz_obj = self.pool.get('basic.calendar.timezone')
ical = tz_obj.export_cal(cr, uid, None, \
data[map_field], ical, context=context)
timezones.append(data[map_field])
elif data[map_field]:
if map_type in ("char", "text"):
if field in ('exdate'):
vevent.add(field).value = map(parser.parse, (data[map_field]).split(','))
else:
vevent.add(field).value = tools.ustr(data[map_field])
vevent.add(field).value = tools.ustr(data[map_field])
elif map_type in ('datetime', 'date') and data[map_field]:
dtfield = vevent.add(field)
dtfield.value = parser.parse(data[map_field])
@ -276,8 +329,16 @@ class CalDAV(object):
if val1 == data[map_field]:
vevent.add(field).value = key1
return vevent
def check_import(self, cr, uid, vals, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
ids = []
model_obj = self.pool.get(context.get('model'))
recur_pool = {}
@ -309,6 +370,14 @@ class CalDAV(object):
return ids
def export_cal(self, cr, uid, datas, vobj=None, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get Data's for caldav
@param context: A standard dictionary for contextual values
"""
try:
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
ical = vobject.iCalendar()
@ -318,6 +387,14 @@ class CalDAV(object):
raise osv.except_osv(('Error !'), (str(e)))
def import_cal(self, cr, uid, content, data_id=None, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param data_id: Get Datas ID or False
@param context: A standard dictionary for contextual values
"""
ical_data = base64.decodestring(content)
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
parsedCal = vobject.readOne(ical_data)
@ -333,6 +410,7 @@ class CalDAV(object):
self.ical_reset('value')
return res
class Calendar(CalDAV, osv.osv):
_name = 'basic.calendar'
_description = 'Calendar'
@ -352,27 +430,35 @@ class Calendar(CalDAV, osv.osv):
'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
}
_columns = {
'name': fields.char("Name", size=64),
'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
'active': fields.boolean('Active'),
'create_date': fields.datetime('Created Date'),
'write_date': fields.datetime('Modifided Date'),
'name': fields.char("Name", size=64),
'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
'active': fields.boolean('Active'),
'create_date': fields.datetime('Created Date'),
'write_date': fields.datetime('Modifided Date'),
}
_defaults = {
'active': lambda *a: True,
'active': lambda *a: True,
}
def export_cal(self, cr, uid, ids, vobj='vevent', context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendars IDs
@param context: A standard dictionary for contextual values
"""
cal = self.browse(cr, uid, ids[0])
ical = vobject.iCalendar()
ical = vobject.iCalendar()
for line in cal.line_ids:
if line.name in ('valarm', 'attendee'):
continue
mod_obj = self.pool.get(line.object_id.model)
data_ids = mod_obj.search(cr, uid, eval(line.domain), context=context)
datas = mod_obj.read(cr, uid, data_ids, context=context)
context.update({'model': line.object_id.model,
context.update({'model': line.object_id.model,
'calendar_id': cal.id
})
self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
@ -380,6 +466,14 @@ class Calendar(CalDAV, osv.osv):
return ical.serialize()
def import_cal(self, cr, uid, content, data_id=None, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param data_id: Get Datas ID or False
@param context: A standard dictionary for contextual values
"""
if not context:
context = {}
vals = []
@ -394,7 +488,7 @@ class Calendar(CalDAV, osv.osv):
cal_children[line.name] = line.object_id.model
for child in parsedCal.getChildren():
if child.name.lower() in cal_children:
context.update({'model': cal_children[child.name.lower()],
context.update({'model': cal_children[child.name.lower()],
'calendar_id': cal.id
})
self.__attribute__ = get_attribute_mapping(cr, uid, child.name.lower(), context=context)
@ -407,27 +501,39 @@ class Calendar(CalDAV, osv.osv):
self.check_import(cr, uid, vals, context=context)
return {}
Calendar()
class basic_calendar_line(osv.osv):
""" Calendar Lines """
_name = 'basic.calendar.lines'
_description = 'Calendar Lines'
_columns = {
'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
('valarm', 'Alarm'), \
('attendee', 'Attendee')], \
string="Type", size=64),
'object_id': fields.many2one('ir.model', 'Object'),
string="Type", size=64),
'object_id': fields.many2one('ir.model', 'Object'),
'calendar_id': fields.many2one('basic.calendar', 'Calendar', \
required=True, ondelete='cascade'),
'domain': fields.char('Domain', size=124),
required=True, ondelete='cascade'),
'domain': fields.char('Domain', size=124),
'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping')
}
}
_defaults = {
'domain': lambda *a: '[]',
'domain': lambda *a: '[]',
}
def create(self, cr, uid, vals, context={}):
""" create calendar's line
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get the Values
@param context: A standard dictionary for contextual values
"""
cr.execute("Select count(id) from basic_calendar_lines \
where name='%s' and calendar_id=%s" % (vals.get('name'), vals.get('calendar_id')))
res = cr.fetchone()
@ -439,41 +545,52 @@ line "%s" more than once' % (vals.get('name'))))
basic_calendar_line()
class basic_calendar_attribute(osv.osv):
_name = 'basic.calendar.attributes'
_description = 'Calendar attributes'
_columns = {
'name': fields.char("Name", size=64, required=True),
_columns = {
'name': fields.char("Name", size=64, required=True),
'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
('alarm', 'Alarm'), \
('attendee', 'Attendee')], \
string="Type", size=64, required=True),
string="Type", size=64, required=True),
}
basic_calendar_attribute()
class basic_calendar_fields(osv.osv):
""" Calendar fields """
_name = 'basic.calendar.fields'
_description = 'Calendar fields'
_columns = {
'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
'type_id': fields.many2one('basic.calendar.lines', 'Type', \
required=True, ondelete='cascade'),
'expr': fields.char("Expression", size=64),
'fn': fields.selection([('field', 'Use the field'),
('const', 'Expression as constant'),
('hours', 'Interval in hours'),
], 'Function'),
'mapping': fields.text('Mapping'),
required=True, ondelete='cascade'),
'expr': fields.char("Expression", size=64),
'fn': fields.selection([('field', 'Use the field'),
('const', 'Expression as constant'),
('hours', 'Interval in hours'),
], 'Function'),
'mapping': fields.text('Mapping'),
}
_defaults = {
'fn': lambda *a: 'field',
'fn': lambda *a: 'field',
}
def check_line(self, cr, uid, vals, name, context=None):
""" check calendar's line
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
f_obj = self.pool.get('ir.model.fields')
field = f_obj.browse(cr, uid, vals['field_id'], context=context)
relation = field.relation
@ -485,8 +602,16 @@ class basic_calendar_fields(osv.osv):
if (relation != 'NULL') and (not relation == line_rel):
raise osv.except_osv(_('Warning !'), _('Please provide proper configuration of "%s" in Calendar Lines' % (name)))
return True
def create(self, cr, uid, vals, context={}):
""" Create Calendar's fields
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
cr.execute('select name from basic_calendar_attributes \
where id=%s' % (vals.get('name')))
name = cr.fetchone()
@ -500,8 +625,16 @@ class basic_calendar_fields(osv.osv):
if res[0] > 0:
raise osv.except_osv(_('Warning !'), _('Can not map the field more than once'))
return super(basic_calendar_fields, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
""" write Calendar's fields
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param vals: Get Values
@param context: A standard dictionary for contextual values
"""
if not vals:
return
for id in ids:
@ -520,6 +653,7 @@ class basic_calendar_fields(osv.osv):
basic_calendar_fields()
class Event(CalDAV, osv.osv_memory):
_name = 'basic.calendar.event'
_calname = 'vevent'
@ -540,7 +674,7 @@ class Event(CalDAV, osv.osv_memory):
'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
'recurid': None,
'recurid': None,
'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
@ -548,69 +682,89 @@ class Event(CalDAV, osv.osv_memory):
'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a reference to contact information associated with the calendar component.
'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
'rstatus': None,
'rstatus': None,
'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
# like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
'x-prop': None,
'x-prop': None,
'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
}
def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
""" Export calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get datas
@param context: A standard dictionary for contextual values
"""
return super(Event, self).export_cal(cr, uid, datas, 'vevent', context=context)
Event()
class ToDo(CalDAV, osv.osv_memory):
_name = 'basic.calendar.todo'
_calname = 'vtodo'
__attribute__ = {
'class': None,
'completed': None,
'created': None,
'description': None,
'dtstamp': None,
'dtstart': None,
'duration': None,
'due': None,
'geo': None,
'last-mod ': None,
'location': None,
'organizer': None,
'percent': None,
'priority': None,
'recurid': None,
'seq': None,
'status': None,
'summary': None,
'uid': None,
'url': None,
'attach': None,
'attendee': None,
'categories': None,
'comment': None,
'contact': None,
'exdate': None,
'exrule': None,
'rstatus': None,
'related': None,
'resources': None,
'rdate': None,
'rrule': None,
'class': None,
'completed': None,
'created': None,
'description': None,
'dtstamp': None,
'dtstart': None,
'duration': None,
'due': None,
'geo': None,
'last-mod ': None,
'location': None,
'organizer': None,
'percent': None,
'priority': None,
'recurid': None,
'seq': None,
'status': None,
'summary': None,
'uid': None,
'url': None,
'attach': None,
'attendee': None,
'categories': None,
'comment': None,
'contact': None,
'exdate': None,
'exrule': None,
'rstatus': None,
'related': None,
'resources': None,
'rdate': None,
'rrule': None,
}
def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param datas: Get datas
@param context: A standard dictionary for contextual values
"""
return super(ToDo, self).export_cal(cr, uid, datas, 'vtodo', context=context)
ToDo()
class Journal(CalDAV):
__attribute__ = {
}
class FreeBusy(CalDAV):
__attribute__ = {
'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a reference to contact information associated with the calendar component.
@ -624,15 +778,15 @@ class FreeBusy(CalDAV):
'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
'rstatus': None,
'X-prop': None,
'rstatus': None,
'X-prop': None,
}
class Timezone(CalDAV, osv.osv_memory):
_name = 'basic.calendar.timezone'
_calname = 'vtimezone'
__attribute__ = {
'tzid': {'field': 'tzid'}, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
@ -641,8 +795,15 @@ class Timezone(CalDAV, osv.osv_memory):
'daylightc': {'tzprop': None}, # Use: R-1,
'x-prop': None, # Use: O-n, Type: Text,
}
def get_name_offset(self, cr, uid, tzid, context={}):
""" Get Name Offset value
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
mytz = pytz.timezone(tzid.title())
mydt = datetime.now(tz=mytz)
offset = mydt.utcoffset()
@ -653,6 +814,14 @@ class Timezone(CalDAV, osv.osv_memory):
return (mydt.tzname(), realoffset)
def export_cal(self, cr, uid, model, tzid, ical, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get Model's name
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': model})
cal_tz = ical.add('vtimezone')
@ -664,8 +833,16 @@ class Timezone(CalDAV, osv.osv_memory):
tz_std.add("DTSTART").value = datetime.now() # TODO
tz_std.add("TZNAME").value = tzname
return ical
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's data
@param context: A standard dictionary for contextual values
"""
for child in ical_data.getChildren():
if child.name.lower() == 'tzid':
tzname = child.value
@ -673,7 +850,7 @@ class Timezone(CalDAV, osv.osv_memory):
vals = map_data(cr, uid, self)
return vals
Timezone()
Timezone()
class Alarm(CalDAV, osv.osv_memory):
@ -689,10 +866,19 @@ class Alarm(CalDAV, osv.osv_memory):
'duration': None, # Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
'repeat': None, # Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
'x-prop': None,
'x-prop': None,
}
def export_cal(self, cr, uid, model, alarm_id, vevent, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get Model's name
@param alarm_id: Get Alarm's Id
@param context: A standard dictionary for contextual values
"""
valarm = vevent.add('valarm')
alarm_object = self.pool.get(model)
alarm_data = alarm_object.read(cr, uid, alarm_id, [])
@ -719,6 +905,14 @@ class Alarm(CalDAV, osv.osv_memory):
return vevent
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's Data
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': context.get('model', None)})
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
@ -753,6 +947,7 @@ class Alarm(CalDAV, osv.osv_memory):
Alarm()
class Attendee(CalDAV, osv.osv_memory):
_name = 'basic.calendar.attendee'
_calname = 'attendee'
@ -772,6 +967,14 @@ class Attendee(CalDAV, osv.osv_memory):
}
def import_cal(self, cr, uid, ical_data, context=None):
""" Import Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ical_data: Get calendar's Data
@param context: A standard dictionary for contextual values
"""
ctx = context.copy()
ctx.update({'model': context.get('model', None)})
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
@ -787,6 +990,15 @@ class Attendee(CalDAV, osv.osv_memory):
return vals
def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}):
""" Export Calendar
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param model: Get model's name
@param attendee_ids: Get Attendee's Id
@param context: A standard dictionary for contextual values
"""
attendee_object = self.pool.get(model)
ctx = context.copy()
ctx.update({'model': model})
@ -812,5 +1024,4 @@ class Attendee(CalDAV, osv.osv_memory):
Attendee()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,3 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 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
import heapq
@ -10,8 +31,8 @@ def memoize(maxsize):
def wrapper(*args):
key = repr(args)
# performance crap
_cache=cache
_heap=heap
_cache = cache
_heap = heap
_heappop = heapq.heappop
_heappush = heapq.heappush
_time = time.time
@ -20,13 +41,13 @@ def memoize(maxsize):
if not _cache.has_key(key):
if _cursize == _maxsize:
# pop oldest element
(_,oldkey) = _heappop(_heap)
(_, oldkey) = _heappop(_heap)
_cache.pop(oldkey)
else:
_cursize += 1
# insert this element
_cache[key] = f(*args)
_heappush(_heap,(_time(),key))
_heappush(_heap, (_time(), key))
wrapper.misses += 1
else:
wrapper.hits += 1
@ -34,6 +55,8 @@ def memoize(maxsize):
wrapper.__doc__ = f.__doc__
wrapper.__name__ = f.__name__
wrapper.hits = wrapper.misses = 0
return wrapper
return wrapper
return decorating_function
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -49,10 +49,10 @@ class tinydav_handler(dav_interface):
"""
PROPS={'DAV:': dav_interface.PROPS['DAV:'], }
M_NS={ "DAV:" : dav_interface.M_NS['DAV:'], }
M_NS={ "DAV:": dav_interface.M_NS['DAV:'], }
def __init__(self, parent, verbose=False):
self.db_name = False
self.db_name = False
self.parent = parent
self.baseuri = parent.baseuri
@ -64,7 +64,7 @@ class tinydav_handler(dav_interface):
if not dbname:
cr.close()
return props
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if node:
props.update(node.get_dav_props(cr))
cr.close()
@ -78,18 +78,18 @@ class tinydav_handler(dav_interface):
pname -- name of the property
"""
if self.M_NS.has_key(ns):
return dav_interface.get_prop(self,uri,ns,propname)
return dav_interface.get_prop(self, uri, ns, propname)
if uri[-1]=='/':uri=uri[:-1]
if uri[-1]=='/':uri = uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
raise DAV_NotFound
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
cr.close()
raise DAV_NotFound
res = node.get_dav_eprop(cr,ns,propname)
res = node.get_dav_eprop(cr, ns, propname)
cr.close()
return res
@ -97,21 +97,21 @@ class tinydav_handler(dav_interface):
""" Return the base URI of this request, or even join it with the
ajoin path elements
"""
return self.baseuri+ '/'.join(ajoin)
return self.baseuri+ '/'.join(ajoin)
def uri2local(self, uri):
uparts=urlparse.urlparse(uri)
reluri=uparts[2]
uparts = urlparse.urlparse(uri)
reluri = uparts[2]
if reluri and reluri[-1]=="/":
reluri=reluri[:-1]
reluri = reluri[:-1]
return reluri
#
# pos: -1 to get the parent of the uri
#
def get_cr(self, uri):
def get_cr(self, uri):
pdb = self.parent.auth_proxy.last_auth
reluri = self.uri2local(uri)
reluri = self.uri2local(uri)
try:
dbname = reluri.split('/')[2]
except:
@ -121,7 +121,7 @@ class tinydav_handler(dav_interface):
if not pdb and dbname:
# if dbname was in our uri, we should have authenticated
# against that.
raise Exception("Programming error")
raise Exception("Programming error")
assert pdb == dbname, " %s != %s" %(pdb, dbname)
user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb]
db,pool = pooler.get_db_and_pool(dbname)
@ -134,29 +134,29 @@ class tinydav_handler(dav_interface):
return None
return pool.get('basic.calendar').get_calendar_object(cr, uid, uri)
def get_data(self,uri):
def get_data(self, uri):
self.parent.log_message('GET: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
try:
if not dbname:
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
try:
try:
datas = node.get_data(cr, uid)
except TypeError,e:
except TypeError, e:
import traceback
self.parent.log_error("GET typeError: %s", str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Forbidden
except IndexError,e :
except IndexError, e :
self.parent.log_error("GET IndexError: %s", str(e))
raise DAV_NotFound(uri2)
except Exception,e:
except Exception, e:
import traceback
self.parent.log_error("GET exception: %s",str(e))
self.parent.log_error("GET exception: %s", str(e))
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Error, 409
return datas
@ -164,7 +164,7 @@ class tinydav_handler(dav_interface):
cr.close()
@memoize(CACHE_SIZE)
def _get_dav_resourcetype(self,uri):
def _get_dav_resourcetype(self, uri):
""" return type of object """
self.parent.log_message('get RT: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -172,21 +172,21 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
return COLLECTION
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
raise DAV_NotFound(uri2)
return OBJECT
finally:
cr.close()
def _get_dav_displayname(self,uri):
def _get_dav_displayname(self, uri):
self.parent.log_message('get DN: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
return COLLECTION
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
cr.close()
raise DAV_NotFound(uri2)
@ -194,7 +194,7 @@ class tinydav_handler(dav_interface):
return node.displayname
@memoize(CACHE_SIZE)
def _get_dav_getcontentlength(self,uri):
def _get_dav_getcontentlength(self, uri):
""" return the content length of an object """
self.parent.log_message('get length: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -212,7 +212,7 @@ class tinydav_handler(dav_interface):
return str(result)
@memoize(CACHE_SIZE)
def _get_dav_getetag(self,uri):
def _get_dav_getetag(self, uri):
""" return the ETag of an object """
self.parent.log_message('get etag: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
@ -230,7 +230,7 @@ class tinydav_handler(dav_interface):
return str(result)
@memoize(CACHE_SIZE)
def get_lastmodified(self,uri):
def get_lastmodified(self, uri):
""" return the last modified date of the object """
if uri[-1]=='/':uri=uri[:-1]
today = time.time()
@ -238,18 +238,18 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
return today
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
if node.write_date:
return time.mktime(time.strptime(node.write_date,'%Y-%m-%d %H:%M:%S'))
return time.mktime(time.strptime(node.write_date, '%Y-%m-%d %H:%M:%S'))
else:
return today
finally:
cr.close()
@memoize(CACHE_SIZE)
def get_creationdate(self,uri):
def get_creationdate(self, uri):
""" return the last modified date of the object """
if uri[-1]=='/':uri=uri[:-1]
@ -257,11 +257,11 @@ class tinydav_handler(dav_interface):
try:
if not dbname:
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
if node.create_date:
result = time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S')
result = time.strptime(node.create_date, '%Y-%m-%d %H:%M:%S')
else:
result = time.gmtime()
return result
@ -269,14 +269,14 @@ class tinydav_handler(dav_interface):
cr.close()
@memoize(CACHE_SIZE)
def _get_dav_getcontenttype(self,uri):
def _get_dav_getcontenttype(self, uri):
self.parent.log_message('get contenttype: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
try:
if not dbname:
return 'httpd/unix-directory'
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
raise DAV_NotFound(uri2)
result = node.mimetype
@ -285,50 +285,50 @@ class tinydav_handler(dav_interface):
finally:
cr.close()
def put(self, uri, data, content_type=None):
""" put the object into the filesystem """
self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), len(data), content_type))
self.parent.log_message('Putting %s (%d), %s'%(misc.ustr(uri), len(data), content_type))
parent='/'.join(uri.split('/')[:-1])
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
raise DAV_Forbidden
try:
node = self.uri2object(cr,uid,pool, uri2[:])
node = self.uri2object(cr, uid, pool, uri2[:])
except:
node = False
node = False
if not node:
raise DAV_Forbidden
else:
try:
node.set_data(cr, uid, data)
except Exception,e:
import traceback
node.set_data(cr, uid, data)
except Exception, e:
import traceback
self.parent.log_error("Cannot save :%s", str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
self.parent.log_message("Exc: %s", traceback.format_exc())
raise DAV_Forbidden
cr.commit()
cr.close()
return 201
def exists(self,uri):
def exists(self, uri):
""" test if a resource exists """
result = False
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
return True
try:
node = self.uri2object(cr,uid,pool, uri2)
node = self.uri2object(cr, uid, pool, uri2)
if node:
result = True
except:
pass
cr.close()
return result
return result
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -26,47 +26,46 @@ import tools
import time
import base64
class node_calendar(object):
class node_calendar(object):
def __init__(self, path, context, calendar):
self.path = path
self.context = context
self.calendar_id = calendar.id
self.mimetype = 'ics'
self.calendar_id = calendar.id
self.mimetype = 'ics'
self.create_date = calendar.create_date
self.write_date = calendar.write_date or calendar.create_date
self.content_length = 0
self.displayname = calendar.name
def get_data(self, cr, uid):
def get_data(self, cr, uid):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
return calendar_obj.export_cal(cr, uid, [self.calendar_id])
def get_data_len(self, cr):
return calendar_obj.export_cal(cr, uid, [self.calendar_id])
def get_data_len(self, cr):
return self.content_length
def set_data(self, cr, uid, data):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
def set_data(self, cr, uid, data):
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
def get_etag(self,cr):
def get_etag(self, cr):
""" Get a tag, unique per object + modification.
see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
return self._get_ttag(cr) + ':' + self._get_wtag(cr)
def _get_wtag(self,cr):
def _get_wtag(self, cr):
""" Return the modification time as a unique, compact string """
if self.write_date:
wtime = time.mktime(time.strptime(self.write_date,'%Y-%m-%d %H:%M:%S'))
wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
else: wtime = time.time()
return str(wtime)
def _get_ttag(self,cr):
def _get_ttag(self, cr):
return 'calendar-%d' % self.calendar_id
def get_dav_eprop(self,cr,ns,prop):
return None
class Calendar(osv.osv):
_inherit = 'basic.calendar'
@ -82,5 +81,7 @@ class Calendar(osv.osv):
return None
calendar_id, calendar_name = res[0]
calendar = self.browse(cr, uid, calendar_id)
return node_calendar(uri, context, calendar)
return node_calendar(uri, context, calendar)
Calendar()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4

View File

@ -31,7 +31,7 @@
<separator string="Value Mapping" colspan="4" />
<field name="mapping" select="1" colspan="4" nolabel="1" />
</form>
</field>
</field>
</form>
<tree string="Attributes Mapping" editable="bottom">
<field name="name" select="1" />

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<wizard string="Import .ics File" model="basic.calendar"
name="calendar.event.import" id="wizard_cal_event_import" />
<wizard string="Subscribe"
model="basic.calendar" name="calendar.event.subscribe"
id="wizard_cal_event_subscribe" multi="True" />
<wizard string="Export .ics File" model="basic.calendar"
name="calendar.event.export" id="wizard_cal_event_export" />
<wizard string="Edit this event" model="basic.calendar.event"
name="calendar.event.edit.this" id="wizard_edit_this_event"
menu="False" />
</data>
</openerp>

View File

@ -0,0 +1,13 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_basic_calendar_all","basic.calendar","model_basic_calendar","base.group_user",1,1,1,1
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event","base.group_user",1,1,1,0
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee","base.group_user",1,1,1,0
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo","base.group_user",1,1,1,0
"access_basic_calendar_alarm_all","basic.calendar.alarm","model_basic_calendar_alarm","base.group_user",1,1,1,1
"access_basic_calendar_lines","basic.calendar.lines","model_basic_calendar_lines","base.group_user",1,1,1,1
"access_basic_calendar_attributes","basic.calendar.attributes","model_basic_calendar_attributes","base.group_user",1,1,1,1
"access_basic_calendar_fields","basic.calendar.fields","model_basic_calendar_fields","base.group_user",1,1,1,1
"access_basic_calendar_timezone","basic.calendar.timezone","model_basic_calendar_timezone","base.group_user",1,1,1,1
"access_calendar_event_export","calendar.event.export","model_calendar_event_export","base.group_user",1,1,1,1
"access_calendar_event_import","calendar.event.import","model_calendar_event_import","base.group_user",1,1,1,1
"access_calendar_event_subscribe","calendar.event.subscribe","model_calendar_event_subscribe","base.group_user",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_basic_calendar_all basic.calendar model_basic_calendar base.group_user 1 1 1 1
3 access_basic_calendar_event_all basic.calendar.event model_basic_calendar_event base.group_user 1 1 1 0
4 access_basic_calendar_attendee_all basic.calendar.attendee model_basic_calendar_attendee base.group_user 1 1 1 0
5 access_calendar_todo_all basic.calendar.todo model_basic_calendar_todo base.group_user 1 1 1 0
6 access_basic_calendar_alarm_all basic.calendar.alarm model_basic_calendar_alarm base.group_user 1 1 1 1
7 access_basic_calendar_lines basic.calendar.lines model_basic_calendar_lines base.group_user 1 1 1 1
8 access_basic_calendar_attributes basic.calendar.attributes model_basic_calendar_attributes base.group_user 1 1 1 1
9 access_basic_calendar_fields basic.calendar.fields model_basic_calendar_fields base.group_user 1 1 1 1
10 access_basic_calendar_timezone basic.calendar.timezone model_basic_calendar_timezone base.group_user 1 1 1 1
11 access_calendar_event_export calendar.event.export model_calendar_event_export base.group_user 1 1 1 1
12 access_calendar_event_import calendar.event.import model_calendar_event_import base.group_user 1 1 1 1
13 access_calendar_event_subscribe calendar.event.subscribe model_calendar_event_subscribe base.group_user 1 1 1 1

View File

@ -1,9 +1,7 @@
# -*- encoding: utf-8 -*-
#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
@ -33,15 +31,16 @@ from tools.config import config
from DAV.WebDAVServer import DAVRequestHandler
from service.websrv_lib import HTTPDir,FixSendError
class DAVHandler(FixSendError,DAVRequestHandler):
class DAVHandler(FixSendError, DAVRequestHandler):
verbose = False
def get_userinfo(self,user,pw):
def get_userinfo(self, user, pw):
return False
def _log(self, message):
netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message)
netsvc.Logger().notifyChannel("webdav", netsvc.LOG_DEBUG, message)
def handle(self):
pass
@ -50,30 +49,30 @@ class DAVHandler(FixSendError,DAVRequestHandler):
def setup(self):
davpath = '/calendar/'
self.baseuri = "http://%s:%d%s"% (self.server.server_name,self.server.server_port,davpath)
self.baseuri = "http://%s:%d%s"% (self.server.server_name, self.server.server_port, davpath)
self.IFACE_CLASS = tinydav_handler(self)
pass
def log_message(self, format, *args):
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_DEBUG_RPC,format % args)
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)
def log_error(self, format, *args):
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_WARNING,format % args)
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_WARNING, format % args)
try:
from service.http_server import reg_http_service,OpenERPAuthProvider
from service.http_server import reg_http_service, OpenERPAuthProvider
davpath = '/calendar/'
handler = DAVHandler
handler.verbose = config.get_misc('webdav','verbose',True)
handler.debug = config.get_misc('webdav','debug',True)
reg_http_service(HTTPDir(davpath,DAVHandler,OpenERPAuthProvider()))
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% davpath)
handler.verbose = config.get_misc('webdav', 'verbose', True)
handler.debug = config.get_misc('webdav', 'debug', True)
reg_http_service(HTTPDir(davpath, DAVHandler, OpenERPAuthProvider()))
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% davpath)
except Exception, e:
logger = netsvc.Logger()
logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)
#eof
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4

View File

@ -19,9 +19,8 @@
#
##############################################################################
import wizard_cal_export
import wizard_cal_import
import wizard_cal_subscribe
import calendar_event_export
import calendar_event_import
import calendar_event_subscribe
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,70 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import netsvc
import pooler
import time
import tools
import wizard
import base64
class calendar_event_export(osv.osv_memory):
"""
Export Calendar Event.
"""
def process_export_ics(self, cr, uid, ids, context):
"""
Get Default value for file_path field.
"""
model = context.get('model', 'basic.calendar')
model_obj = self.pool.get(model)
calendar = model_obj.export_cal(cr, uid, context['active_ids'], context)
self.write(cr, uid, ids, {'file_path': base64.encodestring(calendar)})
return False
def default_get(self, cr, uid, fields, context):
"""
Get Default value for Name field.
"""
model = context.get('model', 'basic.calendar')
model_obj = self.pool.get(model)
calendar = model_obj.export_cal(cr, uid, context['active_ids'], context)
res = super(calendar_event_export, self).default_get( cr, uid, fields, context=context)
name = 'OpenERP %s.ics' % (model_obj._description)
if 'name' in fields:
res.update({'name': name})
return res
_name = "calendar.event.export"
_description = "Event Export"
_columns = {
'file_path':fields.binary('Save ICS file', filters='*.ics', readonly=True),
'name':fields.char('File name', size=34, required=True, help='Save in .ics format')
}
calendar_event_export()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_export" model="ir.ui.view">
<field name="name">calendar.event.export.form</field>
<field name="model">calendar.event.export</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Export ICS">
<group colspan="4" >
<field name="name"/>
<field name="file_path" colspan="4" width="300"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button name="process_export_ics" icon="gtk-execute" string="Genetate ics" type="object"/>
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_export" model="ir.actions.act_window">
<field name="name">Export .ics File</field>
<field name="res_model">calendar.event.export</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_export"/>
<field name="target">new</field>
</record>
<act_window id="action_calendar_event_export_values"
key2="client_action_multi" name="Export .ics File"
res_model="calendar.event.export" src_model="basic.calendar"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -0,0 +1,82 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import netsvc
import pooler
import time
import tools
import wizard
import base64
class calendar_event_import(osv.osv_memory):
"""
Import Calendar Event.
"""
cnt = 0
def process_imp_ics(self, cr, uid, ids, context=None):
"""
Process Import ics File.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendar event imports IDs
@return: dictionary of calendar evet import window with Import successful msg.
"""
for data in self.read(cr, uid, ids):
model = data.get('model', 'basic.calendar')
model_obj = self.pool.get(model)
context.update({'model': model})
data_obj = self.pool.get('ir.model.data')
id2 = data_obj._get_id(cr, uid, 'caldav', 'view_calendar_event_import_display')
if id2:
id2 = data_obj.browse(cr, uid, id2, context=context).res_id
vals = model_obj.import_cal(cr, uid, data['file_path'], context['active_id'], context)
global cnt
if vals:
cnt = len(vals)
value = {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'calendar.event.import',
'views': [(id2,'form'),(False,'tree'),(False,'calendar'),(False,'graph')],
'type': 'ir.actions.act_window',
'target': 'new'
}
return value
_name = "calendar.event.import"
_description = "Event Import"
_columns = {
'file_path': fields.binary('Select ICS file', filters='*.ics', required=True),
'msg': fields.text('', readonly=True),
}
_defaults = {
'msg':lambda *a:'Import Sucessful'
}
calendar_event_import()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_import" model="ir.ui.view">
<field name="name">calendar.event.import.form</field>
<field name="model">calendar.event.import</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Import ICS">
<group colspan="4" >
<separator string="Select ICS file"/>
<field name="file_path" colspan="4" width="300" nolabel="1"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" name="process_imp_ics" string="_Import" type="object"/>
</group>
</form>
</field>
</record>
<record id="view_calendar_event_import_display" model="ir.ui.view">
<field name="name">calendar.event.import.form.display</field>
<field name="model">calendar.event.import</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Import Message">
<group colspan="4" >
<field name="msg" colspan="4" width="300" nolabel="1"/>
</group>
<separator string="" colspan="4" />
<group colspan="4">
<button icon="gtk-ok" special="cancel" string="Ok"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_import" model="ir.actions.act_window">
<field name="name">Import .ics File</field>
<field name="res_model">calendar.event.import</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_import"/>
<field name="target">new</field>
</record>
<act_window id="action_calendar_event_import_values"
key2="client_action_multi" name="Import .ics File"
res_model="calendar.event.import" src_model="basic.calendar"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -0,0 +1,91 @@
# -*- 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/>.
#
##############################################################################
from osv import fields, osv
from tools.translate import _
import netsvc
import pooler
import time
import tools
import wizard
import base64
import urllib
class calendar_event_subscribe(osv.osv_memory):
"""
Subscribe Calendar Event.
"""
cnt = 0
def process_imp_ics(self, cr, uid, ids, context=None):
"""
process ics file.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of calendar event subscribes IDs
@return: dictionary of calendar evet subscribe window with Import successful msg.
"""
global cnt
for data in self.read(cr, uid, ids):
try:
f = urllib.urlopen(data['url_path'])
caldata = f.fp.read()
f.close()
except Exception,e:
raise osv.except_osv(_('Error!'), _('Please provide Proper URL !'))
model = data.get('model', 'basic.calendar')
model_obj = self.pool.get(model)
context.update({'url': data['url_path'],
'model': data.get('model', 'basic.calendar')})
vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), \
context['active_id'], context)
if vals:
cnt = vals['count']
data_obj = self.pool.get('ir.model.data')
id2 = data_obj._get_id(cr, uid, 'caldav', 'view_calendar_event_subscribe_display')
if id2:
id2 = data_obj.browse(cr, uid, id2, context=context).res_id
value = {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'calendar.event.subscribe',
'views': [(id2,'form'),(False,'tree'),(False,'calendar'),(False,'graph')],
'type': 'ir.actions.act_window',
'target': 'new'
}
return value
_name = "calendar.event.subscribe"
_description = "Event subscribe"
_columns = {
'url_path': fields.char('Provide path for remote calendar', size=124, required=True),
'msg': fields.text('', readonly=True),
}
_defaults = {
'msg':lambda *a:'Import Sucessful.'
}
calendar_event_subscribe()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_subscribe" model="ir.ui.view">
<field name="name">calendar.event.subscribe.form</field>
<field name="model">calendar.event.subscribe</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Subscribe to Remote Calendar">
<group colspan="4" >
<separator string="Provide path for Remote Calendar"/>
<field name="url_path" colspan="4" width="300" nolabel="1" widget="url"/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" name="process_imp_ics" string="_Subscribe" type="object"/>
</group>
</form>
</field>
</record>
<record id="view_calendar_event_subscribe_display" model="ir.ui.view">
<field name="name">calendar.event.subscribe.form.display</field>
<field name="model">calendar.event.subscribe</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Message...">
<group colspan="4" >
<field name="msg" colspan="4" width="300" nolabel="1"/>
</group>
<separator string="" colspan="4" />
<group colspan="4">
<button icon="gtk-ok" special="cancel" string="Ok"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_subscribe" model="ir.actions.act_window">
<field name="name">Subscribe</field>
<field name="res_model">calendar.event.subscribe</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_subscribe"/>
<field name="target">new</field>
</record>
<act_window id="action_calendar_event_subscribe_values"
key2="client_action_multi" name="Subscribe"
res_model="calendar.event.subscribe" src_model="basic.calendar"
view_mode="form" target="new" view_type="form" />
</data>
</openerp>

View File

@ -1,65 +0,0 @@
# -*- 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 wizard
import base64
import pooler
class cal_event_export_wizard(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="Export ICS">
<field name="name"/>
<field name="file_path" colspan="4" width="300"/>
</form>'''
form1_fields = {
'file_path': {
'string': 'Save ICS file',
'type': 'binary',
'required': True,
'filters': '*.ics'
},
'name': {
'string': 'File name',
'type': 'char',
'size': 34,
'required': True,
'help': 'Save in .ics format'},
}
def _process_export_ics(self, cr, uid, data, context):
model = data.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
calendar = model_obj.export_cal(cr, uid, data['ids'], context)
return {'file_path': base64.encodestring(calendar), \
'name': 'OpenERP %s.ics' % (model_obj._description)}
states = {
'init': {
'actions': [_process_export_ics],
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, \
'state': [('end', '_Cancel', 'gtk-cancel')]}},
}
cal_event_export_wizard('calendar.event.export')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,88 +0,0 @@
# -*- 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 wizard
import pooler
class cal_event_import_wizard(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="Import ICS">
<separator string="Select ICS file"/>
<field name="file_path" colspan="4" width="300" nolabel="1"/>
</form>'''
form1_fields = {
'file_path': {
'string': 'Select ICS file',
'type': 'binary',
'required': True,
'filters': '*.ics'
}
}
display = '''<?xml version="1.0"?>
<form string="Import Message">
<field name="msg" colspan="4" width="300" nolabel="1"/>
</form>'''
display_fields = {
'msg': {
'string': '',
'type': 'text',
'readonly': True,
}
}
def _process_imp_ics(self, cr, uid, data, context=None):
model = data.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
context.update({'model': model})
vals = model_obj.import_cal(cr, uid, data['form']['file_path'], data['id'], context)
global cnt
cnt = 0
if vals:
cnt = len(vals)
return {}
def _result_set(self, cr, uid, data, context=None):
msg = (cnt and "Imported %s components" % cnt) or 'Import Sucessful'
return {'msg': msg}
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch': form1, 'fields': form1_fields, \
'state': [('end', '_Cancel', 'gtk-cancel'), ('open', '_Import', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _process_imp_ics, 'state': 'display'}
},
'display': {
'actions': [_result_set],
'result': {'type': 'form', 'arch': display, 'fields': display_fields, \
'state': [('end', 'Ok', 'gtk-ok')]}
},
}
cal_event_import_wizard('calendar.event.import')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,97 +0,0 @@
# -*- 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/>.
#
##############################################################################
from tools.translate import _
import base64
import pooler
import urllib
import wizard
class cal_event_subscribe_wizard(wizard.interface):
form1 = '''<?xml version="1.0"?>
<form string="Subscribe to Remote Calendar">
<separator string="Provide path for Remote Calendar"/>
<field name="url_path" colspan="4" width="300" nolabel="1" widget="url"/>
</form>'''
form1_fields = {
'url_path': {
'string': 'Provide path for remote calendar',
'type': 'char',
'required': True,
'size': 124
}
}
display = '''<?xml version="1.0"?>
<form string="Message...">
<field name="msg" colspan="4" width="300" nolabel="1"/>
</form>'''
display_fields = {
'msg': {
'string': '',
'type': 'text',
'readonly': True,
}
}
def _process_imp_ics(self, cr, uid, data, context=None):
global cnt
cnt = 0
try:
f = urllib.urlopen(data['form']['url_path'])
caldata = f.fp.read()
f.close()
except Exception,e:
raise wizard.except_wizard(_('Error!'), _('Please provide Proper URL !'))
model = data.get('model')
model_obj = pooler.get_pool(cr.dbname).get(model)
context.update({'url': data['form']['url_path'],
'model': data.get('model')})
vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), \
data['id'], context)
if vals:
cnt = vals['count']
return {}
def _result_set(self, cr, uid, data, context=None):
return {'msg': 'Import Sucessful.'}
states = {
'init': {
'actions': [],
'result': {'type': 'form', 'arch': form1, 'fields': form1_fields, \
'state': [('end', '_Cancel', 'gtk-cancel'), ('open', '_Subscribe', 'gtk-ok')]}
},
'open': {
'actions': [],
'result': {'type': 'action', 'action': _process_imp_ics, 'state': 'display'}
},
'display': {
'actions': [_result_set],
'result': {'type': 'form', 'arch': display, 'fields': display_fields, \
'state': [('end', 'Ok', 'gtk-ok')]}
},
}
cal_event_subscribe_wizard('calendar.event.subscribe')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -31,7 +31,6 @@ import crm_claim
import crm_fundraising
import crm_helpdesk
import report
import wizard

View File

@ -61,20 +61,28 @@ between mails and Open ERP.""",
],
'update_xml': [
'wizard/crm_send_email_view.xml',
'wizard/crm_lead_to_partner_view.xml',
'wizard/crm_lead_to_opportunity_view.xml',
'wizard/crm_phonecall_to_phonecall_view.xml',
'wizard/crm_phonecall_to_partner_view.xml',
'wizard/crm_phonecall_to_opportunity_view.xml',
'wizard/crm_opportunity_to_phonecall_view.xml',
'wizard/crm_partner_to_opportunity_view.xml',
'wizard/crm_send_email_view.xml',
'wizard/crm_email_add_cc_view.xml',
'crm_wizard.xml',
'crm_view.xml',
'crm_action_rule_view.xml',
'crm_lead_wizard.xml',
'crm_lead_view.xml',
'crm_lead_menu.xml',
'crm_meeting_wizard.xml',
'crm_meeting_view.xml',
'crm_meeting_menu.xml',
'crm_phonecall_wizard.xml',
'crm_phonecall_view.xml',
'crm_phonecall_menu.xml',
'crm_opportunity_wizard.xml',
'crm_opportunity_view.xml',
'crm_opportunity_menu.xml',
'crm_fund_view.xml',
@ -94,7 +102,6 @@ between mails and Open ERP.""",
'report/crm_fundraising_report_view.xml',
'report/crm_opportunity_report_view.xml' ,
'report/crm_phonecall_report_view.xml',
'report/crm_helpdesk_report_view.xml',
'process/crm_configuration_process.xml',

View File

@ -20,161 +20,242 @@
##############################################################################
import time
import re
import os
import base64
import tools
import mx.DateTime
import datetime
from datetime import datetime
from datetime import timedelta
import tools
from osv import fields
from osv import orm
from osv import osv
from osv.orm import except_orm
from tools.translate import _
MAX_LEVEL = 15
AVAILABLE_STATES = [
('draft','Draft'),
('open','Open'),
('draft', 'Draft'),
('open', 'Open'),
('cancel', 'Cancelled'),
('done', 'Closed'),
('pending','Pending')
('pending', 'Pending')
]
AVAILABLE_PRIORITIES = [
('5','Lowest'),
('4','Low'),
('3','Normal'),
('2','High'),
('1','Highest')
('5', 'Lowest'),
('4', 'Low'),
('3', 'Normal'),
('2', 'High'),
('1', 'Highest')
]
class crm_case_section(osv.osv):
""" Cases Section"""
_name = "crm.case.section"
_description = "Sales Teams"
_order = "name"
_columns = {
'name': fields.char('Sales Team',size=64, required=True, translate=True),
'code': fields.char('Code',size=8),
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the sales team without removing it."),
'name': fields.char('Sales Team', size=64, required=True, translate=True),
'code': fields.char('Code', size=8),
'active': fields.boolean('Active', help="If the active field is set to \
true, it will allow you to hide the sales team without removing it."),
'allow_unlink': fields.boolean('Allow Delete', help="Allows to delete non draft cases"),
'user_id': fields.many2one('res.users', 'Responsible User'),
'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by Open ERP about cases in this sales team"),
'reply_to': fields.char('Reply-To', size=64, help="The email address put \
in the 'Reply-To' of all emails sent by Open ERP about cases in this sales team"),
'parent_id': fields.many2one('crm.case.section', 'Parent Section'),
'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Sections'),
}
_defaults = {
'active': lambda *a: 1,
'allow_unlink': lambda *a: 1,
}
_sql_constraints = [
('code_uniq', 'unique (code)', 'The code of the section must be unique !')
]
def _check_recursion(self, cr, uid, ids):
"""
Checks for recursion level for sections
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of section ids
"""
level = 100
while len(ids):
cr.execute('select distinct parent_id from crm_case_section where id =ANY(%s)',(ids,))
ids = filter(None, map(lambda x:x[0], cr.fetchall()))
cr.execute('select distinct parent_id from crm_case_section where id =ANY(%s)', (ids,))
ids = filter(None, map(lambda x: x[0], cr.fetchall()))
if not level:
return False
level -= 1
return True
_constraints = [
(_check_recursion, 'Error ! You cannot create recursive sections.', ['parent_id'])
]
def name_get(self, cr, uid, ids, context={}):
"""Overrides orm name_get method
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of section ids
"""
if not len(ids):
return []
reads = self.read(cr, uid, ids, ['name','parent_id'], context)
reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
res = []
for record in reads:
name = record['name']
if record['parent_id']:
name = record['parent_id'][1]+' / '+name
name = record['parent_id'][1] + ' / ' + name
res.append((record['id'], name))
return res
crm_case_section()
class crm_case_categ(osv.osv):
""" Category of Case """
_name = "crm.case.categ"
_description = "Category of case"
_columns = {
'name': fields.char('Case Category Name', size=64, required=True, translate=True),
'name': fields.char('Case Category Name', size=64, required=True, translate=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'object_id': fields.many2one('ir.model','Object Name'),
'object_id': fields.many2one('ir.model', 'Object Name'),
}
def _find_object_id(self, cr, uid, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
object_id = context and context.get('object_id', False) or False
ids =self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
_defaults = {
'object_id' : _find_object_id
}
#
#
crm_case_categ()
class crm_case_resource_type(osv.osv):
""" Resource Type of case """
_name = "crm.case.resource.type"
_description = "Resource Type of case"
_rec_name = "name"
_columns = {
'name': fields.char('Case Resource Type', size=64, required=True, translate=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'object_id': fields.many2one('ir.model','Object Name'),
'object_id': fields.many2one('ir.model', 'Object Name'),
}
def _find_object_id(self, cr, uid, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
object_id = context and context.get('object_id', False) or False
ids =self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
_defaults = {
'object_id' : _find_object_id
}
}
crm_case_resource_type()
class crm_case_stage(osv.osv):
""" Stage of case """
_name = "crm.case.stage"
_description = "Stage of case"
_rec_name = 'name'
_order = "sequence"
_columns = {
'name': fields.char('Stage Name', size=64, required=True, translate=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of case stages."),
'object_id': fields.many2one('ir.model','Object Name'),
'sequence': fields.integer('Sequence', help="Gives the sequence order \
when displaying a list of case stages."),
'object_id': fields.many2one('ir.model', 'Object Name'),
'probability': fields.float('Probability (%)', required=True),
'on_change': fields.boolean('Change Probability Automatically',help="Change Probability on next and previous stages."),
'on_change': fields.boolean('Change Probability Automatically',\
help="Change Probability on next and previous stages."),
'requirements': fields.text('Requirements')
}
def _find_object_id(self, cr, uid, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
object_id = context and context.get('object_id', False) or False
ids =self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
_defaults = {
'sequence': lambda *args: 1,
'probability': lambda *args: 0.0,
'object_id' : _find_object_id
}
crm_case_stage()
def _links_get(self, cr, uid, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
obj = self.pool.get('res.request.link')
ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['object', 'name'], context)
return [(r['object'], r['name']) for r in res]
class crm_case(osv.osv):
""" CRM Case """
_name = "crm.case"
_description = "Case"
def _email_last(self, cursor, user, ids, name, arg, context=None):
"""Return History
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of cases IDs
@param context: A standard dictionary for contextual values
"""
res = {}
for case in self.browse(cursor, user, ids):
if case.history_line:
@ -184,85 +265,161 @@ class crm_case(osv.osv):
return res
def copy(self, cr, uid, id, default=None, context={}):
if not default: default = {}
default.update( {'state':'draft', 'id':False})
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of cases IDs
@param context: A standard dictionary for contextual values
"""
if not default:
default = {}
default.update({'state': 'draft', 'id': False})
return super(crm_case, self).copy(cr, uid, id, default, context)
def _get_log_ids(self, cr, uid, ids, field_names, arg, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case logs IDs
@param context: A standard dictionary for contextual values
"""
result = {}
history_obj = False
model_obj = self.pool.get('ir.model')
if 'history_line' in field_names:
history_obj = self.pool.get('crm.case.history')
name = 'history_line'
if 'log_ids' in field_names:
history_obj = self.pool.get('crm.case.log')
name = 'log_ids'
if not history_obj:
return result
for case in self.browse(cr, uid, ids, context):
model_ids = model_obj.search(cr, uid, [('model','=',case._name)])
history_ids = history_obj.search(cr, uid, [('model_id','=',model_ids[0]),('res_id','=',case.id)])
model_ids = model_obj.search(cr, uid, [('model', '=', case._name)])
history_ids = history_obj.search(cr, uid, [('model_id', '=', model_ids[0]),\
('res_id', '=', case.id)])
if history_ids:
result[case.id] = {name:history_ids}
result[case.id] = {name: history_ids}
else:
result[case.id] = {name:[]}
result[case.id] = {name: []}
return result
_columns = {
'id': fields.integer('ID', readonly=True),
'name': fields.char('Description', size=1024, required=True),
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the case without removing it."),
'active': fields.boolean('Active', help="If the active field is set to\
true, it will allow you to hide the case without removing it."),
'description': fields.text('Description'),
'section_id': fields.many2one('crm.case.section', 'Sales Team', select=True, help='Sales team to which Case belongs to. Define Responsible user and Email account for mail gateway.'),
'section_id': fields.many2one('crm.case.section', 'Sales Team',\
select=True, help='Sales team to which Case belongs to.\
Define Responsible user and Email account for mail gateway.'),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'email_cc': fields.text('Watchers Emails', size=252 , help="These people will receive a copy of the future" \
" communication between partner and users by email"),
'email_cc': fields.text('Watchers Emails', size=252 , help="These people\
will receive a copy of the future" \
" communication between partner and users by email"),
'probability': fields.float('Probability'),
'email_last': fields.function(_email_last, method=True,
string='Latest E-Mail', type='text'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', domain="[('partner_id','=',partner_id)]"),
'create_date': fields.datetime('Creation Date' ,readonly=True),
'write_date': fields.datetime('Update Date' ,readonly=True),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact',\
domain="[('partner_id','=',partner_id)]"),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Update Date' , readonly=True),
'date_deadline': fields.date('Deadline'),
'user_id': fields.many2one('res.users', 'Responsible'),
'history_line': fields.function(_get_log_ids, method=True, type='one2many', multi="history_line", relation="crm.case.history", string="Communication"),
'log_ids': fields.function(_get_log_ids, method=True, type='one2many', multi="log_ids", relation="crm.case.log", string="Logs History"),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.opportunity')]"),
'history_line': fields.function(_get_log_ids, method=True, type='one2many',\
multi="history_line", relation="crm.case.history", string="Communication"),
'log_ids': fields.function(_get_log_ids, method=True, type='one2many',\
multi="log_ids", relation="crm.case.log", string="Logs History"),
'stage_id': fields.many2one ('crm.case.stage', 'Stage',\
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.opportunity')]"),
'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True,
help='The state is set to \'Draft\', when a case is created.\
\nIf the case is in progress the state is set to \'Open\'.\
\nWhen the case is over, the state is set to \'Done\'.\
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
'company_id': fields.many2one('res.company','Company'),
'company_id': fields.many2one('res.company', 'Company'),
}
def _get_default_partner_address(self, cr, uid, context):
if not context.get('portal',False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
if not context.get('portal', False):
return False
return self.pool.get('res.users').browse(cr, uid, uid, context).address_id.id
def _get_default_partner(self, cr, uid, context):
if not context.get('portal',False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context)
if not user.address_id:
return False
return user.address_id.partner_id.id
def _get_default_email(self, cr, uid, context):
if not context.get('portal',False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context)
if not user.address_id:
return False
return user.address_id.email
def _get_default_user(self, cr, uid, context):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
if context.get('portal', False):
return False
return uid
def _get_section(self, cr, uid, context):
user = self.pool.get('res.users').browse(cr, uid, uid,context=context)
return user.context_section_id.id or False
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.context_section_id.id or False
_defaults = {
'active': lambda *a: 1,
@ -272,11 +429,20 @@ class crm_case(osv.osv):
'email_from': _get_default_email,
'state': lambda *a: 'draft',
'section_id': _get_section,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
}
_order = 'date_deadline desc, create_date desc,id desc'
def unlink(self, cr, uid, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of cases IDs
@param context: A standard dictionary for contextual values"""
for case in self.browse(cr, uid, ids, context):
if (not case.section_id.allow_unlink) and (case.state <> 'draft'):
raise osv.except_osv(_('Warning !'),
@ -284,17 +450,32 @@ class crm_case(osv.osv):
return super(crm_case, self).unlink(cr, uid, ids, context)
def stage_next(self, cr, uid, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of stage nexts IDs
@param context: A standard dictionary for contextual values"""
s = self.get_stage_dict(cr, uid, ids, context=context)
for case in self.browse(cr, uid, ids, context):
section = (case.section_id.id or False)
if section in s:
st = case.stage_id.id or False
if st in s[section]:
if st in s[section]:
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
return True
def get_stage_dict(self, cr, uid, ids, context={}):
sid = self.pool.get('crm.case.stage').search(cr, uid, [('object_id.model', '=', self._name)], context=context)
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of stage dicts IDs
@param context: A standard dictionary for contextual values"""
sid = self.pool.get('crm.case.stage').search(cr, uid,\
[('object_id.model', '=', self._name)], context=context)
s = {}
previous = {}
for stage in self.pool.get('crm.case.stage').browse(cr, uid, sid, context=context):
@ -303,11 +484,19 @@ class crm_case(osv.osv):
s[section][previous.get(section, False)] = stage.id
previous[section] = stage.id
return s
def stage_previous(self, cr, uid, ids, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of stage previouss IDs
@param context: A standard dictionary for contextual values"""
s = self.get_stage_dict(cr, uid, ids, context=context)
for case in self.browse(cr, uid, ids, context):
section = (case.section_id.id or False)
if section in s:
st = case.stage_id.id or False
s[section] = dict([(v, k) for (k, v) in s[section].iteritems()])
@ -315,29 +504,41 @@ class crm_case(osv.osv):
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
return True
def history(self, cr, uid, ids, keyword, history=False, email=False, details=None, email_from=False, context={}):
def history(self, cr, uid, ids, keyword, history=False, email=False, details=None, context={}):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Historys IDs
@param context: A standard dictionary for contextual values"""
cases = self.browse(cr, uid, ids, context=context)
return self._history(cr, uid, cases, keyword=keyword,\
history=history, email=email, details=details, email_from=email_from, \
return self.__history(cr, uid, cases, keyword=keyword, \
history=history, email=email, details=details, \
context=context)
def __history(self, cr, uid, cases, keyword, history=False, email=False, details=None, email_from=False, context={}):
model_obj = self.pool.get('ir.model')
if email and type(email) == type([]):
email = ','.join(email)
if email_from and type(email_from) == type([]):
email_from = ','.join(email_from)
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Historys IDs
@param context: A standard dictionary for contextual values"""
model_obj = self.pool.get('ir.model')
for case in cases:
model_ids = model_obj.search(cr, uid, [('model','=',case._name)])
model_ids = model_obj.search(cr, uid, [('model', '=', case._name)])
data = {
'name': keyword,
'name': keyword,
'user_id': uid,
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'model_id' : model_ids and model_ids[0] or False,
'res_id': case.id,
'section_id': case.section_id.id
}
obj = self.pool.get('crm.case.log')
if history:
obj = self.pool.get('crm.case.history')
@ -355,40 +556,78 @@ class crm_case(osv.osv):
_history = __history
def create(self, cr, uid, *args, **argv):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param *args: Give Tuple Value
@Paran **args: Give Dictionay of Keyword Value
"""
res = super(crm_case, self).create(cr, uid, *args, **argv)
cases = self.browse(cr, uid, [res])
cases[0].state # to fill the browse record cache
self._action(cr,uid, cases, 'draft')
self._action(cr, uid, cases, 'draft')
return res
def add_reply(self, cursor, user, ids, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
for case in self.browse(cursor, user, ids, context=context):
if case.email_last:
description = case.email_last
self.write(cursor, user, case.id, {
'description': '> ' + description.replace('\n','\n> '),
'description': '> ' + description.replace('\n', '\n> '),
}, context=context)
return True
def case_log(self, cr, uid, ids,context={}, email=False, *args):
def case_log(self, cr, uid, ids, context={}, email=False, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
self.__history(cr, uid, cases, _('Historize'), history=True, email=email)
return self.write(cr, uid, ids, {'description': False, 'som': False,
'canal_id': False})
def case_log_reply(self, cr, uid, ids, context={}, email=False, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
for case in cases:
if not case.email_from:
raise osv.except_osv(_('Error!'),
_('You must put a Partner eMail to use this action!'))
if not case.user_id:
raise osv.except_osv(_('Error!'),
_('You must define a responsible user for this case in order to use this action!'))
if not case.description:
raise osv.except_osv(_('Error!'),
_('Can not send mail with empty body,you should have description in the body'))
self.__history(cr, uid, cases, _('Send'), history=True, email=False)
for case in cases:
self.write(cr, uid, [case.id], {
'description': False,
@ -405,49 +644,82 @@ class crm_case(osv.osv):
_("No E-Mail ID Found for your Company address!"))
tools.email_send(
emailfrom,
emails,
'['+str(case.id)+'] '+case.name,
self.format_body(body),
reply_to=case.section_id.reply_to,
openobject_id=str(case.id)
emailfrom,
emails,
'[' + str(case.id) + '] ' + case.name,
self.format_body(body),
reply_to = case.section_id.reply_to,
openobject_id = str(case.id)
)
self.__history(cr, uid, [case], _('Send'), history=True, email=emails, details=body, email_from=emailfrom)
return True
def onchange_partner_id(self, cr, uid, ids, part, email=False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
"""
if not part:
return {'value':{'partner_address_id': False,
return {'value': {'partner_address_id': False,
'email_from': False,
}}
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data = {'partner_address_id': addr['contact']}
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
return {'value':data}
return {'value': data}
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
"""
data = {}
if not add:
return {'value': {'email_from': False, 'partner_name2': False}}
address= self.pool.get('res.partner.address').browse(cr, uid, add)
address = self.pool.get('res.partner.address').browse(cr, uid, add)
data['email_from'] = address.email
return {'value': data}
def case_close(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.__history(cr, uid, cases, _('Close'))
self.write(cr, uid, ids, {'state':'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, ids, {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
#
# We use the cache of cases to keep the old case state
#
self._action(cr,uid, cases, 'done')
self._action(cr, uid, cases, 'done')
return True
def case_escalate(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
for case in cases:
data = {'active':True, 'user_id': False}
data = {'active': True, 'user_id': False}
if case.section_id.parent_id:
data['section_id'] = case.section_id.parent_id.id
if case.section_id.parent_id.user_id:
@ -457,78 +729,129 @@ class crm_case(osv.osv):
self.write(cr, uid, ids, data)
cases = self.browse(cr, uid, ids)
self.__history(cr, uid, cases, _('Escalate'))
self._action(cr,uid, cases, 'escalate')
self._action(cr, uid, cases, 'escalate')
return True
def case_open(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
self.__history(cr, uid, cases, _('Open'))
for case in cases:
data = {'state':'open', 'active':True}
data = {'state': 'open', 'active': True}
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, ids, data)
self._action(cr,uid, cases, 'open')
self._action(cr, uid, cases, 'open')
return True
def case_cancel(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.__history(cr, uid, cases, _('Cancel'))
self.write(cr, uid, ids, {'state':'cancel', 'active':True})
self._action(cr,uid, cases, 'cancel')
self.write(cr, uid, ids, {'state': 'cancel', 'active': True})
self._action(cr, uid, cases, 'cancel')
return True
def case_pending(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.__history(cr, uid, cases, _('Pending'))
self.write(cr, uid, ids, {'state':'pending', 'active':True})
self._action(cr,uid, cases, 'pending')
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
self._action(cr, uid, cases, 'pending')
return True
def case_reset(self, cr, uid, ids, *args):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.__history(cr, uid, cases, _('Draft'))
self.write(cr, uid, ids, {'state':'draft', 'active':True})
self._action(cr,uid, cases, 'draft')
return True
self.write(cr, uid, ids, {'state': 'draft', 'active': True})
self._action(cr, uid, cases, 'draft')
return True
crm_case()
class crm_case_log(osv.osv):
""" Case Communication History """
_name = "crm.case.log"
_description = "Case Communication History"
_order = "id desc"
_columns = {
'name': fields.char('Status', size=64),
'date': fields.datetime('Date'),
'section_id': fields.many2one('crm.case.section', 'Section'),
'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
'model_id': fields.many2one('ir.model', "Model"),
'res_id': fields.integer('Resource ID'),
'date': fields.datetime('Date'),
'section_id': fields.many2one('crm.case.section', 'Section'),
'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
'model_id': fields.many2one('ir.model', "Model"),
'res_id': fields.integer('Resource ID'),
}
_defaults = {
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
}
crm_case_log()
class crm_case_history(osv.osv):
"""Case history"""
_name = "crm.case.history"
_description = "Case history"
_order = "id desc"
_inherits = {'crm.case.log':"log_id"}
_inherits = {'crm.case.log': "log_id"}
def _note_get(self, cursor, user, ids, name, arg, context=None):
""" @param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case Historys IDs
@param context: A standard dictionary for contextual values
"""
res = {}
for hist in self.browse(cursor, user, ids, context or {}):
res[hist.id] = (hist.email or '/') + ' (' + str(hist.date) + ')\n'
res[hist.id] += (hist.description or '')
return res
_columns = {
'description': fields.text('Description'),
'note': fields.function(_note_get, method=True, string="Description", type="text"),
@ -536,62 +859,9 @@ class crm_case_history(osv.osv):
'email_from' : fields.char('Email From', size=84),
'log_id': fields.many2one('crm.case.log','Log',ondelete='cascade'),
}
crm_case_history()
class crm_email_add_cc_wizard(osv.osv_memory):
_name = "crm.email.add.cc"
_description = "Email Add CC"
_columns = {
'name': fields.selection([('user','User'),('partner','Partner'),('email','Email Address')], 'Send to', required=True),
'user_id': fields.many2one('res.users',"User"),
'partner_id': fields.many2one('res.partner',"Partner"),
'email': fields.char('Email', size=32),
'subject': fields.char('Subject', size=32),
}
def change_email(self, cr, uid, ids, user, partner):
if (not partner and not user):
return {'value':{'email': False}}
email = False
if partner:
addr = self.pool.get('res.partner').address_get(cr, uid, [partner], ['contact'])
if addr:
email = self.pool.get('res.partner.address').read(cr, uid,addr['contact'] , ['email'])['email']
elif user:
addr = self.pool.get('res.users').read(cr, uid, user, ['address_id'])['address_id']
if addr:
email = self.pool.get('res.partner.address').read(cr, uid,addr[0] , ['email'])['email']
return {'value':{'email': email}}
def add_cc(self, cr, uid, ids, context={}):
data = self.read(cr, uid, ids[0])
email = data['email']
subject = data['subject']
if not context:
return {}
history_line = self.pool.get('crm.case.history').browse(cr, uid, context['active_id'])
model = history_line.log_id.model_id.model
model_pool = self.pool.get(model)
case = model_pool.browse(cr, uid, history_line.log_id.res_id)
body = history_line.description.replace('\n','\n> ')
flag = tools.email_send(
email,
[case.user_id.address_id.email],
subject or '['+str(case.id)+'] '+case.name,
model_pool.format_body(body),
openobject_id=str(case.id),
subtype="html"
)
if flag:
model_pool.write(cr, uid, case.id, {'email_cc' : case.email_cc and case.email_cc +','+ email or email})
self.__history(cr, uid, [case], _('Send'), history=True, email=email, details=body, email_from=case.user_id.address_id.email)
else:
raise osv.except_osv(_('Email Fail!'),("Lastest Email is not sent successfully"))
return {}
crm_email_add_cc_wizard()
class users(osv.osv):
_inherit = 'res.users'
@ -607,8 +877,6 @@ class res_partner(osv.osv):
_columns = {
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
}
res_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,26 +27,47 @@ import tools
import mx.DateTime
from tools.translate import _
from osv import fields
from osv import osv
from osv import fields
from osv import osv
from osv import orm
from osv.orm import except_orm
import crm
class case(osv.osv):
""" Case """
_inherit = 'crm.case'
_description = 'case'
_columns = {
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
}
def remind_partner(self, cr, uid, ids, context={}, attach=False):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Remind Partner's IDs
@param context: A standard dictionary for contextual values
"""
return self.remind_user(cr, uid, ids, context, attach,
destination=False)
def remind_user(self, cr, uid, ids, context={}, attach=False,
destination=True):
def remind_user(self, cr, uid, ids, context={}, attach=False,destination=True):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Remind user's IDs
@param context: A standard dictionary for contextual values
"""
for case in self.browse(cr, uid, ids):
if not case.section_id.reply_to:
raise osv.except_osv(_('Error!'), ("Reply To is not specified in Section"))
@ -90,31 +111,37 @@ class case(osv.osv):
return True
def _check(self, cr, uid, ids=False, context={}):
'''
"""
Function called by the scheduler to process cases for date actions
Only works on not done and cancelled cases
'''
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
cr.execute('select * from crm_case \
where (date_action_last<%s or date_action_last is null) \
and (date_action_next<=%s or date_action_next is null) \
and state not in (\'cancel\',\'done\')',
(time.strftime("%Y-%m-%d %H:%M:%S"),
time.strftime('%Y-%m-%d %H:%M:%S')))
ids2 = map(lambda x: x[0], cr.fetchall() or [])
cases = self.browse(cr, uid, ids2, context)
return self._action(cr, uid, cases, False, context=context)
def _action(self, cr, uid, cases, state_to, scrit=None, context={}):
ids2 = map(lambda x: x[0], cr.fetchall() or [])
cases = self.browse(cr, uid, ids2, context)
return self._action(cr, uid, cases, False, context=context)
def _action(self, cr, uid, cases, state_to, scrit=None, context={}):
if not context:
context = {}
context['state_to'] = state_to
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('name','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
def format_body(self, body):
def format_body(self, body):
return self.pool.get('base.action.rule').format_body(body)
def format_mail(self, obj, body):
@ -122,6 +149,7 @@ class case(osv.osv):
case()
class base_action_rule(osv.osv):
""" Base Action Rule """
_inherit = 'base.action.rule'
_description = 'Action Rules'
@ -143,8 +171,14 @@ class base_action_rule(osv.osv):
return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
def do_check(self, cr, uid, action, obj, context={}):
""" @param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values"""
ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context)
if hasattr(obj, 'section_id'):
ok = ok and (not action.trg_section_id or action.trg_section_id.id==obj.section_id.id)
if hasattr(obj, 'categ_id'):
@ -164,13 +198,19 @@ class base_action_rule(osv.osv):
return ok
def do_action(self, cr, uid, action, model_obj, obj, context={}):
res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)
""" @param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)
write = {}
if hasattr(action, act_section_id) and action.act_section_id:
obj.section_id = action.act_section_id
write['section_id'] = action.act_section_id.id
write['section_id'] = action.act_section_id.id
if hasattr(obj, 'email_cc') and action.act_email_cc:
if '@' in (obj.email_cc or ''):
emails = obj.email_cc.split(",")
@ -178,9 +218,10 @@ class base_action_rule(osv.osv):
write['email_cc'] = obj.email_cc+','+obj.act_email_cc
else:
write['email_cc'] = obj.act_email_cc
model_obj.write(cr, uid, [obj.id], write, context)
emails = []
if hasattr(obj, 'email_from') and action.act_mail_to_partner:
emails.append(obj.email_from)
emails = filter(None, emails)
@ -188,29 +229,44 @@ class base_action_rule(osv.osv):
emails = list(set(emails))
self.email_send(cr, uid, obj, emails, action.act_mail_body)
return True
base_action_rule()
class base_action_rule_line(osv.osv):
""" Base Action Rule Line """
_inherit = 'base.action.rule.line'
_description = 'Base Action Rule Line'
def state_get(self, cr, uid, context={}):
res = super(base_action_rule_line, self).state_get(cr, uid, context=context)
"""@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
res = super(base_action_rule_line, self).state_get(cr, uid, context=context)
return res + [('escalate','Escalate')] + crm.AVAILABLE_STATES
def priority_get(self, cr, uid, context={}):
res = super(base_action_rule_line, self).priority_get(cr, uid, context=context)
"""@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values """
res = super(base_action_rule_line, self).priority_get(cr, uid, context=context)
return res + crm.AVAILABLE_PRIORITIES
_columns = {
_columns = {
'trg_section_id': fields.many2one('crm.case.section', 'Section'),
'trg_max_history': fields.integer('Maximum Communication History'),
'trg_categ_id': fields.many2one('crm.case.categ', 'Category'),
'trg_categ_id': fields.many2one('crm.case.categ', 'Category'),
'regex_history' : fields.char('Regular Expression on Case History', size=128),
'act_section_id': fields.many2one('crm.case.section', 'Set section to'),
'act_section_id': fields.many2one('crm.case.section', 'Set section to'),
'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'),
'act_mail_to_partner': fields.boolean('Mail to partner',help="Check this if you want the rule to send an email to the partner."),
'act_mail_to_partner': fields.boolean('Mail to partner',help="Check this \
if you want the rule to send an email to the partner."),
}
base_action_rule_line()

40
addons/crm/crm_claim.py Normal file → Executable file
View File

@ -23,6 +23,9 @@ from osv import fields, osv
import crm
class crm_claim(osv.osv):
"""
Crm claim
"""
_name = "crm.claim"
_description = "Claim Cases"
_order = "id desc"
@ -32,28 +35,39 @@ class crm_claim(osv.osv):
'date': fields.datetime('Date'),
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128),
'canal_id': fields.many2one('res.partner.canal', 'Channel',help="The channels represent the different communication modes available with the customer." \
" With each commercial opportunity, you can indicate the canall which is this opportunity source."),
'canal_id': fields.many2one('res.partner.canal', 'Channel',\
help="The channels represent the different communication\
modes available with the customer." \
" With each commercial opportunity, you can indicate the\
canall which is this opportunity source."),
'planned_revenue': fields.float('Planned Revenue'),
'planned_cost': fields.float('Planned Costs'),
'som': fields.many2one('res.partner.som', 'State of Mind', help="The minds states allow to define a value scale which represents" \
"the partner mentality in relation to our services.The scale has" \
"to be created with a factor for each level from 0 (Very dissatisfied) to 10 (Extremely satisfied)."),
'categ_id': fields.many2one('crm.case.categ','Category', domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.claim')]"),
'som': fields.many2one('res.partner.som', 'State of Mind', \
help="The minds states allow to define a value scale which represents" \
"the partner mentality in relation to our services.The scale has" \
"to be created with a factor for each level from 0 (Very dissatisfied) \
to 10 (Extremely satisfied)."),
'categ_id': fields.many2one('crm.case.categ', 'Category',\
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.claim')]"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
'type_id': fields.many2one('crm.case.resource.type', 'Claim Type', domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.claim')]"),
'type_id': fields.many2one('crm.case.resource.type', 'Claim Type',\
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.claim')]"),
'partner_name': fields.char("Employee's Name", size=64),
'partner_mobile': fields.char('Mobile', size=32),
'partner_phone': fields.char('Phone', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.claim')]"),
'probability': fields.float('Probability (%)'),
'stage_id': fields.many2one ('crm.case.stage', 'Stage',\
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.claim')]"),
'probability': fields.float('Probability (%)'),
}
_defaults = {
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
}
crm_claim()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Some files were not shown because too many files have changed in this diff Show More