Merge lp:~openerp-dev/openobject-server/trunk-trunk-datadir-chs into lp:openobject-server
- trunk-trunk-datadir-chs
- Merge into trunk
Proposed by
Christophe Simonis (OpenERP)
Status: | Merged |
---|---|
Merged at revision: | 5103 |
Proposed branch: | lp:~openerp-dev/openobject-server/trunk-trunk-datadir-chs |
Merge into: | lp:openobject-server |
Diff against target: |
1115 lines (+642/-168) 13 files modified
openerp/addons/base/ir/ir_attachment.py (+41/-29) openerp/addons/base/module/wizard/base_module_import.py (+0/-2) openerp/addons/base/tests/test_ir_attachment.py (+68/-65) openerp/cli/server.py (+1/-1) openerp/http.py (+1/-22) openerp/modules/module.py (+9/-7) openerp/release.py (+2/-2) openerp/service/db.py (+1/-6) openerp/service/server.py (+2/-3) openerp/tools/__init__.py (+1/-0) openerp/tools/appdirs.py (+477/-0) openerp/tools/config.py (+29/-4) openerp/tools/translate.py (+10/-27) |
To merge this branch: | bzr merge lp:~openerp-dev/openobject-server/trunk-trunk-datadir-chs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+202059@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 5046. By Christophe Simonis (OpenERP)
-
[FIX] attachments: filestore use dbname instead of dbuuid
- 5047. By Antony Lesuisse (OpenERP)
-
[MERGE] trunk
- 5048. By Antony Lesuisse (OpenERP)
-
[FIX] move appsdirs to tools
- 5049. By Christophe Simonis (OpenERP)
-
merge upstream
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'openerp/addons/base/ir/ir_attachment.py' | |||
2 | --- openerp/addons/base/ir/ir_attachment.py 2014-01-16 09:17:16 +0000 | |||
3 | +++ openerp/addons/base/ir/ir_attachment.py 2014-02-27 16:58:29 +0000 | |||
4 | @@ -64,19 +64,37 @@ | |||
5 | 64 | data[attachment.id] = False | 64 | data[attachment.id] = False |
6 | 65 | return data | 65 | return data |
7 | 66 | 66 | ||
8 | 67 | def _storage(self, cr, uid, context=None): | ||
9 | 68 | return self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'ir_attachment.location', 'file') | ||
10 | 69 | |||
11 | 70 | @tools.ormcache() | ||
12 | 71 | def _filestore(self, cr, uid, context=None): | ||
13 | 72 | return os.path.join(tools.config['data_dir'], 'filestore', cr.dbname) | ||
14 | 73 | |||
15 | 67 | # 'data' field implementation | 74 | # 'data' field implementation |
16 | 68 | def _full_path(self, cr, uid, location, path): | 75 | def _full_path(self, cr, uid, location, path): |
26 | 69 | # location = 'file:filestore' | 76 | # sanitize ath |
27 | 70 | assert location.startswith('file:'), "Unhandled filestore location %s" % location | 77 | path = re.sub('[.]', '', path) |
19 | 71 | location = location[5:] | ||
20 | 72 | |||
21 | 73 | # sanitize location name and path | ||
22 | 74 | location = re.sub('[.]','',location) | ||
23 | 75 | location = location.strip('/\\') | ||
24 | 76 | |||
25 | 77 | path = re.sub('[.]','',path) | ||
28 | 78 | path = path.strip('/\\') | 78 | path = path.strip('/\\') |
30 | 79 | return os.path.join(tools.config['root_path'], location, cr.dbname, path) | 79 | return os.path.join(self._filestore(cr, uid), path) |
31 | 80 | |||
32 | 81 | def _get_path(self, cr, uid, location, bin_data): | ||
33 | 82 | sha = hashlib.sha1(bin_data).hexdigest() | ||
34 | 83 | |||
35 | 84 | # retro compatibility | ||
36 | 85 | fname = sha[:3] + '/' + sha | ||
37 | 86 | full_path = self._full_path(cr, uid, location, fname) | ||
38 | 87 | if os.path.isfile(full_path): | ||
39 | 88 | return fname, full_path # keep existing path | ||
40 | 89 | |||
41 | 90 | # scatter files across 256 dirs | ||
42 | 91 | # we use '/' in the db (even on windows) | ||
43 | 92 | fname = sha[:2] + '/' + sha | ||
44 | 93 | full_path = self._full_path(cr, uid, location, fname) | ||
45 | 94 | dirname = os.path.dirname(full_path) | ||
46 | 95 | if not os.path.isdir(dirname): | ||
47 | 96 | os.makedirs(dirname) | ||
48 | 97 | return fname, full_path | ||
49 | 80 | 98 | ||
50 | 81 | def _file_read(self, cr, uid, location, fname, bin_size=False): | 99 | def _file_read(self, cr, uid, location, fname, bin_size=False): |
51 | 82 | full_path = self._full_path(cr, uid, location, fname) | 100 | full_path = self._full_path(cr, uid, location, fname) |
52 | @@ -92,18 +110,13 @@ | |||
53 | 92 | 110 | ||
54 | 93 | def _file_write(self, cr, uid, location, value): | 111 | def _file_write(self, cr, uid, location, value): |
55 | 94 | bin_value = value.decode('base64') | 112 | bin_value = value.decode('base64') |
68 | 95 | fname = hashlib.sha1(bin_value).hexdigest() | 113 | fname, full_path = self._get_path(cr, uid, location, bin_value) |
69 | 96 | # scatter files across 1024 dirs | 114 | if not os.path.exists(full_path): |
70 | 97 | # we use '/' in the db (even on windows) | 115 | try: |
71 | 98 | fname = fname[:3] + '/' + fname | 116 | with open(full_path, 'wb') as fp: |
72 | 99 | full_path = self._full_path(cr, uid, location, fname) | 117 | fp.write(bin_value) |
73 | 100 | try: | 118 | except IOError: |
74 | 101 | dirname = os.path.dirname(full_path) | 119 | _logger.error("_file_write writing %s", full_path) |
63 | 102 | if not os.path.isdir(dirname): | ||
64 | 103 | os.makedirs(dirname) | ||
65 | 104 | open(full_path,'wb').write(bin_value) | ||
66 | 105 | except IOError: | ||
67 | 106 | _logger.error("_file_write writing %s",full_path) | ||
75 | 107 | return fname | 120 | return fname |
76 | 108 | 121 | ||
77 | 109 | def _file_delete(self, cr, uid, location, fname): | 122 | def _file_delete(self, cr, uid, location, fname): |
78 | @@ -122,10 +135,10 @@ | |||
79 | 122 | if context is None: | 135 | if context is None: |
80 | 123 | context = {} | 136 | context = {} |
81 | 124 | result = {} | 137 | result = {} |
83 | 125 | location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location') | 138 | location = self._storage(cr, uid, context) |
84 | 126 | bin_size = context.get('bin_size') | 139 | bin_size = context.get('bin_size') |
85 | 127 | for attach in self.browse(cr, uid, ids, context=context): | 140 | for attach in self.browse(cr, uid, ids, context=context): |
87 | 128 | if location and attach.store_fname: | 141 | if location != 'db' and attach.store_fname: |
88 | 129 | result[attach.id] = self._file_read(cr, uid, location, attach.store_fname, bin_size) | 142 | result[attach.id] = self._file_read(cr, uid, location, attach.store_fname, bin_size) |
89 | 130 | else: | 143 | else: |
90 | 131 | result[attach.id] = attach.db_datas | 144 | result[attach.id] = attach.db_datas |
91 | @@ -137,9 +150,9 @@ | |||
92 | 137 | return True | 150 | return True |
93 | 138 | if context is None: | 151 | if context is None: |
94 | 139 | context = {} | 152 | context = {} |
96 | 140 | location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location') | 153 | location = self._storage(cr, uid, context) |
97 | 141 | file_size = len(value.decode('base64')) | 154 | file_size = len(value.decode('base64')) |
99 | 142 | if location: | 155 | if location != 'db': |
100 | 143 | attach = self.browse(cr, uid, id, context=context) | 156 | attach = self.browse(cr, uid, id, context=context) |
101 | 144 | if attach.store_fname: | 157 | if attach.store_fname: |
102 | 145 | self._file_delete(cr, uid, location, attach.store_fname) | 158 | self._file_delete(cr, uid, location, attach.store_fname) |
103 | @@ -285,8 +298,8 @@ | |||
104 | 285 | if isinstance(ids, (int, long)): | 298 | if isinstance(ids, (int, long)): |
105 | 286 | ids = [ids] | 299 | ids = [ids] |
106 | 287 | self.check(cr, uid, ids, 'unlink', context=context) | 300 | self.check(cr, uid, ids, 'unlink', context=context) |
109 | 288 | location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location') | 301 | location = self._storage(cr, uid, context) |
110 | 289 | if location: | 302 | if location != 'db': |
111 | 290 | for attach in self.browse(cr, uid, ids, context=context): | 303 | for attach in self.browse(cr, uid, ids, context=context): |
112 | 291 | if attach.store_fname: | 304 | if attach.store_fname: |
113 | 292 | self._file_delete(cr, uid, location, attach.store_fname) | 305 | self._file_delete(cr, uid, location, attach.store_fname) |
114 | @@ -303,4 +316,3 @@ | |||
115 | 303 | cr, uid, 'base', 'action_attachment', context=context) | 316 | cr, uid, 'base', 'action_attachment', context=context) |
116 | 304 | 317 | ||
117 | 305 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | 318 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
118 | 306 | |||
119 | 307 | 319 | ||
120 | === modified file 'openerp/addons/base/module/wizard/base_module_import.py' | |||
121 | --- openerp/addons/base/module/wizard/base_module_import.py 2013-10-18 15:48:05 +0000 | |||
122 | +++ openerp/addons/base/module/wizard/base_module_import.py 2014-02-27 16:58:29 +0000 | |||
123 | @@ -28,8 +28,6 @@ | |||
124 | 28 | from openerp.osv import osv, fields | 28 | from openerp.osv import osv, fields |
125 | 29 | from openerp.tools.translate import _ | 29 | from openerp.tools.translate import _ |
126 | 30 | 30 | ||
127 | 31 | ADDONS_PATH = tools.config['addons_path'].split(",")[-1] | ||
128 | 32 | |||
129 | 33 | class base_module_import(osv.osv_memory): | 31 | class base_module_import(osv.osv_memory): |
130 | 34 | """ Import Module """ | 32 | """ Import Module """ |
131 | 35 | 33 | ||
132 | 36 | 34 | ||
133 | === modified file 'openerp/addons/base/tests/test_ir_attachment.py' | |||
134 | --- openerp/addons/base/tests/test_ir_attachment.py 2012-12-16 19:03:17 +0000 | |||
135 | +++ openerp/addons/base/tests/test_ir_attachment.py 2014-02-27 16:58:29 +0000 | |||
136 | @@ -1,89 +1,92 @@ | |||
137 | 1 | import hashlib | 1 | import hashlib |
138 | 2 | import os | 2 | import os |
139 | 3 | 3 | ||
140 | 4 | import unittest2 | ||
141 | 5 | |||
142 | 6 | import openerp | 4 | import openerp |
143 | 7 | import openerp.tests.common | 5 | import openerp.tests.common |
144 | 8 | 6 | ||
145 | 7 | HASH_SPLIT = 2 # FIXME: testing implementations detail is not a good idea | ||
146 | 8 | |||
147 | 9 | class test_ir_attachment(openerp.tests.common.TransactionCase): | 9 | class test_ir_attachment(openerp.tests.common.TransactionCase): |
150 | 10 | 10 | def setUp(self): | |
151 | 11 | def test_00_attachment_flow(self): | 11 | super(test_ir_attachment, self).setUp() |
152 | 12 | registry, cr, uid = self.registry, self.cr, self.uid | 12 | registry, cr, uid = self.registry, self.cr, self.uid |
155 | 13 | root_path = openerp.tools.config['root_path'] | 13 | self.ira = registry('ir.attachment') |
156 | 14 | ira = registry('ir.attachment') | 14 | self.filestore = self.ira._filestore(cr, uid) |
157 | 15 | 15 | ||
158 | 16 | # Blob1 | 16 | # Blob1 |
163 | 17 | blob1 = 'blob1' | 17 | self.blob1 = 'blob1' |
164 | 18 | blob1_b64 = blob1.encode('base64') | 18 | self.blob1_b64 = self.blob1.encode('base64') |
165 | 19 | blob1_hash = hashlib.sha1(blob1).hexdigest() | 19 | blob1_hash = hashlib.sha1(self.blob1).hexdigest() |
166 | 20 | blob1_fname = blob1_hash[:3] + '/' + blob1_hash | 20 | self.blob1_fname = blob1_hash[:HASH_SPLIT] + '/' + blob1_hash |
167 | 21 | 21 | ||
168 | 22 | # Blob2 | 22 | # Blob2 |
169 | 23 | blob2 = 'blob2' | 23 | blob2 = 'blob2' |
173 | 24 | blob2_b64 = blob2.encode('base64') | 24 | self.blob2_b64 = blob2.encode('base64') |
174 | 25 | blob2_hash = hashlib.sha1(blob2).hexdigest() | 25 | |
175 | 26 | blob2_fname = blob2_hash[:3] + '/' + blob2_hash | 26 | def test_01_store_in_db(self): |
176 | 27 | registry, cr, uid = self.registry, self.cr, self.uid | ||
177 | 28 | |||
178 | 29 | # force storing in database | ||
179 | 30 | registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'db') | ||
180 | 27 | 31 | ||
181 | 28 | # 'ir_attachment.location' is undefined test database storage | 32 | # 'ir_attachment.location' is undefined test database storage |
213 | 29 | a1 = ira.create(cr, uid, {'name': 'a1', 'datas': blob1_b64}) | 33 | a1 = self.ira.create(cr, uid, {'name': 'a1', 'datas': self.blob1_b64}) |
214 | 30 | a1_read = ira.read(cr, uid, [a1], ['datas']) | 34 | a1_read = self.ira.read(cr, uid, [a1], ['datas']) |
215 | 31 | self.assertEqual(a1_read[0]['datas'], blob1_b64) | 35 | self.assertEqual(a1_read[0]['datas'], self.blob1_b64) |
216 | 32 | 36 | ||
217 | 33 | cr.execute("select id,db_datas from ir_attachment where id = %s", (a1,) ) | 37 | a1_db_datas = self.ira.browse(cr, uid, a1).db_datas |
218 | 34 | a1_db_datas = str(cr.fetchall()[0][1]) | 38 | self.assertEqual(a1_db_datas, self.blob1_b64) |
219 | 35 | self.assertEqual(a1_db_datas, blob1_b64) | 39 | |
220 | 36 | 40 | def test_02_store_on_disk(self): | |
221 | 37 | # define a location for filestore | 41 | registry, cr, uid = self.registry, self.cr, self.uid |
222 | 38 | registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'file:///filestore') | 42 | |
223 | 39 | 43 | a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64}) | |
224 | 40 | # Test file storage | 44 | a2_store_fname = self.ira.browse(cr, uid, a2).store_fname |
225 | 41 | a2 = ira.create(cr, uid, {'name': 'a2', 'datas': blob1_b64}) | 45 | |
226 | 42 | a2_read = ira.read(cr, uid, [a2], ['datas']) | 46 | self.assertEqual(a2_store_fname, self.blob1_fname) |
227 | 43 | self.assertEqual(a2_read[0]['datas'], blob1_b64) | 47 | self.assertTrue(os.path.isfile(os.path.join(self.filestore, a2_store_fname))) |
228 | 44 | 48 | ||
229 | 45 | cr.execute("select id,store_fname from ir_attachment where id = %s", (a2,) ) | 49 | def test_03_no_duplication(self): |
230 | 46 | a2_store_fname = cr.fetchall()[0][1] | 50 | registry, cr, uid = self.registry, self.cr, self.uid |
231 | 47 | self.assertEqual(a2_store_fname, blob1_fname) | 51 | |
232 | 48 | 52 | a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64}) | |
233 | 49 | a2_fn = os.path.join(root_path, 'filestore', cr.dbname, blob1_hash[:3], blob1_hash) | 53 | a2_store_fname = self.ira.browse(cr, uid, a2).store_fname |
234 | 50 | fc = file(a2_fn).read() | 54 | |
235 | 51 | self.assertEqual(fc, blob1) | 55 | a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64}) |
236 | 52 | 56 | a3_store_fname = self.ira.browse(cr, uid, a3).store_fname | |
237 | 53 | # create a3 with same blob | 57 | |
207 | 54 | a3 = ira.create(cr, uid, {'name': 'a3', 'datas': blob1_b64}) | ||
208 | 55 | a3_read = ira.read(cr, uid, [a3], ['datas']) | ||
209 | 56 | self.assertEqual(a3_read[0]['datas'], blob1_b64) | ||
210 | 57 | |||
211 | 58 | cr.execute("select id,store_fname from ir_attachment where id = %s", (a3,) ) | ||
212 | 59 | a3_store_fname = cr.fetchall()[0][1] | ||
238 | 60 | self.assertEqual(a3_store_fname, a2_store_fname) | 58 | self.assertEqual(a3_store_fname, a2_store_fname) |
239 | 61 | 59 | ||
250 | 62 | # create a4 blob2 | 60 | def test_04_keep_file(self): |
251 | 63 | a4 = ira.create(cr, uid, {'name': 'a4', 'datas': blob2_b64}) | 61 | registry, cr, uid = self.registry, self.cr, self.uid |
252 | 64 | a4_read = ira.read(cr, uid, [a4], ['datas']) | 62 | |
253 | 65 | self.assertEqual(a4_read[0]['datas'], blob2_b64) | 63 | a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64}) |
254 | 66 | 64 | a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64}) | |
255 | 67 | a4_fn = os.path.join(root_path, 'filestore', cr.dbname, blob2_hash[:3], blob2_hash) | 65 | |
256 | 68 | self.assertTrue(os.path.isfile(a4_fn)) | 66 | a2_store_fname = self.ira.browse(cr, uid, a2).store_fname |
257 | 69 | 67 | a2_fn = os.path.join(self.filestore, a2_store_fname) | |
258 | 70 | # delete a3 but file stays | 68 | |
259 | 71 | ira.unlink(cr, uid, [a3]) | 69 | self.ira.unlink(cr, uid, [a3]) |
260 | 72 | self.assertTrue(os.path.isfile(a2_fn)) | 70 | self.assertTrue(os.path.isfile(a2_fn)) |
261 | 73 | 71 | ||
262 | 74 | # delete a2 it is unlinked | 72 | # delete a2 it is unlinked |
264 | 75 | ira.unlink(cr, uid, [a2]) | 73 | self.ira.unlink(cr, uid, [a2]) |
265 | 76 | self.assertFalse(os.path.isfile(a2_fn)) | 74 | self.assertFalse(os.path.isfile(a2_fn)) |
266 | 77 | 75 | ||
274 | 78 | # update a4 blob2 by blob1 | 76 | def test_05_change_data_change_file(self): |
275 | 79 | ira.write(cr, uid, [a4], {'datas': blob1_b64}) | 77 | registry, cr, uid = self.registry, self.cr, self.uid |
276 | 80 | a4_read = ira.read(cr, uid, [a4], ['datas']) | 78 | |
277 | 81 | self.assertEqual(a4_read[0]['datas'], blob1_b64) | 79 | a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64}) |
278 | 82 | 80 | a2_store_fname = self.ira.browse(cr, uid, a2).store_fname | |
279 | 83 | # file of a4 disapear and a2 reappear | 81 | a2_fn = os.path.join(self.filestore, a2_store_fname) |
280 | 84 | self.assertFalse(os.path.isfile(a4_fn)) | 82 | |
281 | 85 | self.assertTrue(os.path.isfile(a2_fn)) | 83 | self.assertTrue(os.path.isfile(a2_fn)) |
282 | 86 | 84 | ||
286 | 87 | # everybody applause | 85 | self.ira.write(cr, uid, [a2], {'datas': self.blob2_b64}) |
287 | 88 | 86 | self.assertFalse(os.path.isfile(a2_fn)) | |
288 | 89 | 87 | ||
289 | 88 | new_a2_store_fname = self.ira.browse(cr, uid, a2).store_fname | ||
290 | 89 | self.assertNotEqual(a2_store_fname, new_a2_store_fname) | ||
291 | 90 | |||
292 | 91 | new_a2_fn = os.path.join(self.filestore, new_a2_store_fname) | ||
293 | 92 | self.assertTrue(os.path.isfile(new_a2_fn)) | ||
294 | 90 | 93 | ||
295 | === modified file 'openerp/cli/server.py' | |||
296 | --- openerp/cli/server.py 2014-02-09 01:46:36 +0000 | |||
297 | +++ openerp/cli/server.py 2014-02-27 16:58:29 +0000 | |||
298 | @@ -72,7 +72,7 @@ | |||
299 | 72 | """ | 72 | """ |
300 | 73 | config = openerp.tools.config | 73 | config = openerp.tools.config |
301 | 74 | _logger.info("OpenERP version %s", __version__) | 74 | _logger.info("OpenERP version %s", __version__) |
303 | 75 | for name, value in [('addons paths', config['addons_path']), | 75 | for name, value in [('addons paths', openerp.modules.module.ad_paths), |
304 | 76 | ('database hostname', config['db_host'] or 'localhost'), | 76 | ('database hostname', config['db_host'] or 'localhost'), |
305 | 77 | ('database port', config['db_port'] or '5432'), | 77 | ('database port', config['db_port'] or '5432'), |
306 | 78 | ('database user', config['db_user'])]: | 78 | ('database user', config['db_user'])]: |
307 | 79 | 79 | ||
308 | === modified file 'openerp/http.py' | |||
309 | --- openerp/http.py 2014-02-26 16:16:27 +0000 | |||
310 | +++ openerp/http.py 2014-02-27 16:58:29 +0000 | |||
311 | @@ -995,33 +995,12 @@ | |||
312 | 995 | start_response(status, new_headers) | 995 | start_response(status, new_headers) |
313 | 996 | return self.app(environ, start_wrapped) | 996 | return self.app(environ, start_wrapped) |
314 | 997 | 997 | ||
315 | 998 | def session_path(): | ||
316 | 999 | try: | ||
317 | 1000 | import pwd | ||
318 | 1001 | username = pwd.getpwuid(os.geteuid()).pw_name | ||
319 | 1002 | except ImportError: | ||
320 | 1003 | try: | ||
321 | 1004 | username = getpass.getuser() | ||
322 | 1005 | except Exception: | ||
323 | 1006 | username = "unknown" | ||
324 | 1007 | path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) | ||
325 | 1008 | try: | ||
326 | 1009 | os.mkdir(path, 0700) | ||
327 | 1010 | except OSError as exc: | ||
328 | 1011 | if exc.errno == errno.EEXIST: | ||
329 | 1012 | # directory exists: ensure it has the correct permissions | ||
330 | 1013 | # this will fail if the directory is not owned by the current user | ||
331 | 1014 | os.chmod(path, 0700) | ||
332 | 1015 | else: | ||
333 | 1016 | raise | ||
334 | 1017 | return path | ||
335 | 1018 | |||
336 | 1019 | class Root(object): | 998 | class Root(object): |
337 | 1020 | """Root WSGI application for the OpenERP Web Client. | 999 | """Root WSGI application for the OpenERP Web Client. |
338 | 1021 | """ | 1000 | """ |
339 | 1022 | def __init__(self): | 1001 | def __init__(self): |
340 | 1023 | # Setup http sessions | 1002 | # Setup http sessions |
342 | 1024 | path = session_path() | 1003 | path = openerp.tools.config.session_dir |
343 | 1025 | _logger.debug('HTTP sessions stored in: %s', path) | 1004 | _logger.debug('HTTP sessions stored in: %s', path) |
344 | 1026 | self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession) | 1005 | self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession) |
345 | 1027 | 1006 | ||
346 | 1028 | 1007 | ||
347 | === modified file 'openerp/modules/module.py' | |||
348 | --- openerp/modules/module.py 2014-02-18 10:18:47 +0000 | |||
349 | +++ openerp/modules/module.py 2014-02-27 16:58:29 +0000 | |||
350 | @@ -3,7 +3,7 @@ | |||
351 | 3 | # | 3 | # |
352 | 4 | # OpenERP, Open Source Management Solution | 4 | # OpenERP, Open Source Management Solution |
353 | 5 | # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). | 5 | # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). |
355 | 6 | # Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>). | 6 | # Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>). |
356 | 7 | # | 7 | # |
357 | 8 | # This program is free software: you can redistribute it and/or modify | 8 | # This program is free software: you can redistribute it and/or modify |
358 | 9 | # it under the terms of the GNU Affero General Public License as | 9 | # it under the terms of the GNU Affero General Public License as |
359 | @@ -40,9 +40,6 @@ | |||
360 | 40 | _logger = logging.getLogger(__name__) | 40 | _logger = logging.getLogger(__name__) |
361 | 41 | _test_logger = logging.getLogger('openerp.tests') | 41 | _test_logger = logging.getLogger('openerp.tests') |
362 | 42 | 42 | ||
363 | 43 | # addons path ','.joined | ||
364 | 44 | _ad = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons') # default addons path (base) | ||
365 | 45 | |||
366 | 46 | # addons path as a list | 43 | # addons path as a list |
367 | 47 | ad_paths = [] | 44 | ad_paths = [] |
368 | 48 | 45 | ||
369 | @@ -90,8 +87,13 @@ | |||
370 | 90 | if ad_paths: | 87 | if ad_paths: |
371 | 91 | return | 88 | return |
372 | 92 | 89 | ||
375 | 93 | ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(',')) | 90 | ad_paths = [tools.config.addons_data_dir] |
376 | 94 | ad_paths.append(os.path.abspath(_ad)) # for get_module_path | 91 | ad_paths += map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(',')) |
377 | 92 | |||
378 | 93 | # add base module path | ||
379 | 94 | base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons')) | ||
380 | 95 | ad_paths += [base_path] | ||
381 | 96 | |||
382 | 95 | sys.meta_path.append(AddonsImportHook()) | 97 | sys.meta_path.append(AddonsImportHook()) |
383 | 96 | 98 | ||
384 | 97 | def get_module_path(module, downloaded=False, display_warning=True): | 99 | def get_module_path(module, downloaded=False, display_warning=True): |
385 | @@ -108,7 +110,7 @@ | |||
386 | 108 | return opj(adp, module) | 110 | return opj(adp, module) |
387 | 109 | 111 | ||
388 | 110 | if downloaded: | 112 | if downloaded: |
390 | 111 | return opj(_ad, module) | 113 | return opj(tools.config.addons_data_dir, module) |
391 | 112 | if display_warning: | 114 | if display_warning: |
392 | 113 | _logger.warning('module %s: module not found', module) | 115 | _logger.warning('module %s: module not found', module) |
393 | 114 | return False | 116 | return False |
394 | 115 | 117 | ||
395 | === modified file 'openerp/release.py' | |||
396 | --- openerp/release.py 2014-02-11 10:53:15 +0000 | |||
397 | +++ openerp/release.py 2014-02-27 16:58:29 +0000 | |||
398 | @@ -32,7 +32,7 @@ | |||
399 | 32 | # (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0) | 32 | # (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0) |
400 | 33 | version_info = (8, 0, 0, ALPHA, 1) | 33 | version_info = (8, 0, 0, ALPHA, 1) |
401 | 34 | version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') | 34 | version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') |
403 | 35 | serie = major_version = '.'.join(map(str, version_info[:2])) | 35 | series = serie = major_version = '.'.join(map(str, version_info[:2])) |
404 | 36 | 36 | ||
405 | 37 | description = 'OpenERP Server' | 37 | description = 'OpenERP Server' |
406 | 38 | long_desc = '''OpenERP is a complete ERP and CRM. The main features are accounting (analytic | 38 | long_desc = '''OpenERP is a complete ERP and CRM. The main features are accounting (analytic |
407 | @@ -50,6 +50,6 @@ | |||
408 | 50 | author_email = 'info@openerp.com' | 50 | author_email = 'info@openerp.com' |
409 | 51 | license = 'AGPL-3' | 51 | license = 'AGPL-3' |
410 | 52 | 52 | ||
412 | 53 | nt_service_name = "openerp-server-" + serie | 53 | nt_service_name = "openerp-server-" + series |
413 | 54 | 54 | ||
414 | 55 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | 55 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
415 | 56 | 56 | ||
416 | === modified file 'openerp/service/db.py' | |||
417 | --- openerp/service/db.py 2014-02-20 13:27:00 +0000 | |||
418 | +++ openerp/service/db.py 2014-02-27 16:58:29 +0000 | |||
419 | @@ -276,15 +276,10 @@ | |||
420 | 276 | cr.autocommit(True) # avoid transaction block | 276 | cr.autocommit(True) # avoid transaction block |
421 | 277 | try: | 277 | try: |
422 | 278 | cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name)) | 278 | cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name)) |
423 | 279 | _logger.info('RENAME DB: %s -> %s', old_name, new_name) | ||
424 | 279 | except Exception, e: | 280 | except Exception, e: |
425 | 280 | _logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e) | 281 | _logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e) |
426 | 281 | raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e)) | 282 | raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e)) |
427 | 282 | else: | ||
428 | 283 | fs = os.path.join(openerp.tools.config['root_path'], 'filestore') | ||
429 | 284 | if os.path.exists(os.path.join(fs, old_name)): | ||
430 | 285 | os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name)) | ||
431 | 286 | |||
432 | 287 | _logger.info('RENAME DB: %s -> %s', old_name, new_name) | ||
433 | 288 | return True | 283 | return True |
434 | 289 | 284 | ||
435 | 290 | def exp_db_exist(db_name): | 285 | def exp_db_exist(db_name): |
436 | 291 | 286 | ||
437 | === modified file 'openerp/service/server.py' | |||
438 | --- openerp/service/server.py 2014-02-21 23:10:10 +0000 | |||
439 | +++ openerp/service/server.py 2014-02-27 16:58:29 +0000 | |||
440 | @@ -108,15 +108,14 @@ | |||
441 | 108 | self.handler = EventHandler(self) | 108 | self.handler = EventHandler(self) |
442 | 109 | self.notifier = pyinotify.Notifier(self.wm, self.handler, timeout=0) | 109 | self.notifier = pyinotify.Notifier(self.wm, self.handler, timeout=0) |
443 | 110 | mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE # IN_MOVED_FROM, IN_MOVED_TO ? | 110 | mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE # IN_MOVED_FROM, IN_MOVED_TO ? |
445 | 111 | for path in openerp.tools.config.options["addons_path"].split(','): | 111 | for path in openerp.modules.modules.ad_paths: |
446 | 112 | _logger.info('Watching addons folder %s', path) | 112 | _logger.info('Watching addons folder %s', path) |
447 | 113 | self.wm.add_watch(path, mask, rec=True) | 113 | self.wm.add_watch(path, mask, rec=True) |
448 | 114 | 114 | ||
449 | 115 | def process_data(self, files): | 115 | def process_data(self, files): |
450 | 116 | xml_files = [i for i in files if i.endswith('.xml')] | 116 | xml_files = [i for i in files if i.endswith('.xml')] |
451 | 117 | addons_path = openerp.tools.config.options["addons_path"].split(',') | ||
452 | 118 | for i in xml_files: | 117 | for i in xml_files: |
454 | 119 | for path in addons_path: | 118 | for path in openerp.modules.modules.ad_paths: |
455 | 120 | if i.startswith(path): | 119 | if i.startswith(path): |
456 | 121 | # find out wich addons path the file belongs to | 120 | # find out wich addons path the file belongs to |
457 | 122 | # and extract it's module name | 121 | # and extract it's module name |
458 | 123 | 122 | ||
459 | === modified file 'openerp/tools/__init__.py' | |||
460 | --- openerp/tools/__init__.py 2013-02-12 14:24:10 +0000 | |||
461 | +++ openerp/tools/__init__.py 2014-02-27 16:58:29 +0000 | |||
462 | @@ -21,6 +21,7 @@ | |||
463 | 21 | 21 | ||
464 | 22 | import copy | 22 | import copy |
465 | 23 | import win32 | 23 | import win32 |
466 | 24 | import appdirs | ||
467 | 24 | from config import config | 25 | from config import config |
468 | 25 | from misc import * | 26 | from misc import * |
469 | 26 | from convert import * | 27 | from convert import * |
470 | 27 | 28 | ||
471 | === added file 'openerp/tools/appdirs.py' | |||
472 | --- openerp/tools/appdirs.py 1970-01-01 00:00:00 +0000 | |||
473 | +++ openerp/tools/appdirs.py 2014-02-27 16:58:29 +0000 | |||
474 | @@ -0,0 +1,477 @@ | |||
475 | 1 | #!/usr/bin/env python | ||
476 | 2 | # -*- coding: utf-8 -*- | ||
477 | 3 | # Copyright (c) 2005-2010 ActiveState Software Inc. | ||
478 | 4 | # Copyright (c) 2013 Eddy Petrișor | ||
479 | 5 | |||
480 | 6 | """Utilities for determining application-specific dirs. | ||
481 | 7 | |||
482 | 8 | See <http://github.com/ActiveState/appdirs> for details and usage. | ||
483 | 9 | """ | ||
484 | 10 | # Dev Notes: | ||
485 | 11 | # - MSDN on where to store app data files: | ||
486 | 12 | # http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 | ||
487 | 13 | # - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html | ||
488 | 14 | # - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html | ||
489 | 15 | |||
490 | 16 | __version_info__ = (1, 3, 0) | ||
491 | 17 | __version__ = '.'.join(map(str, __version_info__)) | ||
492 | 18 | |||
493 | 19 | |||
494 | 20 | import sys | ||
495 | 21 | import os | ||
496 | 22 | |||
497 | 23 | PY3 = sys.version_info[0] == 3 | ||
498 | 24 | |||
499 | 25 | if PY3: | ||
500 | 26 | unicode = str | ||
501 | 27 | |||
502 | 28 | |||
503 | 29 | |||
504 | 30 | def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): | ||
505 | 31 | r"""Return full path to the user-specific data dir for this application. | ||
506 | 32 | |||
507 | 33 | "appname" is the name of application. | ||
508 | 34 | If None, just the system directory is returned. | ||
509 | 35 | "appauthor" (only required and used on Windows) is the name of the | ||
510 | 36 | appauthor or distributing body for this application. Typically | ||
511 | 37 | it is the owning company name. This falls back to appname. | ||
512 | 38 | "version" is an optional version path element to append to the | ||
513 | 39 | path. You might want to use this if you want multiple versions | ||
514 | 40 | of your app to be able to run independently. If used, this | ||
515 | 41 | would typically be "<major>.<minor>". | ||
516 | 42 | Only applied when appname is present. | ||
517 | 43 | "roaming" (boolean, default False) can be set True to use the Windows | ||
518 | 44 | roaming appdata directory. That means that for users on a Windows | ||
519 | 45 | network setup for roaming profiles, this user data will be | ||
520 | 46 | sync'd on login. See | ||
521 | 47 | <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> | ||
522 | 48 | for a discussion of issues. | ||
523 | 49 | |||
524 | 50 | Typical user data directories are: | ||
525 | 51 | Mac OS X: ~/Library/Application Support/<AppName> | ||
526 | 52 | Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined | ||
527 | 53 | Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName> | ||
528 | 54 | Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> | ||
529 | 55 | Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> | ||
530 | 56 | Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> | ||
531 | 57 | |||
532 | 58 | For Unix, we follow the XDG spec and support $XDG_DATA_HOME. | ||
533 | 59 | That means, by deafult "~/.local/share/<AppName>". | ||
534 | 60 | """ | ||
535 | 61 | if sys.platform == "win32": | ||
536 | 62 | if appauthor is None: | ||
537 | 63 | appauthor = appname | ||
538 | 64 | const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" | ||
539 | 65 | path = os.path.normpath(_get_win_folder(const)) | ||
540 | 66 | if appname: | ||
541 | 67 | path = os.path.join(path, appauthor, appname) | ||
542 | 68 | elif sys.platform == 'darwin': | ||
543 | 69 | path = os.path.expanduser('~/Library/Application Support/') | ||
544 | 70 | if appname: | ||
545 | 71 | path = os.path.join(path, appname) | ||
546 | 72 | else: | ||
547 | 73 | path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) | ||
548 | 74 | if appname: | ||
549 | 75 | path = os.path.join(path, appname) | ||
550 | 76 | if appname and version: | ||
551 | 77 | path = os.path.join(path, version) | ||
552 | 78 | return path | ||
553 | 79 | |||
554 | 80 | |||
555 | 81 | def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): | ||
556 | 82 | """Return full path to the user-shared data dir for this application. | ||
557 | 83 | |||
558 | 84 | "appname" is the name of application. | ||
559 | 85 | If None, just the system directory is returned. | ||
560 | 86 | "appauthor" (only required and used on Windows) is the name of the | ||
561 | 87 | appauthor or distributing body for this application. Typically | ||
562 | 88 | it is the owning company name. This falls back to appname. | ||
563 | 89 | "version" is an optional version path element to append to the | ||
564 | 90 | path. You might want to use this if you want multiple versions | ||
565 | 91 | of your app to be able to run independently. If used, this | ||
566 | 92 | would typically be "<major>.<minor>". | ||
567 | 93 | Only applied when appname is present. | ||
568 | 94 | "multipath" is an optional parameter only applicable to *nix | ||
569 | 95 | which indicates that the entire list of data dirs should be | ||
570 | 96 | returned. By default, the first item from XDG_DATA_DIRS is | ||
571 | 97 | returned, or '/usr/local/share/<AppName>', | ||
572 | 98 | if XDG_DATA_DIRS is not set | ||
573 | 99 | |||
574 | 100 | Typical user data directories are: | ||
575 | 101 | Mac OS X: /Library/Application Support/<AppName> | ||
576 | 102 | Unix: /usr/local/share/<AppName> or /usr/share/<AppName> | ||
577 | 103 | Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> | ||
578 | 104 | Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) | ||
579 | 105 | Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. | ||
580 | 106 | |||
581 | 107 | For Unix, this is using the $XDG_DATA_DIRS[0] default. | ||
582 | 108 | |||
583 | 109 | WARNING: Do not use this on Windows. See the Vista-Fail note above for why. | ||
584 | 110 | """ | ||
585 | 111 | if sys.platform == "win32": | ||
586 | 112 | if appauthor is None: | ||
587 | 113 | appauthor = appname | ||
588 | 114 | path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) | ||
589 | 115 | if appname: | ||
590 | 116 | path = os.path.join(path, appauthor, appname) | ||
591 | 117 | elif sys.platform == 'darwin': | ||
592 | 118 | path = os.path.expanduser('/Library/Application Support') | ||
593 | 119 | if appname: | ||
594 | 120 | path = os.path.join(path, appname) | ||
595 | 121 | else: | ||
596 | 122 | # XDG default for $XDG_DATA_DIRS | ||
597 | 123 | # only first, if multipath is False | ||
598 | 124 | path = os.getenv('XDG_DATA_DIRS', | ||
599 | 125 | os.pathsep.join(['/usr/local/share', '/usr/share'])) | ||
600 | 126 | pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ] | ||
601 | 127 | if appname: | ||
602 | 128 | if version: | ||
603 | 129 | appname = os.path.join(appname, version) | ||
604 | 130 | pathlist = [ os.sep.join([x, appname]) for x in pathlist ] | ||
605 | 131 | |||
606 | 132 | if multipath: | ||
607 | 133 | path = os.pathsep.join(pathlist) | ||
608 | 134 | else: | ||
609 | 135 | path = pathlist[0] | ||
610 | 136 | return path | ||
611 | 137 | |||
612 | 138 | if appname and version: | ||
613 | 139 | path = os.path.join(path, version) | ||
614 | 140 | return path | ||
615 | 141 | |||
616 | 142 | |||
617 | 143 | def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): | ||
618 | 144 | r"""Return full path to the user-specific config dir for this application. | ||
619 | 145 | |||
620 | 146 | "appname" is the name of application. | ||
621 | 147 | If None, just the system directory is returned. | ||
622 | 148 | "appauthor" (only required and used on Windows) is the name of the | ||
623 | 149 | appauthor or distributing body for this application. Typically | ||
624 | 150 | it is the owning company name. This falls back to appname. | ||
625 | 151 | "version" is an optional version path element to append to the | ||
626 | 152 | path. You might want to use this if you want multiple versions | ||
627 | 153 | of your app to be able to run independently. If used, this | ||
628 | 154 | would typically be "<major>.<minor>". | ||
629 | 155 | Only applied when appname is present. | ||
630 | 156 | "roaming" (boolean, default False) can be set True to use the Windows | ||
631 | 157 | roaming appdata directory. That means that for users on a Windows | ||
632 | 158 | network setup for roaming profiles, this user data will be | ||
633 | 159 | sync'd on login. See | ||
634 | 160 | <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> | ||
635 | 161 | for a discussion of issues. | ||
636 | 162 | |||
637 | 163 | Typical user data directories are: | ||
638 | 164 | Mac OS X: same as user_data_dir | ||
639 | 165 | Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined | ||
640 | 166 | Win *: same as user_data_dir | ||
641 | 167 | |||
642 | 168 | For Unix, we follow the XDG spec and support $XDG_DATA_HOME. | ||
643 | 169 | That means, by deafult "~/.local/share/<AppName>". | ||
644 | 170 | """ | ||
645 | 171 | if sys.platform in [ "win32", "darwin" ]: | ||
646 | 172 | path = user_data_dir(appname, appauthor, None, roaming) | ||
647 | 173 | else: | ||
648 | 174 | path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) | ||
649 | 175 | if appname: | ||
650 | 176 | path = os.path.join(path, appname) | ||
651 | 177 | if appname and version: | ||
652 | 178 | path = os.path.join(path, version) | ||
653 | 179 | return path | ||
654 | 180 | |||
655 | 181 | |||
656 | 182 | def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): | ||
657 | 183 | """Return full path to the user-shared data dir for this application. | ||
658 | 184 | |||
659 | 185 | "appname" is the name of application. | ||
660 | 186 | If None, just the system directory is returned. | ||
661 | 187 | "appauthor" (only required and used on Windows) is the name of the | ||
662 | 188 | appauthor or distributing body for this application. Typically | ||
663 | 189 | it is the owning company name. This falls back to appname. | ||
664 | 190 | "version" is an optional version path element to append to the | ||
665 | 191 | path. You might want to use this if you want multiple versions | ||
666 | 192 | of your app to be able to run independently. If used, this | ||
667 | 193 | would typically be "<major>.<minor>". | ||
668 | 194 | Only applied when appname is present. | ||
669 | 195 | "multipath" is an optional parameter only applicable to *nix | ||
670 | 196 | which indicates that the entire list of config dirs should be | ||
671 | 197 | returned. By default, the first item from XDG_CONFIG_DIRS is | ||
672 | 198 | returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set | ||
673 | 199 | |||
674 | 200 | Typical user data directories are: | ||
675 | 201 | Mac OS X: same as site_data_dir | ||
676 | 202 | Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in | ||
677 | 203 | $XDG_CONFIG_DIRS | ||
678 | 204 | Win *: same as site_data_dir | ||
679 | 205 | Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) | ||
680 | 206 | |||
681 | 207 | For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False | ||
682 | 208 | |||
683 | 209 | WARNING: Do not use this on Windows. See the Vista-Fail note above for why. | ||
684 | 210 | """ | ||
685 | 211 | if sys.platform in [ "win32", "darwin" ]: | ||
686 | 212 | path = site_data_dir(appname, appauthor) | ||
687 | 213 | if appname and version: | ||
688 | 214 | path = os.path.join(path, version) | ||
689 | 215 | else: | ||
690 | 216 | # XDG default for $XDG_CONFIG_DIRS | ||
691 | 217 | # only first, if multipath is False | ||
692 | 218 | path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') | ||
693 | 219 | pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ] | ||
694 | 220 | if appname: | ||
695 | 221 | if version: | ||
696 | 222 | appname = os.path.join(appname, version) | ||
697 | 223 | pathlist = [ os.sep.join([x, appname]) for x in pathlist ] | ||
698 | 224 | |||
699 | 225 | if multipath: | ||
700 | 226 | path = os.pathsep.join(pathlist) | ||
701 | 227 | else: | ||
702 | 228 | path = pathlist[0] | ||
703 | 229 | return path | ||
704 | 230 | |||
705 | 231 | def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): | ||
706 | 232 | r"""Return full path to the user-specific cache dir for this application. | ||
707 | 233 | |||
708 | 234 | "appname" is the name of application. | ||
709 | 235 | If None, just the system directory is returned. | ||
710 | 236 | "appauthor" (only required and used on Windows) is the name of the | ||
711 | 237 | appauthor or distributing body for this application. Typically | ||
712 | 238 | it is the owning company name. This falls back to appname. | ||
713 | 239 | "version" is an optional version path element to append to the | ||
714 | 240 | path. You might want to use this if you want multiple versions | ||
715 | 241 | of your app to be able to run independently. If used, this | ||
716 | 242 | would typically be "<major>.<minor>". | ||
717 | 243 | Only applied when appname is present. | ||
718 | 244 | "opinion" (boolean) can be False to disable the appending of | ||
719 | 245 | "Cache" to the base app data dir for Windows. See | ||
720 | 246 | discussion below. | ||
721 | 247 | |||
722 | 248 | Typical user cache directories are: | ||
723 | 249 | Mac OS X: ~/Library/Caches/<AppName> | ||
724 | 250 | Unix: ~/.cache/<AppName> (XDG default) | ||
725 | 251 | Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache | ||
726 | 252 | Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache | ||
727 | 253 | |||
728 | 254 | On Windows the only suggestion in the MSDN docs is that local settings go in | ||
729 | 255 | the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming | ||
730 | 256 | app data dir (the default returned by `user_data_dir` above). Apps typically | ||
731 | 257 | put cache data somewhere *under* the given dir here. Some examples: | ||
732 | 258 | ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache | ||
733 | 259 | ...\Acme\SuperApp\Cache\1.0 | ||
734 | 260 | OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. | ||
735 | 261 | This can be disabled with the `opinion=False` option. | ||
736 | 262 | """ | ||
737 | 263 | if sys.platform == "win32": | ||
738 | 264 | if appauthor is None: | ||
739 | 265 | appauthor = appname | ||
740 | 266 | path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) | ||
741 | 267 | if appname: | ||
742 | 268 | path = os.path.join(path, appauthor, appname) | ||
743 | 269 | if opinion: | ||
744 | 270 | path = os.path.join(path, "Cache") | ||
745 | 271 | elif sys.platform == 'darwin': | ||
746 | 272 | path = os.path.expanduser('~/Library/Caches') | ||
747 | 273 | if appname: | ||
748 | 274 | path = os.path.join(path, appname) | ||
749 | 275 | else: | ||
750 | 276 | path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) | ||
751 | 277 | if appname: | ||
752 | 278 | path = os.path.join(path, appname) | ||
753 | 279 | if appname and version: | ||
754 | 280 | path = os.path.join(path, version) | ||
755 | 281 | return path | ||
756 | 282 | |||
757 | 283 | def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): | ||
758 | 284 | r"""Return full path to the user-specific log dir for this application. | ||
759 | 285 | |||
760 | 286 | "appname" is the name of application. | ||
761 | 287 | If None, just the system directory is returned. | ||
762 | 288 | "appauthor" (only required and used on Windows) is the name of the | ||
763 | 289 | appauthor or distributing body for this application. Typically | ||
764 | 290 | it is the owning company name. This falls back to appname. | ||
765 | 291 | "version" is an optional version path element to append to the | ||
766 | 292 | path. You might want to use this if you want multiple versions | ||
767 | 293 | of your app to be able to run independently. If used, this | ||
768 | 294 | would typically be "<major>.<minor>". | ||
769 | 295 | Only applied when appname is present. | ||
770 | 296 | "opinion" (boolean) can be False to disable the appending of | ||
771 | 297 | "Logs" to the base app data dir for Windows, and "log" to the | ||
772 | 298 | base cache dir for Unix. See discussion below. | ||
773 | 299 | |||
774 | 300 | Typical user cache directories are: | ||
775 | 301 | Mac OS X: ~/Library/Logs/<AppName> | ||
776 | 302 | Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined | ||
777 | 303 | Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs | ||
778 | 304 | Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs | ||
779 | 305 | |||
780 | 306 | On Windows the only suggestion in the MSDN docs is that local settings | ||
781 | 307 | go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in | ||
782 | 308 | examples of what some windows apps use for a logs dir.) | ||
783 | 309 | |||
784 | 310 | OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` | ||
785 | 311 | value for Windows and appends "log" to the user cache dir for Unix. | ||
786 | 312 | This can be disabled with the `opinion=False` option. | ||
787 | 313 | """ | ||
788 | 314 | if sys.platform == "darwin": | ||
789 | 315 | path = os.path.join( | ||
790 | 316 | os.path.expanduser('~/Library/Logs'), | ||
791 | 317 | appname) | ||
792 | 318 | elif sys.platform == "win32": | ||
793 | 319 | path = user_data_dir(appname, appauthor, version); version=False | ||
794 | 320 | if opinion: | ||
795 | 321 | path = os.path.join(path, "Logs") | ||
796 | 322 | else: | ||
797 | 323 | path = user_cache_dir(appname, appauthor, version); version=False | ||
798 | 324 | if opinion: | ||
799 | 325 | path = os.path.join(path, "log") | ||
800 | 326 | if appname and version: | ||
801 | 327 | path = os.path.join(path, version) | ||
802 | 328 | return path | ||
803 | 329 | |||
804 | 330 | |||
805 | 331 | class AppDirs(object): | ||
806 | 332 | """Convenience wrapper for getting application dirs.""" | ||
807 | 333 | def __init__(self, appname, appauthor=None, version=None, | ||
808 | 334 | roaming=False, multipath=False): | ||
809 | 335 | self.appname = appname | ||
810 | 336 | self.appauthor = appauthor | ||
811 | 337 | self.version = version | ||
812 | 338 | self.roaming = roaming | ||
813 | 339 | self.multipath = multipath | ||
814 | 340 | @property | ||
815 | 341 | def user_data_dir(self): | ||
816 | 342 | return user_data_dir(self.appname, self.appauthor, | ||
817 | 343 | version=self.version, roaming=self.roaming) | ||
818 | 344 | @property | ||
819 | 345 | def site_data_dir(self): | ||
820 | 346 | return site_data_dir(self.appname, self.appauthor, | ||
821 | 347 | version=self.version, multipath=self.multipath) | ||
822 | 348 | @property | ||
823 | 349 | def user_config_dir(self): | ||
824 | 350 | return user_config_dir(self.appname, self.appauthor, | ||
825 | 351 | version=self.version, roaming=self.roaming) | ||
826 | 352 | @property | ||
827 | 353 | def site_config_dir(self): | ||
828 | 354 | return site_data_dir(self.appname, self.appauthor, | ||
829 | 355 | version=self.version, multipath=self.multipath) | ||
830 | 356 | @property | ||
831 | 357 | def user_cache_dir(self): | ||
832 | 358 | return user_cache_dir(self.appname, self.appauthor, | ||
833 | 359 | version=self.version) | ||
834 | 360 | @property | ||
835 | 361 | def user_log_dir(self): | ||
836 | 362 | return user_log_dir(self.appname, self.appauthor, | ||
837 | 363 | version=self.version) | ||
838 | 364 | |||
839 | 365 | |||
840 | 366 | |||
841 | 367 | |||
842 | 368 | #---- internal support stuff | ||
843 | 369 | |||
844 | 370 | def _get_win_folder_from_registry(csidl_name): | ||
845 | 371 | """This is a fallback technique at best. I'm not sure if using the | ||
846 | 372 | registry for this guarantees us the correct answer for all CSIDL_* | ||
847 | 373 | names. | ||
848 | 374 | """ | ||
849 | 375 | import _winreg | ||
850 | 376 | |||
851 | 377 | shell_folder_name = { | ||
852 | 378 | "CSIDL_APPDATA": "AppData", | ||
853 | 379 | "CSIDL_COMMON_APPDATA": "Common AppData", | ||
854 | 380 | "CSIDL_LOCAL_APPDATA": "Local AppData", | ||
855 | 381 | }[csidl_name] | ||
856 | 382 | |||
857 | 383 | key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, | ||
858 | 384 | r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") | ||
859 | 385 | dir, type = _winreg.QueryValueEx(key, shell_folder_name) | ||
860 | 386 | return dir | ||
861 | 387 | |||
862 | 388 | def _get_win_folder_with_pywin32(csidl_name): | ||
863 | 389 | from win32com.shell import shellcon, shell | ||
864 | 390 | dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) | ||
865 | 391 | # Try to make this a unicode path because SHGetFolderPath does | ||
866 | 392 | # not return unicode strings when there is unicode data in the | ||
867 | 393 | # path. | ||
868 | 394 | try: | ||
869 | 395 | dir = unicode(dir) | ||
870 | 396 | |||
871 | 397 | # Downgrade to short path name if have highbit chars. See | ||
872 | 398 | # <http://bugs.activestate.com/show_bug.cgi?id=85099>. | ||
873 | 399 | has_high_char = False | ||
874 | 400 | for c in dir: | ||
875 | 401 | if ord(c) > 255: | ||
876 | 402 | has_high_char = True | ||
877 | 403 | break | ||
878 | 404 | if has_high_char: | ||
879 | 405 | try: | ||
880 | 406 | import win32api | ||
881 | 407 | dir = win32api.GetShortPathName(dir) | ||
882 | 408 | except ImportError: | ||
883 | 409 | pass | ||
884 | 410 | except UnicodeError: | ||
885 | 411 | pass | ||
886 | 412 | return dir | ||
887 | 413 | |||
888 | 414 | def _get_win_folder_with_ctypes(csidl_name): | ||
889 | 415 | import ctypes | ||
890 | 416 | |||
891 | 417 | csidl_const = { | ||
892 | 418 | "CSIDL_APPDATA": 26, | ||
893 | 419 | "CSIDL_COMMON_APPDATA": 35, | ||
894 | 420 | "CSIDL_LOCAL_APPDATA": 28, | ||
895 | 421 | }[csidl_name] | ||
896 | 422 | |||
897 | 423 | buf = ctypes.create_unicode_buffer(1024) | ||
898 | 424 | ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) | ||
899 | 425 | |||
900 | 426 | # Downgrade to short path name if have highbit chars. See | ||
901 | 427 | # <http://bugs.activestate.com/show_bug.cgi?id=85099>. | ||
902 | 428 | has_high_char = False | ||
903 | 429 | for c in buf: | ||
904 | 430 | if ord(c) > 255: | ||
905 | 431 | has_high_char = True | ||
906 | 432 | break | ||
907 | 433 | if has_high_char: | ||
908 | 434 | buf2 = ctypes.create_unicode_buffer(1024) | ||
909 | 435 | if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): | ||
910 | 436 | buf = buf2 | ||
911 | 437 | |||
912 | 438 | return buf.value | ||
913 | 439 | |||
914 | 440 | if sys.platform == "win32": | ||
915 | 441 | try: | ||
916 | 442 | import win32com.shell | ||
917 | 443 | _get_win_folder = _get_win_folder_with_pywin32 | ||
918 | 444 | except ImportError: | ||
919 | 445 | try: | ||
920 | 446 | import ctypes | ||
921 | 447 | _get_win_folder = _get_win_folder_with_ctypes | ||
922 | 448 | except ImportError: | ||
923 | 449 | _get_win_folder = _get_win_folder_from_registry | ||
924 | 450 | |||
925 | 451 | |||
926 | 452 | |||
927 | 453 | #---- self test code | ||
928 | 454 | |||
929 | 455 | if __name__ == "__main__": | ||
930 | 456 | appname = "MyApp" | ||
931 | 457 | appauthor = "MyCompany" | ||
932 | 458 | |||
933 | 459 | props = ("user_data_dir", "site_data_dir", | ||
934 | 460 | "user_config_dir", "site_config_dir", | ||
935 | 461 | "user_cache_dir", "user_log_dir") | ||
936 | 462 | |||
937 | 463 | print("-- app dirs (with optional 'version')") | ||
938 | 464 | dirs = AppDirs(appname, appauthor, version="1.0") | ||
939 | 465 | for prop in props: | ||
940 | 466 | print("%s: %s" % (prop, getattr(dirs, prop))) | ||
941 | 467 | |||
942 | 468 | print("\n-- app dirs (without optional 'version')") | ||
943 | 469 | dirs = AppDirs(appname, appauthor) | ||
944 | 470 | for prop in props: | ||
945 | 471 | print("%s: %s" % (prop, getattr(dirs, prop))) | ||
946 | 472 | |||
947 | 473 | print("\n-- app dirs (without optional 'appauthor')") | ||
948 | 474 | dirs = AppDirs(appname) | ||
949 | 475 | for prop in props: | ||
950 | 476 | print("%s: %s" % (prop, getattr(dirs, prop))) | ||
951 | 477 | |||
952 | 0 | 478 | ||
953 | === modified file 'openerp/tools/config.py' | |||
954 | --- openerp/tools/config.py 2014-02-12 22:52:40 +0000 | |||
955 | +++ openerp/tools/config.py 2014-02-27 16:58:29 +0000 | |||
956 | @@ -3,7 +3,7 @@ | |||
957 | 3 | # | 3 | # |
958 | 4 | # OpenERP, Open Source Management Solution | 4 | # OpenERP, Open Source Management Solution |
959 | 5 | # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). | 5 | # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). |
961 | 6 | # Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>). | 6 | # Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>). |
962 | 7 | # | 7 | # |
963 | 8 | # This program is free software: you can redistribute it and/or modify | 8 | # This program is free software: you can redistribute it and/or modify |
964 | 9 | # it under the terms of the GNU Affero General Public License as | 9 | # it under the terms of the GNU Affero General Public License as |
965 | @@ -29,6 +29,7 @@ | |||
966 | 29 | import openerp.loglevels as loglevels | 29 | import openerp.loglevels as loglevels |
967 | 30 | import logging | 30 | import logging |
968 | 31 | import openerp.release as release | 31 | import openerp.release as release |
969 | 32 | import appdirs | ||
970 | 32 | 33 | ||
971 | 33 | class MyOption (optparse.Option, object): | 34 | class MyOption (optparse.Option, object): |
972 | 34 | """ optparse Option with two additional attributes. | 35 | """ optparse Option with two additional attributes. |
973 | @@ -59,6 +60,9 @@ | |||
974 | 59 | 60 | ||
975 | 60 | DEFAULT_LOG_HANDLER = [':INFO'] | 61 | DEFAULT_LOG_HANDLER = [':INFO'] |
976 | 61 | 62 | ||
977 | 63 | def _get_default_datadir(): | ||
978 | 64 | return appdirs.user_data_dir(appname='OpenERP', appauthor=release.author) | ||
979 | 65 | |||
980 | 62 | class configmanager(object): | 66 | class configmanager(object): |
981 | 63 | def __init__(self, fname=None): | 67 | def __init__(self, fname=None): |
982 | 64 | # Options not exposed on the command line. Command line options will be added | 68 | # Options not exposed on the command line. Command line options will be added |
983 | @@ -106,6 +110,9 @@ | |||
984 | 106 | help="specify additional addons paths (separated by commas).", | 110 | help="specify additional addons paths (separated by commas).", |
985 | 107 | action="callback", callback=self._check_addons_path, nargs=1, type="string") | 111 | action="callback", callback=self._check_addons_path, nargs=1, type="string") |
986 | 108 | group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web") | 112 | group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web") |
987 | 113 | |||
988 | 114 | group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(), | ||
989 | 115 | help="Directory where to store OpenERP data") | ||
990 | 109 | parser.add_option_group(group) | 116 | parser.add_option_group(group) |
991 | 110 | 117 | ||
992 | 111 | # XML-RPC / HTTP | 118 | # XML-RPC / HTTP |
993 | @@ -348,6 +355,7 @@ | |||
994 | 348 | # (../etc from the server) | 355 | # (../etc from the server) |
995 | 349 | # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write, | 356 | # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write, |
996 | 350 | # else he won't be able to save the configurations, or even to start the server... | 357 | # else he won't be able to save the configurations, or even to start the server... |
997 | 358 | # TODO use appdirs | ||
998 | 351 | if os.name == 'nt': | 359 | if os.name == 'nt': |
999 | 352 | rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf') | 360 | rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf') |
1000 | 353 | else: | 361 | else: |
1001 | @@ -358,7 +366,6 @@ | |||
1002 | 358 | or os.environ.get('OPENERP_SERVER') or rcfilepath) | 366 | or os.environ.get('OPENERP_SERVER') or rcfilepath) |
1003 | 359 | self.load() | 367 | self.load() |
1004 | 360 | 368 | ||
1005 | 361 | |||
1006 | 362 | # Verify that we want to log or not, if not the output will go to stdout | 369 | # Verify that we want to log or not, if not the output will go to stdout |
1007 | 363 | if self.options['logfile'] in ('None', 'False'): | 370 | if self.options['logfile'] in ('None', 'False'): |
1008 | 364 | self.options['logfile'] = False | 371 | self.options['logfile'] = False |
1009 | @@ -387,7 +394,6 @@ | |||
1010 | 387 | elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER: | 394 | elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER: |
1011 | 388 | self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg]) | 395 | self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg]) |
1012 | 389 | 396 | ||
1013 | 390 | |||
1014 | 391 | if isinstance(self.options['log_handler'], basestring): | 397 | if isinstance(self.options['log_handler'], basestring): |
1015 | 392 | self.options['log_handler'] = self.options['log_handler'].split(',') | 398 | self.options['log_handler'] = self.options['log_handler'].split(',') |
1016 | 393 | 399 | ||
1017 | @@ -399,7 +405,8 @@ | |||
1018 | 399 | 'list_db', 'xmlrpcs', 'proxy_mode', | 405 | 'list_db', 'xmlrpcs', 'proxy_mode', |
1019 | 400 | 'test_file', 'test_enable', 'test_commit', 'test_report_directory', | 406 | 'test_file', 'test_enable', 'test_commit', 'test_report_directory', |
1020 | 401 | 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent', | 407 | 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent', |
1022 | 402 | 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'auto_reload' | 408 | 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', |
1023 | 409 | 'auto_reload', 'data_dir', | ||
1024 | 403 | ] | 410 | ] |
1025 | 404 | 411 | ||
1026 | 405 | for arg in keys: | 412 | for arg in keys: |
1027 | @@ -617,6 +624,24 @@ | |||
1028 | 617 | def __getitem__(self, key): | 624 | def __getitem__(self, key): |
1029 | 618 | return self.options[key] | 625 | return self.options[key] |
1030 | 619 | 626 | ||
1031 | 627 | @property | ||
1032 | 628 | def addons_data_dir(self): | ||
1033 | 629 | d = os.path.join(self['data_dir'], 'addons', release.series) | ||
1034 | 630 | if not os.path.exists(d): | ||
1035 | 631 | os.makedirs(d, 0700) | ||
1036 | 632 | else: | ||
1037 | 633 | os.chmod(d, 0700) | ||
1038 | 634 | return d | ||
1039 | 635 | |||
1040 | 636 | @property | ||
1041 | 637 | def session_dir(self): | ||
1042 | 638 | d = os.path.join(self['data_dir'], 'sessions', release.series) | ||
1043 | 639 | if not os.path.exists(d): | ||
1044 | 640 | os.makedirs(d, 0700) | ||
1045 | 641 | else: | ||
1046 | 642 | os.chmod(d, 0700) | ||
1047 | 643 | return d | ||
1048 | 644 | |||
1049 | 620 | config = configmanager() | 645 | config = configmanager() |
1050 | 621 | 646 | ||
1051 | 622 | 647 | ||
1052 | 623 | 648 | ||
1053 | === modified file 'openerp/tools/translate.py' | |||
1054 | --- openerp/tools/translate.py 2014-02-06 11:02:20 +0000 | |||
1055 | +++ openerp/tools/translate.py 2014-02-27 16:58:29 +0000 | |||
1056 | @@ -778,49 +778,32 @@ | |||
1057 | 778 | if model_obj._sql_constraints: | 778 | if model_obj._sql_constraints: |
1058 | 779 | push_local_constraints(module, model_obj, 'sql_constraints') | 779 | push_local_constraints(module, model_obj, 'sql_constraints') |
1059 | 780 | 780 | ||
1060 | 781 | def get_module_from_path(path, mod_paths=None): | ||
1061 | 782 | if not mod_paths: | ||
1062 | 783 | # First, construct a list of possible paths | ||
1063 | 784 | def_path = os.path.abspath(os.path.join(config.config['root_path'], 'addons')) # default addons path (base) | ||
1064 | 785 | ad_paths= map(lambda m: os.path.abspath(m.strip()),config.config['addons_path'].split(',')) | ||
1065 | 786 | mod_paths=[def_path] | ||
1066 | 787 | for adp in ad_paths: | ||
1067 | 788 | mod_paths.append(adp) | ||
1068 | 789 | if not os.path.isabs(adp): | ||
1069 | 790 | mod_paths.append(adp) | ||
1070 | 791 | elif adp.startswith(def_path): | ||
1071 | 792 | mod_paths.append(adp[len(def_path)+1:]) | ||
1072 | 793 | for mp in mod_paths: | ||
1073 | 794 | if path.startswith(mp) and (os.path.dirname(path) != mp): | ||
1074 | 795 | path = path[len(mp)+1:] | ||
1075 | 796 | return path.split(os.path.sep)[0] | ||
1076 | 797 | return 'base' # files that are not in a module are considered as being in 'base' module | ||
1077 | 798 | 781 | ||
1078 | 799 | modobj = registry['ir.module.module'] | 782 | modobj = registry['ir.module.module'] |
1079 | 800 | installed_modids = modobj.search(cr, uid, [('state', '=', 'installed')]) | 783 | installed_modids = modobj.search(cr, uid, [('state', '=', 'installed')]) |
1080 | 801 | installed_modules = map(lambda m: m['name'], modobj.read(cr, uid, installed_modids, ['name'])) | 784 | installed_modules = map(lambda m: m['name'], modobj.read(cr, uid, installed_modids, ['name'])) |
1081 | 802 | 785 | ||
1090 | 803 | root_path = os.path.join(config.config['root_path'], 'addons') | 786 | path_list = list(openerp.modules.module.ad_paths) |
1083 | 804 | |||
1084 | 805 | apaths = map(os.path.abspath, map(str.strip, config.config['addons_path'].split(','))) | ||
1085 | 806 | if root_path in apaths: | ||
1086 | 807 | path_list = apaths | ||
1087 | 808 | else : | ||
1088 | 809 | path_list = [root_path,] + apaths | ||
1089 | 810 | |||
1091 | 811 | # Also scan these non-addon paths | 787 | # Also scan these non-addon paths |
1092 | 812 | for bin_path in ['osv', 'report' ]: | 788 | for bin_path in ['osv', 'report' ]: |
1093 | 813 | path_list.append(os.path.join(config.config['root_path'], bin_path)) | 789 | path_list.append(os.path.join(config.config['root_path'], bin_path)) |
1094 | 814 | 790 | ||
1095 | 815 | _logger.debug("Scanning modules at paths: ", path_list) | 791 | _logger.debug("Scanning modules at paths: ", path_list) |
1096 | 816 | 792 | ||
1098 | 817 | mod_paths = [] | 793 | mod_paths = list(path_list) |
1099 | 794 | |||
1100 | 795 | def get_module_from_path(path): | ||
1101 | 796 | for mp in mod_paths: | ||
1102 | 797 | if path.startswith(mp) and (os.path.dirname(path) != mp): | ||
1103 | 798 | path = path[len(mp)+1:] | ||
1104 | 799 | return path.split(os.path.sep)[0] | ||
1105 | 800 | return 'base' # files that are not in a module are considered as being in 'base' module | ||
1106 | 818 | 801 | ||
1107 | 819 | def verified_module_filepaths(fname, path, root): | 802 | def verified_module_filepaths(fname, path, root): |
1108 | 820 | fabsolutepath = join(root, fname) | 803 | fabsolutepath = join(root, fname) |
1109 | 821 | frelativepath = fabsolutepath[len(path):] | 804 | frelativepath = fabsolutepath[len(path):] |
1110 | 822 | display_path = "addons%s" % frelativepath | 805 | display_path = "addons%s" % frelativepath |
1112 | 823 | module = get_module_from_path(fabsolutepath, mod_paths=mod_paths) | 806 | module = get_module_from_path(fabsolutepath) |
1113 | 824 | if ('all' in modules or module in modules) and module in installed_modules: | 807 | if ('all' in modules or module in modules) and module in installed_modules: |
1114 | 825 | return module, fabsolutepath, frelativepath, display_path | 808 | return module, fabsolutepath, frelativepath, display_path |
1115 | 826 | return None, None, None, None | 809 | return None, None, None, None |