Browse Source

Merge pull request #107 from savoirfairelinux/8.0-prototype

8.0 prototype
pull/269/merge
Pedro M. Baeza 9 years ago
parent
commit
d076cfa484
  1. 124
      module_prototyper/README.rst
  2. 25
      module_prototyper/__init__.py
  3. 46
      module_prototyper/__openerp__.py
  4. 78
      module_prototyper/data/README.rst
  5. 338
      module_prototyper/i18n/fr.po
  6. 327
      module_prototyper/i18n/module_prototyper.pot
  7. 25
      module_prototyper/models/__init__.py
  8. 49
      module_prototyper/models/ir_model_fields.py
  9. 73
      module_prototyper/models/licenses.py
  10. 566
      module_prototyper/models/module_prototyper.py
  11. 2
      module_prototyper/security/ir.model.access.csv
  12. BIN
      module_prototyper/static/description/icon.png
  13. 7
      module_prototyper/templates/8.0/__init__.py.template
  14. 60
      module_prototyper/templates/8.0/__openerp__.py.template
  15. 8
      module_prototyper/templates/8.0/data/model_name.xml.template
  16. 8
      module_prototyper/templates/8.0/demo/model_name.xml.template
  17. 23
      module_prototyper/templates/8.0/header.template
  18. 9
      module_prototyper/templates/8.0/models/__init__.py.template
  19. 35
      module_prototyper/templates/8.0/models/model_name.py.template
  20. 4
      module_prototyper/templates/8.0/security/ir.model.access.csv.template
  21. 10
      module_prototyper/templates/8.0/security/model_name.xml.template
  22. 29
      module_prototyper/templates/8.0/views/model_menus.xml.template
  23. 17
      module_prototyper/templates/8.0/views/model_views.xml.template
  24. 25
      module_prototyper/tests/__init__.py
  25. 113
      module_prototyper/tests/test_prototype.py
  26. 80
      module_prototyper/tests/test_prototype_module_export.py
  27. 51
      module_prototyper/views/ir_model_fields_view.xml
  28. 146
      module_prototyper/views/module_prototyper_view.xml
  29. 22
      module_prototyper/wizard/__init__.py
  30. 132
      module_prototyper/wizard/module_prototyper_module_export.py
  31. 43
      module_prototyper/wizard/module_prototyper_module_export_view.xml

124
module_prototyper/README.rst

@ -0,0 +1,124 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=================
Module Prototyper
=================
This module allows the administrator to prototype new features and export them as module.
Functional people can prepare the job for a developer who is left with the logic to implement
in addition to everything the prototype does not export yet.
Installation
============
Make sure you have Jinja2 version 2.7.3 or higher installed::
$ pip install --upgrade Jinja2==2.7.3
Configuration
=============
No configuration required.
Usage
=====
To use this module, you need to:
* install the dependencies of your future module
* customize your instance by adding fields and creating inherited views
* create your menu items and their window actions
* prepare your data and demo data by creating filters
* create your own groups with access rights and record rules
* add your own access rights and record rules to an existing group
Once you have customized your instance properly, you can go to Settings > Module Prototypes > Prototypes
and create a new prototype:
* fill in the information of your module (enter the name, the description, the logo, etc.)
* in the Depencencies tab, select all the other modules that yours will be depending on
* in the Data & Demo tab, select your filters for data and demo data
* in the Fields tab, select the fields you created or customized
* in the Interface tab, select your menu items and your views
* in the Security tab, select your groups, access rights and record rules
* save and click on export
You will get a zip file containing your module ready to be installed and compliant with the
conventions of the OCA. You can then provide the module to a developer who have to implement
things like default values or onchange methods.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/8.0
Known issues / Roadmap
======================
* `#104`_ - Include controllers.py and templates.xml from scaffold.
* Attach images to the prototype and export them to be used in the 'images' module manifest.
* Export data and demo data
* Export reports
* Export workflows, nodes, transitions, actions
* Export groups, access rights and record rules
* Allow selecting and exporting website stuff
.. _#104: https://github.com/OCA/server-tools/issues/104
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/server-tools/issues/new?body=module:%20module_prototyper%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* David Arnold <blaggacao@users.noreply.github.com>
* Jordi Riera <jordi.riera@savoirfairelinux.com>
* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
* El hadji Dem <elhadji.dem@savoirfairelinux.com>
* Savoir-faire Linux <support@savoirfairelinux.com>
* Vincent Vinet <vincent.vinet@savoirfairelinux.com>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.
Changelog
=========
v0.3
----
* Replace ir.ui.model by ir.ui.view in generated xml views
* Improve pep8 compatibility of generation of models
v0.2
----
* Renamed from prototype to module_prototyper as discussed in #108
* Menu in Settings that gather element used to create a prototype (menu views, views, fields)
v0.1
----
* The set up of openerp.py is covered, description, maintainer, website, name, technical name...
* Views and menus can be set through odoo and gathered in prototype. The files will be automatically generated and add to the data section of the openerp.py. Be aware some advanced feature as domain or context might still missing.
* Dependencies can be set through the Dependencies tab
* Custom fields can be added. A file by model will be generated with all the fields of the model. The init.py files are updated accordingly. Be aware that some features are not implemented yet, as the domain, the context.

25
module_prototyper/__init__.py

@ -0,0 +1,25 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
models,
wizard
)

46
module_prototyper/__openerp__.py

@ -0,0 +1,46 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Module Prototyper',
'version': '8.0.0.3.0',
'author': 'Savoir-faire Linux, Odoo Community Association (OCA)',
'maintainer': 'Savoir-faire Linux',
'website': 'http://www.savoirfairelinux.com',
'license': 'AGPL-3',
'category': 'Others',
'summary': 'Prototype your module.',
'depends': [
'admin_technical_features',
],
'external_dependencies': {
'python': [],
},
'data': [
'wizard/module_prototyper_module_export_view.xml',
'views/module_prototyper_view.xml',
'views/ir_model_fields_view.xml',
'security/ir.model.access.csv',
],
'installable': True,
'application': True,
}

78
module_prototyper/data/README.rst

@ -0,0 +1,78 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
===========
Module name
===========
This module was written to extend the functionality of ... to support ...
and allow you to ...
Installation
============
To install this module, you need to:
* do this ...
Configuration
=============
To configure this module, you need to:
* go to ...
Usage
=====
To use this module, you need to:
* go to ...
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch}
.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt
.. branch is "8.0" for example
For further information, please visit:
* https://www.odoo.com/forum/help-1
Known issues / Roadmap
======================
* ...
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/{project_repo}/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/{project_repo}/issues/new?body=module:%20{module_name}%0Aversion:%20{version}%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Firstname Lastname <email.address@example.org>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

338
module_prototyper/i18n/fr.po

