Merge lp:~therp-nl/openupgrade-tools/7.0-add-database_cleanup into lp:openupgrade-tools

Proposed by Stefan Rijnhart (Opener)
Status: Work in progress
Proposed branch: lp:~therp-nl/openupgrade-tools/7.0-add-database_cleanup
Merge into: lp:openupgrade-tools
Diff against target: 1054 lines (+972/-0)
15 files modified
database_cleanup/__init__.py (+1/-0)
database_cleanup/__openerp__.py (+55/-0)
database_cleanup/model/__init__.py (+6/-0)
database_cleanup/model/purge_columns.py (+154/-0)
database_cleanup/model/purge_data.py (+106/-0)
database_cleanup/model/purge_models.py (+127/-0)
database_cleanup/model/purge_modules.py (+91/-0)
database_cleanup/model/purge_tables.py (+138/-0)
database_cleanup/model/purge_wizard.py (+64/-0)
database_cleanup/view/menu.xml (+48/-0)
database_cleanup/view/purge_columns.xml (+37/-0)
database_cleanup/view/purge_data.xml (+37/-0)
database_cleanup/view/purge_models.xml (+36/-0)
database_cleanup/view/purge_modules.xml (+36/-0)
database_cleanup/view/purge_tables.xml (+36/-0)
To merge this branch: bzr merge lp:~therp-nl/openupgrade-tools/7.0-add-database_cleanup
Reviewer Review Type Date Requested Status
Pedro Manuel Baeza code review and test Needs Fixing
Review via email: mp+203633@code.launchpad.net

Description of the change

Partial implementation of https://blueprints.launchpad.net/openupgrade-server/+spec/service-module-feature.

This proposal adds a module to clean up after a homebrew migration process such as OpenUpgrade.

To post a comment you must log in.
2. By Stefan Rijnhart (Opener)

[FIX] Typo, comment

3. By Stefan Rijnhart (Opener)

[FIX] Remove unused import, add missing copyright notice

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Hi Stefan,

Thanks for this handy module!

I had an error when purging a model having attachments (poweremail.mailbox). Not sure if this issue lies in the ir.attachment code or the purge though.

In openerp/addons/base/ir/ir_attachment.py the method check() calls exists on the deleted model.

            mids = self.pool.get(model).exists(cr, uid, mids)
            ima.check(cr, uid, model, mode)
            self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context)

Maybe should the purge delete the attachments or drop the relationship beforehand?

Traceback (most recent call last):
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/netsvc.py", line 292, in dispatch_rpc
    result = ExportService.getService(service_name).dispatch(method, params)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/service/web_services.py", line 626, in dispatch
    res = fn(db, uid, *params)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/osv/osv.py", line 190, in execute_kw
    return self.execute(db, uid, obj, method, *args, **kw or {})
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/osv/osv.py", line 132, in wrapper
    return f(self, dbname, *args, **kwargs)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/osv/osv.py", line 199, in execute
    res = self.execute_cr(cr, uid, obj, method, *args, **kw)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/osv/osv.py", line 187, in execute_cr
    return getattr(object, method)(cr, uid, *args, **kw)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/openupgrade-tools/database_cleanup/model/purge_wizard.py", line 58, in purge_all
    context=context)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/openupgrade-tools/database_cleanup/model/purge_models.py", line 73, in purge
    context=context)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/addons/base_calendar/crm_meeting.py", line 160, in write
    return super(ir_attachment, self).write(cr, uid, ids, vals, context=context)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/addons/document/document.py", line 137, in write
    return super(document_file, self).write(cr, uid, ids, vals, context)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/addons/base/ir/ir_attachment.py", line 277, in write
    self.check(cr, uid, ids, 'write', context=context, values=vals)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/addons/document/document.py", line 77, in check
    super(document_file, self).check(cr, uid, ids, mode, context=context, values=values)
  File "/home/gbaconnier/code/instances/openerp_foobar/trunk7/parts/server/openerp/addons/base/ir/ir_attachment.py", line 211, in check
    mids = self.pool.get(model).exists(cr, uid, mids)
AttributeError: 'NoneType' object has no attribute 'exists'

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

>
> Maybe should the purge delete the attachments or drop the relationship
> beforehand?
>

This is already what is done through these lines:

                attachment_ids = attachment_pool.search(
                    cr, uid, [('res_model', '=', line.name)], context=context)
                if attachment_ids:
                    attachment_pool.write(
                        cr, uid, attachment_ids, {'res_model': False},
                        context=context)

I have to figure out what happens.

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

I got it. It happens on models still in ir.model but not in the registry because the python modules are not there.

I replaced the lines

                    attachment_pool.write(
                        cr, uid, attachment_ids, {'res_model': False},
                        context=context)
By
                    cr.execute(
                        "UPDATE ir_attachment SET res_model = FALSE "
                        "WHERE id IN %s",
                        (tuple(attachment_ids), ))

And it worked. Is it an acceptable solution?

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

When removing the models from ir.model, shouldn't we remove the ir.model.fields having a relation pointing to them?

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

> When removing the models from ir.model, shouldn't we remove the
> ir.model.fields having a relation pointing to them?

My proposal: https://code.launchpad.net/~camptocamp/openupgrade-tools/7.0-add-database_cleanup-purge-model-relations/+merge/204440

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

> I got it. It happens on models still in ir.model but not in the registry
> because the python modules are not there.
>
> I replaced the lines
>
> attachment_pool.write(
> cr, uid, attachment_ids, {'res_model': False},
> context=context)
> By
> cr.execute(
> "UPDATE ir_attachment SET res_model = FALSE "
> "WHERE id IN %s",
> (tuple(attachment_ids), ))
>
> And it worked. Is it an acceptable solution?

MP's here https://code.launchpad.net/~camptocamp/openupgrade-tools/7.0-add-database_cleanup-purge-model-attachment-error/+merge/204442

4. By Guewen Baconnier @ Camptocamp

[FIX] avoid ''NoneType' object has no attribute 'exists'' error when purging models

5. By Guewen Baconnier @ Camptocamp

[IMP] Remove the fields having a relation to the purged models.

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Thank you very much, Guewen! I merged both your contributions into this branch.

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Oo I don't know what I did... But my rev5 is obviously wrong (replaced the wrong lines), here is the correct fix https://code.launchpad.net/~camptocamp/openupgrade-tools/7.0-add-database_cleanup-purge-model-attachment-error2/+merge/205125

Sorry!

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

> Oo I don't know what I did... But my rev5 is obviously wrong (replaced the
> wrong lines), here is the correct fix https://code.launchpad.net/~camptocamp
> /openupgrade-tools/7.0-add-database_cleanup-purge-model-attachment-
> error2/+merge/205125
>
> Sorry!

The one merged in 4, not 5. Decidely need holidays ;-)

6. By Guewen Baconnier @ Camptocamp

[FIX] error in previous merge

7. By Stefan Rijnhart (Opener)

[FIX] Don't remove uid field from wkf_instance, which is written in
    raw SQL query (but never read afterwards). Workaround for
    lp:1277899

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Apparently, base.sql comes with its own inconsistencies. Fixed in http://bazaar.launchpad.net/~therp-nl/openupgrade-tools/7.0-add-database_cleanup/revision/7.

8. By Stefan Rijnhart (Opener)

[FIX] Preserve dangling workflow table which is in use

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Discovered a workflow table without corresponding model that should not be purged.

9. By Stefan Rijnhart (Opener)

[RFR] Group models per table when detecting columns to purge
      to prevent problems with models sharing the same table

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

After some extensive testing I fixed the case of 'sale_id' not being in stock.picking.in, thus to be purged from stock.picking. The fix allows for multiple models to share the same table in a generic way.

10. By Stefan Rijnhart (Opener)

[ADD] Allow purging of dangling data entries

11. By Stefan Rijnhart (Opener)

[FIX] Data purging now working

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

The module now allows to purge entries from ir.model.data that refer to a nonexisting row in its model's table.

12. By Stefan Rijnhart (Opener)

[IMP] Docstrings

13. By Stefan Rijnhart (Opener)

[FIX] Label
[FIX] Catch attempt to unlink field from nonexisting model

14. By Stefan Rijnhart (Opener)

[RFR] Flake8

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Stefan, finally I have tried your module and it's fantastic. For the most of the sections, it works like intended, but in "Purge obsolete tables", I find some tables that cannot be removed due to foreign keys in other tables. Use DROP CASCADE is too risky to be implemented, but you can enclose removal in a try and warns user afterwards, but continuing with tables that possibly contain the foreign key it complains about.

Also a warn that it's convenient to remove obsolete fields before purging obsolete tables is a good improvements.

One more thing: if I try to click on line button to remove tables one by one, I get line form view, but button cannot be clicked. Maybe readonly attribute?

Thanks for this excellent module.

Regards.

P.S.: Don't you think this module better fits on server-env-tools?

review: Needs Fixing (code review and test)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Thanks for the review, Pedro! Glad you like it. I'll have a look at continuing after a drop table failed.

You can only click on the icons on individual line after you save the wizard form first. There is nothing that I can do about that, I think.

I could propose on server-env-tools, but as OpenERP 7.0 is pretty good in not leaving these remnants after module updates and uninstalls, this module is typically used after a home grown migration. We would need consensus on whether or not to adopt tools for home grown migration within OCA first. I expect such a discussion to pop up after the formalization of the association in the course of this year. Until there is a clear decision on this topic, I think these projects are better of separately.

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Yeah, you are right about the inclusion on server-env-tools just right now.

About wizard saving before clicking on buttons, maybe you can present it on a popup or go to view mode to avoid this. Can it be done?

Regards.

Revision history for this message
Stéphane Bidoul (Acsone) (sbi) wrote :

Hi Stefan,

I just stumbled upon this module. This is most useful!

It seems to work fine on 8.0, except the individual purge buttons are disabled in the lists. Not sure why yet.

Is this the latest version, or did it find a home somewhere else?

Thanks!

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

@Stéphane: glad you found this useful. Doesn't saving the wizard form first work to access the list buttons in 8.0 like it does in 7.0?

I never got round to implementing Pedro's suggestion. I could leave it and propose to server-tools 7.0 as is. It can be improved afterwards. What do you think?

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

If you don't have to work on the improvement, the module itself is valuable, so I agree that you should propose to server-tools. Please put a section TODO on the module description with the considerations for being taking into account for other possible contributors.

Revision history for this message
Stéphane Bidoul (Acsone) (sbi) wrote :

@Stephan,

I indeed found that saving first solved the button issue, shame on me :)

The module is very useful as is, so I'm +1 to have it proposed to OCA/server-tools.

I have a short patch/hack to make it work on 8.0 in case anyone is interested.

Revision history for this message
Stéphane Bidoul (Acsone) (sbi) wrote :

@Stefan,

My colleague Anthony has made the pull request to OCA/server-tool.
https://github.com/OCA/server-tools/pull/95

