[Merge]Merge lp:openobject-server
bzr revid: vja@tinyerp.com-20130128054701-0cs8j5tzl9qwaniy
This commit is contained in:
commit
608f2a7949
33
.bzrignore
33
.bzrignore
|
@ -1,14 +1,21 @@
|
|||
.*
|
||||
*.egg-info
|
||||
*.orig
|
||||
*.vim
|
||||
.*.swp
|
||||
.bzrignore
|
||||
.idea
|
||||
.project
|
||||
.pydevproject
|
||||
.ropeproject
|
||||
.settings
|
||||
.DS_Store
|
||||
openerp/addons/*
|
||||
openerp/filestore*
|
||||
.Python
|
||||
*.pyc
|
||||
*.pyo
|
||||
bin/*
|
||||
build/
|
||||
RE:^bin/
|
||||
RE:^dist/
|
||||
RE:^include/
|
||||
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
|
||||
RE:^addons/\w+/doc/_build/
|
||||
include/
|
||||
lib/
|
||||
share/
|
||||
doc/_build/*
|
||||
win32/*.bat
|
||||
win32/meta.py
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
.. _adding-command:
|
||||
|
||||
Adding a new command
|
||||
====================
|
||||
|
||||
``oe`` uses the argparse_ library to implement commands. Each
|
||||
command lives in its own ``openerpcommand/<command>.py`` file.
|
||||
|
||||
.. _argparse: http://docs.python.org/2.7/library/argparse.html
|
||||
|
||||
To create a new command, probably the most simple way to get started is to
|
||||
copy/paste an existing command, say ``openerpcommand/initialize.py`` to
|
||||
``openerpcommand/foo.py``. In the newly created file, the important bits
|
||||
are the ``run(args)`` and ``add_parser(subparsers)`` functions.
|
||||
|
||||
``add_parser``'s responsability is to create a (sub-)parser for the command,
|
||||
i.e. describe the different options and flags. The last thing it does is to set
|
||||
``run`` as the function to call when the command is invoked.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
> def add_parser(subparsers):
|
||||
> parser = subparsers.add_parser('<command-name>',
|
||||
> description='...')
|
||||
> parser.add_argument(...)
|
||||
> ...
|
||||
> parser.set_defaults(run=run)
|
||||
|
||||
``run(args)`` actually implements the command. It should be kept as simple as
|
||||
possible and delegate most of its work to small functions (probably placed at
|
||||
the top of the new file). In other words, its responsability is mainly to
|
||||
deal with the presence/absence/pre-processing of ``argparse``'s arguments.
|
||||
|
||||
Finally, the module must be added to ``openerpcommand/__init__.py``.
|
|
@ -0,0 +1,40 @@
|
|||
.. _commands:
|
||||
|
||||
Available commands
|
||||
==================
|
||||
|
||||
This page explain some of the available ``oe`` commands. For an overview about
|
||||
``oe``, see :doc:`openerp-command`.
|
||||
|
||||
Keep in mind that ``oe --help`` and ``oe <command> --help`` already give a lot
|
||||
of information about the commands and their options and flags.
|
||||
|
||||
``web``
|
||||
-------
|
||||
|
||||
The ``web`` command is used to create a single OpenERP server process to handle
|
||||
regular HTTP requests and XML-RPC requests. It is possible to execute such
|
||||
process multiple times, possibly on different machines.
|
||||
|
||||
It is possible to chose the ``--threaded`` or ``--gevent`` flags. It is
|
||||
recommanded to use ``--threaded`` only when running a single process.
|
||||
``--gevent`` is experimental; it is planned to use it for the embedded chat
|
||||
feature.
|
||||
|
||||
Example invocation::
|
||||
|
||||
> oe web --addons ../../addons/trunk:../../web/trunk/addons --threaded
|
||||
|
||||
``cron``
|
||||
--------
|
||||
|
||||
The ``cron`` command is used to create a single OpenERP process to execute
|
||||
so-called cron jobs, also called scheduled tasks in the OpenERP interface. As
|
||||
for the ``web`` command, multiple cron processes can be run side by side.
|
||||
|
||||
It is necessary to specify on the command-line which database need to be
|
||||
watched by the cron process with the ``--database`` option.
|
||||
|
||||
Example invocation::
|
||||
|
||||
> oe cron --addons ../../addons/trunk:../../web/trunk/addons --database production
|
|
@ -0,0 +1,256 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# OpenERP Technical Documentation configuration file, created by
|
||||
# sphinx-quickstart on Fri Feb 17 16:14:06 2012.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../openerp'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'OpenERP Server Developers Documentation'
|
||||
copyright = u'2012, OpenERP s.a.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '7.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '7.0b'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'flask'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_themes']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'openerp-server-doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
|
||||
[u'OpenERP s.a.'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('http://docs.python.org/', None),
|
||||
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
|
||||
}
|
|
@ -16,20 +16,20 @@ Summary
|
|||
|
||||
Configuring and starting an OpenERP server with Gunicorn is straightfoward. The
|
||||
different sections below give more details but the following steps are all it
|
||||
takes::
|
||||
takes:
|
||||
|
||||
1. Use a configuration file, passing it to ``gunicorn`` using the ``-c``
|
||||
option.
|
||||
2. Within the same configuration file, also configure OpenERP.
|
||||
3. Run ``gunicorn openerp:wsgi.core.application -c gunicorn.conf.py``.
|
||||
1. Use a configuration file, passing it to ``gunicorn`` using the ``-c``
|
||||
option.
|
||||
2. Within the same configuration file, also configure OpenERP.
|
||||
3. Run ``gunicorn openerp:service.wsgi_server.application -c openerp-wsgi.py``.
|
||||
|
||||
Sample configuration file
|
||||
-------------------------
|
||||
|
||||
A sample ``gunicorn.conf.py`` configuration file for Gunicorn can be found in
|
||||
the OpenERP server source tree. It is fairly well commented and easily
|
||||
A sample ``openerp-wsgi.py`` configuration file for WSGI servers can be found
|
||||
in the OpenERP server source tree. It is fairly well commented and easily
|
||||
customizable for your own usage. While reading the remaining of this page, it
|
||||
is advised you take a look at the sample ``gunicorn.conf.py`` file as it makes
|
||||
is advised you take a look at the sample ``openerp-wsgi.py`` file as it makes
|
||||
things easier to follow.
|
||||
|
||||
Configuration
|
||||
|
@ -37,7 +37,7 @@ Configuration
|
|||
|
||||
Gunicorn can be configured by a configuration file and/or command-line
|
||||
arguments. For a list of available options, you can refer to the official
|
||||
Gunicorn documentation http://gunicorn.org/configure.html.
|
||||
Gunicorn documentation http://docs.gunicorn.org/en/latest/configure.html.
|
||||
|
||||
When the OpenERP server is started on its own, by using the ``openerp-server``
|
||||
script, it can also be configured by a configuration file or its command-line
|
||||
|
@ -46,7 +46,7 @@ as the Gunicorn configuration file is a full-fledged Python file, we can
|
|||
``import openerp`` in it and configure directly the server.
|
||||
|
||||
The principle can be summarized with this three lines (although they are spread
|
||||
across the whole sample ``gunicorn.conf.py`` file)::
|
||||
across the whole sample ``openerp-wsgi.py`` file)::
|
||||
|
||||
import openerp
|
||||
conf = openerp.tools.config
|
||||
|
@ -57,45 +57,16 @@ containing the OpenERP server implementation). The second one is really to
|
|||
shorten repeated usage of the same variable. The third one sets a parameter, in
|
||||
this case the equivalent of the ``--addons-path`` command-line option.
|
||||
|
||||
Finally, Gunicorn offers a few hooks so we can call our own code at some points
|
||||
in its execution. The most important one is the ``on_starting`` hook. It lets
|
||||
us properly initialize the ``openerp`` library before Gunicorn starts handling
|
||||
requests. ``pre_request`` and ``post_request`` are called before and after
|
||||
requests are handled. We provide functions in ``openerp.wsgi.core`` that can be
|
||||
used to define those hooks: a typical Gunicorn configuration for OpenERP will
|
||||
thus contains::
|
||||
|
||||
on_starting = openerp.wsgi.core.on_starting
|
||||
pre_request = openerp.wsgi.core.pre_request
|
||||
post_request = openerp.wsgi.core.post_request
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
Once a proper configuration file is available, running the OpenERP server with
|
||||
Gunicorn can be done with the following command::
|
||||
|
||||
> gunicorn openerp:wsgi.core.application -c gunicorn.conf.py
|
||||
> gunicorn openerp:service.wsgi_server.application -c openerp-wsgi.py
|
||||
|
||||
``openerp`` must be importable by Python. The simplest way is to run the above
|
||||
command from the server source directory (i.e. the directory containing the
|
||||
``openerp`` module). Alternatively, the module can be installed on your machine
|
||||
as a regular Python library or added to your ``PYTHONPATH``.
|
||||
|
||||
Running behind a reverse proxy
|
||||
------------------------------
|
||||
|
||||
If you intend to run Gunicorn behind a reverse proxy (nginx_ is recommended),
|
||||
an alternative entry point is available in ``openerp.wsgi.proxied``. That entry
|
||||
point uses werkzeug's ProxyFix_ class to set a few headers. You first have to
|
||||
explicitely import that sub-module if you want to use it. So add this line in
|
||||
the configuration file::
|
||||
|
||||
import openerp.wsgi.proxied
|
||||
|
||||
and then adapt the command-line::
|
||||
|
||||
> gunicorn openerp:wsgi.proxied.application -c gunicorn.conf.py
|
||||
|
||||
.. _nginx: http://nginx.org/en/
|
||||
.. _ProxyFix: http://werkzeug.pocoo.org/docs/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix
|
|
@ -16,7 +16,17 @@ OpenERP Server
|
|||
04_security
|
||||
05_test_framework
|
||||
06_misc
|
||||
09_deployment
|
||||
deployment-gunicorn
|
||||
|
||||
OpenERP Command
|
||||
'''''''''''''''
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
openerp-command.rst
|
||||
commands.rst
|
||||
adding-command.rst
|
||||
|
||||
OpenERP Server API
|
||||
''''''''''''''''''
|
||||
|
@ -24,7 +34,6 @@ OpenERP Server API
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api_core.rst
|
||||
api_models.rst
|
||||
|
||||
Concepts
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
.. _openerp-command:
|
||||
|
||||
The ``oe`` script
|
||||
=================
|
||||
|
||||
The ``oe`` script provides a set of command-line tools around the OpenERP
|
||||
framework. It is meant to replace the older ``openerp-server`` script (which
|
||||
is still available).
|
||||
|
||||
Using ``oe``
|
||||
------------
|
||||
|
||||
In contrast to the previous ``openerp-server`` script, ``oe`` defines a few
|
||||
commands, each with its own set of flags and options. You can get some
|
||||
information for any of them with
|
||||
|
||||
::
|
||||
|
||||
> oe <command> --help
|
||||
|
||||
For instance::
|
||||
|
||||
> oe run-tests --help
|
||||
|
||||
Some ``oe`` options can be provided via environment variables. For instance::
|
||||
|
||||
> export OPENERP_DATABASE=trunk
|
||||
> export OPENERP_HOST=127.0.0.1
|
||||
> export OPENERP_PORT=8069
|
||||
|
||||
Depending on your needs, you can group all of the above in one single script;
|
||||
for instance here is a, say, ``test-trunk-view-validation.sh`` file::
|
||||
|
||||
COMMAND_REPO=/home/thu/repos/command/trunk/
|
||||
SERVER_REPO=/home/thu/repos/server/trunk
|
||||
|
||||
export PYTHONPATH=$SERVER_REPO:$COMMAND_REPO
|
||||
export PATH=$SERVER_REPO:$COMMAND_REPO:$PATH
|
||||
export OPENERP_DATABASE=trunk
|
||||
export OPENERP_HOST=127.0.0.1
|
||||
export OPENERP_PORT=8069
|
||||
|
||||
# The -d ignored is actually needed by `oe` even though `test_view_validation`
|
||||
# itself does not need it.
|
||||
oe run-tests -d ignored -m openerp.test_view_validation
|
||||
|
||||
Available commands
|
||||
-------------------
|
||||
|
||||
See the :doc:`commands` page.
|
||||
|
||||
Adding new commands
|
||||
-------------------
|
||||
|
||||
See the :doc:`adding-command` page.
|
||||
|
||||
Bash completion
|
||||
---------------
|
||||
|
||||
A preliminary ``oe-bash-completion`` file is provided. After sourcing it,
|
||||
|
||||
::
|
||||
|
||||
> . oe-bash-completion
|
||||
|
||||
completion (using the TAB character) in Bash should work.
|
|
@ -0,0 +1,5 @@
|
|||
#! /usr/bin/env python2
|
||||
|
||||
if __name__ == '__main__':
|
||||
import openerpcommand.main
|
||||
openerpcommand.main.run()
|
|
@ -0,0 +1,89 @@
|
|||
_oe()
|
||||
{
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
cmd="${COMP_WORDS[0]}"
|
||||
subcmd=""
|
||||
if [[ ${COMP_CWORD} > 0 ]] ; then
|
||||
subcmd="${COMP_WORDS[1]}"
|
||||
fi
|
||||
|
||||
# oe
|
||||
|
||||
opts="initialize model read run-tests scaffold update \
|
||||
call open show consume-nothing consume-memory leak-memory \
|
||||
consume-cpu bench-read bench-fields-view-get bench-dummy bench-login \
|
||||
bench-sale-mrp --help"
|
||||
|
||||
if [[ ${prev} == oe && ${cur} != -* ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe call
|
||||
|
||||
opts="--database --user --password --host --port --help"
|
||||
|
||||
if [[ ${subcmd} == call ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe initialize
|
||||
|
||||
opts="--database --addons --all-modules --exclude --no-create --help"
|
||||
|
||||
if [[ ${subcmd} == initialize ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe model
|
||||
|
||||
opts="--database --model --field --verbose --help"
|
||||
|
||||
if [[ ${subcmd} == model ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe read
|
||||
|
||||
opts="--database --model --id --field --verbose --short --help"
|
||||
|
||||
if [[ ${subcmd} == read ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe run-tests
|
||||
|
||||
opts="--database --addons --module --dry-run --help"
|
||||
|
||||
if [[ ${subcmd} == run-tests ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe scaffold
|
||||
|
||||
opts="--help"
|
||||
|
||||
if [[ ${subcmd} == scaffold ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# fallback for unimplemented completion
|
||||
|
||||
opts="--help"
|
||||
|
||||
if [[ true ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
complete -F _oe oe
|
|
@ -51,6 +51,8 @@ wsgi.register_wsgi_handler = wsgi.wsgi_server.register_wsgi_handler
|
|||
# its own copy of the data structure and we don't need to care about
|
||||
# locks between threads.
|
||||
multi_process = False
|
||||
# Is the server running with gevent.
|
||||
evented = False
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010, 2012 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.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
|
||||
|
@ -80,6 +80,7 @@ The kernel of OpenERP, needed for all installation.
|
|||
'res/res_bank_view.xml',
|
||||
'res/res_country_view.xml',
|
||||
'res/res_currency_view.xml',
|
||||
'res/wizard/change_password_wizard_view.xml',
|
||||
'res/res_users_view.xml',
|
||||
'res/res_partner_data.xml',
|
||||
'res/ir_property_view.xml',
|
||||
|
@ -93,19 +94,17 @@ The kernel of OpenERP, needed for all installation.
|
|||
'res/res_partner_image_demo.xml',
|
||||
],
|
||||
'test': [
|
||||
'test/base_test.xml',
|
||||
'test/base_test.yml',
|
||||
'test/test_context.xml',
|
||||
'test/bug_lp541545.xml',
|
||||
'test/test_osv_expression.yml',
|
||||
'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
|
||||
# Commented because this takes some time.
|
||||
# This must be (un)commented with the corresponding import statement
|
||||
# in test/__init__.py.
|
||||
# 'test/test_ir_cron.yml', # <-- These tests perform a roolback.
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'css': ['static/src/css/modules.css'],
|
||||
'js': [
|
||||
'static/src/js/apps.js',
|
||||
],
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
<record id="main_company" model="res.company">
|
||||
<field name="name">Your Company</field>
|
||||
<field name="partner_id" ref="main_partner"/>
|
||||
<field name="rml_header1">Your Company Slogan</field>
|
||||
<field name="rml_header1">Your Company Tagline</field>
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
</record>
|
||||
|
||||
|
@ -89,6 +89,13 @@ Administrator</field>
|
|||
<record id="EUR" model="res.currency">
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="ir_mail_server_localhost0" model="ir.mail_server">
|
||||
<field name="name">localhost</field>
|
||||
<field name="smtp_host">localhost</field>
|
||||
<field eval="25" name="smtp_port"/>
|
||||
<field eval="10" name="sequence"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -510,14 +510,6 @@
|
|||
<field name="currency_id" ref="CRC"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="ir_mail_server_localhost0" model="ir.mail_server">
|
||||
<field name="name">localhost</field>
|
||||
<field name="smtp_host">localhost</field>
|
||||
<field eval="25" name="smtp_port"/>
|
||||
<field eval="10" name="sequence"/>
|
||||
</record>
|
||||
|
||||
<record id="MUR" model="res.currency">
|
||||
<field name="name">MUR</field>
|
||||
<field name="symbol">Rs</field>
|
||||
|
@ -1977,6 +1969,7 @@
|
|||
<field name="symbol">£</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="position">before</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateGBP" model="res.currency.rate">
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -22,17 +22,16 @@
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import tools
|
||||
|
||||
import netsvc
|
||||
from osv import fields,osv
|
||||
from report.report_sxw import report_sxw, report_rml
|
||||
from tools.config import config
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from tools.translate import _
|
||||
from socket import gethostname
|
||||
import time
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import netsvc, tools
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.report.report_sxw import report_sxw, report_rml
|
||||
from openerp.tools.config import config
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -615,16 +614,6 @@ class actions_server(osv.osv):
|
|||
|
||||
if action.state == 'email':
|
||||
email_from = config['email_from']
|
||||
address = str(action.email)
|
||||
try:
|
||||
address = eval(str(action.email), cxt)
|
||||
except:
|
||||
pass
|
||||
|
||||
if not address:
|
||||
_logger.info('No partner email address specified, not sending any email.')
|
||||
continue
|
||||
|
||||
if not email_from:
|
||||
_logger.debug('--email-from command line option is not specified, using a fallback value instead.')
|
||||
if user.email:
|
||||
|
@ -632,16 +621,27 @@ class actions_server(osv.osv):
|
|||
else:
|
||||
email_from = "%s@%s" % (user.login, gethostname())
|
||||
|
||||
try:
|
||||
address = eval(str(action.email), cxt)
|
||||
except Exception:
|
||||
address = str(action.email)
|
||||
|
||||
if not address:
|
||||
_logger.info('No partner email address specified, not sending any email.')
|
||||
continue
|
||||
|
||||
# handle single and multiple recipient addresses
|
||||
addresses = address if isinstance(address, (tuple, list)) else [address]
|
||||
subject = self.merge_message(cr, uid, action.subject, action, context)
|
||||
body = self.merge_message(cr, uid, action.message, action, context)
|
||||
|
||||
ir_mail_server = self.pool.get('ir.mail_server')
|
||||
msg = ir_mail_server.build_email(email_from, [address], subject, body)
|
||||
msg = ir_mail_server.build_email(email_from, addresses, subject, body)
|
||||
res_email = ir_mail_server.send_email(cr, uid, msg)
|
||||
if res_email:
|
||||
_logger.info('Email successfully sent to: %s', address)
|
||||
_logger.info('Email successfully sent to: %s', addresses)
|
||||
else:
|
||||
_logger.warning('Failed to send email to: %s', address)
|
||||
_logger.warning('Failed to send email to: %s', addresses)
|
||||
|
||||
if action.state == 'trigger':
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
|
|
|
@ -19,13 +19,165 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import hashlib
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
|
||||
from osv import fields,osv
|
||||
from osv.orm import except_orm
|
||||
import tools
|
||||
from openerp import tools
|
||||
from openerp.osv import fields,osv
|
||||
|
||||
class ir_attachment(osv.osv):
|
||||
"""Attachments are used to link binary files or url to any openerp document.
|
||||
|
||||
External attachment storage
|
||||
---------------------------
|
||||
|
||||
The 'data' function field (_data_get,data_set) is implemented using
|
||||
_file_read, _file_write and _file_delete which can be overridden to
|
||||
implement other storage engines, shuch methods should check for other
|
||||
location pseudo uri (example: hdfs://hadoppserver)
|
||||
|
||||
The default implementation is the file:dirname location that stores files
|
||||
on the local filesystem using name based on their sha1 hash
|
||||
"""
|
||||
def _name_get_resname(self, cr, uid, ids, object, method, context):
|
||||
data = {}
|
||||
for attachment in self.browse(cr, uid, ids, context=context):
|
||||
model_object = attachment.res_model
|
||||
res_id = attachment.res_id
|
||||
if model_object and res_id:
|
||||
model_pool = self.pool.get(model_object)
|
||||
res = model_pool.name_get(cr,uid,[res_id],context)
|
||||
res_name = res and res[0][1] or False
|
||||
if res_name:
|
||||
field = self._columns.get('res_name',False)
|
||||
if field and len(res_name) > field.size:
|
||||
res_name = res_name[:field.size-3] + '...'
|
||||
data[attachment.id] = res_name
|
||||
else:
|
||||
data[attachment.id] = False
|
||||
return data
|
||||
|
||||
# 'data' field implementation
|
||||
def _full_path(self, cr, uid, location, path):
|
||||
# location = 'file:filestore'
|
||||
assert location.startswith('file:'), "Unhandled filestore location %s" % location
|
||||
location = location[5:]
|
||||
|
||||
# sanitize location name and path
|
||||
location = re.sub('[.]','',location)
|
||||
location = location.strip('/\\')
|
||||
|
||||
path = re.sub('[.]','',path)
|
||||
path = path.strip('/\\')
|
||||
return os.path.join(tools.config['root_path'], location, cr.dbname, path)
|
||||
|
||||
def _file_read(self, cr, uid, location, fname, bin_size=False):
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
r = ''
|
||||
try:
|
||||
if bin_size:
|
||||
r = os.path.getsize(full_path)
|
||||
else:
|
||||
r = open(full_path).read().encode('base64')
|
||||
except IOError:
|
||||
_logger.error("_read_file reading %s",full_path)
|
||||
return r
|
||||
|
||||
def _file_write(self, cr, uid, location, value):
|
||||
bin_value = value.decode('base64')
|
||||
fname = hashlib.sha1(bin_value).hexdigest()
|
||||
# scatter files across 1024 dirs
|
||||
# we use '/' in the db (even on windows)
|
||||
fname = fname[:3] + '/' + fname
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
try:
|
||||
dirname = os.path.dirname(full_path)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
open(full_path,'wb').write(bin_value)
|
||||
except IOError:
|
||||
_logger.error("_file_write writing %s",full_path)
|
||||
return fname
|
||||
|
||||
def _file_delete(self, cr, uid, location, fname):
|
||||
count = self.search(cr, 1, [('store_fname','=',fname)], count=True)
|
||||
if count <= 1:
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
try:
|
||||
os.unlink(full_path)
|
||||
except OSError:
|
||||
_logger.error("_file_delete could not unlink %s",full_path)
|
||||
except IOError:
|
||||
# Harmless and needed for race conditions
|
||||
_logger.error("_file_delete could not unlink %s",full_path)
|
||||
|
||||
def _data_get(self, cr, uid, ids, name, arg, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
result = {}
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
bin_size = context.get('bin_size')
|
||||
for attach in self.browse(cr, uid, ids, context=context):
|
||||
if location and attach.store_fname:
|
||||
result[attach.id] = self._file_read(cr, uid, location, attach.store_fname, bin_size)
|
||||
else:
|
||||
result[attach.id] = attach.db_datas
|
||||
return result
|
||||
|
||||
def _data_set(self, cr, uid, id, name, value, arg, context=None):
|
||||
# We dont handle setting data to null
|
||||
if not value:
|
||||
return True
|
||||
if context is None:
|
||||
context = {}
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
file_size = len(value.decode('base64'))
|
||||
if location:
|
||||
attach = self.browse(cr, uid, id, context=context)
|
||||
if attach.store_fname:
|
||||
self._file_delete(cr, uid, location, attach.store_fname)
|
||||
fname = self._file_write(cr, uid, location, value)
|
||||
super(ir_attachment, self).write(cr, uid, [id], {'store_fname': fname, 'file_size': file_size}, context=context)
|
||||
else:
|
||||
super(ir_attachment, self).write(cr, uid, [id], {'db_datas': value, 'file_size': file_size}, context=context)
|
||||
return True
|
||||
|
||||
_name = 'ir.attachment'
|
||||
_columns = {
|
||||
'name': fields.char('Attachment Name',size=256, required=True),
|
||||
'datas_fname': fields.char('File Name',size=256),
|
||||
'description': fields.text('Description'),
|
||||
'res_name': fields.function(_name_get_resname, type='char', size=128, string='Resource Name', store=True),
|
||||
'res_model': fields.char('Resource Model',size=64, readonly=True, help="The database object this attachment will be attached to"),
|
||||
'res_id': fields.integer('Resource ID', readonly=True, help="The record id this is attached to"),
|
||||
'create_date': fields.datetime('Date Created', readonly=True),
|
||||
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', change_default=True),
|
||||
'type': fields.selection( [ ('url','URL'), ('binary','Binary'), ],
|
||||
'Type', help="Binary File or URL", required=True, change_default=True),
|
||||
'url': fields.char('Url', size=1024),
|
||||
# al: We keep shitty field names for backward compatibility with document
|
||||
'datas': fields.function(_data_get, fnct_inv=_data_set, string='File Content', type="binary", nodrop=True),
|
||||
'store_fname': fields.char('Stored Filename', size=256),
|
||||
'db_datas': fields.binary('Database Data'),
|
||||
'file_size': fields.integer('File Size'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'type': 'binary',
|
||||
'file_size': 0,
|
||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(ir_attachment, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
|
||||
cr.commit()
|
||||
|
||||
def check(self, cr, uid, ids, mode, context=None, values=None):
|
||||
"""Restricts the access to an ir.attachment, according to referred model
|
||||
In the 'document' module, it is overriden to relax this hard rule, since
|
||||
|
@ -111,6 +263,8 @@ class ir_attachment(osv.osv):
|
|||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
self.check(cr, uid, ids, 'write', context=context, values=vals)
|
||||
if 'file_size' in vals:
|
||||
del vals['file_size']
|
||||
return super(ir_attachment, self).write(cr, uid, ids, vals, context)
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
|
@ -119,70 +273,22 @@ class ir_attachment(osv.osv):
|
|||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
self.check(cr, uid, ids, 'unlink', context=context)
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
if location:
|
||||
for attach in self.browse(cr, uid, ids, context=context):
|
||||
if attach.store_fname:
|
||||
self._file_delete(cr, uid, location, attach.store_fname)
|
||||
return super(ir_attachment, self).unlink(cr, uid, ids, context)
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
self.check(cr, uid, [], mode='create', context=context, values=values)
|
||||
if 'file_size' in values:
|
||||
del values['file_size']
|
||||
return super(ir_attachment, self).create(cr, uid, values, context)
|
||||
|
||||
def action_get(self, cr, uid, context=None):
|
||||
return self.pool.get('ir.actions.act_window').for_xml_id(
|
||||
cr, uid, 'base', 'action_attachment', context=context)
|
||||
|
||||
def _name_get_resname(self, cr, uid, ids, object, method, context):
|
||||
data = {}
|
||||
for attachment in self.browse(cr, uid, ids, context=context):
|
||||
model_object = attachment.res_model
|
||||
res_id = attachment.res_id
|
||||
if model_object and res_id:
|
||||
model_pool = self.pool.get(model_object)
|
||||
res = model_pool.name_get(cr,uid,[res_id],context)
|
||||
res_name = res and res[0][1] or False
|
||||
if res_name:
|
||||
field = self._columns.get('res_name',False)
|
||||
if field and len(res_name) > field.size:
|
||||
res_name = res_name[:field.size-3] + '...'
|
||||
data[attachment.id] = res_name
|
||||
else:
|
||||
data[attachment.id] = False
|
||||
return data
|
||||
|
||||
_name = 'ir.attachment'
|
||||
_columns = {
|
||||
'name': fields.char('Attachment Name',size=256, required=True),
|
||||
'datas': fields.binary('Data'),
|
||||
'datas_fname': fields.char('File Name',size=256),
|
||||
'description': fields.text('Description'),
|
||||
'res_name': fields.function(_name_get_resname, type='char', size=128,
|
||||
string='Resource Name', store=True),
|
||||
'res_model': fields.char('Resource Object',size=64, readonly=True,
|
||||
help="The database object this attachment will be attached to"),
|
||||
'res_id': fields.integer('Resource ID', readonly=True,
|
||||
help="The record id this is attached to"),
|
||||
'url': fields.char('Url', size=512, oldname="link"),
|
||||
'type': fields.selection(
|
||||
[ ('url','URL'), ('binary','Binary'), ],
|
||||
'Type', help="Binary File or external URL", required=True, change_default=True),
|
||||
|
||||
'create_date': fields.datetime('Date Created', readonly=True),
|
||||
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', change_default=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'type': 'binary',
|
||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(ir_attachment, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
|
||||
cr.commit()
|
||||
|
||||
ir_attachment()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -22,11 +22,12 @@
|
|||
Store database-specific configuration parameters
|
||||
"""
|
||||
|
||||
from osv import osv,fields
|
||||
import uuid
|
||||
import datetime
|
||||
from tools import misc, config
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools import misc, config
|
||||
|
||||
"""
|
||||
A dictionary holding some configuration parameters to be initialized when the database is created.
|
||||
|
|
|
@ -18,23 +18,18 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import calendar
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import psycopg2
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
import netsvc
|
||||
import openerp
|
||||
import pooler
|
||||
import tools
|
||||
from osv import fields, osv
|
||||
from tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from tools.translate import _
|
||||
from openerp import netsvc
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -140,13 +135,18 @@ class ir_cron(osv.osv):
|
|||
_logger.debug('%.3fs (%s, %s)' % (end_time - start_time, model_name, method_name))
|
||||
except Exception, e:
|
||||
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
|
||||
else:
|
||||
msg = "Method `%s.%s` do not exist." % (model._name, method_name) \
|
||||
if model else "Model `%s` do not exist." % model._name
|
||||
_logger.warning(msg)
|
||||
|
||||
def _process_job(self, cr, job):
|
||||
def _process_job(self, job_cr, job, cron_cr):
|
||||
""" Run a given job taking care of the repetition.
|
||||
|
||||
The cursor has a lock on the job (aquired by _acquire_job()).
|
||||
|
||||
:param job_cr: cursor to use to execute the job, safe to commit/rollback
|
||||
:param job: job to be run (as a dictionary).
|
||||
:param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
|
||||
must not be committed/rolled back!
|
||||
"""
|
||||
try:
|
||||
now = datetime.now()
|
||||
|
@ -158,19 +158,19 @@ class ir_cron(osv.osv):
|
|||
if numbercall > 0:
|
||||
numbercall -= 1
|
||||
if not ok or job['doall']:
|
||||
self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
|
||||
self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
|
||||
if numbercall:
|
||||
nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
|
||||
ok = True
|
||||
addsql = ''
|
||||
if not numbercall:
|
||||
addsql = ', active=False'
|
||||
cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
|
||||
cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
|
||||
(nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
|
||||
|
||||
finally:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
job_cr.commit()
|
||||
cron_cr.commit()
|
||||
|
||||
@classmethod
|
||||
def _acquire_job(cls, db_name):
|
||||
|
@ -186,44 +186,14 @@ class ir_cron(osv.osv):
|
|||
"""
|
||||
db = openerp.sql_db.db_connect(db_name)
|
||||
cr = db.cursor()
|
||||
jobs = []
|
||||
try:
|
||||
# Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
|
||||
cr.execute("""SELECT * FROM ir_cron
|
||||
WHERE numbercall != 0
|
||||
AND active AND nextcall <= (now() at time zone 'UTC')
|
||||
ORDER BY priority""")
|
||||
for job in cr.dictfetchall():
|
||||
task_cr = db.cursor()
|
||||
try:
|
||||
# Try to grab an exclusive lock on the job row from within the task transaction
|
||||
acquired_lock = False
|
||||
task_cr.execute("""SELECT *
|
||||
FROM ir_cron
|
||||
WHERE id=%s
|
||||
FOR UPDATE NOWAIT""",
|
||||
(job['id'],), log_exceptions=False)
|
||||
acquired_lock = True
|
||||
except psycopg2.OperationalError, e:
|
||||
if e.pgcode == '55P03':
|
||||
# Class 55: Object not in prerequisite state; 55P03: lock_not_available
|
||||
_logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
|
||||
continue
|
||||
else:
|
||||
# Unexpected OperationalError
|
||||
raise
|
||||
finally:
|
||||
if not acquired_lock:
|
||||
# we're exiting due to an exception while acquiring the lot
|
||||
task_cr.close()
|
||||
|
||||
# Got the lock on the job row, run its code
|
||||
_logger.debug('Starting job `%s`.', job['name'])
|
||||
openerp.modules.registry.RegistryManager.check_registry_signaling(db_name)
|
||||
registry = openerp.pooler.get_pool(db_name)
|
||||
registry[cls._name]._process_job(task_cr, job)
|
||||
openerp.modules.registry.RegistryManager.signal_caches_change(db_name)
|
||||
return True
|
||||
|
||||
jobs = cr.dictfetchall()
|
||||
except psycopg2.ProgrammingError, e:
|
||||
if e.pgcode == '42P01':
|
||||
# Class 42 — Syntax Error or Access Rule Violation; 42P01: undefined_table
|
||||
|
@ -233,12 +203,43 @@ class ir_cron(osv.osv):
|
|||
raise
|
||||
except Exception:
|
||||
_logger.warning('Exception in cron:', exc_info=True)
|
||||
|
||||
finally:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
return False
|
||||
for job in jobs:
|
||||
lock_cr = db.cursor()
|
||||
try:
|
||||
# Try to grab an exclusive lock on the job row from within the task transaction
|
||||
lock_cr.execute("""SELECT *
|
||||
FROM ir_cron
|
||||
WHERE id=%s
|
||||
FOR UPDATE NOWAIT""",
|
||||
(job['id'],), log_exceptions=False)
|
||||
|
||||
# Got the lock on the job row, run its code
|
||||
_logger.debug('Starting job `%s`.', job['name'])
|
||||
job_cr = db.cursor()
|
||||
try:
|
||||
openerp.modules.registry.RegistryManager.check_registry_signaling(db_name)
|
||||
registry = openerp.pooler.get_pool(db_name)
|
||||
registry[cls._name]._process_job(job_cr, job, lock_cr)
|
||||
openerp.modules.registry.RegistryManager.signal_caches_change(db_name)
|
||||
except Exception:
|
||||
_logger.exception('Unexpected exception while processing cron job %r', job)
|
||||
finally:
|
||||
job_cr.close()
|
||||
|
||||
except psycopg2.OperationalError, e:
|
||||
if e.pgcode == '55P03':
|
||||
# Class 55: Object not in prerequisite state; 55P03: lock_not_available
|
||||
_logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
|
||||
continue
|
||||
else:
|
||||
# Unexpected OperationalError
|
||||
raise
|
||||
finally:
|
||||
# we're exiting due to an exception while acquiring the lock
|
||||
lock_cr.close()
|
||||
|
||||
def _try_lock(self, cr, uid, ids, context=None):
|
||||
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<record id="ir_cron_view_tree" model="ir.ui.view">
|
||||
<field name="model">ir.cron</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Scheduled Actions">
|
||||
<tree string="Scheduled Actions" colors="grey:(not active)">
|
||||
<field name="priority" string="Sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="nextcall"/>
|
||||
|
@ -48,6 +48,7 @@
|
|||
<field name="interval_type"/>
|
||||
<field name="numbercall"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class ir_default(osv.osv):
|
||||
_name = 'ir.default'
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from openerp.osv import fields,osv
|
||||
|
||||
|
||||
class ir_exports(osv.osv):
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import collections
|
||||
import datetime
|
||||
import functools
|
||||
import operator
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
##############################################################################
|
||||
|
||||
from openerp import exceptions
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class ir_filters(osv.osv):
|
||||
_name = 'ir.filters'
|
||||
|
|
|
@ -31,8 +31,7 @@ import re
|
|||
import smtplib
|
||||
import threading
|
||||
|
||||
from osv import osv
|
||||
from osv import fields
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import html2text
|
||||
import openerp.tools as tools
|
||||
|
@ -183,10 +182,12 @@ class ir_mail_server(osv.osv):
|
|||
"(this is very verbose and may include confidential info!)"),
|
||||
'sequence': fields.integer('Priority', help="When no specific mail server is requested for a mail, the highest priority one "
|
||||
"is used. Default priority is 10 (smaller number = higher priority)"),
|
||||
'active': fields.boolean('Active')
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'smtp_port': 25,
|
||||
'active': True,
|
||||
'sequence': 10,
|
||||
'smtp_encryption': 'none',
|
||||
}
|
||||
|
@ -369,7 +370,7 @@ class ir_mail_server(osv.osv):
|
|||
return msg
|
||||
|
||||
def send_email(self, cr, uid, message, mail_server_id=None, smtp_server=None, smtp_port=None,
|
||||
smtp_user=None, smtp_password=None, smtp_encryption='none', smtp_debug=False,
|
||||
smtp_user=None, smtp_password=None, smtp_encryption=None, smtp_debug=False,
|
||||
context=None):
|
||||
"""Sends an email directly (no queuing).
|
||||
|
||||
|
@ -387,7 +388,7 @@ class ir_mail_server(osv.osv):
|
|||
extracted from the combined list of ``To``, ``CC`` and ``BCC`` headers.
|
||||
:param mail_server_id: optional id of ir.mail_server to use for sending. overrides other smtp_* arguments.
|
||||
:param smtp_server: optional hostname of SMTP server to use
|
||||
:param smtp_encryption: one of 'none', 'starttls' or 'ssl' (see ir.mail_server fields for explanation)
|
||||
:param smtp_encryption: optional TLS mode, one of 'none', 'starttls' or 'ssl' (see ir.mail_server fields for explanation)
|
||||
:param smtp_port: optional SMTP port, if mail_server_id is not passed
|
||||
:param smtp_user: optional SMTP user, if mail_server_id is not passed
|
||||
:param smtp_password: optional SMTP password to use, if mail_server_id is not passed
|
||||
|
@ -421,12 +422,6 @@ class ir_mail_server(osv.osv):
|
|||
mail_server_ids = self.search(cr, uid, [], order='sequence', limit=1)
|
||||
if mail_server_ids:
|
||||
mail_server = self.browse(cr, uid, mail_server_ids[0])
|
||||
else:
|
||||
# we were passed an explicit smtp_server or nothing at all
|
||||
smtp_server = smtp_server or tools.config.get('smtp_server')
|
||||
smtp_port = tools.config.get('smtp_port', 25) if smtp_port is None else smtp_port
|
||||
smtp_user = smtp_user or tools.config.get('smtp_user')
|
||||
smtp_password = smtp_password or tools.config.get('smtp_password')
|
||||
|
||||
if mail_server:
|
||||
smtp_server = mail_server.smtp_host
|
||||
|
@ -435,6 +430,14 @@ class ir_mail_server(osv.osv):
|
|||
smtp_port = mail_server.smtp_port
|
||||
smtp_encryption = mail_server.smtp_encryption
|
||||
smtp_debug = smtp_debug or mail_server.smtp_debug
|
||||
else:
|
||||
# we were passed an explicit smtp_server or nothing at all
|
||||
smtp_server = smtp_server or tools.config.get('smtp_server')
|
||||
smtp_port = tools.config.get('smtp_port', 25) if smtp_port is None else smtp_port
|
||||
smtp_user = smtp_user or tools.config.get('smtp_user')
|
||||
smtp_password = smtp_password or tools.config.get('smtp_password')
|
||||
if smtp_encryption is None and tools.config.get('smtp_ssl'):
|
||||
smtp_encryption = 'starttls' # STARTTLS is the new meaning of the smtp_ssl flag as of v7.0
|
||||
|
||||
if not smtp_server:
|
||||
raise osv.except_osv(
|
||||
|
@ -453,7 +456,7 @@ class ir_mail_server(osv.osv):
|
|||
return message_id
|
||||
|
||||
try:
|
||||
smtp = self.connect(smtp_server, smtp_port, smtp_user, smtp_password, smtp_encryption, smtp_debug)
|
||||
smtp = self.connect(smtp_server, smtp_port, smtp_user, smtp_password, smtp_encryption or False, smtp_debug)
|
||||
smtp.sendmail(smtp_from, smtp_to_list, message.as_string())
|
||||
finally:
|
||||
try:
|
||||
|
|
|
@ -199,7 +199,7 @@ class ir_model(osv.osv):
|
|||
|
||||
def instanciate(self, cr, user, model, context=None):
|
||||
class x_custom_model(osv.osv):
|
||||
pass
|
||||
_custom = True
|
||||
x_custom_model._name = model
|
||||
x_custom_model._module = False
|
||||
a = x_custom_model.create_instance(self.pool, cr)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv
|
||||
from openerp.osv import osv
|
||||
|
||||
|
||||
class ir_needaction_mixin(osv.AbstractModel):
|
||||
|
|
|
@ -18,15 +18,13 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv, expression
|
||||
import time
|
||||
from operator import itemgetter
|
||||
from functools import partial
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from tools.misc import unquote as unquote
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv, expression
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.misc import unquote as unquote
|
||||
|
||||
class ir_rule(osv.osv):
|
||||
_name = 'ir.rule'
|
||||
|
|
|
@ -22,10 +22,9 @@
|
|||
import logging
|
||||
import time
|
||||
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
|
||||
import openerp
|
||||
from openerp.osv import osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import tools
|
||||
import logging
|
||||
|
||||
from openerp import tools
|
||||
import openerp.modules
|
||||
from openerp.osv import fields, osv
|
||||
from tools.translate import _
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
import base64
|
||||
import re
|
||||
import threading
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp import tools
|
||||
import openerp.modules
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
def one_in(setA, setB):
|
||||
|
|
|
@ -19,14 +19,15 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from lxml import etree
|
||||
from tools import graph
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
from tools.view_validation import valid_view
|
||||
import os
|
||||
import logging
|
||||
from lxml import etree
|
||||
import os
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields,osv
|
||||
from openerp.tools import graph
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.view_validation import valid_view
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -50,15 +50,11 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Views">
|
||||
<field name="name" filter_domain="['|', ('name','ilike',self), ('model','ilike',self)]" string="View"/>
|
||||
<filter icon="terp-stock_zoom"
|
||||
string="Search"
|
||||
domain="[('type', '=', 'search')]"/>
|
||||
<filter icon="gtk-indent"
|
||||
string="Tree"
|
||||
domain="[('type', '=', 'tree')]"/>
|
||||
<filter icon="gtk-new"
|
||||
string="Form"
|
||||
domain="[('type', '=','form')]"/>
|
||||
<filter string="Form" domain="[('type', '=','form')]"/>
|
||||
<filter string="Tree" domain="[('type', '=', 'tree')]"/>
|
||||
<filter string="Kanban" domain="[('type', '=', 'kanban')]"/>
|
||||
<filter string="Search" domain="[('type', '=', 'search')]"/>
|
||||
<field name="model"/>
|
||||
<field name="inherit_id"/>
|
||||
<field name="type"/>
|
||||
<group expand="0" string="Group By...">
|
||||
|
|
|
@ -18,11 +18,10 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv,fields
|
||||
from osv.orm import except_orm
|
||||
import pickle
|
||||
from tools.translate import _
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.osv.orm import except_orm
|
||||
|
||||
EXCLUDED_FIELDS = set((
|
||||
'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue