diff --git a/CHANGELOG b/CHANGELOG index 25b4421d36..a15bcb8530 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +* Remove workflow module +* Simplify workflow engine +* Add ir.model.button for access rights * Replace fill by expand * Integer, Numeric and Float allow None as value * NULL value is None and not False diff --git a/MANIFEST.in b/MANIFEST.in index cf8175e978..8347bfeda3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -22,5 +22,3 @@ include trytond/res/locale/*.po include trytond/test/*.xml include trytond/webdav/*.xml include trytond/webdav/locale/*.po -include trytond/workflow/*.xml -include trytond/workflow/locale/*.po diff --git a/TODO b/TODO index df3e4b424e..02674b3941 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,6 @@ - Add check on action groups when executing wizard -- Handle button as a field to allow to override it with python code - Allow to create fields from the client interface - Improve encoding of default properties -- Use determined action name in workflow like for default value - Add check for index and constraint name lenght (limit 31) - Change _constraint to pass a BrowseRecordList instead of a list of ids - Add order attribute on page tag diff --git a/doc/ref/models/models.rst b/doc/ref/models/models.rst index 855ea6e418..2c02ac3bb7 100644 --- a/doc/ref/models/models.rst +++ b/doc/ref/models/models.rst @@ -103,6 +103,20 @@ ModelView It adds requirements to display a view of the model in the client. +Class attributes: + +.. attribute:: ModelView._buttons + + It contains a dictionary with button name as key and the states dictionary + for the button. This states dictionary will be used to generate the views + containing the button. + +Static methods: + +.. staticmethod:: ModelView.button + + Decorate button method to check group access. + Instance methods: .. method:: ModelView.fields_view_get([view_id[, view_type[, toolbar[, hexmd5]]]]) @@ -297,11 +311,6 @@ Instance methods: Helper method that checks if there is no recursion in the tree composed with ``parent`` as parent field name. -.. method:: ModelStorage.workflow_trigger_trigger(ids) - - Trigger a trigger event on the :ref:`workflow ` of - records. - ======== ModelSQL ======== @@ -378,31 +387,30 @@ Instance methods: - a list of arguments for the tables -============= -ModelWorkflow -============= +======== +Workflow +======== -.. class:: ModelWorkflow +.. class:: Workflow -It adds workflow capability to :class:`ModelStorage`. +A Mix-in class to handle transition check. -Instance methods: +Class attribute: -.. method:: ModelWorkflow.workflow_trigger_create(ids) +.. attribute:: Workflow._transition_state - Trigger create event on the :ref:`workflow ` of records. + The name of the field that will be used to check state transition. -.. method:: ModelWorkflow.workflow_trigger_write(ids) +.. attribute:: Workflow._transitions - Trigger write event on the :ref:`workflow ` of records. + A set containing tuples of from and to state. -.. method:: ModelWorkflow.workflow_trigger_validate(ids) +Static methods: - Trigger validate event on the :ref:`workflow ` of records. +.. staticmethod:: Workflow.transition(state) -.. method:: ModelWorkflow.workflow_trigger_delete(ids) - - Trigger delete event on the :ref:`workflow ` of records. + Decorate method to filter ids for which the transition is valid and finally + to update the state of the filtered ids. ============== ModelSingleton diff --git a/doc/topics/modules/index.rst b/doc/topics/modules/index.rst index c72a2ebd4c..b3791bde4f 100644 --- a/doc/topics/modules/index.rst +++ b/doc/topics/modules/index.rst @@ -6,7 +6,7 @@ Modules The modules of Tryton extend the functionality of the platform. The server comes by default with only a basic functionality included in these modules: -``ir``, ``res``, ``webdav``, ``workflow``. +``ir``, ``res``, ``webdav``. Module Structure ================ diff --git a/doc/topics/views/index.rst b/doc/topics/views/index.rst index 82fa81cd99..1c32f999a1 100644 --- a/doc/topics/views/index.rst +++ b/doc/topics/views/index.rst @@ -273,14 +273,11 @@ Display a button. * ``string``: The string that will be displayed inside the button. - * ``type``: It can be ``workflow``, ``object`` or ``action``. The default - is ``workflow``. It defines which type of action must be run when - clicking on it. + * ``type``: It can be ``object`` or ``action``. The default is ``object``. + It defines which type of action must be run when clicking on it. * ``name``: The name of the action: - * ``workflow``: the name of the signal that will be sent. - * ``object``: the name of the function that will called. The function must have this syntax: diff --git a/setup.py b/setup.py index 34e7a4214d..83ac40faca 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,6 @@ setup(name=PACKAGE, 'trytond.ir.ui': ['*.xml', '*.rng', '*.rnc'], 'trytond.res': ['*.xml', 'locale/*.po'], 'trytond.webdav': ['*.xml', 'locale/*.po'], - 'trytond.workflow': ['*.xml', 'locale/*.po'], 'trytond.test': ['*.xml'], }, scripts=['bin/trytond'], diff --git a/trytond/backend/mysql/database.py b/trytond/backend/mysql/database.py index 082fc15701..3e1c4eae23 100644 --- a/trytond/backend/mysql/database.py +++ b/trytond/backend/mysql/database.py @@ -177,7 +177,7 @@ class Database(DatabaseInterface): if (len(line)>0) and (not line.isspace()): cursor.execute(line) - for i in ('ir', 'workflow', 'res', 'webdav'): + for i in ('ir', 'res', 'webdav'): root_path = os.path.join(os.path.dirname(__file__), '..', '..') tryton_file = os.path.join(root_path, i, '__tryton__.py') with open(tryton_file) as fp: @@ -297,12 +297,6 @@ class Cursor(CursorInterface): 'ir_ui_menu', 'res_user', 'res_group', - 'wkf', - 'wkf_activity', - 'wkf_transition', - 'wkf_instance', - 'wkf_workitem', - 'wkf_witm_trans', 'ir_module_module', 'ir_module_module_dependency', 'ir_translation', diff --git a/trytond/backend/mysql/init.sql b/trytond/backend/mysql/init.sql index d2b3c86eef..a3f0f6b064 100644 --- a/trytond/backend/mysql/init.sql +++ b/trytond/backend/mysql/init.sql @@ -91,75 +91,6 @@ CREATE TABLE `res_user-res_group` ( CONSTRAINT `res_user-res_group_group_fkey` FOREIGN KEY (`group`) REFERENCES res_group (id) ON DELETE CASCADE ) ENGINE=InnoDB; -CREATE TABLE wkf ( - id BIGINT AUTO_INCREMENT NOT NULL, - name VARCHAR(255), - model VARCHAR(255), - on_create BOOLEAN NOT NULL, - PRIMARY KEY(id) -) ENGINE=InnoDB; - -CREATE TABLE wkf_activity ( - id BIGINT AUTO_INCREMENT NOT NULL, - workflow BIGINT, - subflow BIGINT, - split_mode VARCHAR(255), - join_mode VARCHAR(255), - name VARCHAR(255), - signal_send VARCHAR(255), - flow_start BOOLEAN NOT NULL, - flow_stop BOOLEAN NOT NULL, - stop_other BOOLEAN NOT NULL, - action VARCHAR(255), - PRIMARY KEY(id), - CONSTRAINT wkf_activity_workflow_fkey FOREIGN KEY (workflow) REFERENCES wkf (id) ON DELETE CASCADE, - CONSTRAINT wkf_activity_subflow_fkey FOREIGN KEY (subflow) REFERENCES wkf (id) ON DELETE SET NULL -) ENGINE=InnoDB; - -CREATE TABLE wkf_transition ( - id BIGINT AUTO_INCREMENT NOT NULL, - act_from BIGINT, - act_to BIGINT, - `condition` VARCHAR(255), - trigger_expr_id VARCHAR(255), - `signal` VARCHAR(255), - `group` BIGINT, - PRIMARY KEY(id), - CONSTRAINT wkf_transition_act_from_fkey FOREIGN KEY (act_from) REFERENCES wkf_activity (id) ON DELETE CASCADE, - CONSTRAINT wkf_transition_act_to_fkey FOREIGN KEY (act_to) REFERENCES wkf_activity (id) ON DELETE CASCADE, - CONSTRAINT wkf_transition_group_fkey FOREIGN KEY (`group`) REFERENCES res_group (id) ON DELETE SET NULL -) ENGINE=InnoDB; - -CREATE TABLE wkf_instance ( - id BIGINT AUTO_INCREMENT NOT NULL, - workflow BIGINT, - uid BIGINT DEFAULT 0, - res_id BIGINT NOT NULL DEFAULT 0, - res_type VARCHAR(255) NOT NULL, - state VARCHAR(255) NOT NULL, - PRIMARY KEY(id), - CONSTRAINT wkf_instance_workflow_fkey FOREIGN KEY (workflow) REFERENCES wkf (id) ON DELETE RESTRICT -) ENGINE=InnoDB; - -CREATE TABLE wkf_workitem ( - id BIGINT AUTO_INCREMENT NOT NULL, - activity BIGINT, - instance BIGINT, - subflow BIGINT, - state VARCHAR(255), - PRIMARY KEY(id), - CONSTRAINT wkf_workitem_activity_fkey FOREIGN KEY (activity) REFERENCES wkf_activity (id) ON DELETE CASCADE, - CONSTRAINT wkf_workitem_instance_fkey FOREIGN KEY (instance) REFERENCES wkf_instance (id) ON DELETE CASCADE, - CONSTRAINT wkf_workitem_subflow_fkey FOREIGN KEY (subflow) REFERENCES wkf_instance (id) ON DELETE CASCADE -) ENGINE=InnoDB; - -CREATE TABLE wkf_witm_trans ( - trans_id BIGINT, - inst_id BIGINT, - CONSTRAINT wkf_witm_trans_trans_id_fkey FOREIGN KEY (trans_id) REFERENCES wkf_transition (id) ON DELETE CASCADE, - CONSTRAINT wkf_witm_trans_inst_id_fkey FOREIGN KEY (inst_id) REFERENCES wkf_instance (id) ON DELETE CASCADE -) ENGINE=InnoDB; - CREATE TABLE ir_module_module ( id BIGINT AUTO_INCREMENT NOT NULL, create_uid BIGINT, diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py index 19b61e4a11..84c2941944 100644 --- a/trytond/backend/postgresql/database.py +++ b/trytond/backend/postgresql/database.py @@ -230,7 +230,7 @@ class Database(DatabaseInterface): if (len(line)>0) and (not line.isspace()): cursor.execute(line) - for i in ('ir', 'workflow', 'res', 'webdav'): + for i in ('ir', 'res', 'webdav'): root_path = os.path.join(os.path.dirname(__file__), '..', '..') tryton_file = os.path.join(root_path, i, '__tryton__.py') with open(tryton_file) as fp: @@ -383,12 +383,6 @@ class Cursor(CursorInterface): "'ir_ui_menu', " "'res_user', " "'res_group', " - "'wkf', " - "'wkf_activity', " - "'wkf_transition', " - "'wkf_instance', " - "'wkf_workitem', " - "'wkf_witm_trans', " "'ir_module_module', " "'ir_module_module_dependency', " "'ir_translation', " diff --git a/trytond/backend/postgresql/init.sql b/trytond/backend/postgresql/init.sql index bb83dbd803..5389ec333f 100644 --- a/trytond/backend/postgresql/init.sql +++ b/trytond/backend/postgresql/init.sql @@ -109,89 +109,6 @@ CREATE TABLE "res_user-res_group" ( PRIMARY KEY(id) ); -CREATE SEQUENCE wkf_id_seq; - -CREATE TABLE wkf ( - id INTEGER DEFAULT NEXTVAL('wkf_id_seq') NOT NULL, - name VARCHAR, - model VARCHAR, - on_create BOOL NOT NULL, - PRIMARY KEY(id) -); - -CREATE SEQUENCE wkf_activity_id_seq; - -CREATE TABLE wkf_activity ( - id INTEGER DEFAULT NEXTVAL('wkf_activity_id_seq') NOT NULL, - workflow INTEGER, - subflow INTEGER, - split_mode VARCHAR NOT NULL, - join_mode VARCHAR NOT NULL, - name VARCHAR NOT NULL, - signal_send VARCHAR, - flow_start BOOLEAN NOT NULL, - flow_stop BOOLEAN NOT NULL, - stop_other BOOLEAN NOT NULL, - action VARCHAR, - PRIMARY KEY(id), - FOREIGN KEY (workflow) REFERENCES wkf (id) ON DELETE CASCADE, - FOREIGN KEY (subflow) REFERENCES wkf (id) ON DELETE SET NULL -); - -CREATE SEQUENCE wkf_transition_id_seq; - -CREATE TABLE wkf_transition ( - id INTEGER DEFAULT NEXTVAL('wkf_transition_id_seq') NOT NULL, - act_from INTEGER NOT NULL, - act_to INTEGER NOT NULL, - condition VARCHAR NOT NULL, - trigger_expr_id VARCHAR, - signal VARCHAR, - "group" INTEGER, - PRIMARY KEY(id), - FOREIGN KEY (act_from) REFERENCES wkf_activity (id) ON DELETE CASCADE, - FOREIGN KEY (act_to) REFERENCES wkf_activity (id) ON DELETE CASCADE, - FOREIGN KEY ("group") REFERENCES res_group (id) ON DELETE SET NULL -); - -CREATE SEQUENCE wkf_instance_id_seq; - -CREATE TABLE wkf_instance ( - id INTEGER DEFAULT NEXTVAL('wkf_instance_id_seq') NOT NULL, - workflow INTEGER, - uid INTEGER DEFAULT 0, - res_id INT NOT NULL DEFAULT 0, - res_type VARCHAR NOT NULL, - state VARCHAR NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (workflow) REFERENCES wkf (id) ON DELETE RESTRICT -); - -CREATE SEQUENCE wkf_workitem_id_seq; - -CREATE TABLE wkf_workitem ( - id INTEGER DEFAULT NEXTVAL('wkf_workitem_id_seq') NOT NULL, - activity INTEGER NOT NULL, - instance INTEGER NOT NULL, - subflow INTEGER, - state VARCHAR, - PRIMARY KEY(id), - FOREIGN KEY (activity) REFERENCES wkf_activity (id) ON DELETE CASCADE, - FOREIGN KEY (instance) REFERENCES wkf_instance (id) ON DELETE CASCADE, - FOREIGN KEY (subflow) REFERENCES wkf_instance (id) ON DELETE CASCADE -); - -CREATE SEQUENCE wkf_witm_trans_id_seq; - -CREATE TABLE wkf_witm_trans ( - id INTEGER DEFAULT NEXTVAL('wkf_witm_trans_id_seq') NOT NULL, - trans_id INTEGER NOT NULL, - inst_id INTEGER NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (trans_id) REFERENCES wkf_transition ON DELETE CASCADE, - FOREIGN KEY (inst_id) REFERENCES wkf_instance ON DELETE CASCADE -); - CREATE SEQUENCE ir_module_module_id_seq; CREATE TABLE ir_module_module ( diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index e7deb8c1ec..457864c099 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -218,7 +218,7 @@ class Database(DatabaseInterface): if (len(line)>0) and (not line.isspace()): cursor.execute(line) - for i in ('ir', 'workflow', 'res', 'webdav'): + for i in ('ir', 'res', 'webdav'): root_path = os.path.join(os.path.dirname(__file__), '..', '..') tryton_file = os.path.join(root_path, i, '__tryton__.py') with open(tryton_file) as fp: @@ -323,12 +323,6 @@ class Cursor(CursorInterface): "'ir_ui_menu', " "'res_user', " "'res_group', " - "'wkf', " - "'wkf_activity', " - "'wkf_transition', " - "'wkf_instance', " - "'wkf_workitem', " - "'wkf_witm_trans', " "'ir_module_module', " "'ir_module_module_dependency', " "'ir_translation', " diff --git a/trytond/backend/sqlite/init.sql b/trytond/backend/sqlite/init.sql index e20292c445..2243542b84 100644 --- a/trytond/backend/sqlite/init.sql +++ b/trytond/backend/sqlite/init.sql @@ -77,60 +77,6 @@ CREATE TABLE "res_user-res_group" ( "group" INTEGER ); -CREATE TABLE wkf ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name VARCHAR, - model VARCHAR, - on_create BOOLEAN -); - -CREATE TABLE wkf_activity ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - workflow INTEGER, - subflow INTEGER, - split_mode VARCHAR, - join_mode VARCHAR, - name VARCHAR, - signal_send VARCHAR, - flow_start BOOLEAN, - flow_stop BOOLEAN, - stop_other BOOLEAN, - action VARCHAR -); - -CREATE TABLE wkf_transition ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - act_from INTEGER, - act_to INTEGER, - condition VARCHAR, - trigger_expr_id VARCHAR, - signal VARCHAR, - "group" INTEGER -); - -CREATE TABLE wkf_instance ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - workflow INTEGER, - uid INTEGER DEFAULT 0, - res_id INTEGER DEFAULT 0, - res_type VARCHAR, - state VARCHAR -); - -CREATE TABLE wkf_workitem ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - activity INTEGER, - instance INTEGER, - subflow INTEGER, - state VARCHAR -); - -CREATE TABLE wkf_witm_trans ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - trans_id INTEGER, - inst_id INTEGER -); - CREATE TABLE ir_module_module ( id INTEGER PRIMARY KEY AUTOINCREMENT, create_uid INTEGER, diff --git a/trytond/convert.py b/trytond/convert.py index 557ce028f2..57122a8027 100644 --- a/trytond/convert.py +++ b/trytond/convert.py @@ -794,7 +794,6 @@ def post_import(pool, module, to_delete): cursor = Transaction().cursor mdata_delete = [] modeldata_obj = pool.get("ir.model.data") - transition_delete = [] with Transaction().set_context(active_test=False): mdata_ids = modeldata_obj.search([ @@ -806,48 +805,6 @@ def post_import(pool, module, to_delete): for mrec in modeldata_obj.browse(mdata_ids): mdata_id, model, db_id = mrec.id, mrec.model, mrec.db_id - # Whe skip transitions, they will be deleted with the - # corresponding activity: - if model == 'workflow.transition': - transition_delete.append((mdata_id, db_id)) - continue - - if model == 'workflow.activity': - - wkf_todo = [] - # search for records that are in the state/activity that - # we want to delete... - cursor.execute('SELECT res_type, res_id ' \ - 'FROM wkf_instance ' \ - 'WHERE id IN (' \ - 'SELECT instance FROM wkf_workitem ' \ - 'WHERE activity = %s)', (db_id,)) - #... connect the transitions backward... - wkf_todo.extend(cursor.fetchall()) - cursor.execute("UPDATE wkf_transition " \ - 'SET condition = \'True\', "group" = NULL, ' \ - "signal = NULL, act_to = act_from, " \ - "act_from = %s " \ - "WHERE act_to = %s", (db_id, db_id)) - # ... and force the record to follow them: - for wkf_model, wkf_model_id in wkf_todo: - model_obj = pool.get(wkf_model) - #XXX must perhaps use workflow_trigger_trigger? - model_obj.workflow_trigger_write(wkf_model_id) - - # Collect the ids of these transition in model_data - cursor.execute( - "SELECT md.id FROM ir_model_data md " \ - "JOIN wkf_transition t ON "\ - "(md.model='workflow.transition' and md.db_id=t.id)" \ - "WHERE t.act_to = %s", (db_id,)) - mdata_delete.extend([x[0] for x in cursor.fetchall()]) - - # And finally delete the transitions - cursor.execute("DELETE FROM wkf_transition " \ - "WHERE act_to = %s", (db_id,)) - - logging.getLogger("convert").info( 'Deleting %s@%s' % (db_id, model)) try: @@ -873,20 +830,6 @@ def post_import(pool, module, to_delete): 'Exception: %s' % (db_id, model, tb_s)) - transition_obj = pool.get('workflow.transition') - for mdata_id, db_id in transition_delete: - logging.getLogger("convert").info( - 'Deleting %s@workflow.transition' % (db_id,)) - try: - transition_obj.delete(db_id) - mdata_delete.append(mdata_id) - cursor.commit() - except Exception: - cursor.rollback() - logging.getLogger("convert").error( - 'Could not delete id: %d of model workflow.transition' % - (db_id,)) - # Clean model_data: if mdata_delete: modeldata_obj.delete(mdata_delete) diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 29827d3a09..05e8bab003 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -481,6 +481,60 @@ class ModelFieldAccess(ModelSQL, ModelView): ModelFieldAccess() +class ModelButton(ModelSQL, ModelView): + "Model Button" + _name = 'ir.model.button' + _description = __doc__ + name = fields.Char('Name', required=True, readonly=True) + model = fields.Many2One('ir.model', 'Model', required=True, readonly=True, + ondelete='CASCADE', select=True) + groups = fields.Many2Many('ir.model.button-res.group', 'button', 'group', + 'Groups') + + def __init__(self): + super(ModelButton, self).__init__() + self._sql_constraints += [ + ('name_model_uniq', 'UNIQUE(name, model)', + 'The button name in model must be unique!'), + ] + self._order.insert(0, ('model', 'ASC')) + + def create(self, values): + result = super(ModelButton, self).create(values) + # Restart the cache for get_groups + self.get_groups.reset() + return result + + def write(self, ids, values): + result = super(ModelButton, self).write(ids, values) + # Restart the cache for get_groups + self.get_groups.reset() + return result + + def delete(self, ids): + result = super(ModelButton, self).delete(ids) + # Restart the cache for get_groups + self.get_groups.reset() + return result + + @Cache('ir.model.button') + def get_groups(self, model, name): + ''' + Return a set of group ids for the named button on the model. + ''' + button_ids = self.search([ + ('model', '=', model), + ('name', '=', name), + ]) + if not button_ids: + return set() + button_id, = button_ids + button = self.browse(button_id) + return set(g.id for g in button.groups) + +ModelButton() + + class ModelData(ModelSQL, ModelView): "Model data" _name = 'ir.model.data' diff --git a/trytond/ir/model.xml b/trytond/ir/model.xml index 5ab74b7e67..06fd61cdad 100644 --- a/trytond/ir/model.xml +++ b/trytond/ir/model.xml @@ -261,5 +261,52 @@ this repository contains the full copyright notices and license terms. --> + + ir.model.button + tree + + + + + + ]]> + + + + ir.model.button + form + + + + + + Buttons + ir.model.button + + + + + + + + + + + + + diff --git a/trytond/ir/module/module.py b/trytond/ir/module/module.py index 61d78a2cda..123ec9046e 100644 --- a/trytond/ir/module/module.py +++ b/trytond/ir/module/module.py @@ -9,6 +9,7 @@ from trytond.wizard import Wizard, StateView, Button, StateTransition, \ from trytond.backend import Database, TableHandler from trytond.pool import Pool from trytond.transaction import Transaction +from trytond.pyson import Eval class Module(ModelSQL, ModelView): @@ -43,12 +44,6 @@ class Module(ModelSQL, ModelView): ] self._order.insert(0, ('name', 'ASC')) self._rpc.update({ - 'button_install': True, - 'button_install_cancel': True, - 'button_uninstall': True, - 'button_uninstall_cancel': True, - 'button_upgrade': True, - 'button_upgrade_cancel': True, 'on_write': False, }) self._error_messages.update({ @@ -58,6 +53,26 @@ class Module(ModelSQL, ModelView): 'uninstall_dep': 'The modules you are trying to uninstall ' \ 'depends on installed modules:', }) + self._buttons.update({ + 'install': { + 'invisible': Eval('state') != 'uninstalled', + }, + 'install_cancel': { + 'invisible': Eval('state') != 'to_install', + }, + 'uninstall': { + 'invisible': Eval('state') != 'installed', + }, + 'uninstall_cancel': { + 'invisible': Eval('state') != 'to_remove', + }, + 'upgrade': { + 'invisible': Eval('state') != 'installed', + }, + 'upgrade_cancel': { + 'invisible': Eval('state') != 'to upgrade', + }, + }) def default_state(self): return 'uninstalled' @@ -66,7 +81,7 @@ class Module(ModelSQL, ModelView): def get_module_info(name): "Return the content of the __tryton__.py" try: - if name in ['ir', 'workflow', 'res', 'webdav']: + if name in ['ir', 'res', 'webdav']: file_p = tools.file_open(os.path.join(name, '__tryton__.py')) else: file_p = tools.file_open(os.path.join(name, '__tryton__.py')) @@ -198,16 +213,19 @@ class Module(ModelSQL, ModelView): 'state': 'to upgrade', }) - def button_install(self, ids): + @ModelView.button + def install(self, ids): return self.state_install(ids) - def button_install_cancel(self, ids): + @ModelView.button + def install_cancel(self, ids): self.write(ids, { 'state': 'uninstalled', }) return True - def button_uninstall(self, ids): + @ModelView.button + def uninstall(self, ids): cursor = Transaction().cursor for module in self.browse(ids): cursor.execute('SELECT m.state, m.name ' \ @@ -225,14 +243,17 @@ class Module(ModelSQL, ModelView): self.write(ids, {'state': 'to remove'}) return True - def button_uninstall_cancel(self, ids): + @ModelView.button + def uninstall_cancel(self, ids): self.write(ids, {'state': 'installed'}) return True - def button_upgrade(self, ids): + @ModelView.button + def upgrade(self, ids): return self.state_upgrade(ids) - def button_upgrade_cancel(self, ids): + @ModelView.button + def upgrade_cancel(self, ids): self.write(ids, {'state': 'installed'}) return True diff --git a/trytond/ir/module/module.xml b/trytond/ir/module/module.xml index 37f92539bf..8426f239be 100644 --- a/trytond/ir/module/module.xml +++ b/trytond/ir/module/module.xml @@ -32,17 +32,17 @@ this repository contains the full copyright notices and license terms. -->