Commit authorship has been preserved. I hope you take no issue with this.

Of course, feel free to clone the git branch and re-do the PR.

Best regards,

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Hi Stéphane, thanks for picking this up. I see that I am already too late for the review, as it is already merged ;-)

Unmerged revisions

14. By Stefan Rijnhart (Opener)

[RFR] Flake8

13. By Stefan Rijnhart (Opener)

[FIX] Label
[FIX] Catch attempt to unlink field from nonexisting model

12. By Stefan Rijnhart (Opener)

[IMP] Docstrings

11. By Stefan Rijnhart (Opener)

[FIX] Data purging now working

10. By Stefan Rijnhart (Opener)

[ADD] Allow purging of dangling data entries

9. By Stefan Rijnhart (Opener)

[RFR] Group models per table when detecting columns to purge
      to prevent problems with models sharing the same table

8. By Stefan Rijnhart (Opener)

[FIX] Preserve dangling workflow table which is in use

7. By Stefan Rijnhart (Opener)

[FIX] Don't remove uid field from wkf_instance, which is written in
    raw SQL query (but never read afterwards). Workaround for
    lp:1277899

6. By Guewen Baconnier @ Camptocamp

[FIX] error in previous merge

5. By Guewen Baconnier @ Camptocamp

[IMP] Remove the fields having a relation to the purged models.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'database_cleanup'
=== added file 'database_cleanup/__init__.py'
--- database_cleanup/__init__.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/__init__.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,1 @@
1from . import model
02
=== added file 'database_cleanup/__openerp__.py'
--- database_cleanup/__openerp__.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/__openerp__.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,55 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22{
23 'name': 'Database cleanup',
24 'version': '0.1',
25 'author': 'Therp BV',
26 'depends': ['base'],
27 'license': 'AGPL-3',
28 'category': 'Tools',
29 'data': [
30 'view/purge_modules.xml',
31 'view/purge_models.xml',
32 'view/purge_columns.xml',
33 'view/purge_tables.xml',
34 'view/purge_data.xml',
35 'view/menu.xml',
36 ],
37 'description': """\
38Clean your OpenERP database from remnants of modules, models, columns and
39tables left by uninstalled modules (prior to 7.0) or a homebrew database
40upgrade to a new major version of OpenERP.
41
42After installation of this module, go to the Settings menu -> Technical ->
43Database cleanup. Go through the modules, models, columns and tables
44entries under this menu (in that order) and find out if there is orphaned data
45in your database. You can either delete entries by line, or sweep all entries
46in one big step (if you are *really* confident).
47
48Caution! This module is potentially harmful and can *easily* destroy the
49integrity of your data. Do not use if you are not entirely comfortable
50with the technical details of the OpenERP data model of *all* the modules
51that have ever been installed on your database, and do not purge any module,
52model, column or table if you do not know exactly what you are doing.
53""",
54
55}
056
=== added directory 'database_cleanup/model'
=== added file 'database_cleanup/model/__init__.py'
--- database_cleanup/model/__init__.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/__init__.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,6 @@
1from . import purge_wizard
2from . import purge_modules
3from . import purge_models
4from . import purge_columns
5from . import purge_tables
6from . import purge_data
07
=== added file 'database_cleanup/model/purge_columns.py'
--- database_cleanup/model/purge_columns.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_columns.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,154 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from openerp.osv import orm, fields
23from openerp.tools.translate import _
24
25
26class CleanupPurgeLineColumn(orm.TransientModel):
27 _inherit = 'cleanup.purge.line'
28 _name = 'cleanup.purge.line.column'
29
30 _columns = {
31 'model_id': fields.many2one(
32 'ir.model', 'Model',
33 required=True, ondelete='CASCADE'),
34 'wizard_id': fields.many2one(
35 'cleanup.purge.wizard.column', 'Purge Wizard', readonly=True),
36 }
37
38 def purge(self, cr, uid, ids, context=None):
39 """
40 Unlink columns upon manual confirmation.
41 """
42 for line in self.browse(cr, uid, ids, context=context):
43 if line.purged:
44 continue
45
46 model_pool = self.pool[line.model_id.model]
47
48 # Check whether the column actually still exists.
49 # Inheritance such as stock.picking.in from stock.picking
50 # can lead to double attempts at removal
51 cr.execute(
52 'SELECT count(attname) FROM pg_attribute '
53 'WHERE attrelid = '
54 '( SELECT oid FROM pg_class WHERE relname = %s ) '
55 'AND attname = %s',
56 (model_pool._table, line.name))
57 if not cr.fetchone()[0]:
58 continue
59
60 self.logger.info(
61 'Dropping column %s from table %s',
62 line.name, model_pool._table)
63 cr.execute(
64 """
65 ALTER TABLE "%s" DROP COLUMN "%s"
66 """ % (model_pool._table, line.name))
67 line.write({'purged': True})
68 cr.commit()
69 return True
70
71
72class CleanupPurgeWizardColumn(orm.TransientModel):
73 _inherit = 'cleanup.purge.wizard'
74 _name = 'cleanup.purge.wizard.column'
75
76 # List of known columns in use without corresponding fields
77 # Format: {table: [fields]}
78 blacklist = {
79 'wkf_instance': ['uid'], # lp:1277899
80 }
81
82 def default_get(self, cr, uid, fields, context=None):
83 res = super(CleanupPurgeWizardColumn, self).default_get(
84 cr, uid, fields, context=context)
85 if 'name' in fields:
86 res['name'] = _('Purge columns')
87 return res
88
89 def get_orphaned_columns(self, cr, uid, model_pools, context=None):
90 """
91 From openobject-server/openerp/osv/orm.py
92 Iterate on the database columns to identify columns
93 of fields which have been removed
94 """
95
96 columns = list(set([
97 column for model_pool in model_pools
98 for column in model_pool._columns
99 if not (isinstance(model_pool._columns[column], fields.function)
100 and not model_pool._columns[column].store)
101 ]))
102 columns += orm.MAGIC_COLUMNS
103 columns += self.blacklist.get(model_pools[0]._table, [])
104
105 cr.execute("SELECT a.attname"
106 " FROM pg_class c, pg_attribute a"
107 " WHERE c.relname=%s"
108 " AND c.oid=a.attrelid"
109 " AND a.attisdropped=%s"
110 " AND pg_catalog.format_type(a.atttypid, a.atttypmod)"
111 " NOT IN ('cid', 'tid', 'oid', 'xid')"
112 " AND a.attname NOT IN %s",
113 (model_pools[0]._table, False, tuple(columns))),
114 return [column[0] for column in cr.fetchall()]
115
116 def find(self, cr, uid, context=None):
117 """
118 Search for columns that are not in the corresponding model.
119
120 Group models by table to prevent false positives for columns
121 that are only in some of the models sharing the same table.
122 Example of this is 'sale_id' not being a field of stock.picking.in
123 """
124 res = []
125 model_pool = self.pool['ir.model']
126 model_ids = model_pool.search(cr, uid, [], context=context)
127
128 # mapping of tables to tuples (model id, [pool1, pool2, ...])
129 table2model = {}
130
131 for model in model_pool.browse(cr, uid, model_ids, context=context):
132 model_pool = self.pool.get(model.model)
133 if not model_pool or not model_pool._auto:
134 continue
135 table2model.setdefault(
136 model_pool._table, (model.id, []))[1].append(model_pool)
137
138 for table, model_spec in table2model.iteritems():
139 for column in self.get_orphaned_columns(
140 cr, uid, model_spec[1], context=context):
141 res.append((0, 0, {
142 'name': column,
143 'model_id': model_spec[0]}))
144 if not res:
145 raise orm.except_orm(
146 _('Nothing to do'),
147 _('No orphaned columns found'))
148 return res
149
150 _columns = {
151 'purge_line_ids': fields.one2many(
152 'cleanup.purge.line.column',
153 'wizard_id', 'Columns to purge'),
154 }
0155
=== added file 'database_cleanup/model/purge_data.py'
--- database_cleanup/model/purge_data.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_data.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,106 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from openerp.osv import orm, fields
23from openerp.tools.translate import _
24
25
26class CleanupPurgeLineData(orm.TransientModel):
27 _inherit = 'cleanup.purge.line'
28 _name = 'cleanup.purge.line.data'
29
30 _columns = {
31 'data_id': fields.many2one(
32 'ir.model.data', 'Data entry',
33 ondelete='SET NULL'),
34 'wizard_id': fields.many2one(
35 'cleanup.purge.wizard.data', 'Purge Wizard', readonly=True),
36 }
37
38 def purge(self, cr, uid, ids, context=None):
39 """
40 Unlink data entries upon manual confirmation.
41 """
42 data_ids = []
43 for line in self.browse(cr, uid, ids, context=context):
44 if line.purged or not line.data_id:
45 continue
46 data_ids.append(line.data_id.id)
47 self.logger.info('Purging data entry: %s', line.name)
48 self.pool['ir.model.data'].unlink(cr, uid, data_ids, context=context)
49 return self.write(cr, uid, ids, {'purged': True}, context=context)
50
51
52class CleanupPurgeWizardData(orm.TransientModel):
53 _inherit = 'cleanup.purge.wizard'
54 _name = 'cleanup.purge.wizard.data'
55
56 def default_get(self, cr, uid, fields, context=None):
57 res = super(CleanupPurgeWizardData, self).default_get(
58 cr, uid, fields, context=context)
59 if 'name' in fields:
60 res['name'] = _('Purge data')
61 return res
62
63 def find(self, cr, uid, context=None):
64 """
65 Collect all rows from ir_model_data that refer
66 to a nonexisting model, or to a nonexisting
67 row in the model's table.
68 """
69 res = []
70 data_pool = self.pool['ir.model.data']
71 data_ids = []
72 unknown_models = []
73 cr.execute("""SELECT DISTINCT(model) FROM ir_model_data""")
74 for (model,) in cr.fetchall():
75 if not model:
76 continue
77 if not self.pool.get(model):
78 unknown_models.append(model)
79 continue
80 cr.execute(
81 """
82 SELECT id FROM ir_model_data
83 WHERE model = %%s
84 AND res_id IS NOT NULL
85 AND res_id NOT IN (
86 SELECT id FROM %s)
87 """ % self.pool[model]._table, (model,))
88 data_ids += [data_row[0] for data_row in cr.fetchall()]
89 data_ids += data_pool.search(
90 cr, uid, [('model', 'in', unknown_models)], context=context)
91 for data in data_pool.browse(cr, uid, data_ids, context=context):
92 res.append((0, 0, {
93 'data_id': data.id,
94 'name': "%s.%s, object of type %s" % (
95 data.module, data.name, data.model)}))
96 if not res:
97 raise orm.except_orm(
98 _('Nothing to do'),
99 _('No orphaned data entries found'))
100 return res
101
102 _columns = {
103 'purge_line_ids': fields.one2many(
104 'cleanup.purge.line.data',
105 'wizard_id', 'Data to purge'),
106 }
0107
=== added file 'database_cleanup/model/purge_models.py'
--- database_cleanup/model/purge_models.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_models.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,127 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from openerp.osv import orm, fields
23from openerp.tools.translate import _
24from openerp.addons.base.ir.ir_model import MODULE_UNINSTALL_FLAG
25
26
27class IrModel(orm.Model):
28 _inherit = 'ir.model'
29
30 def _drop_table(self, cr, uid, ids, context=None):
31 # Allow to skip this step during model unlink
32 # The super method crashes if the model cannot be instantiated
33 if context and context.get('no_drop_table'):
34 return True
35 return super(IrModel, self)._drop_table(cr, uid, ids, context=context)
36
37
38class CleanupPurgeLineModel(orm.TransientModel):
39 _inherit = 'cleanup.purge.line'
40 _name = 'cleanup.purge.line.model'
41
42 _columns = {
43 'wizard_id': fields.many2one(
44 'cleanup.purge.wizard.model', 'Purge Wizard', readonly=True),
45 }
46
47 def purge(self, cr, uid, ids, context=None):
48 """
49 Unlink models upon manual confirmation.
50 """
51 model_pool = self.pool['ir.model']
52 attachment_pool = self.pool['ir.attachment']
53 constraint_pool = self.pool['ir.model.constraint']
54 fields_pool = self.pool['ir.model.fields']
55
56 local_context = (context or {}).copy()
57 local_context.update({
58 MODULE_UNINSTALL_FLAG: True,
59 'no_drop_table': True,
60 })
61
62 for line in self.browse(cr, uid, ids, context=context):
63 cr.execute(
64 "SELECT id, model from ir_model WHERE model = %s",
65 (line.name,))
66 row = cr.fetchone()
67 if row:
68 self.logger.info('Purging model %s', row[1])
69 attachment_ids = attachment_pool.search(
70 cr, uid, [('res_model', '=', line.name)], context=context)
71 if attachment_ids:
72 cr.execute(
73 "UPDATE ir_attachment SET res_model = FALSE "
74 "WHERE id in %s",
75 (tuple(attachment_ids), ))
76 constraint_ids = constraint_pool.search(
77 cr, uid, [('model', '=', line.name)], context=context)
78 if constraint_ids:
79 constraint_pool.unlink(
80 cr, uid, constraint_ids, context=context)
81 relation_ids = fields_pool.search(
82 cr, uid, [('relation', '=', row[1])], context=context)
83 for relation in relation_ids:
84 try:
85 # Fails if the model on the target side
86 # cannot be instantiated
87 fields_pool.unlink(cr, uid, [relation],
88 context=local_context)
89 except AttributeError:
90 pass
91 model_pool.unlink(cr, uid, [row[0]], context=local_context)
92 line.write({'purged': True})
93 cr.commit()
94 return True
95
96
97class CleanupPurgeWizardModel(orm.TransientModel):
98 _inherit = 'cleanup.purge.wizard'
99 _name = 'cleanup.purge.wizard.model'
100
101 def default_get(self, cr, uid, fields, context=None):
102 res = super(CleanupPurgeWizardModel, self).default_get(
103 cr, uid, fields, context=context)
104 if 'name' in fields:
105 res['name'] = _('Purge models')
106 return res
107
108 def find(self, cr, uid, context=None):
109 """
110 Search for models that cannot be instantiated.
111 """
112 res = []
113 cr.execute("SELECT model from ir_model")
114 for (model,) in cr.fetchall():
115 if not self.pool.get(model):
116 res.append((0, 0, {'name': model}))
117 if not res:
118 raise orm.except_orm(
119 _('Nothing to do'),
120 _('No orphaned models found'))
121 return res
122
123 _columns = {
124 'purge_line_ids': fields.one2many(
125 'cleanup.purge.line.model',
126 'wizard_id', 'Models to purge'),
127 }
0128
=== added file 'database_cleanup/model/purge_modules.py'
--- database_cleanup/model/purge_modules.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_modules.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,91 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from openerp import pooler
23from openerp.osv import orm, fields
24from openerp.modules.module import get_module_path
25from openerp.tools.translate import _
26
27
28class CleanupPurgeLineModule(orm.TransientModel):
29 _inherit = 'cleanup.purge.line'
30 _name = 'cleanup.purge.line.module'
31
32 _columns = {
33 'wizard_id': fields.many2one(
34 'cleanup.purge.wizard.module', 'Purge Wizard', readonly=True),
35 }
36
37 def purge(self, cr, uid, ids, context=None):
38 """
39 Uninstall modules upon manual confirmation, then reload
40 the database.
41 """
42 module_pool = self.pool['ir.module.module']
43 lines = self.browse(cr, uid, ids, context=context)
44 module_names = [line.name for line in lines if not line.purged]
45 module_ids = module_pool.search(
46 cr, uid, [('name', 'in', module_names)], context=context)
47 if not module_ids:
48 return True
49 self.logger.info('Purging modules %s', ', '.join(module_names))
50 module_pool.write(
51 cr, uid, module_ids, {'state': 'to remove'}, context=context)
52 cr.commit()
53 _db, _pool = pooler.restart_pool(cr.dbname, update_module=True)
54 module_pool.unlink(cr, uid, module_ids, context=context)
55 return self.write(cr, uid, ids, {'purged': True}, context=context)
56
57
58class CleanupPurgeWizardModule(orm.TransientModel):
59 _inherit = 'cleanup.purge.wizard'
60 _name = 'cleanup.purge.wizard.module'
61
62 def default_get(self, cr, uid, fields, context=None):
63 res = super(CleanupPurgeWizardModule, self).default_get(
64 cr, uid, fields, context=context)
65 if 'name' in fields:
66 res['name'] = _('Purge modules')
67 return res
68
69 def find(self, cr, uid, context=None):
70 module_pool = self.pool['ir.module.module']
71 module_ids = module_pool.search(cr, uid, [], context=context)
72 res = []
73 for module in module_pool.browse(cr, uid, module_ids, context=context):
74 if get_module_path(module.name):
75 continue
76 if module.state == 'uninstalled':
77 module_pool.unlink(cr, uid, module.id, context=context)
78 continue
79 res.append((0, 0, {'name': module.name}))
80
81 if not res:
82 raise orm.except_orm(
83 _('Nothing to do'),
84 _('No modules found to purge'))
85 return res
86
87 _columns = {
88 'purge_line_ids': fields.one2many(
89 'cleanup.purge.line.module',
90 'wizard_id', 'Modules to purge'),
91 }
092
=== added file 'database_cleanup/model/purge_tables.py'
--- database_cleanup/model/purge_tables.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_tables.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,138 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from openerp.osv import orm, fields
23from openerp.tools.translate import _
24
25
26class CleanupPurgeLineTable(orm.TransientModel):
27 _inherit = 'cleanup.purge.line'
28 _name = 'cleanup.purge.line.table'
29
30 _columns = {
31 'wizard_id': fields.many2one(
32 'cleanup.purge.wizard.table', 'Purge Wizard', readonly=True),
33 }
34
35 def purge(self, cr, uid, ids, context=None):
36 """
37 Unlink tables upon manual confirmation.
38 """
39 lines = self.browse(cr, uid, ids, context=context)
40 tables = [line.name for line in lines]
41 for line in lines:
42 if line.purged:
43 continue
44
45 # Retrieve constraints on the tables to be dropped
46 # This query is referenced in numerous places
47 # on the Internet but credits probably go to Tom Lane
48 # in this post http://www.postgresql.org/\
49 # message-id/22895.1226088573@sss.pgh.pa.us
50 # Only using the constraint name and the source table,
51 # but I'm leaving the rest in for easier debugging
52 cr.execute(
53 """
54 SELECT conname, confrelid::regclass, af.attname AS fcol,
55 conrelid::regclass, a.attname AS col
56 FROM pg_attribute af, pg_attribute a,
57 (SELECT conname, conrelid, confrelid,conkey[i] AS conkey,
58 confkey[i] AS confkey
59 FROM (select conname, conrelid, confrelid, conkey,
60 confkey, generate_series(1,array_upper(conkey,1)) AS i
61 FROM pg_constraint WHERE contype = 'f') ss) ss2
62 WHERE af.attnum = confkey AND af.attrelid = confrelid AND
63 a.attnum = conkey AND a.attrelid = conrelid
64 AND confrelid::regclass = '%s'::regclass;
65 """ % line.name)
66
67 for constraint in cr.fetchall():
68 if constraint[3] in tables:
69 self.logger.info(
70 'Dropping constraint %s on table %s (to be dropped)',
71 constraint[0], constraint[3])
72 cr.execute(
73 "ALTER TABLE %s DROP CONSTRAINT %s" % (
74 constraint[3], constraint[0]))
75
76 self.logger.info(
77 'Dropping table %s', line.name)
78 cr.execute("DROP TABLE \"%s\"" % (line.name,))
79 line.write({'purged': True})
80 cr.commit()
81 return True
82
83
84class CleanupPurgeWizardTable(orm.TransientModel):
85 _inherit = 'cleanup.purge.wizard'
86 _name = 'cleanup.purge.wizard.table'
87
88 def default_get(self, cr, uid, fields, context=None):
89 res = super(CleanupPurgeWizardTable, self).default_get(
90 cr, uid, fields, context=context)
91 if 'name' in fields:
92 res['name'] = _('Purge tables')
93 return res
94
95 def find(self, cr, uid, context=None):
96 """
97 Search for tables that cannot be instantiated.
98 Ignore views for now.
99 """
100 model_ids = self.pool['ir.model'].search(cr, uid, [], context=context)
101 # Start out with known tables with no model
102 known_tables = ['wkf_witm_trans']
103 for model in self.pool['ir.model'].browse(
104 cr, uid, model_ids, context=context):
105
106 model_pool = self.pool.get(model.model)
107 if not model_pool:
108 continue
109 known_tables.append(model_pool._table)
110 known_tables += [
111 column._sql_names(model_pool)[0]
112 for column in model_pool._columns.values()
113 if column._type == 'many2many'
114 # unstored function fields of type m2m don't have _rel
115 and hasattr(column, '_rel')
116 ]
117
118 # Cannot pass table names as a psycopg argument
119 known_tables_repr = ",".join(
120 [("'%s'" % table) for table in known_tables])
121 cr.execute(
122 """
123 SELECT table_name FROM information_schema.tables
124 WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
125 AND table_name NOT IN (%s)""" % known_tables_repr)
126
127 res = [(0, 0, {'name': row[0]}) for row in cr.fetchall()]
128 if not res:
129 raise orm.except_orm(
130 _('Nothing to do'),
131 _('No orphaned tables found'))
132 return res
133
134 _columns = {
135 'purge_line_ids': fields.one2many(
136 'cleanup.purge.line.table',
137 'wizard_id', 'Tables to purge'),
138 }
0139
=== added file 'database_cleanup/model/purge_wizard.py'
--- database_cleanup/model/purge_wizard.py 1970-01-01 00:00:00 +0000
+++ database_cleanup/model/purge_wizard.py 2014-03-14 13:27:36 +0000
@@ -0,0 +1,64 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22import logging
23from openerp.osv import orm, fields
24
25
26class CleanupPurgeLine(orm.AbstractModel):
27 """ Abstract base class for the purge wizard lines """
28 _name = 'cleanup.purge.line'
29 _columns = {
30 'name': fields.char('Name', size=256, readonly=True),
31 'purged': fields.boolean('Purged', readonly=True),
32 }
33
34 logger = logging.getLogger('openerp.addons.database_cleanup')
35
36 def purge(self, cr, uid, ids, context=None):
37 raise NotImplementedError
38
39
40class PurgeWizard(orm.AbstractModel):
41 """ Abstract base class for the purge wizards """
42 _name = 'cleanup.purge.wizard'
43
44 def default_get(self, cr, uid, fields, context=None):
45 res = super(PurgeWizard, self).default_get(
46 cr, uid, fields, context=context)
47 if 'purge_line_ids' in fields:
48 res['purge_line_ids'] = self.find(cr, uid, context=None)
49 return res
50
51 def find(self, cr, uid, ids, context=None):
52 raise NotImplementedError
53
54 def purge_all(self, cr, uid, ids, context=None):
55 line_pool = self.pool[self._columns['purge_line_ids']._obj]
56 for wizard in self.browse(cr, uid, ids, context=context):
57 line_pool.purge(
58 cr, uid, [line.id for line in wizard.purge_line_ids],
59 context=context)
60 return True
61
62 _columns = {
63 'name': fields.char('Name', size=64, readonly=True),
64 }
065
=== added directory 'database_cleanup/static'
=== added directory 'database_cleanup/static/src'
=== added directory 'database_cleanup/static/src/img'
=== added file 'database_cleanup/static/src/img/icon.png'
1Binary files database_cleanup/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and database_cleanup/static/src/img/icon.png 2014-03-14 13:27:36 +0000 differ66Binary files database_cleanup/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and database_cleanup/static/src/img/icon.png 2014-03-14 13:27:36 +0000 differ
=== added directory 'database_cleanup/view'
=== added file 'database_cleanup/view/menu.xml'
--- database_cleanup/view/menu.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/menu.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,48 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record model="ir.ui.menu" id="menu_database_cleanup">
6 <field name="name">Database cleanup</field>
7 <field name="sequence" eval="10" />
8 <!-- attach to Settings -> Technical -->
9 <field name="parent_id" ref="base.menu_custom"/>
10 </record>
11
12 <record model="ir.ui.menu" id="menu_purge_modules">
13 <field name="name">Purge obsolete modules</field>
14 <field name="sequence" eval="10" />
15 <field name="action" ref="action_purge_modules" />
16 <field name="parent_id" ref="menu_database_cleanup"/>
17 </record>
18
19 <record model="ir.ui.menu" id="menu_purge_models">
20 <field name="name">Purge obsolete models</field>
21 <field name="sequence" eval="20" />
22 <field name="action" ref="action_purge_models" />
23 <field name="parent_id" ref="menu_database_cleanup"/>
24 </record>
25
26 <record model="ir.ui.menu" id="menu_purge_columns">
27 <field name="name">Purge obsolete columns</field>
28 <field name="sequence" eval="30" />
29 <field name="action" ref="action_purge_columns" />
30 <field name="parent_id" ref="menu_database_cleanup"/>
31 </record>
32
33 <record model="ir.ui.menu" id="menu_purge_tables">
34 <field name="name">Purge obsolete tables</field>
35 <field name="sequence" eval="40" />
36 <field name="action" ref="action_purge_tables" />
37 <field name="parent_id" ref="menu_database_cleanup"/>
38 </record>
39
40 <record model="ir.ui.menu" id="menu_purge_data">
41 <field name="name">Purge obsolete data entries</field>
42 <field name="sequence" eval="50" />
43 <field name="action" ref="action_purge_data" />
44 <field name="parent_id" ref="menu_database_cleanup"/>
45 </record>
46
47 </data>
48</openerp>
049
=== added file 'database_cleanup/view/purge_columns.xml'
--- database_cleanup/view/purge_columns.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/purge_columns.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,37 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="purge_columns_view" model="ir.ui.view">
6 <field name="name">Form view for purge columns wizard</field>
7 <field name="model">cleanup.purge.wizard.column</field>
8 <field name="arch" type="xml">
9 <form string="Purge columns" version="7.0">
10 <h1>
11 <field name="name"/>
12 </h1>
13 <button type="object" name="purge_all" string="Purge all columns" />
14 <field name="purge_line_ids" colspan="4" nolabel="1">
15 <tree string="Purge columns">
16 <field name="name" />
17 <field name="model_id" />
18 <field name="purged" invisible="0" />
19 <button type="object" name="purge"
20 icon="gtk-cancel" string="Purge this column"
21 attrs="{'invisible': [('purged', '=', True)]}"/>
22 </tree>
23 </field>
24 </form>
25 </field>
26 </record>
27
28 <record id="action_purge_columns" model="ir.actions.act_window">
29 <field name="name">Purge columns</field>
30 <field name="type">ir.actions.act_window</field>
31 <field name="res_model">cleanup.purge.wizard.column</field>
32 <field name="view_type">form</field>
33 <field name="view_mode">form</field>
34 </record>
35
36 </data>
37</openerp>
038
=== added file 'database_cleanup/view/purge_data.xml'
--- database_cleanup/view/purge_data.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/purge_data.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,37 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="purge_data_view" model="ir.ui.view">
6 <field name="name">Form view for purge data wizard</field>
7 <field name="model">cleanup.purge.wizard.data</field>
8 <field name="arch" type="xml">
9 <form string="Purge data entries that refer to missing resources" version="7.0">
10 <h1>
11 <field name="name"/>
12 </h1>
13 <button type="object" name="purge_all" string="Purge all data" />
14 <field name="purge_line_ids" colspan="4" nolabel="1">
15 <tree string="Purge data">
16 <field name="name" />
17 <field name="data_id" />
18 <field name="purged" invisible="0" />
19 <button type="object" name="purge"
20 icon="gtk-cancel" string="Purge this data"
21 attrs="{'invisible': [('purged', '=', True)]}"/>
22 </tree>
23 </field>
24 </form>
25 </field>
26 </record>
27
28 <record id="action_purge_data" model="ir.actions.act_window">
29 <field name="name">Purge data entries that refer to missing resources</field>
30 <field name="type">ir.actions.act_window</field>
31 <field name="res_model">cleanup.purge.wizard.data</field>
32 <field name="view_type">form</field>
33 <field name="view_mode">form</field>
34 </record>
35
36 </data>
37</openerp>
038
=== added file 'database_cleanup/view/purge_models.xml'
--- database_cleanup/view/purge_models.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/purge_models.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,36 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="purge_models_view" model="ir.ui.view">
6 <field name="name">Form view for purge models wizard</field>
7 <field name="model">cleanup.purge.wizard.model</field>
8 <field name="arch" type="xml">
9 <form string="Purge models" version="7.0">
10 <h1>
11 <field name="name"/>
12 </h1>
13 <button type="object" name="purge_all" string="Purge all models" />
14 <field name="purge_line_ids" colspan="4" nolabel="1">
15 <tree string="Purge models">
16 <field name="name" />
17 <field name="purged" invisible="0" />
18 <button type="object" name="purge"
19 icon="gtk-cancel" string="Purge this model"
20 attrs="{'invisible': [('purged', '=', True)]}"/>
21 </tree>
22 </field>
23 </form>
24 </field>
25 </record>
26
27 <record id="action_purge_models" model="ir.actions.act_window">
28 <field name="name">Purge models</field>
29 <field name="type">ir.actions.act_window</field>
30 <field name="res_model">cleanup.purge.wizard.model</field>
31 <field name="view_type">form</field>
32 <field name="view_mode">form</field>
33 </record>
34
35 </data>
36</openerp>
037
=== added file 'database_cleanup/view/purge_modules.xml'
--- database_cleanup/view/purge_modules.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/purge_modules.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,36 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="purge_modules_view" model="ir.ui.view">
6 <field name="name">Form view for purge modules wizard</field>
7 <field name="model">cleanup.purge.wizard.module</field>
8 <field name="arch" type="xml">
9 <form string="Purge modules" version="7.0">
10 <h1>
11 <field name="name"/>
12 </h1>
13 <button type="object" name="purge_all" string="Purge all modules" />
14 <field name="purge_line_ids" colspan="4" nolabel="1">
15 <tree string="Purge modules">
16 <field name="name" />
17 <field name="purged" invisible="0" />
18 <button type="object" name="purge"
19 icon="gtk-cancel" string="Purge this module"
20 attrs="{'invisible': [('purged', '=', True)]}"/>
21 </tree>
22 </field>
23 </form>
24 </field>
25 </record>
26
27 <record id="action_purge_modules" model="ir.actions.act_window">
28 <field name="name">Purge modules</field>
29 <field name="type">ir.actions.act_window</field>
30 <field name="res_model">cleanup.purge.wizard.module</field>
31 <field name="view_type">form</field>
32 <field name="view_mode">form</field>
33 </record>
34
35 </data>
36</openerp>
037
=== added file 'database_cleanup/view/purge_tables.xml'
--- database_cleanup/view/purge_tables.xml 1970-01-01 00:00:00 +0000
+++ database_cleanup/view/purge_tables.xml 2014-03-14 13:27:36 +0000
@@ -0,0 +1,36 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="purge_tables_view" model="ir.ui.view">
6 <field name="name">Form view for purge tables wizard</field>
7 <field name="model">cleanup.purge.wizard.table</field>
8 <field name="arch" type="xml">
9 <form string="Purge tables" version="7.0">
10 <h1>
11 <field name="name"/>
12 </h1>
13 <button type="object" name="purge_all" string="Purge all tables" />
14 <field name="purge_line_ids" colspan="4" nolabel="1">
15 <tree string="Purge tables">
16 <field name="name" />
17 <field name="purged" invisible="0" />
18 <button type="object" name="purge"
19 icon="gtk-cancel" string="Purge this table"
20 attrs="{'invisible': [('purged', '=', True)]}"/>
21 </tree>
22 </field>
23 </form>
24 </field>
25 </record>
26
27 <record id="action_purge_tables" model="ir.actions.act_window">
28 <field name="name">Purge tables</field>
29 <field name="type">ir.actions.act_window</field>
30 <field name="res_model">cleanup.purge.wizard.table</field>
31 <field name="view_type">form</field>
32 <field name="view_mode">form</field>
33 </record>
34
35 </data>
36</openerp>

Subscribers

People subscribed via source and target branches