@ -0,0 +1,338 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_prototyper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-02 17:07+0000\n"
"PO-Revision-Date: 2015-01-02 12:18-0500\n"
"Last-Translator: Jordi Riera <jordi.riera@savoirfairelinux.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.5.4\n"
#. module: module_prototyper
#: selection:module_prototyper.module.export,api_version:0
msgid "8.0"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper.module.export,api_version:0
msgid "API version"
msgstr "Version de l'API"
#. module: module_prototyper
#: field:module_prototyper,right_ids:0
msgid "Access Rights"
msgstr "Liste des contrôles d'accès"
#. module: module_prototyper
#: field:module_prototyper,application:0
msgid "Application"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,author:0
msgid "Author"
msgstr "Auteur"
#. module: module_prototyper
#: field:module_prototyper,auto_install:0
msgid "Auto Install"
msgstr "Installation automatique"
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Cancel"
msgstr "Annuler"
#. module: module_prototyper
#: field:module_prototyper,category_id:0
msgid "Category"
msgstr "Catégorie"
#. module: module_prototyper
#: help:module_prototyper,application:0
msgid "Check if the module is an Odoo application."
msgstr "Cocher si le module est une application Odoo."
#. module: module_prototyper
#: help:module_prototyper,auto_install:0
msgid "Check if the module should be install by default."
msgstr "Cocher si le module devrait être installé par défaut."
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Close"
msgstr "Fermer"
#. module: module_prototyper
#: field:module_prototyper,create_uid:0
#: field:module_prototyper.module.export,create_uid:0
msgid "Created by"
msgstr "Créé par"
#. module: module_prototyper
#: field:module_prototyper,create_date:0
#: field:module_prototyper.module.export,create_date:0
msgid "Created on"
msgstr "Créé le"
#. module: module_prototyper
#: field:module_prototyper,data_ids:0
msgid "Data filters"
msgstr "Filtres pour les données"
#. module: module_prototyper
#: field:module_prototyper,demo_ids:0
msgid "Demo filters"
msgstr "Filtres pour les données de démo"
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,dependency_ids:0
msgid "Dependencies"
msgstr "Dépendances"
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,description:0
msgid "Description"
msgstr "Descriptions"
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export"
msgstr "Exporter"
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Complete"
msgstr "Export terminé"
#. module: module_prototyper
#: model:ir.actions.act_window,name:module_prototyper.button_module_export_action
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Module"
msgstr "Exporter le module"
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Settings"
msgstr "Configuration de l'export"
#. module: module_prototyper
#: model:ir.model,name:module_prototyper.model_ir_model_fields
#: model:ir.ui.menu,name:module_prototyper.ir_model_model_fields
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,field_ids:0
msgid "Fields"
msgstr "Champs"
#. module: module_prototyper
#: field:module_prototyper.module.export,data:0
msgid "File"
msgstr "Fichier"
#. module: module_prototyper
#: field:module_prototyper.module.export,name:0
msgid "File Name"
msgstr "Nom du fichier"
#. module: module_prototyper
#: field:module_prototyper,group_ids:0
msgid "Groups"
msgstr "Groupes"
#. module: module_prototyper
#: field:ir.model.fields,helper:0
msgid "Helper"
msgstr "Texte d'aider"
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Here is the exported module:"
msgstr "Voici le module exporté :"
#. module: module_prototyper
#: field:module_prototyper,id:0 field:module_prototyper.module.export,id:0
msgid "ID"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,icon_image:0
msgid "Icon"
msgstr "Icône"
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
msgid "Interface"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,write_uid:0
#: field:module_prototyper.module.export,write_uid:0
msgid "Last Updated by"
msgstr "Mis à jour par"
#. module: module_prototyper
#: field:module_prototyper,write_date:0
#: field:module_prototyper.module.export,write_date:0
msgid "Last Updated on"
msgstr "Mis à jour le"
#. module: module_prototyper
#: field:module_prototyper,licence:0
msgid "Licence"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,maintainer:0
msgid "Maintainer"
msgstr "Mainteneur"
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_grant_menu_access
#: field:module_prototyper,menu_ids:0
msgid "Menu Items"
msgstr "Éléments du menu"
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
msgid "Module"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,human_name:0
msgid "Module Name"
msgstr "Nom du module"
#. module: module_prototyper
#: model:ir.model,name:module_prototyper.model_module_prototyper
msgid "Module Prototyper"
msgstr ""
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_module_prototyper
msgid "Module Prototypers"
msgstr ""
#. module: module_prototyper
#: view:ir.model:module_prototyper.view_ir_model_form
msgid "Notes"
msgstr ""
#. module: module_prototyper
#: field:ir.model.fields,notes:0
msgid "Notes to developers."
msgstr "Notes aux développeurs."
#. module: module_prototyper
#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form
msgid ""
"Notes to help developers to understand the work or advanced features that "
"should be added, ie: onchange, etc."
msgstr ""
"Notes pour aider les développeurs à comprendre le travail effectué ou les "
"ajouts à faire. Exemple: onchanges, etc."
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_tree_view
msgid "Prototype"
msgstr ""
#. module: module_prototyper
#: model:ir.actions.act_window,name:module_prototyper.open_module_prototyper_list
#: model:ir.ui.menu,name:module_prototyper.menu_open_module_prototyper
msgid "Prototypes"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,rule_ids:0
msgid "Record Rules"
msgstr "Règles sur les enregistrements"
#. module: module_prototyper
#: field:module_prototyper.module.export,state:0
msgid "State"
msgstr "État"
#. module: module_prototyper
#: field:module_prototyper,summary:0
msgid "Summary"
msgstr "Résumé"
#. module: module_prototyper
#: field:module_prototyper,name:0
msgid "Technical Name"
msgstr "Nom technique"
#. module: module_prototyper
#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form
msgid "Text that will be set as the helper of the field..."
msgstr "Texte qui sera placé en tant que texte d'aide du champs."
#. module: module_prototyper
#: help:module_prototyper,human_name:0
msgid ""
"The Module Name will be used as the displayed name of the exported module."
msgstr "Le nom du module sera affiché en tant que nom du module exporté."
#. module: module_prototyper
#: help:module_prototyper,icon_image:0
msgid ""
"The icon set up here will be used as the icon for the exported module also"
msgstr "L'icône sera utilisé comme icône du module exporté."
#. module: module_prototyper
#: help:module_prototyper,data_ids:0
msgid "The records matching the filters will be added as data."
msgstr ""
"Les enregistrements correspondants aux critères des filtres seront exportés "
"dans le module comme données."
#. module: module_prototyper
#: help:module_prototyper,demo_ids:0
msgid "The records matching the filters will be added as demo data."
msgstr ""
"Les enregistrements correspondants aux critères des filtres seront exportés "
"dans le module comme données de démo."
#. module: module_prototyper
#: help:module_prototyper,name:0
msgid ""
"The technical name will be used to define the name of the exported module, "
"the name of the model."
msgstr "Le nom technique sera utilisé comme nom du module exporté."
#. module: module_prototyper
#: field:module_prototyper,version:0
msgid "Version"
msgstr ""
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_action_ui_view
#: field:module_prototyper,view_ids:0
msgid "Views"
msgstr "Vues"
#. module: module_prototyper
#: field:module_prototyper,website:0
msgid "Website"
msgstr "Site web"
#. module: module_prototyper
#: selection:module_prototyper.module.export,state:0
msgid "choose"
msgstr "choisir"
#. module: module_prototyper
#: selection:module_prototyper.module.export,state:0
msgid "get"
msgstr "récupérer"

