diff options
Diffstat (limited to 'odoo/cli')
23 files changed, 826 insertions, 0 deletions
diff --git a/odoo/cli/__init__.py b/odoo/cli/__init__.py new file mode 100644 index 00000000000..2ba56ac4cf1 --- /dev/null +++ b/odoo/cli/__init__.py @@ -0,0 +1,13 @@ +import logging +import sys +import os + +import openerp + +from command import Command, main + +import deploy +import scaffold +import server +import shell +import start diff --git a/odoo/cli/command.py b/odoo/cli/command.py new file mode 100644 index 00000000000..2f2fffa18b4 --- /dev/null +++ b/odoo/cli/command.py @@ -0,0 +1,64 @@ +import logging +import sys +import os +from os.path import join as joinpath, isdir + +import openerp +from openerp.modules import get_modules, get_module_path + +commands = {} + +class CommandType(type): + def __init__(cls, name, bases, attrs): + super(CommandType, cls).__init__(name, bases, attrs) + name = getattr(cls, name, cls.__name__.lower()) + cls.name = name + if name != 'command': + commands[name] = cls + +class Command(object): + """Subclass this class to define new openerp subcommands """ + __metaclass__ = CommandType + + def run(self, args): + pass + +class Help(Command): + """Display the list of available commands""" + def run(self, args): + print "Available commands:\n" + names = commands.keys() + padding = max([len(k) for k in names]) + 2 + for k in sorted(names): + name = k.ljust(padding, ' ') + doc = (commands[k].__doc__ or '').strip() + print " %s%s" % (name, doc) + print "\nUse '%s <command> --help' for individual command help." % sys.argv[0].split(os.path.sep)[-1] + +def main(): + args = sys.argv[1:] + + # The only shared option is '--addons-path=' needed to discover additional + # commands from modules + if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"): + # parse only the addons-path, do not setup the logger... + openerp.tools.config._parse_config([args[0]]) + args = args[1:] + + # Default legacy command + command = "server" + + # TODO: find a way to properly discover addons subcommands without importing the world + # Subcommand discovery + if len(args) and not args[0].startswith("-"): + logging.disable(logging.CRITICAL) + for module in get_modules(): + if isdir(joinpath(get_module_path(module), 'cli')): + __import__('openerp.addons.' + module) + logging.disable(logging.NOTSET) + command = args[0] + args = args[1:] + + if command in commands: + o = commands[command]() + o.run(args) diff --git a/odoo/cli/deploy.py b/odoo/cli/deploy.py new file mode 100644 index 00000000000..2e97d838ec2 --- /dev/null +++ b/odoo/cli/deploy.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import os +import requests +import sys +import tempfile +import zipfile + +from . import Command + +class Deploy(Command): + """Deploy a module on an Odoo instance""" + def __init__(self): + super(Deploy, self).__init__() + self.session = requests.session() + + def deploy_module(self, module_path, url, login, password, db='', force=False): + url = url.rstrip('/') + csrf_token = self.authenticate(url, login, password, db) + module_file = self.zip_module(module_path) + try: + return self.upload_module(url, module_file, force=force, csrf_token=csrf_token) + finally: + os.remove(module_file) + + def upload_module(self, server, module_file, force=False, csrf_token=None): + print("Uploading module file...") + url = server + '/base_import_module/upload' + + post_data = {'force': '1' if force else ''} + if csrf_token: post_data['csrf_token'] = csrf_token + + with open(module_file, 'rb') as f: + res = self.session.post(url, files={'mod_file': f}, data=post_data) + res.raise_for_status() + + return res.text + + def authenticate(self, server, login, password, db=''): + print("Authenticating on server '%s' ..." % server) + + # Fixate session with a given db if any + self.session.get(server + '/web/login', params=dict(db=db)) + + args = dict(login=login, password=password, db=db) + res = self.session.post(server + '/base_import_module/login', args) + if res.status_code == 404: + raise Exception("The server '%s' does not have the 'base_import_module' installed." % server) + elif res.status_code != 200: + raise Exception(res.text) + + return res.headers.get('x-csrf-token') + + def zip_module(self, path): + path = os.path.abspath(path) + if not os.path.isdir(path): + raise Exception("Could not find module directory '%s'" % path) + container, module_name = os.path.split(path) + temp = tempfile.mktemp(suffix='.zip') + try: + print("Zipping module directory...") + with zipfile.ZipFile(temp, 'w') as zfile: + for root, dirs, files in os.walk(path): + for file in files: + file_path = os.path.join(root, file) + zfile.write(file_path, file_path.split(container).pop()) + return temp + except Exception: + os.remove(temp) + raise + + def run(self, cmdargs): + parser = argparse.ArgumentParser( + prog="%s deploy" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__ + ) + parser.add_argument('path', help="Path of the module to deploy") + parser.add_argument('url', nargs='?', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069") + parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.') + parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)') + parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)') + parser.add_argument('--verify-ssl', action='store_true', help='Verify SSL certificate') + parser.add_argument('--force', action='store_true', help='Force init even if module is already installed. (will update `noupdate="1"` records)') + if not cmdargs: + sys.exit(parser.print_help()) + + args = parser.parse_args(args=cmdargs) + + if not args.verify_ssl: + self.session.verify = False + + try: + if not args.url.startswith(('http://', 'https://')): + args.url = 'https://%s' % args.url + result = self.deploy_module(args.path, args.url, args.login, args.password, args.db, force=args.force) + print(result) + except Exception, e: + sys.exit("ERROR: %s" % e) diff --git a/odoo/cli/scaffold.py b/odoo/cli/scaffold.py new file mode 100644 index 00000000000..221f192347e --- /dev/null +++ b/odoo/cli/scaffold.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import os +import re +import sys + +import jinja2 + +from . import Command + +from openerp.modules.module import (get_module_root, MANIFEST, load_information_from_description_file as load_manifest) + + +class Scaffold(Command): + """ Generates an Odoo module skeleton. """ + + def run(self, cmdargs): + # TODO: bash completion file + parser = argparse.ArgumentParser( + prog="%s scaffold" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__, + epilog=self.epilog(), + ) + parser.add_argument( + '-t', '--template', type=template, default=template('default'), + help="Use a custom module template, can be a template name or the" + " path to a module template (default: %(default)s)") + parser.add_argument('name', help="Name of the module to create") + parser.add_argument( + 'dest', default='.', nargs='?', + help="Directory to create the module in (default: %(default)s)") + + if not cmdargs: + sys.exit(parser.print_help()) + args = parser.parse_args(args=cmdargs) + + args.template.render_to( + snake(args.name), + directory(args.dest, create=True), + {'name': args.name}) + + def epilog(self): + return "Built-in templates available are: %s" % ', '.join( + d for d in os.listdir(builtins()) + if d != 'base' + ) + +builtins = lambda *args: os.path.join( + os.path.abspath(os.path.dirname(__file__)), + 'templates', + *args) + +def snake(s): + """ snake cases ``s`` + + :param str s: + :return: str + """ + # insert a space before each uppercase character preceded by a + # non-uppercase letter + s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s) + # lowercase everything, split on whitespace and join + return '_'.join(s.lower().split()) +def pascal(s): + return ''.join( + ss.capitalize() + for ss in re.sub('[_\s]+', ' ', s).split() + ) + +def directory(p, create=False): + expanded = os.path.abspath( + os.path.expanduser( + os.path.expandvars(p))) + if create and not os.path.exists(expanded): + os.makedirs(expanded) + if not os.path.isdir(expanded): + die("%s is not a directory" % p) + return expanded + +env = jinja2.Environment() +env.filters['snake'] = snake +env.filters['pascal'] = pascal +class template(object): + def __init__(self, identifier): + # TODO: archives (zipfile, tarfile) + self.id = identifier + # is identifier a builtin? + self.path = builtins(identifier) + if os.path.isdir(self.path): + return + # is identifier a directory? + self.path = identifier + if os.path.isdir(self.path): + return + die("{} is not a valid module template".format(identifier)) + + def __str__(self): + return self.id + + def files(self): + """ Lists the (local) path and content of all files in the template + """ + for root, _, files in os.walk(self.path): + for f in files: + path = os.path.join(root, f) + yield path, open(path, 'rb').read() + + def render_to(self, modname, directory, params=None): + """ Render this module template to ``dest`` with the provided + rendering parameters + """ + # overwrite with local + for path, content in self.files(): + local = os.path.relpath(path, self.path) + # strip .template extension + root, ext = os.path.splitext(local) + if ext == '.template': + local = root + dest = os.path.join(directory, modname, local) + destdir = os.path.dirname(dest) + if not os.path.exists(destdir): + os.makedirs(destdir) + + with open(dest, 'wb') as f: + if ext not in ('.py', '.xml', '.csv', '.js', '.rst', '.html', '.template'): + f.write(content) + else: + env.from_string(content.decode('utf-8'))\ + .stream(params or {})\ + .dump(f, encoding='utf-8') + +def die(message, code=1): + print >>sys.stderr, message + sys.exit(code) + +def warn(message): + # ASK: shall we use logger ? + print "WARNING: " + message diff --git a/odoo/cli/server.py b/odoo/cli/server.py new file mode 100644 index 00000000000..314072b69a8 --- /dev/null +++ b/odoo/cli/server.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +""" +OpenERP - Server +OpenERP is an ERP+CRM program for small and medium businesses. + +The whole source code is distributed under the terms of the +GNU Public Licence. + +(c) 2003-TODAY, Fabien Pinckaers - OpenERP SA +""" + +import atexit +import csv +import logging +import os +import signal +import sys +import threading +import traceback +import time + +import openerp + +from . import Command + +__author__ = openerp.release.author +__version__ = openerp.release.version + +# Also use the `openerp` logger for the main script. +_logger = logging.getLogger('openerp') + +def check_root_user(): + """Warn if the process's user is 'root' (on POSIX system).""" + if os.name == 'posix': + import pwd + if pwd.getpwuid(os.getuid())[0] == 'root': + sys.stderr.write("Running as user 'root' is a security risk.\n") + +def check_postgres_user(): + """ Exit if the configured database user is 'postgres'. + + This function assumes the configuration has been initialized. + """ + config = openerp.tools.config + if config['db_user'] == 'postgres': + sys.stderr.write("Using the database user 'postgres' is a security risk, aborting.") + sys.exit(1) + +def report_configuration(): + """ Log the server version and some configuration values. + + This function assumes the configuration has been initialized. + """ + config = openerp.tools.config + _logger.info("OpenERP version %s", __version__) + _logger.info('addons paths: %s', openerp.modules.module.ad_paths) + host = config['db_host'] or os.environ.get('PGHOST', 'default') + port = config['db_port'] or os.environ.get('PGPORT', 'default') + user = config['db_user'] or os.environ.get('PGUSER', 'default') + _logger.info('database: %s@%s:%s', user, host, port) + +def rm_pid_file(main_pid): + config = openerp.tools.config + if config['pidfile'] and main_pid == os.getpid(): + try: + os.unlink(config['pidfile']) + except OSError: + pass + +def setup_pid_file(): + """ Create a file with the process id written in it. + + This function assumes the configuration has been initialized. + """ + config = openerp.tools.config + if not openerp.evented and config['pidfile']: + pid = os.getpid() + with open(config['pidfile'], 'w') as fd: + fd.write(str(pid)) + atexit.register(rm_pid_file, pid) + +def export_translation(): + config = openerp.tools.config + dbname = config['db_name'] + + if config["language"]: + msg = "language %s" % (config["language"],) + else: + msg = "new language" + _logger.info('writing translation file for %s to %s', msg, + config["translate_out"]) + + fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower() + + with open(config["translate_out"], "w") as buf: + registry = openerp.modules.registry.Registry.new(dbname) + with openerp.api.Environment.manage(): + with registry.cursor() as cr: + openerp.tools.trans_export(config["language"], + config["translate_modules"] or ["all"], buf, fileformat, cr) + + _logger.info('translation file written successfully') + +def import_translation(): + config = openerp.tools.config + context = {'overwrite': config["overwrite_existing_translations"]} + dbname = config['db_name'] + + registry = openerp.modules.registry.Registry.new(dbname) + with openerp.api.Environment.manage(): + with registry.cursor() as cr: + openerp.tools.trans_load( + cr, config["translate_in"], config["language"], context=context, + ) + +def main(args): + check_root_user() + openerp.tools.config.parse_config(args) + check_postgres_user() + report_configuration() + + config = openerp.tools.config + + # the default limit for CSV fields in the module is 128KiB, which is not + # quite sufficient to import images to store in attachment. 500MiB is a + # bit overkill, but better safe than sorry I guess + csv.field_size_limit(500 * 1024 * 1024) + + preload = [] + if config['db_name']: + preload = config['db_name'].split(',') + for db_name in preload: + try: + openerp.service.db._create_empty_database(db_name) + except openerp.service.db.DatabaseExists: + pass + + if config["translate_out"]: + export_translation() + sys.exit(0) + + if config["translate_in"]: + import_translation() + sys.exit(0) + + # This needs to be done now to ensure the use of the multiprocessing + # signaling mecanism for registries loaded with -d + if config['workers']: + openerp.multi_process = True + + stop = config["stop_after_init"] + + setup_pid_file() + rc = openerp.service.server.start(preload=preload, stop=stop) + sys.exit(rc) + +class Server(Command): + """Start the odoo server (default command)""" + def run(self, args): + main(args) diff --git a/odoo/cli/shell.py b/odoo/cli/shell.py new file mode 100644 index 00000000000..0d1ed59813e --- /dev/null +++ b/odoo/cli/shell.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import code +import os +import signal +import sys + +import openerp +from . import Command + +def raise_keyboard_interrupt(*a): + raise KeyboardInterrupt() + +class Console(code.InteractiveConsole): + def __init__(self, locals=None, filename="<console>"): + code.InteractiveConsole.__init__(self, locals, filename) + try: + import readline + import rlcompleter + except ImportError: + print 'readline or rlcompleter not available, autocomplete disabled.' + else: + readline.set_completer(rlcompleter.Completer(locals).complete) + readline.parse_and_bind("tab: complete") + +class Shell(Command): + """Start odoo in an interactive shell""" + def init(self, args): + openerp.tools.config.parse_config(args) + openerp.cli.server.report_configuration() + openerp.service.server.start(preload=[], stop=True) + signal.signal(signal.SIGINT, raise_keyboard_interrupt) + + def console(self, local_vars): + if not os.isatty(sys.stdin.fileno()): + exec sys.stdin in local_vars + else: + if 'env' not in local_vars: + print 'No environment set, use `odoo.py shell -d dbname` to get one.' + for i in sorted(local_vars): + print '%s: %s' % (i, local_vars[i]) + Console(locals=local_vars).interact() + + def shell(self, dbname): + local_vars = { + 'openerp': openerp + } + with openerp.api.Environment.manage(): + if dbname: + registry = openerp.registry(dbname) + with registry.cursor() as cr: + uid = openerp.SUPERUSER_ID + ctx = openerp.api.Environment(cr, uid, {})['res.users'].context_get() + env = openerp.api.Environment(cr, uid, ctx) + local_vars['env'] = env + local_vars['self'] = env.user + self.console(local_vars) + else: + self.console(local_vars) + + def run(self, args): + self.init(args) + self.shell(openerp.tools.config['db_name']) + return 0 diff --git a/odoo/cli/start.py b/odoo/cli/start.py new file mode 100644 index 00000000000..b0cc932e19d --- /dev/null +++ b/odoo/cli/start.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import glob +import os +import sys + +from . import Command +from .server import main +from openerp.modules.module import get_module_root, MANIFEST +from openerp.service.db import _create_empty_database, DatabaseExists + + +class Start(Command): + """Quick start the Odoo server for your project""" + + def get_module_list(self, path): + mods = glob.glob(os.path.join(path, '*/%s' % MANIFEST)) + return [mod.split(os.path.sep)[-2] for mod in mods] + + + def run(self, cmdargs): + parser = argparse.ArgumentParser( + prog="%s start" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__ + ) + parser.add_argument('--path', default=".", + help="Directory where your project's modules are stored (will autodetect from current dir)") + parser.add_argument("-d", "--database", dest="db_name", default=None, + help="Specify the database name (default to project's directory name") + + + args, unknown = parser.parse_known_args(args=cmdargs) + + project_path = os.path.abspath(os.path.expanduser(os.path.expandvars(args.path))) + module_root = get_module_root(project_path) + db_name = None + if module_root: + # started in a module so we choose this module name for database + db_name = project_path.split(os.path.sep)[-1] + # go to the parent's directory of the module root + project_path = os.path.abspath(os.path.join(project_path, os.pardir)) + + # check if one of the subfolders has at least one module + mods = self.get_module_list(project_path) + if mods and '--addons-path' not in cmdargs: + cmdargs.append('--addons-path=%s' % project_path) + + if not args.db_name: + args.db_name = db_name or project_path.split(os.path.sep)[-1] + cmdargs.extend(('-d', args.db_name)) + + # TODO: forbid some database names ? eg template1, ... + try: + _create_empty_database(args.db_name) + except DatabaseExists, e: + pass + except Exception, e: + die("Could not create database `%s`. (%s)" % (args.db_name, e)) + + if '--db-filter' not in cmdargs: + cmdargs.append('--db-filter=^%s$' % args.db_name) + + # Remove --path /-p options from the command arguments + def to_remove(i, l): + return l[i] == '-p' or l[i].startswith('--path') or \ + (i > 0 and l[i-1] in ['-p', '--path']) + cmdargs = [v for i, v in enumerate(cmdargs) + if not to_remove(i, cmdargs)] + + main(cmdargs) + +def die(message, code=1): + print >>sys.stderr, message + sys.exit(code) diff --git a/odoo/cli/templates/default/__init__.py.template b/odoo/cli/templates/default/__init__.py.template new file mode 100644 index 00000000000..aa4d0fd63a9 --- /dev/null +++ b/odoo/cli/templates/default/__init__.py.template @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models diff --git a/odoo/cli/templates/default/__openerp__.py.template b/odoo/cli/templates/default/__openerp__.py.template new file mode 100644 index 00000000000..97b8372007c --- /dev/null +++ b/odoo/cli/templates/default/__openerp__.py.template @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +{ + 'name': "{{ name }}", + + 'summary': """ + Short (1 phrase/line) summary of the module's purpose, used as + subtitle on modules listing or apps.openerp.com""", + + 'description': """ + Long description of module's purpose + """, + + 'author': "My Company", + 'website': "http://www.yourcompany.com", + + # 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 + # for the full list + 'category': 'Uncategorized', + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['base'], + + # always loaded + 'data': [ + # 'security/ir.model.access.csv', + 'views/views.xml', + 'views/templates.xml', + ], + # only loaded in demonstration mode + 'demo': [ + 'demo/demo.xml', + ], +} diff --git a/odoo/cli/templates/default/controllers/__init__.py.template b/odoo/cli/templates/default/controllers/__init__.py.template new file mode 100644 index 00000000000..b0f26a9a602 --- /dev/null +++ b/odoo/cli/templates/default/controllers/__init__.py.template @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers diff --git a/odoo/cli/templates/default/controllers/controllers.py.template b/odoo/cli/templates/default/controllers/controllers.py.template new file mode 100644 index 00000000000..505208171fc --- /dev/null +++ b/odoo/cli/templates/default/controllers/controllers.py.template @@ -0,0 +1,23 @@ +{%- set mod = name|snake -%} +{%- set model = "%s.%s"|format(mod, mod) -%} +{%- set root = "/%s/%s"|format(mod, mod) -%} +# -*- coding: utf-8 -*- +from openerp import http + +# class {{ mod|pascal }}(http.Controller): +# @http.route('{{ root }}/', auth='public') +# def index(self, **kw): +# return "Hello, world" + +# @http.route('{{ root }}/objects/', auth='public') +# def list(self, **kw): +# return http.request.render('{{ mod }}.listing', { +# 'root': '{{ root }}', +# 'objects': http.request.env['{{ model }}'].search([]), +# }) + +# @http.route('{{ root }}/objects/<model("{{ model }}"):obj>/', auth='public') +# def object(self, obj, **kw): +# return http.request.render('{{ mod }}.object', { +# 'object': obj +# }) diff --git a/odoo/cli/templates/default/demo/demo.xml.template b/odoo/cli/templates/default/demo/demo.xml.template new file mode 100644 index 00000000000..5ca25672404 --- /dev/null +++ b/odoo/cli/templates/default/demo/demo.xml.template @@ -0,0 +1,12 @@ +{%- set mod= name|snake -%} +{%- set model = "%s.%s"|format(mod, mod) -%} +<openerp> + <data> + <!-- {% for item in range(5) %} --> + <!-- <record id="object{{ item }}" model="{{ model }}"> --> + <!-- <field name="name">Object {{ item }}</field> --> + <!-- <field name="value">{{ item * 10 }}</field> --> + <!-- </record> --> + <!-- {% endfor %} --> + </data> +</openerp> diff --git a/odoo/cli/templates/default/models/__init__.py.template b/odoo/cli/templates/default/models/__init__.py.template new file mode 100644 index 00000000000..cde864bae21 --- /dev/null +++ b/odoo/cli/templates/default/models/__init__.py.template @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/odoo/cli/templates/default/models/models.py.template b/odoo/cli/templates/default/models/models.py.template new file mode 100644 index 00000000000..562aa3a851b --- /dev/null +++ b/odoo/cli/templates/default/models/models.py.template @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api + +# class {{ name|snake }}(models.Model): +# _name = '{{ name|snake }}.{{ name|snake }}' + +# name = fields.Char() +# value = fields.Integer() +# value2 = fields.Float(compute="_value_pc", store=True) +# description = fields.Text() +# +# @api.depends('value') +# def _value_pc(self): +# self.value2 = float(self.value) / 100 diff --git a/odoo/cli/templates/default/security/ir.model.access.csv.template b/odoo/cli/templates/default/security/ir.model.access.csv.template new file mode 100644 index 00000000000..71a2352fedc --- /dev/null +++ b/odoo/cli/templates/default/security/ir.model.access.csv.template @@ -0,0 +1,6 @@ +{%- set snake_name = name|snake -%} +{%- set id = "access_%s_%s"|format(snake_name, snake_name) -%} +{%- set name = "%s.%s"|format(snake_name, snake_name) -%} +{%- set model_id = "model_%s_%s"|format(snake_name, snake_name) -%} +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +{{ id }},{{ name }},{{ model_id }},,1,0,0,0 diff --git a/odoo/cli/templates/default/views/templates.xml.template b/odoo/cli/templates/default/views/templates.xml.template new file mode 100644 index 00000000000..b9166501678 --- /dev/null +++ b/odoo/cli/templates/default/views/templates.xml.template @@ -0,0 +1,22 @@ +<openerp> + <data> + <!-- <template id="listing"> --> + <!-- <ul> --> + <!-- <li t-foreach="objects" t-as="object"> --> + <!-- <a t-attf-href="#{ root }/objects/#{ object.id }"> --> + <!-- <t t-esc="object.display_name"/> --> + <!-- </a> --> + <!-- </li> --> + <!-- </ul> --> + <!-- </template> --> + <!-- <template id="object"> --> + <!-- <h1><t t-esc="object.display_name"/></h1> --> + <!-- <dl> --> + <!-- <t t-foreach="object._fields" t-as="field"> --> + <!-- <dt><t t-esc="field"/></dt> --> + <!-- <dd><t t-esc="object[field]"/></dd> --> + <!-- </t> --> + <!-- </dl> --> + <!-- </template> --> + </data> +</openerp> diff --git a/odoo/cli/templates/default/views/views.xml.template b/odoo/cli/templates/default/views/views.xml.template new file mode 100644 index 00000000000..b19f7f7067b --- /dev/null +++ b/odoo/cli/templates/default/views/views.xml.template @@ -0,0 +1,61 @@ +{%- set mod = name|snake -%} +{%- set model = "%s.%s"|format(mod, mod) -%} +<openerp> + <data> + <!-- explicit list view definition --> + <!-- + <record model="ir.ui.view" id="{{mod}}.list"> + <field name="name">{{name}} list</field> + <field name="model">{{model}}</field> + <field name="arch" type="xml"> + <tree> + <field name="name"/> + <field name="value"/> + <field name="value2"/> + </tree> + </field> + </record> + --> + + <!-- actions opening views on models --> + <!-- + <record model="ir.actions.act_window" id="{{mod}}.action_window"> + <field name="name">{{name}} window</field> + <field name="res_model">{{model}}</field> + <field name="view_mode">tree,form</field> + </record> + --> + + <!-- server action to the one above --> + <!-- + <record model="ir.actions.server" id="{{mod}}.action_server"> + <field name="name">{{name}} server</field> + <field name="model_id" ref="model_{{mod}}_{{mod}}"/> + <field name="code"> + action = { + "type": "ir.actions.act_window", + "view_mode": "tree,form", + "res_model": self._name, + } + </field> + </record> + --> + + <!-- Top menu item --> + <!-- + <menuitem name="{{name}}" id="{{mod}}.menu_root"/> + --> + <!-- menu categories --> + <!-- + <menuitem name="Menu 1" id="{{mod}}.menu_1" parent="{{mod}}.menu_root"/> + <menuitem name="Menu 2" id="{{mod}}.menu_2" parent="{{mod}}.menu_root"/> + --> + <!-- actions --> + <!-- + <menuitem name="List" id="{{mod}}.menu_1_list" parent="{{mod}}.menu_1" + action="{{mod}}.action_window"/> + <menuitem name="Server to list" id="{{mod}}" parent="{{mod}}.menu_2" + action="{{mod}}.action_server"/> + --> + </data> +</openerp> diff --git a/odoo/cli/templates/theme/__init__.py.template b/odoo/cli/templates/theme/__init__.py.template new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/odoo/cli/templates/theme/__init__.py.template @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/odoo/cli/templates/theme/__openerp__.py.template b/odoo/cli/templates/theme/__openerp__.py.template new file mode 100644 index 00000000000..f6d99c2be45 --- /dev/null +++ b/odoo/cli/templates/theme/__openerp__.py.template @@ -0,0 +1,24 @@ +{ + # Theme information + 'name': "{{name}}", + 'description': """ + """, + 'category': 'Theme', + 'version': '1.0', + 'depends': ['website'], + + # templates + 'data': [ + 'views/options.xml', + 'views/snippets.xml', + ], + + # demo pages + 'demo': [ + 'demo/pages.xml', + ], + + # Your information + 'author': "My Company", + 'website': "", +} diff --git a/odoo/cli/templates/theme/demo/pages.xml.template b/odoo/cli/templates/theme/demo/pages.xml.template new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/odoo/cli/templates/theme/demo/pages.xml.template diff --git a/odoo/cli/templates/theme/static/src/less/custom.less.template b/odoo/cli/templates/theme/static/src/less/custom.less.template new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/odoo/cli/templates/theme/static/src/less/custom.less.template diff --git a/odoo/cli/templates/theme/views/options.xml.template b/odoo/cli/templates/theme/views/options.xml.template new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/odoo/cli/templates/theme/views/options.xml.template diff --git a/odoo/cli/templates/theme/views/snippets.xml.template b/odoo/cli/templates/theme/views/snippets.xml.template new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/odoo/cli/templates/theme/views/snippets.xml.template |
