You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
9.4 KiB

8 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Adapted by Nicolas Bessi. Copyright Camptocamp SA
  5. # Based on Florent Xicluna original code. Copyright Wingo SA
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. import logging
  22. import os
  23. import ConfigParser
  24. from lxml import etree
  25. from itertools import chain
  26. from odoo import api, models, fields
  27. from odoo.tools.config import config as system_base_config
  28. from .system_info import get_server_environment
  29. _logger = logging.getLogger(__name__)
  30. try:
  31. from odoo.addons import server_environment_files
  32. _dir = os.path.dirname(server_environment_files.__file__)
  33. except ImportError:
  34. _logger.info('not using server_environment_files for configuration,'
  35. ' no directory found')
  36. _dir = None
  37. # Same dict as RawConfigParser._boolean_states
  38. _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
  39. '0': False, 'no': False, 'false': False, 'off': False}
  40. if not system_base_config.get('running_env', False):
  41. raise Exception(
  42. "The parameter 'running_env' has not be set neither in base config "
  43. "file option -c or in openerprc.\n"
  44. "We strongly recommend against using the rc file but instead use an "
  45. "explicit config file with this content:\n"
  46. "[options]\nrunning_env = dev"
  47. )
  48. ck_path = None
  49. if _dir:
  50. ck_path = os.path.join(_dir, system_base_config['running_env'])
  51. if not os.path.exists(ck_path):
  52. raise Exception(
  53. "Provided server environment does not exist, "
  54. "please add a folder %s" % ck_path
  55. )
  56. def setboolean(obj, attr, _bool=None):
  57. """Replace the attribute with a boolean."""
  58. if _bool is None:
  59. _bool = dict(_boolean_states)
  60. res = _bool[getattr(obj, attr).lower()]
  61. setattr(obj, attr, res)
  62. return res
  63. # Borrowed from MarkupSafe
  64. def _escape(s):
  65. """Convert the characters &<>'" in string s to HTML-safe sequences."""
  66. return (str(s).replace('&', '&amp;')
  67. .replace('>', '&gt;')
  68. .replace('<', '&lt;')
  69. .replace("'", '&#39;')
  70. .replace('"', '&#34;'))
  71. def _listconf(env_path):
  72. """List configuration files in a folder."""
  73. files = [os.path.join(env_path, name)
  74. for name in sorted(os.listdir(env_path))
  75. if name.endswith('.conf')]
  76. return files
  77. def _load_config_from_server_env_files(config_p):
  78. default = os.path.join(_dir, 'default')
  79. running_env = os.path.join(_dir,
  80. system_base_config['running_env'])
  81. if os.path.isdir(default):
  82. conf_files = _listconf(default) + _listconf(running_env)
  83. else:
  84. conf_files = _listconf(running_env)
  85. try:
  86. config_p.read(conf_files)
  87. except Exception as e:
  88. raise Exception('Cannot read config files "%s": %s' % (conf_files, e))
  89. def _load_config_from_rcfile(config_p):
  90. config_p.read(system_base_config.rcfile)
  91. config_p.remove_section('options')
  92. def _load_config():
  93. """Load the configuration and return a ConfigParser instance."""
  94. config_p = ConfigParser.SafeConfigParser()
  95. # options are case-sensitive
  96. config_p.optionxform = str
  97. if _dir:
  98. _load_config_from_server_env_files(config_p)
  99. _load_config_from_rcfile(config_p)
  100. return config_p
  101. serv_config = _load_config()
  102. class _Defaults(dict):
  103. __slots__ = ()
  104. def __setitem__(self, key, value):
  105. def func(*a):
  106. return str(value)
  107. return dict.__setitem__(self, key, func)
  108. class ServerConfiguration(models.TransientModel):
  109. """Display server configuration."""
  110. _name = 'server.config'
  111. _conf_defaults = _Defaults()
  112. @classmethod
  113. def _build_model(cls, pool, cr):
  114. """Add columns to model dynamically
  115. and init some properties
  116. """
  117. ModelClass = super(ServerConfiguration, cls)._build_model(pool, cr)
  118. ModelClass._add_columns()
  119. ModelClass.running_env = system_base_config['running_env']
  120. # Only show passwords in development
  121. ModelClass.show_passwords = ModelClass.running_env in ('dev',)
  122. ModelClass._arch = None
  123. ModelClass._build_osv()
  124. return ModelClass
  125. @classmethod
  126. def _format_key(cls, section, key):
  127. return '%s | %s' % (section, key)
  128. @classmethod
  129. def _add_columns(cls):
  130. """Add columns to model dynamically"""
  131. cols = chain(
  132. cls._get_base_cols().items(),
  133. cls._get_env_cols().items(),
  134. cls._get_system_cols().items()
  135. )
  136. for col, value in cols:
  137. col_name = col.replace('.', '_')
  138. setattr(ServerConfiguration,
  139. col_name,
  140. fields.Char(string=col, readonly=True))
  141. cls._conf_defaults[col_name] = value
  142. @classmethod
  143. def _get_base_cols(cls):
  144. """ Compute base fields"""
  145. res = {}
  146. for col, item in system_base_config.options.items():
  147. key = cls._format_key('odoo', col)
  148. res[key] = item
  149. return res
  150. @classmethod
  151. def _get_env_cols(cls, sections=None):
  152. """ Compute base fields"""
  153. res = {}
  154. sections = sections if sections else serv_config.sections()
  155. for section in sections:
  156. for col, item in serv_config.items(section):
  157. key = cls._format_key(section, col)
  158. res[key] = item
  159. return res
  160. @classmethod
  161. def _get_system_cols(cls):
  162. """ Compute system fields"""
  163. res = {}
  164. for col, item in get_server_environment():
  165. key = cls._format_key('system', col)
  166. res[key] = item
  167. return res
  168. @classmethod
  169. def _group(cls, items):
  170. """Return an XML chunk which represents a group of fields."""
  171. names = []
  172. for key in sorted(items):
  173. names.append(key.replace('.', '_'))
  174. return ('<group col="2" colspan="4">' +
  175. ''.join(['<field name="%s" readonly="1"/>' %
  176. _escape(name) for name in names]) +
  177. '</group>')
  178. @classmethod
  179. def _build_osv(cls):
  180. """Build the view for the current configuration."""
  181. arch = ('<?xml version="1.0" encoding="utf-8"?>'
  182. '<form string="Configuration Form">'
  183. '<notebook colspan="4">')
  184. # Odoo server configuration
  185. rcfile = system_base_config.rcfile
  186. items = cls._get_base_cols()
  187. arch += '<page string="Odoo">'
  188. arch += '<separator string="%s" colspan="4"/>' % _escape(rcfile)
  189. arch += cls._group(items)
  190. arch += '<separator colspan="4"/></page>'
  191. arch += '<page string="Environment based configurations">'
  192. for section in sorted(serv_config.sections()):
  193. items = cls._get_env_cols(sections=[section])
  194. arch += '<separator string="[%s]" colspan="4"/>' % _escape(section)
  195. arch += cls._group(items)
  196. arch += '<separator colspan="4"/></page>'
  197. # System information
  198. arch += '<page string="System">'
  199. arch += '<separator string="Server Environment" colspan="4"/>'
  200. arch += cls._group(cls._get_system_cols())
  201. arch += '<separator colspan="4"/></page>'
  202. arch += '</notebook></form>'
  203. cls._arch = etree.fromstring(arch)
  204. @api.model
  205. def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
  206. submenu=False):
  207. """Overwrite the default method to render the custom view."""
  208. res = super(ServerConfiguration, self).fields_view_get(view_id,
  209. view_type,
  210. toolbar)
  211. View = self.env['ir.ui.view']
  212. if view_type == 'form':
  213. arch_node = self._arch
  214. xarch, xfields = View.postprocess_and_fields(
  215. self._name, arch_node, view_id)
  216. res['arch'] = xarch
  217. res['fields'] = xfields
  218. return res
  219. @api.model
  220. def _is_secret(self, key):
  221. """
  222. This method is intended to be inherited to defined which keywords
  223. should be secret.
  224. :return: list of secret keywords
  225. """
  226. secret_keys = ['passw', 'key', 'secret', 'token']
  227. return any(secret_key in key for secret_key in secret_keys)
  228. @api.model
  229. def default_get(self, fields_list):
  230. res = {}
  231. current_user = self.env.user
  232. if not current_user.has_group(
  233. 'server_environment.has_server_configuration_access'):
  234. return res
  235. for key in self._conf_defaults:
  236. if not self.show_passwords and self._is_secret(key=key):
  237. res[key] = '**********'
  238. else:
  239. res[key] = self._conf_defaults[key]()
  240. return res