327
module_prototyper/i18n/module_prototyper.pot

@ -0,0 +1,327 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_prototyper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-02 17:07+0000\n"
"PO-Revision-Date: 2015-01-02 17:07+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: module_prototyper
#: selection:module_prototyper.module.export,api_version:0
msgid "8.0"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper.module.export,api_version:0
msgid "API version"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,right_ids:0
msgid "Access Rights"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,application:0
msgid "Application"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,author:0
msgid "Author"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,auto_install:0
msgid "Auto Install"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Cancel"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,category_id:0
msgid "Category"
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,application:0
msgid "Check if the module is an Odoo application."
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,auto_install:0
msgid "Check if the module should be install by default."
msgstr ""
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Close"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,create_uid:0
#: field:module_prototyper.module.export,create_uid:0
msgid "Created by"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,create_date:0
#: field:module_prototyper.module.export,create_date:0
msgid "Created on"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,data_ids:0
msgid "Data filters"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,demo_ids:0
msgid "Demo filters"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,dependency_ids:0
msgid "Dependencies"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,description:0
msgid "Description"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Complete"
msgstr ""
#. module: module_prototyper
#: model:ir.actions.act_window,name:module_prototyper.button_module_export_action
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Module"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Export Settings"
msgstr ""
#. module: module_prototyper
#: model:ir.model,name:module_prototyper.model_ir_model_fields
#: model:ir.ui.menu,name:module_prototyper.ir_model_model_fields
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
#: field:module_prototyper,field_ids:0
msgid "Fields"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper.module.export,data:0
msgid "File"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper.module.export,name:0
msgid "File Name"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,group_ids:0
msgid "Groups"
msgstr ""
#. module: module_prototyper
#: field:ir.model.fields,helper:0
msgid "Helper"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard
msgid "Here is the exported module:"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,id:0
#: field:module_prototyper.module.export,id:0
msgid "ID"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,icon_image:0
msgid "Icon"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
msgid "Interface"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,write_uid:0
#: field:module_prototyper.module.export,write_uid:0
msgid "Last Updated by"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,write_date:0
#: field:module_prototyper.module.export,write_date:0
msgid "Last Updated on"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,licence:0
msgid "Licence"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,maintainer:0
msgid "Maintainer"
msgstr ""
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_grant_menu_access
#: field:module_prototyper,menu_ids:0
msgid "Menu Items"
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view
msgid "Module"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,human_name:0
msgid "Module Name"
msgstr ""
#. module: module_prototyper
#: model:ir.model,name:module_prototyper.model_module_prototyper
msgid "Module Prototyper"
msgstr ""
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_module_prototyper
msgid "Module Prototypers"
msgstr ""
#. module: module_prototyper
#: view:ir.model:module_prototyper.view_ir_model_form
msgid "Notes"
msgstr ""
#. module: module_prototyper
#: field:ir.model.fields,notes:0
msgid "Notes to developers."
msgstr ""
#. module: module_prototyper
#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form
msgid "Notes to help developers to understand the work or advanced features that should be added, ie: onchange, etc."
msgstr ""
#. module: module_prototyper
#: view:module_prototyper:module_prototyper.module_prototyper_base_tree_view
msgid "Prototype"
msgstr ""
#. module: module_prototyper
#: model:ir.actions.act_window,name:module_prototyper.open_module_prototyper_list
#: model:ir.ui.menu,name:module_prototyper.menu_open_module_prototyper
msgid "Prototypes"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,rule_ids:0
msgid "Record Rules"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper.module.export,state:0
msgid "State"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,summary:0
msgid "Summary"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,name:0
msgid "Technical Name"
msgstr ""
#. module: module_prototyper
#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form
msgid "Text that will be set as the helper of the field..."
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,human_name:0
msgid "The Module Name will be used as the displayed name of the exported module."
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,icon_image:0
msgid "The icon set up here will be used as the icon for the exported module also"
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,data_ids:0
msgid "The records matching the filters will be added as data."
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,demo_ids:0
msgid "The records matching the filters will be added as demo data."
msgstr ""
#. module: module_prototyper
#: help:module_prototyper,name:0
msgid "The technical name will be used to define the name of the exported module, the name of the model."
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,version:0
msgid "Version"
msgstr ""
#. module: module_prototyper
#: model:ir.ui.menu,name:module_prototyper.menu_action_ui_view
#: field:module_prototyper,view_ids:0
msgid "Views"
msgstr ""
#. module: module_prototyper
#: field:module_prototyper,website:0
msgid "Website"
msgstr ""
#. module: module_prototyper
#: selection:module_prototyper.module.export,state:0
msgid "choose"
msgstr ""
#. module: module_prototyper
#: selection:module_prototyper.module.export,state:0
msgid "get"
msgstr ""

25
module_prototyper/models/__init__.py

@ -0,0 +1,25 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
module_prototyper,
ir_model_fields
)

49
module_prototyper/models/ir_model_fields.py

@ -0,0 +1,49 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import fields, models
from openerp.tools.translate import _
class ir_model_fields(models.Model):
"""Addition of text fields to fields."""
_inherit = "ir.model.fields"
notes = fields.Text('Notes to developers.')
helper = fields.Text('Helper')
# TODO: Make column1 and 2 required if a model has a m2m to itself
column1 = fields.Char(
'Column1',
help=_("name of the column referring to 'these' records in the "
"relation table"),
)
column2 = fields.Char(
'Column2',
help=_("name of the column referring to 'those' records in the "
"relation table"),
)
limit = fields.Integer('Read limit', help=_("Read limit"))
client_context = fields.Char(
'Context',
help=_("Context to use on the client side when handling the field "
"(python dictionary)"),
)

73
module_prototyper/models/licenses.py

