Merged with current trunk
bzr revid: jean-baptiste.aubort@camptocamp.com-20080723150218-vcnmpetwo1hpp9v5
This commit is contained in:
commit
2f93559d6e
|
@ -45,8 +45,8 @@ logger = netsvc.Logger()
|
|||
|
||||
opj = os.path.join
|
||||
|
||||
_ad = opj(tools.config['root_path'], 'addons') # default addons path (base)
|
||||
ad = tools.config['addons_path'] # alternate addons path
|
||||
_ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base)
|
||||
ad = os.path.abspath(tools.config['addons_path']) # alternate addons path
|
||||
|
||||
sys.path.insert(1, _ad)
|
||||
if ad != _ad:
|
||||
|
@ -131,10 +131,37 @@ class Node(Singleton):
|
|||
s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
|
||||
return s
|
||||
|
||||
def _get_module_path(module):
|
||||
if os.path.exists(opj(ad, module)):
|
||||
def get_module_path(module):
|
||||
"""Return the path of the given module.
|
||||
"""
|
||||
|
||||
if os.path.exists(opj(ad, module)) or os.path.exists(opj(ad, '%s.zip' % module)):
|
||||
return opj(ad, module)
|
||||
return opj(_ad, module)
|
||||
|
||||
if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)):
|
||||
return opj(_ad, module)
|
||||
|
||||
raise IOError, 'Module not found : %s' % module
|
||||
|
||||
def get_module_resource(module, *args):
|
||||
"""Return the full path of a resource of the given module.
|
||||
|
||||
@param module: the module
|
||||
@param args: the resource path components
|
||||
|
||||
@return: absolute path to the resource
|
||||
"""
|
||||
return opj(get_module_path(module), *args)
|
||||
|
||||
def get_modules():
|
||||
"""Returns the list of module names
|
||||
"""
|
||||
|
||||
module_list = os.listdir(ad)
|
||||
module_names = [os.path.basename(m) for m in module_list]
|
||||
module_list += [m for m in os.listdir(_ad) if m not in module_names]
|
||||
|
||||
return module_list
|
||||
|
||||
def create_graph(module_list, force=None):
|
||||
if not force:
|
||||
|
@ -145,9 +172,9 @@ def create_graph(module_list, force=None):
|
|||
for module in module_list:
|
||||
if module[-4:]=='.zip':
|
||||
module = module[:-4]
|
||||
mod_path = _get_module_path(module)
|
||||
terp_file = opj(mod_path, '__terp__.py')
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
||||
mod_path = get_module_path(module)
|
||||
terp_file = get_module_resource(module, '__terp__.py')
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path):
|
||||
try:
|
||||
info = eval(tools.file_open(terp_file).read())
|
||||
except:
|
||||
|
@ -254,24 +281,20 @@ def load_module_graph(cr, graph, status=None, **kwargs):
|
|||
cr.commit()
|
||||
|
||||
def register_classes():
|
||||
module_list = os.listdir(ad)
|
||||
module_names = [os.path.basename(m) for m in module_list]
|
||||
module_list += [m for m in os.listdir(_ad) if m not in module_names]
|
||||
|
||||
module_list = get_modules()
|
||||
for package in create_graph(module_list):
|
||||
m = package.name
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:registering classes' % m)
|
||||
sys.stdout.flush()
|
||||
|
||||
mod_path = _get_module_path(m)
|
||||
if not os.path.isfile(mod_path + '.zip'):
|
||||
mod_path = get_module_path(m)
|
||||
if not os.path.isfile(mod_path+'.zip'):
|
||||
# XXX must restrict to only addons paths
|
||||
imp.load_module(m, *imp.find_module(m))
|
||||
else:
|
||||
import zipimport
|
||||
mod_path = mod_path + '.zip'
|
||||
try:
|
||||
zimp = zipimport.zipimporter(mod_path)
|
||||
zimp = zipimport.zipimporter(mod_path+'.zip')
|
||||
zimp.load_module(m)
|
||||
except zipimport.ZipImportError:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'Couldn\'t find module %s' % m)
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
from osv import fields,osv
|
||||
import tools
|
||||
import time
|
||||
from tools.config import config
|
||||
import netsvc
|
||||
|
||||
class actions(osv.osv):
|
||||
_name = 'ir.actions.actions'
|
||||
|
@ -306,6 +308,7 @@ class actions_server(osv.osv):
|
|||
# False : Finnished correctly
|
||||
# ACTION_ID : Action to launch
|
||||
def run(self, cr, uid, ids, context={}):
|
||||
logger = netsvc.Logger()
|
||||
for action in self.browse(cr, uid, ids, context):
|
||||
if action.state=='python':
|
||||
localdict = {
|
||||
|
@ -316,11 +319,17 @@ class actions_server(osv.osv):
|
|||
'cr': cr,
|
||||
'uid': uid
|
||||
}
|
||||
print action.code
|
||||
exec action.code in localdict
|
||||
print localdict.keys()
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
if action.state == 'email':
|
||||
user = "%s@yahoo.co.in," % (config['smtp_user'])
|
||||
subject = action.name
|
||||
body = action.message
|
||||
if tools.email_send_attach(user, action.address, subject, body) == True:
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (user))
|
||||
else:
|
||||
logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (user))
|
||||
return False
|
||||
actions_server()
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ class ir_rule(osv.osv):
|
|||
query_global, val_global = _query(clause_global, 'OR')
|
||||
if query_global:
|
||||
if query:
|
||||
query = '('+query+') AND '+query_global
|
||||
query = '('+query+') OR '+query_global
|
||||
val.extend(val_global)
|
||||
else:
|
||||
query = query_global
|
||||
|
|
|
@ -43,6 +43,7 @@ TRANSLATION_TYPE = [
|
|||
('xsl', 'XSL'),
|
||||
('help', 'Help'),
|
||||
('code', 'Code'),
|
||||
('constraint', 'Constraint'),
|
||||
]
|
||||
|
||||
class ir_translation(osv.osv, Cacheable):
|
||||
|
@ -138,12 +139,10 @@ class ir_translation(osv.osv, Cacheable):
|
|||
'and name=%s',
|
||||
(lang, tt, str(name)))
|
||||
res = cr.fetchone()
|
||||
if res:
|
||||
self.add((lang, tt, name, source), res[0])
|
||||
return res[0]
|
||||
else:
|
||||
self.add((lang, tt, name, source), False)
|
||||
return False
|
||||
|
||||
trad = res and res[0] or ''
|
||||
self.add((lang, tt, name, source), trad)
|
||||
return trad
|
||||
|
||||
def unlink(self, cursor, user, ids, context=None):
|
||||
self.clear()
|
||||
|
|
|
@ -38,9 +38,7 @@ import release
|
|||
import zipimport
|
||||
|
||||
import wizard
|
||||
|
||||
|
||||
|
||||
import addons
|
||||
|
||||
ver_regexp = re.compile("^(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
|
||||
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
|
||||
|
@ -199,7 +197,7 @@ class module(osv.osv):
|
|||
|
||||
def get_module_info(self, name):
|
||||
try:
|
||||
f = tools.file_open(os.path.join(tools.config['addons_path'], name, '__terp__.py'))
|
||||
f = tools.file_open(os.path.join(name, '__terp__.py'))
|
||||
data = f.read()
|
||||
info = eval(data)
|
||||
if 'version' in info:
|
||||
|
@ -343,18 +341,17 @@ class module(osv.osv):
|
|||
filepath = files[lang]
|
||||
# if filepath does not contain :// we prepend the path of the module
|
||||
if filepath.find('://') == -1:
|
||||
filepath = os.path.join(tools.config['addons_path'], module['name'], filepath)
|
||||
filepath = addons.get_module_resource(module['name'], filepath)
|
||||
tools.trans_load(filepath, lang)
|
||||
return True
|
||||
|
||||
# update the list of available packages
|
||||
def update_list(self, cr, uid, context={}):
|
||||
robj = self.pool.get('ir.module.repository')
|
||||
adp = tools.config['addons_path']
|
||||
res = [0, 0] # [update, add]
|
||||
|
||||
# iterate through installed modules and mark them as being so
|
||||
for name in os.listdir(adp):
|
||||
for name in addons.get_modules():
|
||||
mod_name = name
|
||||
if name[-4:]=='.zip':
|
||||
mod_name=name[:-4]
|
||||
|
@ -384,20 +381,19 @@ class module(osv.osv):
|
|||
self._update_category(cr, uid, ids[0], terp.get('category',
|
||||
'Uncategorized'))
|
||||
continue
|
||||
terp_file = os.path.join(adp, name, '__terp__.py')
|
||||
mod_path = os.path.join(adp, name)
|
||||
terp_file = addons.get_module_resource(name, '__terp__.py')
|
||||
mod_path = addons.get_module_path(name)
|
||||
if os.path.isdir(mod_path) or os.path.islink(mod_path) or zipfile.is_zipfile(mod_path):
|
||||
terp = self.get_module_info(mod_name)
|
||||
if not terp or not terp.get('installable', True):
|
||||
continue
|
||||
if not os.path.isfile(os.path.join(adp, mod_name+'.zip')):
|
||||
if not os.path.isfile(mod_path+'.zip'):
|
||||
import imp
|
||||
# XXX must restrict to only addons paths
|
||||
imp.load_module(name, *imp.find_module(mod_name))
|
||||
else:
|
||||
import zipimport
|
||||
mod_path = os.path.join(adp, mod_name+'.zip')
|
||||
zimp = zipimport.zipimporter(mod_path)
|
||||
zimp = zipimport.zipimporter(mod_path+'.zip')
|
||||
zimp.load_module(mod_name)
|
||||
id = self.create(cr, uid, {
|
||||
'name': mod_name,
|
||||
|
@ -472,7 +468,6 @@ class module(osv.osv):
|
|||
|
||||
def download(self, cr, uid, ids, download=True, context=None):
|
||||
res = []
|
||||
adp = tools.config['addons_path']
|
||||
for mod in self.browse(cr, uid, ids, context=context):
|
||||
if not mod.url:
|
||||
continue
|
||||
|
@ -486,7 +481,7 @@ class module(osv.osv):
|
|||
if not download:
|
||||
continue
|
||||
zipfile = urllib.urlopen(mod.url).read()
|
||||
fname = os.path.join(adp, mod.name+'.zip')
|
||||
fname = addons.get_module_path(mod.name+'.zip')
|
||||
try:
|
||||
fp = file(fname, 'wb')
|
||||
fp.write(zipfile)
|
||||
|
|
|
@ -27,22 +27,26 @@
|
|||
<field name="model">wizard.module.lang.export</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form col="4" string="Export language">
|
||||
<form col="3" string="Export language">
|
||||
<image name="gtk-dialog-info"/>
|
||||
<group col="2" states="choose">
|
||||
<group col="2" states="choose" fill="0" height="500">
|
||||
<separator string="Export translation file" colspan="2"/>
|
||||
<label align="0.0" string="Choose a language to export:"/>
|
||||
<field name="lang" nolabel="1"/>
|
||||
<button icon="gtk-cancel" name="act_cancel" special="cancel" string="Cancel" type="object"/>
|
||||
<button icon="gtk-ok" name="act_getfile" string="Get file" type="object"/>
|
||||
<field name="lang" width="300"/>
|
||||
<field name="format"/>
|
||||
<field name="modules" width="500" height="200"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<newline/><label/>
|
||||
</group>
|
||||
<group col="1" states="get">
|
||||
<separator string="Export done" colspan="1"/>
|
||||
<field name="data" readonly="1"/>
|
||||
<label string="Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it."/>
|
||||
<button icon="gtk-close" name="act_destroy" special="cancel" string="Close" type="object"/>
|
||||
<group col="1" states="get" fill="0">
|
||||
<separator string="Export done"/>
|
||||
<field name="data" readonly="1" nolabel="1"/>
|
||||
<field name="advice" nolabel="1" height="80"/>
|
||||
</group>
|
||||
<group col="2" colspan="3" fill="0">
|
||||
<button states="choose" icon="gtk-cancel" name="act_cancel" special="cancel" string="Cancel" type="object"/>
|
||||
<button states="choose" icon="gtk-ok" name="act_getfile" string="Get file" type="object"/>
|
||||
<button states="get" icon="gtk-close" name="act_destroy" special="cancel" string="Close" type="object"/>
|
||||
</group>
|
||||
<field name="state" readonly="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -122,36 +122,31 @@ class wizard_export_lang(osv.osv_memory):
|
|||
return {'type':'ir.actions.act_window_close' }
|
||||
|
||||
def act_getfile(self, cr, uid, ids, context=None):
|
||||
print "get filE"
|
||||
this = self.browse(cr, uid, ids)[0]
|
||||
print this.lang
|
||||
# set the data
|
||||
file=tools.trans_generate(this.lang, 'all', dbname=cr.dbname)
|
||||
mods = map(lambda m: m.name, this.modules)
|
||||
mods.sort()
|
||||
buf=StringIO.StringIO()
|
||||
writer=csv.writer(buf, 'UNIX')
|
||||
for row in file:
|
||||
writer.writerow(row)
|
||||
del file
|
||||
|
||||
tools.trans_export(this.lang, mods, buf, this.format, dbname=cr.dbname)
|
||||
|
||||
if this.format == 'csv':
|
||||
this.advice = _("Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it.")
|
||||
elif this.format == 'po':
|
||||
this.advice = _("Save this document to a .po file and edit it with a specific software or a text editor. The file encoding is UTF-8.")
|
||||
|
||||
out=base64.encodestring(buf.getvalue())
|
||||
buf.close()
|
||||
|
||||
self.write(cr, uid, ids, {'state':'get','data':out}, context=context)
|
||||
|
||||
return {
|
||||
'view_type': 'form',
|
||||
"view_mode": 'form',
|
||||
'res_model': self._name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target':'new',
|
||||
}
|
||||
return self.write(cr, uid, ids, {'state':'get', 'data':out, 'advice':this.advice}, context=context)
|
||||
|
||||
_name = "wizard.module.lang.export"
|
||||
_columns = {
|
||||
'lang': fields.selection(_get_languages, 'Language',required=True),
|
||||
'format': fields.selection( ( ('csv','CSV File'), ('po','PO File') ), 'File Format', required=True),
|
||||
'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Modules', domain=[('state','=','installed')]),
|
||||
'data': fields.binary('File', readonly=True),
|
||||
'advice': fields.text('', readonly=True),
|
||||
'state': fields.selection( ( ('choose','choose'), # choose language
|
||||
('get','get'), # get the file
|
||||
#('end','end'), # virtual state: unlink self
|
||||
) ),
|
||||
}
|
||||
_defaults = { 'state': lambda *a: 'choose', }
|
||||
|
|
|
@ -57,10 +57,16 @@ class wizard_import_lang(wizard.interface):
|
|||
|
||||
def _import_lang(self, cr, uid, data, context):
|
||||
form=data['form']
|
||||
#buf=base64.decodestring(form['data']).split('\n')
|
||||
fileobj = TemporaryFile('w+')
|
||||
fileobj.write( base64.decodestring(form['data']) )
|
||||
tools.trans_load_data(cr.dbname, fileobj, form['code'], lang_name=form['name'])
|
||||
|
||||
# now we determine the file format
|
||||
fileobj.seek(0)
|
||||
first_line = fileobj.readline().strip()
|
||||
fileformat = first_line.endswith("type,name,res_id,src,value") and 'csv' or 'po'
|
||||
fileobj.seek(0)
|
||||
|
||||
tools.trans_load_data(cr.dbname, fileobj, fileformat, form['code'], lang_name=form['name'])
|
||||
fileobj.close()
|
||||
return {}
|
||||
|
||||
|
|
|
@ -333,7 +333,6 @@ class one2many(_column):
|
|||
for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
|
||||
if r[self._fields_id] in res:
|
||||
res[r[self._fields_id]].append( r['id'] )
|
||||
print 'Ok', res
|
||||
return res
|
||||
|
||||
def set_memory(self, cr, obj, id, field, values, user=None, context=None):
|
||||
|
|
|
@ -625,14 +625,17 @@ class orm_template(object):
|
|||
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
|
||||
raise _('The read method is not implemented on this object !')
|
||||
|
||||
def _validate(self, cr, uid, ids):
|
||||
def _validate(self, cr, uid, ids, context=None):
|
||||
context = context or {}
|
||||
lng = context.get('lang', False) or 'en_US'
|
||||
trans = self.pool.get('ir.translation')
|
||||
field_error = []
|
||||
field_err_str = []
|
||||
for field in self._constraints:
|
||||
if not field[0](self, cr, uid, ids):
|
||||
if len(field)>1:
|
||||
field_error+=field[2]
|
||||
field_err_str.append(field[1])
|
||||
for constraint in self._constraints:
|
||||
fun, msg, fields = constraint
|
||||
if not fun(self, cr, uid, ids):
|
||||
field_error += fields
|
||||
field_err_str.append( trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) or msg )
|
||||
if len(field_err_str):
|
||||
cr.rollback()
|
||||
raise except_orm('ValidateError', ('\n'.join(field_err_str), ','.join(field_error)))
|
||||
|
@ -1141,7 +1144,7 @@ class orm_memory(orm_template):
|
|||
self.datas[id_new]['internal.date_access'] = time.time()
|
||||
for field in upd_todo:
|
||||
self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
|
||||
self._validate(cr, user, [id_new])
|
||||
self._validate(cr, user, [id_new], context)
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_write(user, self._name, id_new, cr)
|
||||
self.clear()
|
||||
|
@ -1167,7 +1170,7 @@ class orm_memory(orm_template):
|
|||
self.datas[id_new]['internal.date_access'] = time.time()
|
||||
for field in upd_todo:
|
||||
self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
|
||||
self._validate(cr, user, [id_new])
|
||||
self._validate(cr, user, [id_new], context)
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_create(user, self._name, id_new, cr)
|
||||
self.clear()
|
||||
|
@ -1839,10 +1842,6 @@ class orm(orm_template):
|
|||
_('You try to bypass an access rule (Document type: %s).') % \
|
||||
self._description)
|
||||
|
||||
cr.execute('delete from inherit ' \
|
||||
'where (obj_type=%s and obj_id in ('+str_d+')) ' \
|
||||
'or (inst_type=%s and inst_id in ('+str_d+'))',
|
||||
(self._name,)+tuple(sub_ids)+(self._name,)+tuple(sub_ids))
|
||||
if d1:
|
||||
cr.execute('delete from "'+self._table+'" ' \
|
||||
'where id in ('+str_d+')'+d1, sub_ids+d2)
|
||||
|
@ -2010,7 +2009,7 @@ class orm(orm_template):
|
|||
v[val]=vals[val]
|
||||
self.pool.get(table).write(cr, user, nids, v, context)
|
||||
|
||||
self._validate(cr, user, ids)
|
||||
self._validate(cr, user, ids, context)
|
||||
|
||||
if context.has_key('read_delta'):
|
||||
del context['read_delta']
|
||||
|
@ -2086,7 +2085,6 @@ class orm(orm_template):
|
|||
upd0 += ','+self._inherits[table]
|
||||
upd1 += ',%d'
|
||||
upd2.append(id)
|
||||
cr.execute('insert into inherit (obj_type,obj_id,inst_type,inst_id) values (%s,%d,%s,%d)', (table,id,self._name,id_new))
|
||||
|
||||
for field in vals:
|
||||
if self._columns[field]._classic_write:
|
||||
|
@ -2122,7 +2120,7 @@ class orm(orm_template):
|
|||
for field in upd_todo:
|
||||
self._columns[field].set(cr, self, id_new, field, vals[field], user, context)
|
||||
|
||||
self._validate(cr, user, [id_new])
|
||||
self._validate(cr, user, [id_new], context)
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_create(user, self._name, id_new, cr)
|
||||
|
|
|
@ -68,33 +68,12 @@ class osv_pool(netsvc.Service):
|
|||
self.exportMethod(self.execute_cr)
|
||||
|
||||
def execute_cr(self, cr, uid, obj, method, *args, **kw):
|
||||
#
|
||||
# TODO: check security level
|
||||
#
|
||||
try:
|
||||
object = pooler.get_pool(cr.dbname).get(obj)
|
||||
if not object:
|
||||
self.abortResponse(1, 'Object Error', 'warning',
|
||||
'Object %s doesn\'t exist' % str(obj))
|
||||
if (not method in getattr(object,'_protected')) and len(args) \
|
||||
and args[0] and len(object._inherits):
|
||||
types = {obj: args[0]}
|
||||
cr.execute('select inst_type,inst_id,obj_id \
|
||||
from inherit \
|
||||
where obj_type=%s \
|
||||
and obj_id in ('+','.join(map(str,args[0]))+')', (obj,))
|
||||
for ty,id,id2 in cr.fetchall():
|
||||
if not ty in types:
|
||||
types[ty]=[]
|
||||
types[ty].append(id)
|
||||
types[obj].remove(id2)
|
||||
for t,ids in types.items():
|
||||
if len(ids):
|
||||
object_t = pooler.get_pool(cr.dbname).get(t)
|
||||
res = getattr(object_t,method)(cr, uid, ids, *args[1:], **kw)
|
||||
else:
|
||||
res = getattr(object,method)(cr, uid, *args, **kw)
|
||||
return res
|
||||
return getattr(object,method)(cr, uid, *args, **kw)
|
||||
except orm.except_orm, inst:
|
||||
self.abortResponse(1, inst.name, 'warning', inst.value)
|
||||
except except_osv, inst:
|
||||
|
|
|
@ -37,6 +37,7 @@ import netsvc
|
|||
import pooler
|
||||
|
||||
import tools
|
||||
import addons
|
||||
import print_xml
|
||||
import render
|
||||
import urllib
|
||||
|
@ -121,7 +122,7 @@ class report_rml(report_int):
|
|||
pos_xml = i.end()
|
||||
|
||||
doc = print_xml.document(cr, uid, {}, {})
|
||||
tmpl_path = os.path.join(tools.config['root_path'], 'addons/custom/corporate_defaults.xml')
|
||||
tmpl_path = addons.get_module_resource('custom', 'corporate_defaults.xml')
|
||||
doc.parse(tmpl_path, [uid], 'res.users', context)
|
||||
corporate_header = doc.xml_get()
|
||||
doc.close()
|
||||
|
@ -146,16 +147,14 @@ class report_rml(report_int):
|
|||
return xml
|
||||
|
||||
# load XSL (parse it to the XML level)
|
||||
styledoc = libxml2.parseDoc(tools.file_open(
|
||||
os.path.join(tools.config['root_path'], self.xsl)).read())
|
||||
xsl_path, tail = os.path.split(os.path.join(tools.config['root_path'],
|
||||
self.xsl))
|
||||
styledoc = libxml2.parseDoc(tools.file_open(self.xsl).read())
|
||||
xsl_path, tail = os.path.split(self.xsl)
|
||||
for child in styledoc.children:
|
||||
if child.name == 'import':
|
||||
if child.hasProp('href'):
|
||||
file = child.prop('href')
|
||||
child.setProp('href', urllib.quote(str(
|
||||
os.path.normpath(os.path.join(xsl_path, file)))))
|
||||
imp_file = child.prop('href')
|
||||
_x, imp_file = tools.file_open(imp_file, subdir=xsl_path, pathinfo=True)
|
||||
child.setProp('href', urllib.quote(str(imp_file)))
|
||||
|
||||
#TODO: get all the translation in one query. That means we have to:
|
||||
# * build a list of items to translate,
|
||||
|
|
|
@ -353,9 +353,7 @@ class document(object):
|
|||
if not context:
|
||||
context={}
|
||||
# parses the xml template to memory
|
||||
self.dom = minidom.parseString(tools.file_open(
|
||||
os.path.join(tools.config['root_path'],
|
||||
filename)).read())
|
||||
self.dom = minidom.parseString(tools.file_open(filename).read())
|
||||
|
||||
# create the xml data from the xml template
|
||||
self.parse_tree(ids, model, context)
|
||||
|
|
|
@ -184,11 +184,12 @@ if tools.config["translate_out"]:
|
|||
import csv
|
||||
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, 'writing translation file for language %s to %s' % (tools.config["language"], tools.config["translate_out"]))
|
||||
trans=tools.trans_generate(tools.config["language"], tools.config["translate_modules"])
|
||||
writer=csv.writer(file(tools.config["translate_out"], "w"), 'UNIX')
|
||||
for row in trans:
|
||||
writer.writerow(row)
|
||||
del trans
|
||||
|
||||
fileformat = os.path.splitext(tools.config["translate_out"])[-1][1:].lower()
|
||||
buf = file(tools.config["translate_out"], "w")
|
||||
tools.trans_export(tools.config["language"], tools.config["translate_modules"], buf, fileformat)
|
||||
buf.close()
|
||||
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, 'translation file written succesfully')
|
||||
sys.exit(0)
|
||||
|
||||
|
|
|
@ -115,11 +115,11 @@ class configmanager(object):
|
|||
|
||||
group = optparse.OptionGroup(parser, "Internationalisation options",
|
||||
"Use these options to translate Tiny ERP to another language."
|
||||
"See i18n section of the user manual. Option '-l' is mandatory.")
|
||||
"See i18n section of the user manual. Options '-l' and '-d' are mandatory.")
|
||||
|
||||
group.add_option('-l', "--language", dest="language", help="specify the language of the translation file. Use it with --i18n-export and --i18n-import")
|
||||
group.add_option("--i18n-export", dest="translate_out", help="export all sentences to be translated to a CSV file and exit")
|
||||
group.add_option("--i18n-import", dest="translate_in", help="import a CSV file with translations and exit")
|
||||
group.add_option("--i18n-export", dest="translate_out", help="export all sentences to be translated to a CSV or a PO file and exit")
|
||||
group.add_option("--i18n-import", dest="translate_in", help="import a CSV or a PO file with translations and exit")
|
||||
group.add_option("--modules", dest="translate_modules", help="specify modules to export. Use in combination with --i18n-export")
|
||||
group.add_option("--addons-path", dest="addons_path", help="specify an alternative addons path.")
|
||||
parser.add_option_group(group)
|
||||
|
@ -180,7 +180,9 @@ class configmanager(object):
|
|||
update[i] = 1
|
||||
self.options['update'] = update
|
||||
|
||||
self.options['translate_modules'] = opt.translate_modules and opt.translate_modules.split(',') or ['all']
|
||||
self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
|
||||
self.options['translate_modules'].sort()
|
||||
|
||||
if opt.pg_path:
|
||||
self.options['pg_path'] = opt.pg_path
|
||||
|
||||
|
|
|
@ -290,11 +290,14 @@ class graph(object):
|
|||
return self.result
|
||||
|
||||
if __name__=='__main__':
|
||||
starting_node = ['mrp'] # put here nodes with flow_start=True
|
||||
nodes = ['project','account','hr','base','product','mrp','test']
|
||||
starting_node = ['profile'] # put here nodes with flow_start=True
|
||||
nodes = ['project','account','hr','base','product','mrp','test','profile']
|
||||
transitions = [
|
||||
('profile','mrp'),
|
||||
('mrp','project'),
|
||||
('project','product'),
|
||||
('mrp','hr'),
|
||||
('mrp','test'),
|
||||
('project','account'),
|
||||
('project','hr'),
|
||||
('product','base'),
|
||||
|
|
|
@ -50,25 +50,23 @@ else:
|
|||
|
||||
# initialize a database with base/base.sql
|
||||
def init_db(cr):
|
||||
f = os.path.join(config['addons_path'], 'base/base.sql')
|
||||
import addons
|
||||
f = addons.get_module_resource('base', 'base.sql')
|
||||
for line in file(f).read().split(';'):
|
||||
if (len(line)>0) and (not line.isspace()):
|
||||
cr.execute(line)
|
||||
cr.commit()
|
||||
|
||||
opj = os.path.join
|
||||
ad = config['addons_path']
|
||||
|
||||
for i in os.listdir(ad):
|
||||
terp_file = opj(ad, i, '__terp__.py')
|
||||
mod_path = opj(ad, i)
|
||||
for i in addons.get_modules():
|
||||
terp_file = addons.get_module_resource(i, '__terp__.py')
|
||||
mod_path = addons.get_module_path(i)
|
||||
info = False
|
||||
if os.path.isfile(terp_file) and not os.path.isfile(opj(ad, i+'.zip')):
|
||||
if os.path.isfile(terp_file) and not os.path.isfile(mod_path+'.zip'):
|
||||
info = eval(file(terp_file).read())
|
||||
elif zipfile.is_zipfile(mod_path):
|
||||
zfile = zipfile.ZipFile(mod_path)
|
||||
elif zipfile.is_zipfile(mod_path+'.zip'):
|
||||
zfile = zipfile.ZipFile(mod_path+'.zip')
|
||||
i = os.path.splitext(i)[0]
|
||||
info = eval(zfile.read(opj(i, '__terp__.py')))
|
||||
info = eval(zfile.read(os.path.join(i, '__terp__.py')))
|
||||
if info:
|
||||
categs = info.get('category', 'Uncategorized').split('/')
|
||||
p_id = None
|
||||
|
@ -170,12 +168,55 @@ def exec_command_pipe(name, *args):
|
|||
#file_path_root = os.getcwd()
|
||||
#file_path_addons = os.path.join(file_path_root, 'addons')
|
||||
|
||||
def file_open(name, mode="r", subdir='addons'):
|
||||
"""Open a file from the Tiny ERP root, using a subdir folder."""
|
||||
def file_open(name, mode="r", subdir='addons', pathinfo=False):
|
||||
"""Open a file from the Tiny ERP root, using a subdir folder.
|
||||
|
||||
>>> file_open('hr/report/timesheer.xsl')
|
||||
>>> file_open('addons/hr/report/timesheet.xsl')
|
||||
>>> file_open('../../base/report/rml_template.xsl', subdir='addons/hr/report', pathinfo=True)
|
||||
|
||||
@param name: name of the file
|
||||
@param mode: file open mode
|
||||
@param subdir: subdirectory
|
||||
@param pathinfo: if True returns tupple (fileobject, filepath)
|
||||
|
||||
@return: fileobject if pathinfo is False else (fileobject, filepath)
|
||||
"""
|
||||
|
||||
adp = os.path.normcase(os.path.abspath(config['addons_path']))
|
||||
rtp = os.path.normcase(os.path.abspath(config['root_path']))
|
||||
|
||||
if name.replace(os.path.sep, '/').startswith('addons/'):
|
||||
subdir = 'addons'
|
||||
name = name[7:]
|
||||
|
||||
# First try to locate in addons_path
|
||||
if subdir:
|
||||
name = os.path.join(config['root_path'], subdir, name)
|
||||
subdir2 = subdir
|
||||
if subdir2.replace(os.path.sep, '/').startswith('addons/'):
|
||||
subdir2 = subdir2[7:]
|
||||
|
||||
subdir2 = (subdir2 != 'addons' or None) and subdir2
|
||||
|
||||
try:
|
||||
if subdir2:
|
||||
fn = os.path.join(adp, subdir2, name)
|
||||
else:
|
||||
fn = os.path.join(adp, name)
|
||||
fn = os.path.normpath(fn)
|
||||
fo = file_open(fn, mode=mode, subdir=None, pathinfo=pathinfo)
|
||||
if pathinfo:
|
||||
return fo, fn
|
||||
return fo
|
||||
except IOError, e:
|
||||
pass
|
||||
|
||||
if subdir:
|
||||
name = os.path.join(rtp, subdir, name)
|
||||
else:
|
||||
name = os.path.join(config['root_path'], name)
|
||||
name = os.path.join(rtp, name)
|
||||
|
||||
name = os.path.normpath(name)
|
||||
|
||||
# Check for a zipfile in the path
|
||||
head = name
|
||||
|
@ -193,15 +234,22 @@ def file_open(name, mode="r", subdir='addons'):
|
|||
import StringIO
|
||||
zfile = zipfile.ZipFile(head+'.zip')
|
||||
try:
|
||||
return StringIO.StringIO(zfile.read(os.path.join(
|
||||
fo = StringIO.StringIO(zfile.read(os.path.join(
|
||||
os.path.basename(head), zipname).replace(
|
||||
os.sep, '/')))
|
||||
|
||||
if pathinfo:
|
||||
return fo, name
|
||||
return fo
|
||||
except:
|
||||
name2 = os.path.normpath(os.path.join(head + '.zip', zipname))
|
||||
pass
|
||||
for i in (name2, name):
|
||||
if i and os.path.isfile(i):
|
||||
return file(i, mode)
|
||||
fo = file(i, mode)
|
||||
if pathinfo:
|
||||
return fo, i
|
||||
return fo
|
||||
|
||||
raise IOError, 'File not found : '+str(name)
|
||||
|
||||
|
@ -287,7 +335,7 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non
|
|||
#----------------------------------------------------------
|
||||
# Emails
|
||||
#----------------------------------------------------------
|
||||
def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_bcc=None, on_error=False, reply_to=False, attach=None, tinycrm=False):
|
||||
def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_bcc=None, on_error=False, reply_to=False, attach=None, tinycrm=False, ssl=False, debug=False):
|
||||
"""Send an email."""
|
||||
if not email_cc:
|
||||
email_cc=[]
|
||||
|
@ -327,6 +375,15 @@ def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_
|
|||
msg.attach(part)
|
||||
try:
|
||||
s = smtplib.SMTP()
|
||||
|
||||
if debug:
|
||||
s.debuglevel = 5
|
||||
|
||||
if ssl:
|
||||
s.ehlo()
|
||||
s.starttls()
|
||||
s.ehlo()
|
||||
|
||||
s.connect(config['smtp_server'])
|
||||
if config['smtp_user'] or config['smtp_password']:
|
||||
s.login(config['smtp_user'], config['smtp_password'])
|
||||
|
@ -335,6 +392,8 @@ def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_
|
|||
except Exception, e:
|
||||
import logging
|
||||
logging.getLogger().error(str(e))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
|
|
@ -35,6 +35,7 @@ import ir
|
|||
import netsvc
|
||||
from tools.misc import UpdateableStr
|
||||
import inspect
|
||||
import mx.DateTime as mxdt
|
||||
|
||||
class UNIX_LINE_TERMINATOR(csv.excel):
|
||||
lineterminator = '\n'
|
||||
|
@ -67,8 +68,124 @@ class GettextAlias(object):
|
|||
|
||||
_ = GettextAlias()
|
||||
|
||||
|
||||
# class to handle po files
|
||||
class TinyPoFile(object):
|
||||
def __init__(self, buffer):
|
||||
self.buffer = buffer
|
||||
|
||||
def __iter__(self):
|
||||
self.buffer.seek(0)
|
||||
self.lines = self.buffer.readlines()
|
||||
self.first = True
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
def unquote(str):
|
||||
return str[1:-1].replace("\\n", "\n") \
|
||||
.replace('\\"', "\"")
|
||||
|
||||
type = name = res_id = source = trad = None
|
||||
|
||||
line = None
|
||||
while not line:
|
||||
if 0 == len(self.lines):
|
||||
raise StopIteration()
|
||||
line = self.lines.pop(0).strip()
|
||||
|
||||
while line.startswith('#'):
|
||||
if line.startswith('#:'):
|
||||
type, name, res_id = line[2:].strip().split(':')
|
||||
line = self.lines.pop(0).strip()
|
||||
if not line.startswith('msgid'):
|
||||
raise Exception("malformed file")
|
||||
source = line[7:-1]
|
||||
line = self.lines.pop(0).strip()
|
||||
if not source and self.first:
|
||||
# if the source is "" and it's the first msgid, it's the special
|
||||
# msgstr with the informations about the traduction and the
|
||||
# traductor; we skip it
|
||||
while line:
|
||||
line = self.lines.pop(0).strip()
|
||||
return next()
|
||||
|
||||
while not line.startswith('msgstr'):
|
||||
if not line:
|
||||
raise Exception('malformed file')
|
||||
source += unquote(line)
|
||||
line = self.lines.pop(0).strip()
|
||||
|
||||
trad = line[8:-1]
|
||||
line = self.lines.pop(0).strip()
|
||||
while line:
|
||||
trad += unquote(line)
|
||||
line = self.lines.pop(0).strip()
|
||||
|
||||
self.first = False
|
||||
return type, name, res_id, source, trad
|
||||
|
||||
def write_infos(self, modules):
|
||||
import release
|
||||
self.buffer.write("# Translation of %(project)s.\n" \
|
||||
"# This file containt the translation of the following modules:\n" \
|
||||
"%(modules)s" \
|
||||
"#\n" \
|
||||
"msgid \"\"\n" \
|
||||
"msgstr \"\"\n" \
|
||||
"\"Project-Id-Version: %(project)s %(version)s\"\n" \
|
||||
"\"Report-Msgid-Bugs-To: %(bugmail)s\"\n" \
|
||||
"\"POT-Creation-Date: %(now)s\"\n" \
|
||||
"\"PO-Revision-Date: %(now)s\"\n" \
|
||||
"\"Last-Translator: <>\"\n" \
|
||||
"\"Language-Team: \"\n" \
|
||||
"\"MIME-Version: 1.0\"\n" \
|
||||
"\"Content-Type: text/plain; charset=UTF-8\"\n" \
|
||||
"\"Content-Transfer-Encoding: \"\n" \
|
||||
"\"Plural-Forms: \"\n" \
|
||||
"\n"
|
||||
|
||||
% { 'project': release.description,
|
||||
'version': release.version,
|
||||
'modules': reduce(lambda s, m: s + "#\t* %s\n" % m, modules, ""),
|
||||
'bugmail': 'support@tinyerp.com', #TODO: use variable from release
|
||||
'now': mxdt.ISO.strUTC(mxdt.ISO.DateTime.utc()),
|
||||
}
|
||||
)
|
||||
|
||||
def write(self, module, type, name, res_id, source, trad):
|
||||
def quote(str):
|
||||
return '"%s"' % str.replace('"','\\"') \
|
||||
.replace('\n', '\\n"\n"')
|
||||
|
||||
self.buffer.write("#. module: %s\n" \
|
||||
"#, python-format\n" \
|
||||
"#: %s:%s:%s\n" \
|
||||
"msgid %s\n" \
|
||||
"msgstr %s\n\n" \
|
||||
% (module, type, name, str(res_id), quote(source), quote(trad))
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Methods to export the translation file
|
||||
|
||||
def trans_export(lang, modules, buffer, format, dbname=None):
|
||||
trans = trans_generate(lang, modules, dbname)
|
||||
if format == 'csv':
|
||||
writer=csv.writer(buffer, 'UNIX')
|
||||
for row in trans:
|
||||
writer.writerow(row)
|
||||
elif format == 'po':
|
||||
trans.pop(0)
|
||||
writer = tools.TinyPoFile(buffer)
|
||||
writer.write_infos(modules)
|
||||
for module, type, name, res_id, src, trad in trans:
|
||||
writer.write(module, type, name, res_id, src, trad)
|
||||
else:
|
||||
raise Exception(_('Bad file format'))
|
||||
del trans
|
||||
|
||||
|
||||
def trans_parse_xsl(de):
|
||||
res = []
|
||||
for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]:
|
||||
|
@ -123,6 +240,9 @@ def trans_generate(lang, modules, dbname=None):
|
|||
logger = netsvc.Logger()
|
||||
if not dbname:
|
||||
dbname=tools.config['db_name']
|
||||
if not modules:
|
||||
modules = ['all']
|
||||
|
||||
pool = pooler.get_pool(dbname)
|
||||
trans_obj = pool.get('ir.translation')
|
||||
model_data_obj = pool.get('ir.model.data')
|
||||
|
@ -239,11 +359,15 @@ def trans_generate(lang, modules, dbname=None):
|
|||
if fname:
|
||||
logger.notifyChannel("init", netsvc.LOG_WARNING, "couldn't export translation for report %s %s %s" % (name, report_type, fname))
|
||||
|
||||
|
||||
for constraint in pool.get(model)._constraints:
|
||||
msg = constraint[1]
|
||||
push_translation(module, 'constraint', model, 0, msg.encode('utf8'))
|
||||
|
||||
for field_name,field_def in pool.get(model)._columns.items():
|
||||
if field_def.translate:
|
||||
name = model + "," + field_name
|
||||
push_translation(module, 'model', name, xml_name, getattr(obj, field_name))
|
||||
trad = getattr(obj, field_name) or ''
|
||||
push_translation(module, 'model', name, xml_name, trad)
|
||||
|
||||
# parse source code for _() calls
|
||||
def get_module_from_path(path):
|
||||
|
@ -251,14 +375,16 @@ def trans_generate(lang, modules, dbname=None):
|
|||
if path.startswith(relative_addons_path):
|
||||
path = path[len(relative_addons_path)+1:]
|
||||
return path.split(os.path.sep)[0]
|
||||
return None
|
||||
|
||||
def parse_py_files(path):
|
||||
for root, dirs, files in os.walk(path):
|
||||
for fname in fnmatch.filter(files, '*.py'):
|
||||
fabsolutepath = join(root, fname)
|
||||
frelativepath = fabsolutepath[len(tools.config['root_path'])+1:]
|
||||
module = get_module_from_path(frelativepath)
|
||||
return 'base' # files that are not in a module are considered as being in 'base' module
|
||||
|
||||
modobj = pool.get('ir.module.module')
|
||||
for root, dirs, files in os.walk(tools.config['root_path']):
|
||||
for fname in fnmatch.filter(files, '*.py'):
|
||||
fabsolutepath = join(root, fname)
|
||||
frelativepath = fabsolutepath[len(tools.config['root_path'])+1:]
|
||||
module = get_module_from_path(frelativepath)
|
||||
is_mod_installed = modobj.search(cr, uid, [('state', '=', 'installed'), ('name', '=', module)]) <> []
|
||||
if (('all' in modules) or (module in modules)) and is_mod_installed:
|
||||
code_string = tools.file_open(fabsolutepath, subdir='').read()
|
||||
iter = re.finditer(
|
||||
'[^a-zA-Z0-9_]_\([\s]*["\'](.+?)["\'][\s]*\)',
|
||||
|
@ -267,13 +393,6 @@ def trans_generate(lang, modules, dbname=None):
|
|||
push_translation(module, 'code', frelativepath, 0, i.group(1).encode('utf8'))
|
||||
|
||||
|
||||
if 'all' in modules:
|
||||
parse_py_files(tools.config['root_path'])
|
||||
else:
|
||||
for m in modules:
|
||||
parse_py_files(join(tools.config['addons_path'], m))
|
||||
|
||||
|
||||
out = [["module","type","name","res_id","src","value"]] # header
|
||||
# translate strings marked as to be translated
|
||||
for module, type, name, id, source in _to_translate:
|
||||
|
@ -287,13 +406,15 @@ def trans_load(db_name, filename, lang, strict=False):
|
|||
logger = netsvc.Logger()
|
||||
try:
|
||||
fileobj = open(filename,'r')
|
||||
fileformat = os.path.splitext(filename)[-1][1:].lower()
|
||||
r = trans_load_data(db_name, fileobj, fileformat, lang, strict=False)
|
||||
fileobj.close()
|
||||
return r
|
||||
except IOError:
|
||||
logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file")
|
||||
r = trans_load_data(db_name, fileobj, lang, strict=False)
|
||||
fileobj.close()
|
||||
return r
|
||||
return None
|
||||
|
||||
def trans_load_data(db_name, fileobj, lang, strict=False, lang_name=None):
|
||||
def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=None):
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO,
|
||||
'loading translation file for language %s' % (lang))
|
||||
|
@ -324,11 +445,18 @@ def trans_load_data(db_name, fileobj, lang, strict=False, lang_name=None):
|
|||
ls = map(lambda x: (x['code'],x['name']), langs)
|
||||
|
||||
fileobj.seek(0)
|
||||
reader = csv.reader(fileobj, quotechar='"', delimiter=',')
|
||||
# read the first line of the file (it contains columns titles)
|
||||
for row in reader:
|
||||
f = row
|
||||
break
|
||||
|
||||
if fileformat == 'csv':
|
||||
reader = csv.reader(fileobj, quotechar='"', delimiter=',')
|
||||
# read the first line of the file (it contains columns titles)
|
||||
for row in reader:
|
||||
f = row
|
||||
break
|
||||
elif fileformat == 'po':
|
||||
reader = TinyPoFile(fileobj)
|
||||
f = ['type', 'name', 'res_id', 'src', 'value']
|
||||
else:
|
||||
raise Exception(_('Bad file format'))
|
||||
|
||||
# read the rest of the file
|
||||
line = 1
|
||||
|
@ -336,8 +464,8 @@ def trans_load_data(db_name, fileobj, lang, strict=False, lang_name=None):
|
|||
line += 1
|
||||
#try:
|
||||
# skip empty rows and rows where the translation field (=last fiefd) is empty
|
||||
if (not row) or (not row[:-1]):
|
||||
print "translate: skip %s" % repr(row)
|
||||
if (not row) or (not row[-1]):
|
||||
#print "translate: skip %s" % repr(row)
|
||||
continue
|
||||
|
||||
# dictionary which holds values for this line of the csv file
|
||||
|
|
2
setup.py
2
setup.py
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
##############################################################################
|
||||
#
|
||||
|
@ -26,7 +27,6 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
###############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
# setup from TinERP
|
||||
# taken from straw http://www.nongnu.org/straw/index.html
|
||||
# taken from gnomolicious http://www.nongnu.org/gnomolicious/
|
||||
|
|
Loading…
Reference in New Issue