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.

165 lines
6.0 KiB

  1. # Copyright 2019 Alexandre Díaz <dev@redneboa.es>
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. import base64
  4. import time
  5. from colorsys import rgb_to_hls, hls_to_rgb
  6. from odoo import models, fields, api
  7. from ..utils import image_to_rgb, convert_to_image, n_rgb_to_hex
  8. URL_BASE = '/web_company_color/static/src/scss/'
  9. URL_SCSS_GEN_TEMPLATE = URL_BASE + 'custom_colors.%d.%s.gen.scss'
  10. class ResCompany(models.Model):
  11. _inherit = 'res.company'
  12. SCSS_TEMPLATE = """
  13. .o_main_navbar {
  14. background-color: %(color_navbar_bg)s !important;
  15. color: %(color_navbar_text)s !important;
  16. > .o_menu_brand {
  17. color: %(color_navbar_text)s !important;
  18. &:hover, &:focus, &:active, &:focus:active {
  19. background-color: %(color_navbar_bg_hover)s !important;
  20. }
  21. }
  22. .show {
  23. .dropdown-toggle {
  24. background-color: %(color_navbar_bg_hover)s !important;
  25. }
  26. }
  27. > ul {
  28. > li {
  29. > a, > label {
  30. color: %(color_navbar_text)s !important;
  31. &:hover, &:focus, &:active, &:focus:active {
  32. background-color: %(color_navbar_bg_hover)s !important;
  33. }
  34. }
  35. }
  36. }
  37. }
  38. """
  39. company_colors = fields.Serialized()
  40. color_navbar_bg = fields.Char('Navbar Background Color',
  41. sparse='company_colors')
  42. color_navbar_bg_hover = fields.Char(
  43. 'Navbar Background Color Hover', sparse='company_colors')
  44. color_navbar_text = fields.Char('Navbar Text Color',
  45. sparse='company_colors')
  46. scss_modif_timestamp = fields.Char('SCSS Modif. Timestamp')
  47. @api.model
  48. def create(self, values):
  49. record = super().create(values)
  50. record.scss_create_or_update_attachment()
  51. return record
  52. @api.multi
  53. def unlink(self):
  54. result = super().unlink()
  55. IrAttachmentObj = self.env['ir.attachment']
  56. for record in self:
  57. IrAttachmentObj.sudo().search([
  58. ('url', 'like', '%s%%' % record._scss_get_url_simplified()),
  59. ]).sudo().unlink()
  60. return result
  61. @api.multi
  62. def write(self, values):
  63. if not self.env.context.get('ignore_company_color', False):
  64. fields_to_check = ('color_navbar_bg',
  65. 'color_navbar_bg_hover',
  66. 'color_navbar_text')
  67. if 'logo' in values:
  68. if values['logo']:
  69. _r, _g, _b = image_to_rgb(convert_to_image(values['logo']))
  70. # Make color 10% darker
  71. _h, _l, _s = rgb_to_hls(_r, _g, _b)
  72. _l = max(0, _l - 0.1)
  73. _rd, _gd, _bd = hls_to_rgb(_h, _l, _s)
  74. # Calc. optimal text color (b/w)
  75. # Grayscale human vision perception (Rec. 709 values)
  76. _a = 1 - (0.2126 * _r + 0.7152 * _g + 0.0722 * _b)
  77. values.update({
  78. 'color_navbar_bg': n_rgb_to_hex(_r, _g, _b),
  79. 'color_navbar_bg_hover': n_rgb_to_hex(_rd, _gd, _bd),
  80. 'color_navbar_text': '#000' if _a < 0.5 else '#fff',
  81. })
  82. else:
  83. values.update(self.default_get(fields_to_check))
  84. result = super().write(values)
  85. if any([field in values for field in fields_to_check]):
  86. self.scss_create_or_update_attachment()
  87. else:
  88. result = super().write(values)
  89. return result
  90. @api.multi
  91. def _scss_get_sanitized_values(self):
  92. self.ensure_one()
  93. values = dict(self.company_colors)
  94. values.update({
  95. 'color_navbar_bg': values['color_navbar_bg'] or '$o-brand-odoo',
  96. 'color_navbar_bg_hover': values['color_navbar_bg_hover']
  97. or '$o-navbar-inverse-link-hover-bg',
  98. 'color_navbar_text': values['color_navbar_text'] or '#FFF',
  99. })
  100. return values
  101. @api.multi
  102. def _scss_generate_content(self):
  103. self.ensure_one()
  104. # ir.attachment need files with content to work
  105. if not self.company_colors:
  106. return "// No Web Company Color SCSS Content\n"
  107. return self.SCSS_TEMPLATE % self._scss_get_sanitized_values()
  108. # URL to scss related with this company, without timestamp
  109. # /web_company_color/static/src/scss/custom_colors.<company_id>
  110. def _scss_get_url_simplified(self):
  111. self.ensure_one()
  112. NTEMPLATE = '.'.join(URL_SCSS_GEN_TEMPLATE.split('.')[:2])
  113. return NTEMPLATE % self.id
  114. @api.multi
  115. def scss_get_url(self, timestamp=None):
  116. self.ensure_one()
  117. return URL_SCSS_GEN_TEMPLATE % (self.id,
  118. timestamp or self.scss_modif_timestamp)
  119. @api.multi
  120. def scss_create_or_update_attachment(self):
  121. IrAttachmentObj = self.env['ir.attachment']
  122. modif_timestamp = str(int(time.time())) # One second resolution
  123. for record in self:
  124. datas = base64.b64encode(
  125. record._scss_generate_content().encode('utf-8'))
  126. custom_attachment = IrAttachmentObj.sudo().search([
  127. ('url', 'like', '%s%%' % record._scss_get_url_simplified())
  128. ])
  129. custom_url = record.scss_get_url(timestamp=modif_timestamp)
  130. values = {
  131. 'datas': datas,
  132. 'url': custom_url,
  133. 'name': custom_url,
  134. 'datas_fname': custom_url.split("/")[-1],
  135. }
  136. if custom_attachment:
  137. custom_attachment.sudo().write(values)
  138. else:
  139. values.update({
  140. 'type': 'binary',
  141. 'mimetype': 'text/scss',
  142. })
  143. IrAttachmentObj.sudo().create(values)
  144. self.write({'scss_modif_timestamp': modif_timestamp})
  145. self.env['ir.qweb'].sudo().clear_caches()