@ -0,0 +1,73 @@
# -*- encoding: utf-8 -*-
# #############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
BASE_GPL = """
This program is free software: you can redistribute it and/or modify
it under the terms of the {name} as
published by the Free Software Foundation{version}.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the {name}
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
GPL3 = "GPL-3"
GPL3_L = "GPL-3 or any later version"
LGPL3 = "LGPL-3"
LGPL3_L = "LGPL-3 or any later version"
AGPL3 = "AGPL-3"
AGPL3_L = "AGPL-3 or any later version"
OSI = "Other OSI approved license"
V3 = " version 3"
V3L = """, either version 3 of the
License, or (at your option) any later version"""
GPL_LICENSES = {
GPL3: ("GNU General Public License", V3),
GPL3_L: ("GNU General Public License", V3L),
LGPL3: ("GNU Lesser General Public License", V3),
LGPL3_L: ("GNU Lesser General Public License", V3L),
AGPL3: ("GNU Affero General Public License", V3),
AGPL3_L: ("GNU Affero General Public License", V3L),
}
BASE_OSI = """
This program is free software: you should have received a copy of the
license under which it is distributed along with the program.
"""
def get_license_text(license):
""" Get the python license header for a license """
if license in GPL_LICENSES:
name, version = GPL_LICENSES[license]
return BASE_GPL.format(name=name, version=version).splitlines()
elif license == OSI:
return BASE_OSI.splitlines()
else:
return ""

566
module_prototyper/models/module_prototyper.py

@ -0,0 +1,566 @@
# -*- encoding: utf-8 -*-
# #############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
import logging
import lxml.etree
import os
import re
import textwrap
from collections import namedtuple
from datetime import date
from jinja2 import Environment, FileSystemLoader
from openerp import models, api, fields
from openerp.tools.safe_eval import safe_eval
from . import licenses
_logger = logging.getLogger(__name__)
YEAR = date.today().year
class ModulePrototyper(models.Model):
"""Module Prototyper gathers different information from all over the
database to build a prototype of module.
We are calling it a prototype as it will most likely need to be reviewed
by a developer to fix glitch that would sneak it during the generation of
files but also to add not supported features.
"""
_name = "module_prototyper"
_description = "Module Prototyper"
def get_default_description(self):
"""
Extract the content of default description
"""
filepath = '{}/../data/README.rst'.format(os.path.dirname(__file__))
with open(filepath, 'r') as content_file:
content = content_file.read()
return content
license = fields.Selection(
[
(licenses.GPL3, 'GPL Version 3'),
(licenses.GPL3_L, 'GPL-3 or later version'),
(licenses.LGPL3, 'LGPL-3'),
(licenses.LGPL3_L, 'LGPL-3 or later version'),
(licenses.AGPL3, 'Affero GPL-3'),
(licenses.OSI, 'Other OSI Approved Licence'),
('Other proprietary', 'Other Proprietary')
],
string='License',
default=licenses.AGPL3,
)
name = fields.Char(
'Technical Name', required=True,
help=('The technical name will be used to define the name of '
'the exported module, the name of the model.')
)
category_id = fields.Many2one('ir.module.category', 'Category')
human_name = fields.Char(
'Module Name', required=True,
help=('The Module Name will be used as the displayed name of the '
'exported module.')
)
summary = fields.Char('Summary', required=True,
help=('Enter a summary of your module'))
description = fields.Text(
'Description',
required=True,
help=('Enter the description of your module, what it does, how to '
'install, configure and use it, the roadmap or known issues. '
'The description will be exported in README.rst'),
default=get_default_description
)
author = fields.Char('Author', required=True, help=('Enter your name'))
maintainer = fields.Char(
'Maintainer',
help=('Enter the name of the person or organization who will '
'maintain this module')
)
website = fields.Char('Website', help=('Enter the URL of your website'))
icon_image = fields.Binary(
'Icon',
help=('The icon set up here will be used as the icon '
'for the exported module also')
)
version = fields.Char(
'Version',
size=9,
default='8.0.1.0.0',
help=('Enter the version of your module with 5 digits')
)
auto_install = fields.Boolean(
'Auto Install',
default=False,
help='Check if the module should be install by default.'
)
application = fields.Boolean(
'Application',
default=False,
help='Check if the module is an Odoo application.'
)
# Relations
dependency_ids = fields.Many2many(
'ir.module.module', 'module_prototyper_module_rel',
'module_prototyper_id', 'module_id',
'Dependencies',
help=('Enter the list of required modules that need to be installed '
'for your module to work properly')
)
data_ids = fields.Many2many(
'ir.filters',
'prototype_data_rel',
'module_prototyper_id', 'filter_id',
'Data filters',
help="The records matching the filters will be added as data."
)
demo_ids = fields.Many2many(
'ir.filters',
'prototype_demo_rel',
'module_prototyper_id', 'filter_id',
'Demo filters',
help="The records matching the filters will be added as demo data."
)
field_ids = fields.Many2many(
'ir.model.fields', 'prototype_fields_rel',
'module_prototyper_id', 'field_id', 'Fields',
help=('Enter the list of fields that you have created or modified '
'and want to export in this module. New models will be '
'exported as long as you choose one of his fields.')
)
menu_ids = fields.Many2many(
'ir.ui.menu', 'prototype_menu_rel',
'module_prototyper_id', 'menu_id', 'Menu Items',
help=('Enter the list of menu items that you have created and want '
'to export in this module. Related windows actions will be '
'exported as well.')
)
view_ids = fields.Many2many(
'ir.ui.view', 'prototype_view_rel',
'module_prototyper_id', 'view_id', 'Views',
help=('Enter the list of views that you have created and want to '
'export in this module.')
)
group_ids = fields.Many2many(
'res.groups', 'prototype_groups_rel',
'module_prototyper_id', 'group_id', 'Groups',
help=('Enter the list of groups that you have created and want to '
'export in this module.')
)
right_ids = fields.Many2many(
'ir.model.access', 'prototype_rights_rel',
'module_prototyper_id', 'right_id',
'Access Rights',
help=('Enter the list of access rights that you have created and '
'want to export in this module.')
)
rule_ids = fields.Many2many(
'ir.rule', 'prototype_rule_rel',
'module_prototyper_id', 'rule_id', 'Record Rules',
help=('Enter the list of record rules that you have created and '
'want to export in this module.')
)
report_ids = fields.Many2many(
'ir.actions.report.xml', 'prototype_report_rel',
'module_prototyper_id', 'report_id', 'Reports',
help=('Enter the list of reports that you have created and '
'want to export in this module.')
)
activity_ids = fields.Many2many(
'workflow.activity', 'prototype_wf_activity_rel',
'module_prototyper_id', 'activity_id', 'Activities',
help=('Enter the list of workflow activities that you have created '
'and want to export in this module')
)
transition_ids = fields.Many2many(
'workflow.transition', 'prototype_wf_transition_rel',
'module_prototyper_id', 'transition_id', 'Transitions',
help=('Enter the list of workflow transitions that you have created '
'and want to export in this module')
)
_env = None
_data_files = ()
_demo_files = ()
_field_descriptions = None
File_details = namedtuple('file_details', ['filename', 'filecontent'])
template_path = '{}/../templates/'.format(os.path.dirname(__file__))
@api.model
def set_jinja_env(self, api_version):
"""Set the Jinja2 environment.
The environment will helps the system to find the templates to render.
:param api_version: string, odoo api
:return: jinja2.Environment instance.
"""
if self._env is None:
self._env = Environment(
lstrip_blocks=True,
trim_blocks=True,
loader=FileSystemLoader(
os.path.join(self.template_path, api_version)
)
)
return self._env
def set_field_descriptions(self):
"""Mock the list of fields into dictionary.
It allows us to add or change attributes of the fields.
:return: None
"""
for field in self.field_ids:
field_description = {}
# This will mock a field record.
# the mock will allow us to add data or modify the data
# of the field (like for the name) with keeping all the
# attributes of the record.
field_description.update({
attr_name: getattr(field, attr_name)
for attr_name in dir(field)
if not attr_name[0] == '_'
})
field_description['name'] = self.unprefix(field.name)
self._field_descriptions[field] = field_description
@api.model
def generate_files(self):
""" Generates the files from the details of the prototype.
:return: tuple
"""
assert self._env is not None, \
'Run set_env(api_version) before to generate files.'
# Avoid sharing these across instances
self._data_files = []
self._demo_files = []
self._field_descriptions = {}
self.set_field_descriptions()
file_details = []
file_details.extend(self.generate_models_details())
file_details.extend(self.generate_views_details())
file_details.extend(self.generate_menus_details())
file_details.append(self.generate_module_init_file_details())
file_details.extend(self.generate_data_files())
# must be the last as the other generations might add information
# to put in the __openerp__: additional dependencies, views files, etc.
file_details.append(self.generate_module_openerp_file_details())
if self.icon_image:
file_details.append(self.save_icon())
return file_details
@api.model
def save_icon(self):
"""Save the icon of the prototype as a image.
The image is used afterwards as the icon of the exported module.
:return: FileDetails instance
"""
# TODO: The image is not always a jpg.
# 2 ways to do it:
# * find a way to detect image type from the data
# * add document as a dependency.
# The second options seems to be better, as Document is a base module.
return self.File_details(
os.path.join('static', 'description', 'icon.jpg'),
base64.b64decode(self.icon_image)
)
@api.model
def generate_module_openerp_file_details(self):
"""Wrapper to generate the __openerp__.py file of the module."""
return self.generate_file_details(
'__openerp__.py',
'__openerp__.py.template',
prototype=self,
data_files=self._data_files,
demo_fiels=self._demo_files,
)
@api.model
def generate_module_init_file_details(self):
"""Wrapper to generate the __init__.py file of the module."""
return self.generate_file_details(
'__init__.py',
'__init__.py.template',
# no import models if no work of fields in
# the prototype
models=bool(self.field_ids)
)
@api.model
def generate_models_details(self):
"""
Finds the models from the list of fields and generates
the __init__ file and each models files (one by class).
"""
files = []
# TODO: doesn't work as need to find the module to import
# and it is not necessary the name of the model the fields
# belongs to.
# ie. field.cell_phone is defined in a model inheriting from
# res.partner.
# How do we find the module the field was defined in?
# dependencies = set([dep.id for dep in self.dependencies])
relations = {}
field_descriptions = self._field_descriptions or {}
for field in field_descriptions.itervalues():
model = field.get('model_id')
relations.setdefault(model, []).append(field)
# dependencies.add(model.id)
# blind update of dependencies.
# self.write({
# 'dependencies': [(6, 0, [id_ for id_ in dependencies])]
# })
files.append(self.generate_models_init_details(relations.keys()))
for model, custom_fields in relations.iteritems():
files.append(self.generate_model_details(model, custom_fields))
return files
@api.model
def generate_models_init_details(self, ir_models):
"""Wrapper to generate the __init__.py file in models folder."""
return self.generate_file_details(
'models/__init__.py',
'models/__init__.py.template',
models=[
self.friendly_name(ir_model.model)
for ir_model in ir_models
]
)
@api.model
def generate_views_details(self):
"""Wrapper to generate the views files."""
relations = {}
for view in self.view_ids:
relations.setdefault(view.model, []).append(view)
views_details = []
for model, views in relations.iteritems():
filepath = 'views/{}_view.xml'.format(
self.friendly_name(self.unprefix(model))
)
views_details.append(
self.generate_file_details(
filepath,
'views/model_views.xml.template',
views=views
)
)
self._data_files.append(filepath)
return views_details
@api.model
def generate_menus_details(self):
"""Wrapper to generate the menus files."""
relations = {}
for menu in self.menu_ids:
if menu.action and menu.action.res_model:
model = self.unprefix(menu.action.res_model)
else:
model = 'ir_ui'
relations.setdefault(model, []).append(menu)
menus_details = []
for model_name, menus in relations.iteritems():
model_name = self.unprefix(model_name)
filepath = 'views/{}_menus.xml'.format(
self.friendly_name(model_name)
)
menus_details.append(
self.generate_file_details(
filepath,
'views/model_menus.xml.template',
menus=menus,
)
)
self._data_files.append(filepath)
return menus_details
@api.model
def generate_model_details(self, model, field_descriptions):
"""Wrapper to generate the python file for the model.
:param model: ir.model record.
:param field_descriptions: list of ir.model.fields records.
:return: FileDetails instance.
"""
python_friendly_name = self.friendly_name(self.unprefix(model.model))
return self.generate_file_details(
'models/{}.py'.format(python_friendly_name),
'models/model_name.py.template',
name=python_friendly_name,
model=model,
fields=field_descriptions,
)
@api.model
def generate_data_files(self):
""" Generate data and demo files """
data, demo = {}, {}
filters = [
(data, ir_filter)
for ir_filter in self.data_ids
] + [
(demo, ir_filter)
for ir_filter in self.demo_ids
]
for target, ir_filter in filters:
model = ir_filter.model_id
model_obj = self.env[model]
target.setdefault(model, model_obj.browse([]))
target[model] |= model_obj.search(safe_eval(ir_filter.domain))
res = []
for prefix, model_data, file_list in [
('data', data, self._data_files),
('demo', demo, self._demo_files)]:
for model_name, records in model_data.iteritems():
fname = self.friendly_name(self.unprefix(model_name))
filename = '{0}/{1}.xml'.format(prefix, fname)
self._data_files.append(filename)
res.append(self.generate_file_details(
filename,
'data/model_name.xml.template',
model=model_name,
records=records,
))
return res
@classmethod
def unprefix(cls, name):
if not name:
return name
return re.sub('^x_', '', name)
@classmethod
def is_prefixed(cls, name):
return bool(re.match('^x_', name))
@classmethod
def friendly_name(cls, name):
return name.replace('.', '_')
@classmethod
def fixup_domain(cls, domain):
""" Fix a domain according to unprefixing of fields """
res = []
for elem in domain:
if len(elem) == 3:
elem = list(elem)
elem[0] = cls.unprefix(elem[0])
res.append(elem)
return res
@classmethod
def fixup_arch(cls, archstr):
doc = lxml.etree.fromstring(archstr)
for elem in doc.xpath("//*[@name]"):
elem.attrib["name"] = cls.unprefix(elem.attrib["name"])
for elem in doc.xpath("//*[@attrs]"):
try:
attrs = safe_eval(elem.attrib["attrs"])
except Exception:
_logger.error("Unable to eval attribute: %s, skipping",
elem.attrib["attrs"])
continue
if isinstance(attrs, dict):
for key, val in attrs.iteritems():
if isinstance(val, (list, tuple)):
attrs[key] = cls.fixup_domain(val)
elem.attrib["attrs"] = repr(attrs)
for elem in doc.xpath("//field"):
# Make fields self-closed by removing useless whitespace
if elem.text and not elem.text.strip():
elem.text = None
return lxml.etree.tostring(doc)
@api.model
def generate_file_details(self, filename, template, **kwargs):
""" generate file details from jinja2 template.
:param filename: name of the file the content is related to
:param template: path to the file to render the content
:param kwargs: arguments of the template
:return: File_details instance
"""
template = self._env.get_template(template)
# keywords used in several templates.
kwargs.update(
{
'export_year': date.today().year,
'author': self.author,
'website': self.website,
'license_text': licenses.get_license_text(self.license),
'cr': self._cr,
# Utility functions
'fixup_arch': self.fixup_arch,
'is_prefixed': self.is_prefixed,
'unprefix': self.unprefix,
'wrap': wrap,
}
)
return self.File_details(filename, template.render(kwargs))
# Utility functions for rendering templates
def wrap(text, **kwargs):
""" Wrap some text for inclusion in a template, returning lines
keyword arguments available, from textwrap.TextWrapper:
width=70
initial_indent=''
subsequent_indent=''
expand_tabs=True
replace_whitespace=True
fix_sentence_endings=False
break_long_words=True
drop_whitespace=True
break_on_hyphens=True
"""
if not text:
return []
wrapper = textwrap.TextWrapper(**kwargs)
# We join the lines and split them again to offer a stable api for
# the jinja2 templates, regardless of replace_whitspace=True|False
text = "\n".join(wrapper.wrap(text))
return text.splitlines()

2
module_prototyper/security/ir.model.access.csv

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_prototype_admin,prototype_system,model_module_prototyper,base.group_system,1,1,1,1

BIN
module_prototyper/static/description/icon.png

After

Width: 500  |  Height: 500  |  Size: 14 KiB

7
module_prototyper/templates/8.0/__init__.py.template

@ -0,0 +1,7 @@
{% extends "header.template" %}
{% block body %}
{% if models %}
from . import models
{% endif %}
{% endblock %}

60
module_prototyper/templates/8.0/__openerp__.py.template

@ -0,0 +1,60 @@
{% extends "header.template" %}
{% block body %}
{
'name': '{{ prototype.human_name }}',
'version': '{{ prototype.version }}',
'author': '{{ prototype.author }}',
'maintainer': '{{ prototype.maintainer }}',
'website': '{{ prototype.website }}',
'license': '{{ prototype.licence }}',
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml # noqa
# for the full list
'category': '{{ prototype.with_context({}).category_id.name }}',{# In english please! #}
'summary': '{{ prototype.summary }}',
'description': """
{{ prototype.description }}
* Module exported by the Module Prototyper module for version 8.0.
* If you have any questions, please contact Savoir-faire Linux
(support@savoirfairelinux.com)
""",
# any module necessary for this one to work correctly
'depends': [
{% for dependency in prototype.dependency_ids %}
'{{ dependency.name }}',
{% endfor %}
],
'external_dependencies': {
'python': [],
},
# always loaded
'data': [
{% for data_file in data_files %}
'{{ data_file }}',
{% endfor %}
],
# only loaded in demonstration mode
'demo': [
{% for demo_file in prototype.demo_ids %}
'{{ demo_file.name }}',
{% endfor %}
],
# used for Javascript Web CLient Testing with QUnit / PhantomJS
# https://www.odoo.com/documentation/8.0/reference/javascript.html#testing-in-odoo-web-client # noqa
'js': [],
'css': [],
'qweb': [],
'installable': True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
'auto_install': {{ prototype.auto_install }},
'application': {{ prototype.application }},
}
{% endblock %}

8
module_prototyper/templates/8.0/data/model_name.xml.template

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<openerp>
<data>
{{ data }}
</data>
</openerp>

8
module_prototyper/templates/8.0/demo/model_name.xml.template

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<openerp>
<data>
{{ demo }}
</data>
</openerp>

23
module_prototyper/templates/8.0/header.template

@ -0,0 +1,23 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) {{ export_year }} {% if author %}{{ author }}{% endif %}
# {% if website %}({{ website }}).{% endif %}
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{% block body %}
{% endblock %}

9
module_prototyper/templates/8.0/models/__init__.py.template

@ -0,0 +1,9 @@
{% extends "header.template" %}
{% block body %}
{% for model in models %}
{% if loop.first %}
{% endif %}
from . import {{ model }}
{% endfor %}
{% endblock %}

35
module_prototyper/templates/8.0/models/model_name.py.template

@ -0,0 +1,35 @@
{% extends "header.template" %}
{% block body %}
from openerp import models, fields
from openerp.tools.translate import _
class {{ unprefix(name) }}(models.Model):
{% if model.state == 'base' %}
_name = "{{ model.model }}"
{% else %}
_inherit = "{{ model.model }}"
{% endif %}
{% if description %}
_description = "{{ description }}"
{% endif %}
{% for field in fields %}
{% for line in wrap(field.notes, replace_whitespace=False) %}
# {{line}}
{% endfor %}
{{ unprefix(field.name) }} = fields.{{ field.ttype|capitalize }}(
string=_("{{ field.field_description }}"),
required={{ field.required }},
translate={{ field.translate }},
readonly={{ field.readonly }}
{% if field.size %}
size={{ field.size }},
{% endif %}
{% if field.helper %}
help=_("{{ field.helper }}"),
{% endif %}
)
{% endfor %}
{% endblock %}

4
module_prototyper/templates/8.0/security/ir.model.access.csv.template

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
{% for rule in access_rules %}
{{ rule.id }},{{ rule.name }},{{ rule.model_id.id }},{{ rule.group_id.id }},{{ rule.perm_write }},{{ rule.perm_write }},{{ rule.perm_create }},{{ rule.perm_unlink }}
{% endfor %}

10
module_prototyper/templates/8.0/security/model_name.xml.template

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<openerp>
<data>
{{ groups }}
{{ rules }}
</data>
</openerp>

29
module_prototyper/templates/8.0/views/model_menus.xml.template

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<openerp>
<data>
{% for menu in menus %}
<record id="action_{{ menu.action.name }}_{{ menu.action.view_type }}_view" model="{{ menu.action.type }}">
<field name="name">{{ unprefix(menu.action.name) }}</field>
<field name="type">{{ menu.action.type }}</field>
<field name="res_model">{{ unprefix(menu.action.res_model) }}</field>
<field name="view_type">{{ menu.action.view_type }}</field>
<field name="view_mode">{{ menu.action.view_mode }}</field>
{% if menu.action.help %}
<field name="help" type="html">{{ menu.action.help }}
</field>
{% endif %}
</record>
<menuitem action="action_{{ unprefix(menu.action.name) }}_{{ menu.action.view_type }}_view"
name="{{ menu.name }}"
id="menu_action_{{ unprefix(menu.name)|replace('.', '_') }}_{{ menu.action.view_type }}"
{% if menu.parent_id %}parent="{{ menu.parent_id.get_xml_id(cr,1,1).values()[0] }}"{% endif %}
sequence="{{ menu.sequence }}"
groups="{% for group in menu.groups_id %}{{ group.get_xml_id(cr,1,1).values()[0] }},{% endfor %}"
/>
{% if not loop.last %}
{% endif %}
{% endfor %}
</data>
</openerp>

17
module_prototyper/templates/8.0/views/model_views.xml.template

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- TODO: put here a reminder on what to do at the first edition -->
{% for view in views %}
<record id="{{ unprefix(view.name)|replace('.', '_')}}_view" model="ir.ui.view">
<field name="name">{{ unprefix(view.name) }}.view</field>
<field name="model">{{ unprefix(view.model) }}</field>
<field name="view_type">{{ view.type }}</field>
<field name="inherit_id" ref="{{ view.inherit_id.get_xml_id(cr,1,1).values()[0] }}"/>
<field name="arch" type="xml">
{{ fixup_arch(view.arch) }}
</field>
</record>
{% endfor %}
</data>
</openerp>

25
module_prototyper/tests/__init__.py

@ -0,0 +1,25 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
test_prototype_module_export,
test_prototype
)

113
module_prototyper/tests/test_prototype.py

@ -0,0 +1,113 @@
# -*- encoding: utf-8 -*- #
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import ast
import lxml.etree
try:
import pep8
except ImportError:
pep8 = None
from jinja2 import Environment
from jinja2.exceptions import TemplateNotFound
from openerp.tests import common
class TestModulePrototyper(common.TransactionCase):
def setUp(self):
super(TestModulePrototyper, self).setUp()
self.main_model = self.env['module_prototyper']
self.module_category_model = self.env['ir.module.category']
self.module_module_model = self.env['ir.module.module']
self.prototype = self.main_model.create({
'name': 't_name',
'category_id': self.module_category_model.browse(1).id,
'human_name': 't_human_name',
'summary': 't_summary',
'description': 't_description',
'author': 't_author',
'maintainer': 't_maintainer',
'website': 't_website',
'dependencies': [(6, 0, [1, 2, 3, 4])],
})
self.api_version = '8.0'
def test_generate_files_assert_if_no_env(self):
self.assertRaises(
AssertionError,
self.prototype.generate_files
)
def test_generate_files(self):
"""Test generate_files returns a tuple."""
self.prototype.set_jinja_env(self.api_version)
details = self.prototype.generate_files()
self.assertIsInstance(details, list)
# namedtuples in tuple
for file_details in details:
self.assertIsInstance(file_details, tuple)
self.assertIsInstance(file_details.filename, basestring)
self.assertIsInstance(file_details.filecontent, basestring)
name, contents = file_details
if name.endswith(".py"):
# We have a "coding utf-8" line in there, we need to encode
contents = contents.encode("utf-8")
ast.parse(contents)
if pep8:
checker = pep8.Checker(
name,
contents.splitlines(True))
res = checker.check_all()
self.assertFalse(
res,
"Python file {0} has pep8 errors:\n"
"{1}\n{2}".format(name, checker.report.messages,
repr(contents))
)
elif name.endswith(".xml"):
# TODO validate valid odoo xml
lxml.etree.fromstring(contents)
def test_generate_files_raise_templatenotfound_if_not_found(self):
self.prototype.set_jinja_env('t_api_version')
self.assertRaises(
TemplateNotFound,
self.prototype.generate_files
)
def test_set_env(self):
"""test the jinja2 environment is set."""
self.assertIsNone(self.prototype._env)
self.prototype.set_jinja_env(self.api_version)
self.assertIsInstance(
self.prototype._env, Environment
)
def test_friendly_name_return(self):
"""Test if the returns match the pattern."""
name = 'res.partner'
self.assertEqual(
self.prototype.friendly_name(name),
name.replace('.', '_')
)

80
module_prototyper/tests/test_prototype_module_export.py

@ -0,0 +1,80 @@
# -*- encoding: utf-8 -*-
# #############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
import zipfile
import StringIO
class test_prototype_module_export(common.TransactionCase):
def setUp(self):
super(test_prototype_module_export, self).setUp()
self.main_model = self.env['module_prototyper.module.export']
self.prototype_model = self.env['module_prototyper']
self.module_category_model = self.env[
'ir.module.category'
]
self.prototype = self.prototype_model.create({
'name': 't_name',
'category_id': self.module_category_model.browse(1).id,
'human_name': 't_human_name',
'summary': 't_summary',
'description': 't_description',
'author': 't_author',
'maintainer': 't_maintainer',
'website': 't_website',
})
self.exporter = self.main_model.create({'name': 't_name'})
def test_action_export_assert_for_wrong_active_model(self):
"""Test if the assertion raises."""
exporter = self.main_model.with_context(
active_model='t_active_model'
).create({})
self.assertRaises(
AssertionError,
exporter.action_export,
[exporter.id],
)
def test_action_export_update_wizard(self):
"""Test if the wizard is updated during the process."""
exporter = self.main_model.with_context(
active_model=self.prototype_model._name,
active_id=self.prototype.id
).create({})
exporter.action_export(exporter.id)
self.assertEqual(exporter.state, 'get')
self.assertEqual(exporter.name, '{}.zip'.format(self.prototype.name))
def test_zip_files_returns_tuple(self):
"""Test the method return of the method that generate the zip file."""
ret = self.main_model.zip_files(self.exporter, [self.prototype])
self.assertIsInstance(ret, tuple)
self.assertIsInstance(
ret.zip_file, zipfile.ZipFile
)
self.assertIsInstance(
ret.stringIO, StringIO.StringIO
)

51
module_prototyper/views/ir_model_fields_view.xml

@ -0,0 +1,51 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="view_ir_model_fields_form" model="ir.ui.view">
<field name="name">view_ir_model_fields_form</field>
<field name="model">ir.model.fields</field>
<field name="inherit_id" ref="base.view_model_fields_form"/>
<field name="arch" type="xml">
<field name="groups" position="before">
<label for="helper"/>
<field name="helper"
placeholder="Text that will be set as the helper of the field..."/>
<label for="notes"/>
<field name="notes"
placeholder="Notes to help developers to understand the work or advanced features that should be added, ie: onchange, etc."/>
</field>
<field name="relation_field" position="after">
<field name="column1"
attrs="{'invisible': [('ttype', '!=', 'many2many')]}"
/>
<field name="column2"
attrs="{'invisible': [('ttype', '!=', 'many2many')]}"
/>
</field>
<field name="translate" position="after">
<field name="limit"
attrs="{'invisible': [('ttype', '!=', 'many2many')]}"
/>
<field name="client_context"
attrs="{'invisible': [('ttype', 'not in', ['many2one','one2many','many2many'])]}"
/>
</field>
</field>
</record>
<record id="view_ir_model_form" model="ir.ui.view">
<field name="name">view_ir_model_form</field>
<field name="model">ir.model</field>
<field name="inherit_id" ref="base.view_model_form"/>
<field name="arch" type="xml">
<xpath expr="//page/field/form/separator[@string='Groups']"
position="before">
<separator string="Notes"/>
<field name="notes"/>
</xpath>
</field>
</record>
</data>
</openerp>

146
module_prototyper/views/module_prototyper_view.xml

@ -0,0 +1,146 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="module_prototyper_base_tree_view" model="ir.ui.view">
<field name="name">Base tree View for module prototypes</field>
<field name="model">module_prototyper</field>
<field name="arch" type="xml">
<tree string="Prototype">
<field name="human_name"/>
<field name="name"/>
<field name="summary"/>
</tree>
</field>
</record>
<record id="module_prototyper_base_form_view" model="ir.ui.view">
<field name="name">Base form view for module prototypes</field>
<field name="model">module_prototyper</field>
<field name="arch" type="xml">
<form string="Module">
<sheet>
<field name="icon_image" widget="image"
class="oe_avatar oe_left"/>
<div class="oe_title">
<h1>
<field name="human_name"
placeholder="ex: Module Prototyper"/>
</h1>
<div>
<button name="%(button_module_export_action)d"
string="Export" type="action"/>
</div>
</div>
<group>
<group>
<field name="name"
placeholder="ex: module_prototyper"/>
<field name="summary"
placeholder="ex: Prototype your module."/>
<field name="category_id"
placeholder="ex: Others, Sales, Website"/>
<field name="version"/>
<field name="license"/>
</group>
<group>
<field name="author"
placeholder="ex: Odoo Community Association"/>
<field name="website" widget="url"
placeholder="ex: http://odoo-community.org/"/>
<field name="maintainer"
placeholder="ex: Odoo Community Association"/>
<field name="auto_install"/>
<field name="application"/>
</group>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
<page string="Dependencies">
<field name="dependency_ids"/>
</page>
<page string="Fields">
<label for="field_ids"/>
<field name="field_ids"/>
</page>
<page string="Interface">
<label for="view_ids"/>
<field name="view_ids"/>
<label for="menu_ids"/>
<field name="menu_ids"/>
</page>
<page string="Data &amp; Demo">
<label for="data_ids"/>
<field name="data_ids"/>
<label for="demo_ids"/>
<field name="demo_ids"/>
</page>
<page string="Reports">
<label for="report_ids" />
<field name="report_ids" />
</page>
<page string="Security">
<label for="group_ids"/>
<field name="group_ids"/>
<label for="right_ids"/>
<field name="right_ids"/>
<label for="rule_ids"/>
<field name="rule_ids"/>
</page>
<page string="Workflows">
<label for="activity_ids" />
<field name="activity_ids" />
<label for="transition_ids" />
<field name="transition_ids" />
</page>
<page string="Website">
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="open_module_prototyper_list" model="ir.actions.act_window">
<field name="name">Prototypes</field>
<field name="res_model">module_prototyper</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="module_prototyper_base_tree_view"/>
</record>
<!--Here a menu is created to help user to have all what he needs-->
<!--under his mouse with ease-->
<menuitem id="menu_module_prototyper"
name="Module Prototypes"
parent="base.menu_administration"
sequence="1"/>
<menuitem id="menu_open_module_prototyper"
action="open_module_prototyper_list"
parent="menu_module_prototyper"
sequence="1"
groups="base.group_system"/>
<menuitem action="base.action_ui_view"
id="menu_action_ui_view"
parent="menu_module_prototyper"
sequence="2"
groups="base.group_system"/>
<menuitem action="base.grant_menu_access"
id="menu_grant_menu_access"
parent="menu_module_prototyper"
sequence="3"
groups="base.group_system"/>
<menuitem action="base.action_model_fields"
id="ir_model_model_fields"
parent="menu_module_prototyper"
sequence="4"
groups="base.group_system"/>
</data>
</openerp>

22
module_prototyper/wizard/__init__.py

@ -0,0 +1,22 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import module_prototyper_module_export

132
module_prototyper/wizard/module_prototyper_module_export.py

@ -0,0 +1,132 @@
# -*- encoding: utf-8 -*-
# #############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import StringIO
import base64
import os
import zipfile
from collections import namedtuple
from openerp import fields, models, api
class PrototypeModuleExport(models.TransientModel):
_name = "module_prototyper.module.export"
name = fields.Char('File Name', readonly=True)
api_version = fields.Selection(
[
('8.0', '8.0'),
],
'API version',
required=True,
default='8.0'
)
data = fields.Binary('File', readonly=True)
state = fields.Selection(
[
('choose', 'choose'), # choose version
('get', 'get') # get module
],
default='choose'
)
@api.model
def action_export(self, ids):
"""
Export a zip file containing the module based on the information
provided in the prototype, using the templates chosen in the wizard.
"""
if isinstance(ids, (int, long)):
ids = [ids]
wizard = self.browse(ids)
active_model = self._context.get('active_model')
# checking if the wizard was called by a prototype.
msg = '{} has to be called from a "module_prototyper" , not a "{}"'
assert active_model == 'module_prototyper', msg.format(
self, active_model
)
# getting the prototype of the wizard
prototypes = self.env[active_model].browse(
[self._context.get('active_id')]
)
zip_details = self.zip_files(wizard, prototypes)
if len(prototypes) == 1:
zip_name = prototypes[0].name
else:
zip_name = "prototyper_export"
wizard.write(
{
'name': '{}.zip'.format(zip_name),
'state': 'get',
'data': base64.encodestring(zip_details.stringIO.getvalue())
}
)
return {
'type': 'ir.actions.act_window',
'res_model': 'module_prototyper.module.export',
'view_mode': 'form',
'view_type': 'form',
'res_id': wizard.id,
'views': [(False, 'form')],
'target': 'new',
}
@staticmethod
def zip_files(wizard, prototypes):
"""Takes a set of file and zips them.
:param file_details: tuple (filename, file_content)
:return: tuple (zip_file, stringIO)
"""
zip_details = namedtuple('Zip_details', ['zip_file', 'stringIO'])
out = StringIO.StringIO()
with zipfile.ZipFile(out, 'w') as target:
for prototype in prototypes:
# setting the jinja environment.
# They will help the program to find the template to render the
# files with.
prototype.set_jinja_env(wizard.api_version)
# generate_files ask the prototype to investigate the input and
# to generate the file templates according to it. zip_files,
# in another hand, put all the template files into a package
# ready to be saved by the user.
file_details = prototype.generate_files()
for filename, file_content in file_details:
if isinstance(file_content, unicode):
file_content = file_content.encode('utf-8')
# Prefix all names with module technical name
filename = os.path.join(prototype.name, filename)
info = zipfile.ZipInfo(filename)
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 2175008768 # specifies mode 0644
target.writestr(info, file_content)
return zip_details(zip_file=target, stringIO=out)

43
module_prototyper/wizard/module_prototyper_module_export_view.xml

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="view_module_export_wizard" model="ir.ui.view">
<field name="name">Export Module</field>
<field name="model">module_prototyper.module.export</field>
<field name="arch" type="xml">
<form string="Export Module">
<field name="name" invisible="1"/>
<field name="state" invisible="1"/>
<group string="Export Settings" states="choose" col="6">
<group colspan="2">
<field name="api_version"/>
</group>
<group colspan="4">
</group>
</group>
<div states="get">
<h2>Export Complete</h2>
<p>Here is the exported module: <field name="data" readonly="1" filename="name"/></p>
</div>
<footer states="choose">
<button name="action_export" string="Export" type="object" class="oe_highlight"/> or
<button special="cancel" string="Cancel" type="object" class="oe_link"/>
</footer>
<footer states="get">
<button special="cancel" string="Close" type="object"/>
</footer>
</form>
</field>
</record>
<record id="button_module_export_action" model="ir.actions.act_window">
<field name="name">Export Module</field>
<field name="res_model">module_prototyper.module.export</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_module_export_wizard"/>
<field name="target">new</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save