Merge branch 'flectra-license-server' into 'master'
[ADD] support for delayed release See merge request flectra-hq/flectra!163
This commit is contained in:
commit
35046ba1b9
@ -2,8 +2,10 @@
|
|||||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
from flectra import api, fields, models, _
|
from flectra import api, fields, models, _
|
||||||
import json
|
import json, base64, datetime, logging
|
||||||
|
from flectra.exceptions import UserError
|
||||||
|
from flectra.addons.web.models.crypt import *
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ResConfigSettings(models.TransientModel):
|
class ResConfigSettings(models.TransientModel):
|
||||||
|
|
||||||
@ -37,6 +39,8 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
external_report_layout = fields.Selection(related="company_id.external_report_layout")
|
external_report_layout = fields.Selection(related="company_id.external_report_layout")
|
||||||
send_statistics = fields.Boolean(
|
send_statistics = fields.Boolean(
|
||||||
"Send Statistics")
|
"Send Statistics")
|
||||||
|
activator_key = fields.Binary('Upload Activation Key')
|
||||||
|
contract_id = fields.Char('Contract ID')
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_values(self):
|
def get_values(self):
|
||||||
@ -72,6 +76,8 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
self.env['ir.config_parameter'].sudo().set_param(
|
self.env['ir.config_parameter'].sudo().set_param(
|
||||||
"base_setup.send_statistics", send_statistics)
|
"base_setup.send_statistics", send_statistics)
|
||||||
self.env.ref('base.res_partner_rule').write({'active': not self.company_share_partner})
|
self.env.ref('base.res_partner_rule').write({'active': not self.company_share_partner})
|
||||||
|
if self.activator_key:
|
||||||
|
self._check_authorization()
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def open_company(self):
|
def open_company(self):
|
||||||
@ -123,3 +129,22 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
'view_id': template.id,
|
'view_id': template.id,
|
||||||
'target': 'new',
|
'target': 'new',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _check_authorization(self):
|
||||||
|
if self.activator_key and self.contract_id:
|
||||||
|
try:
|
||||||
|
set_param = self.env['ir.config_parameter'].sudo().set_param
|
||||||
|
binary = json.loads(base64.decodestring(self.activator_key)).encode('ascii')
|
||||||
|
binary = base64.decodestring(binary)
|
||||||
|
enc = json.dumps(decrypt(binary, self.contract_id))
|
||||||
|
if enc:
|
||||||
|
dt = datetime.datetime.strptime(json.loads(enc),'"%Y-%m-%d %H:%M:%S"')
|
||||||
|
set_param('database.expiration_date', dt)
|
||||||
|
set_param('contract.validity',
|
||||||
|
base64.encodestring(
|
||||||
|
encrypt(json.dumps(str(dt)),
|
||||||
|
str(dt))))
|
||||||
|
except Exception:
|
||||||
|
_logger.info(_('Please double-check your Contract Key!'), exc_info=True)
|
||||||
|
raise UserError(
|
||||||
|
_('Authorization error!') + ' ' + _('Please double-check your Contract Key!'))
|
||||||
|
@ -203,6 +203,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||||
|
<div class="o_setting_right_pane">
|
||||||
|
<label string="Contract Activation" for="activator_key"/>
|
||||||
|
<div class="text-muted">
|
||||||
|
Contract File
|
||||||
|
<field name="activator_key"/>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted">
|
||||||
|
Contract ID
|
||||||
|
<field name="contract_id"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2>System Parameter</h2>
|
<h2>System Parameter</h2>
|
||||||
<div class="row mt16 o_settings_container" id="send_statistics">
|
<div class="row mt16 o_settings_container" id="send_statistics">
|
||||||
|
@ -52,6 +52,7 @@ import requests
|
|||||||
from flectra.tools import config
|
from flectra.tools import config
|
||||||
from flectra import release
|
from flectra import release
|
||||||
from flectra.http import root
|
from flectra.http import root
|
||||||
|
from ..models.crypt import *
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ env.filters["json"] = json.dumps
|
|||||||
BUNDLE_MAXAGE = 60 * 60 * 24 * 7
|
BUNDLE_MAXAGE = 60 * 60 * 24 * 7
|
||||||
|
|
||||||
DBNAME_PATTERN = '^[a-zA-Z0-9][a-zA-Z0-9_.-]+$'
|
DBNAME_PATTERN = '^[a-zA-Z0-9][a-zA-Z0-9_.-]+$'
|
||||||
|
FILENAME = 'licence'
|
||||||
|
EXT = 'key'
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
# Flectra Web helpers
|
# Flectra Web helpers
|
||||||
@ -1861,3 +1864,16 @@ class ReportController(http.Controller):
|
|||||||
@http.route(['/report/check_wkhtmltopdf'], type='json', auth="user")
|
@http.route(['/report/check_wkhtmltopdf'], type='json', auth="user")
|
||||||
def check_wkhtmltopdf(self):
|
def check_wkhtmltopdf(self):
|
||||||
return request.env['ir.actions.report'].get_wkhtmltopdf_state()
|
return request.env['ir.actions.report'].get_wkhtmltopdf_state()
|
||||||
|
|
||||||
|
class LicensingController(http.Controller):
|
||||||
|
@http.route('/flectra/licensing', type='http', auth="user")
|
||||||
|
def download(self, binary='', **kwargs):
|
||||||
|
filename = '%s.%s' % (FILENAME, EXT)
|
||||||
|
content = binary
|
||||||
|
return request.make_response(
|
||||||
|
content,
|
||||||
|
headers=[
|
||||||
|
('Content-Type', 'plain/text' or 'application/octet-stream'),
|
||||||
|
('Content-Disposition', content_disposition(filename))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
40
addons/web/models/crypt.py
Normal file
40
addons/web/models/crypt.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import Crypto.Random
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
# salt size in bytes
|
||||||
|
SALT_SIZE = 16
|
||||||
|
|
||||||
|
# number of iterations in the key generation
|
||||||
|
NUMBER_OF_ITERATIONS = 20
|
||||||
|
|
||||||
|
# the size multiple required for AES
|
||||||
|
AES_MULTIPLE = 16
|
||||||
|
|
||||||
|
|
||||||
|
def generate_key(password, salt, iterations):
|
||||||
|
assert iterations > 0
|
||||||
|
key = str.encode(password) + salt
|
||||||
|
for i in range(iterations):
|
||||||
|
key = hashlib.sha256(key).digest()
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def pad_text(text, multiple):
|
||||||
|
return (text) + (chr((multiple - (len(text) % multiple))) * ((multiple - (len(text) % multiple))))
|
||||||
|
|
||||||
|
|
||||||
|
def unpad_text(padded_text):
|
||||||
|
return padded_text.decode('utf-8')[:-ord(padded_text.decode('utf-8')[-1])]
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt(plaintext, contract_id):
|
||||||
|
salt = Crypto.Random.get_random_bytes(SALT_SIZE)
|
||||||
|
return salt + (AES.new((generate_key(contract_id, salt, NUMBER_OF_ITERATIONS)), AES.MODE_ECB).encrypt(
|
||||||
|
(pad_text(plaintext, AES_MULTIPLE))))
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt(ciphertext, contract_id):
|
||||||
|
salt = ciphertext[0:SALT_SIZE]
|
||||||
|
return unpad_text(
|
||||||
|
AES.new((generate_key(contract_id, salt, NUMBER_OF_ITERATIONS)), AES.MODE_ECB).decrypt(ciphertext[SALT_SIZE:]))
|
@ -2,9 +2,11 @@
|
|||||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
from flectra import models
|
from flectra import models,api
|
||||||
from flectra.http import request
|
from flectra.http import request
|
||||||
|
from .crypt import *
|
||||||
|
|
||||||
import flectra
|
import flectra
|
||||||
|
|
||||||
@ -22,6 +24,16 @@ class Http(models.AbstractModel):
|
|||||||
user = request.env.user
|
user = request.env.user
|
||||||
display_switch_company_menu = user.has_group('base.group_multi_company') and len(user.company_ids) > 1
|
display_switch_company_menu = user.has_group('base.group_multi_company') and len(user.company_ids) > 1
|
||||||
version_info = flectra.service.common.exp_version()
|
version_info = flectra.service.common.exp_version()
|
||||||
|
ir_module_module_ids = self.env['ir.module.module'].sudo().search(
|
||||||
|
[('contract_certificate', '!=', False), ('state', '=', 'installed')])
|
||||||
|
IrConfig = request.env['ir.config_parameter'].sudo()
|
||||||
|
contracted_module_list, is_valid = None, False
|
||||||
|
if ir_module_module_ids:
|
||||||
|
contracted_module_list = str(self.get_contracted_modules(ir_module_module_ids=ir_module_module_ids))
|
||||||
|
is_valid = self.check_validate_date(IrConfig)
|
||||||
|
else:
|
||||||
|
is_valid = True
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"session_id": request.session.sid,
|
"session_id": request.session.sid,
|
||||||
"uid": request.session.uid,
|
"uid": request.session.uid,
|
||||||
@ -38,9 +50,35 @@ class Http(models.AbstractModel):
|
|||||||
"user_companies": {'current_company': (user.company_id.id, user.company_id.name), 'allowed_companies': [(comp.id, comp.name) for comp in user.company_ids]} if display_switch_company_menu else False,
|
"user_companies": {'current_company': (user.company_id.id, user.company_id.name), 'allowed_companies': [(comp.id, comp.name) for comp in user.company_ids]} if display_switch_company_menu else False,
|
||||||
"currencies": self.get_currencies() if request.session.uid else {},
|
"currencies": self.get_currencies() if request.session.uid else {},
|
||||||
"web.base.url": self.env['ir.config_parameter'].sudo().get_param('web.base.url', default=''),
|
"web.base.url": self.env['ir.config_parameter'].sudo().get_param('web.base.url', default=''),
|
||||||
|
'expiration_date' : IrConfig.get_param('database.expiration_date'),
|
||||||
|
'expiration_reason': IrConfig.get_param('database.expiration_reason'),
|
||||||
|
'contracted_module_list': contracted_module_list,
|
||||||
|
'contract_validation':is_valid
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_currencies(self):
|
def get_currencies(self):
|
||||||
Currency = request.env['res.currency']
|
Currency = request.env['res.currency']
|
||||||
currencies = Currency.search([]).read(['symbol', 'position', 'decimal_places'])
|
currencies = Currency.search([]).read(['symbol', 'position', 'decimal_places'])
|
||||||
return { c['id']: {'symbol': c['symbol'], 'position': c['position'], 'digits': [69,c['decimal_places']]} for c in currencies}
|
return { c['id']: {'symbol': c['symbol'], 'position': c['position'], 'digits': [69,c['decimal_places']]} for c in currencies}
|
||||||
|
|
||||||
|
def get_contracted_modules(self, contract_key='', ir_module_module_ids=None):
|
||||||
|
if ir_module_module_ids:
|
||||||
|
contracted_module_list = ir_module_module_ids.mapped('name')
|
||||||
|
contracts = encrypt(json.dumps(contracted_module_list), contract_key)
|
||||||
|
return contracts
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def contract_validate_file(self, contract_id):
|
||||||
|
ir_module_module_ids = self.env['ir.module.module'].sudo().search(
|
||||||
|
[('contract_certificate', '!=', False), ('state', '=', 'installed')])
|
||||||
|
contracts = self.get_contracted_modules(contract_id,ir_module_module_ids)
|
||||||
|
return json.dumps(base64.encodestring(contracts).decode('ascii'))
|
||||||
|
|
||||||
|
def check_validate_date(self, config):
|
||||||
|
exp_date = config.get_param('database.expiration_date')
|
||||||
|
validity = config.get_param('contract.validity')
|
||||||
|
try:
|
||||||
|
decrypt(base64.decodestring(str.encode(validity)), str(exp_date))
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
flectra.define('FlectraLicensing.DialogRegisterContract', function (require) {
|
||||||
|
"use strict";
|
||||||
|
var Dialog = require('web.Dialog');
|
||||||
|
var rpc = require('web.rpc');
|
||||||
|
|
||||||
|
return Dialog.extend({
|
||||||
|
template: 'FlectraLicense.dialog_contract_registration',
|
||||||
|
init: function (parent) {
|
||||||
|
var options = {
|
||||||
|
title: 'Register Contract',
|
||||||
|
size: 'small',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "save",
|
||||||
|
classes: 'btn-success',
|
||||||
|
click: _.bind(this.save, this)
|
||||||
|
},
|
||||||
|
{text: "Cancel", classes: 'btn-danger', close: true}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
this._super(parent, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function () {
|
||||||
|
var contract_id = this.$el.find('#contract_id').val();
|
||||||
|
var self = this;
|
||||||
|
if (!contract_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rpc.query({
|
||||||
|
model: 'ir.http',
|
||||||
|
method: 'contract_validate_file',
|
||||||
|
args: [contract_id]
|
||||||
|
}).done(function (bin) {
|
||||||
|
self.trigger('get_key', {'key': contract_id, 'binary': bin});
|
||||||
|
self.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
@ -11,6 +11,9 @@ var SystrayMenu = require('web.SystrayMenu');
|
|||||||
var UserMenu = require('web.UserMenu');
|
var UserMenu = require('web.UserMenu');
|
||||||
var UserProfile = require('web.UserProfile');
|
var UserProfile = require('web.UserProfile');
|
||||||
var config = require('web.config');
|
var config = require('web.config');
|
||||||
|
var rpc = require('web.rpc');
|
||||||
|
var qweb = core.qweb;
|
||||||
|
var Dialog = require('FlectraLicensing.DialogRegisterContract');
|
||||||
|
|
||||||
return AbstractWebClient.extend({
|
return AbstractWebClient.extend({
|
||||||
events: {
|
events: {
|
||||||
@ -55,6 +58,9 @@ return AbstractWebClient.extend({
|
|||||||
this.systray_menu.setElement(this.$el.parents().find('.oe_systray'));
|
this.systray_menu.setElement(this.$el.parents().find('.oe_systray'));
|
||||||
var systray_menu_loaded = this.systray_menu.start();
|
var systray_menu_loaded = this.systray_menu.start();
|
||||||
|
|
||||||
|
if ((session.expiration_date && session.expiration_reason === 'contract_expire') || !session['contract_validation']) {
|
||||||
|
this.validate_days_of_contract();
|
||||||
|
}
|
||||||
// Start the menu once both systray and user menus are rendered
|
// Start the menu once both systray and user menus are rendered
|
||||||
// to prevent overflows while loading
|
// to prevent overflows while loading
|
||||||
return $.when(systray_menu_loaded, user_menu_loaded).then(function() {
|
return $.when(systray_menu_loaded, user_menu_loaded).then(function() {
|
||||||
@ -198,6 +204,95 @@ return AbstractWebClient.extend({
|
|||||||
this.menu.reflow();
|
this.menu.reflow();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validate_days_of_contract: function () {
|
||||||
|
var today = new moment();
|
||||||
|
var dbexpiration_date = new moment(session.expiration_date);
|
||||||
|
var duration = moment.duration(dbexpiration_date.diff(today));
|
||||||
|
var params = {
|
||||||
|
'difference': Math.round(duration.asDays()),
|
||||||
|
'reason': session.expiration_reason,
|
||||||
|
};
|
||||||
|
this.show_contract_registration(params);
|
||||||
|
},
|
||||||
|
|
||||||
|
show_contract_registration: function (params) {
|
||||||
|
var self = this;
|
||||||
|
var bg_color = params.difference <= 10 ? '#e55e50' : '#f3be5d';
|
||||||
|
var difference = params.difference || 0;
|
||||||
|
if (difference <= 15 || !session['contract_validation']) {
|
||||||
|
if (difference > 15){
|
||||||
|
difference = 0;
|
||||||
|
bg_color = '#e55e50';
|
||||||
|
}
|
||||||
|
var message = 'Register your contract, only ' + difference + ' days left';
|
||||||
|
var $panel = $(qweb.render('FlectraLicense.contract_expire_panel', {
|
||||||
|
'difference': params.difference,
|
||||||
|
'message': message,
|
||||||
|
'background': bg_color
|
||||||
|
}));
|
||||||
|
$('nav').after($panel);
|
||||||
|
if (difference <= 0) {
|
||||||
|
return self.contract_expired()
|
||||||
|
}
|
||||||
|
$panel.find('#register_contract').bind('click', self.register_contract);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
register_contract: function () {
|
||||||
|
var self = this;
|
||||||
|
var dialog = new Dialog(self).open();
|
||||||
|
dialog.on('get_key', self, function (key) {
|
||||||
|
session.get_file({
|
||||||
|
url: '/flectra/licensing',
|
||||||
|
data: {
|
||||||
|
'binary': key['binary']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
contract_expired: function () {
|
||||||
|
var self = this;
|
||||||
|
var $message = $('#expiration-message').parent();
|
||||||
|
var $clone = $message.clone();
|
||||||
|
$clone.find('#contract-message').text('Contract Expired !!!').addClass('contract-block');
|
||||||
|
$clone.find('button.close').remove();
|
||||||
|
$message.hide();
|
||||||
|
$clone.find('div#register_contract').after(
|
||||||
|
$('<div id="apply_contract" class="noselect">').append(
|
||||||
|
$('<span id="btn_apply_key">').text('Apply Key')));
|
||||||
|
$clone.find('span#btn_register_contract').off('click').on('click', function () {
|
||||||
|
$.unblockUI();
|
||||||
|
self.register_contract();
|
||||||
|
});
|
||||||
|
$clone.find('#register_contract,#apply_contract').addClass('contract-mrg10');
|
||||||
|
$clone.find('span#btn_apply_key').off('click').on('click', function () {
|
||||||
|
$.unblockUI();
|
||||||
|
rpc.query({
|
||||||
|
model: 'ir.actions.act_window',
|
||||||
|
method: 'search_read',
|
||||||
|
domain: [['context', '=', "{'module' : 'general_settings'}"]]
|
||||||
|
}).done(function (res) {
|
||||||
|
if (!res)
|
||||||
|
window.location.reload();
|
||||||
|
self.do_action(res[0]['id']).done(function () {
|
||||||
|
var $el = $('div[name=activator_key]');
|
||||||
|
if ($el && $el[0]){
|
||||||
|
$el[0].scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||||
|
$el.parents('.o_setting_box').animate({backgroundColor: "rgb(239, 234, 208)"}, 2000, function () {
|
||||||
|
$el.parents('.o_setting_box').animate({backgroundColor: ''})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
$.blockUI({
|
||||||
|
message: $clone,
|
||||||
|
css: {cursor: 'auto'},
|
||||||
|
overlayCSS: {cursor: 'auto'}
|
||||||
|
});
|
||||||
|
self.contract_expired();
|
||||||
|
}, 15000);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -134,3 +134,52 @@ div.o_boolean_toggle {
|
|||||||
.bg-info-full {
|
.bg-info-full {
|
||||||
background-color: @brand-info;
|
background-color: @brand-info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#expiration-message {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 10px !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-message {
|
||||||
|
margin: 0 auto;
|
||||||
|
#register_contract, #apply_contract {
|
||||||
|
background: #5dae7e;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
span {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-animate {
|
||||||
|
background-color: rgb(253, 249, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-mrg10 {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noselect {
|
||||||
|
-webkit-touch-callout: none; /* iOS Safari */
|
||||||
|
-webkit-user-select: none; /* Safari */
|
||||||
|
-khtml-user-select: none; /* Konqueror HTML */
|
||||||
|
-moz-user-select: none; /* Firefox */
|
||||||
|
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
@ -102,4 +102,31 @@
|
|||||||
<li class="o_user_bookmark_menu" ><a href="#" title="Bookmark"><i class="fa fa-bookmark"/></a></li>
|
<li class="o_user_bookmark_menu" ><a href="#" title="Bookmark"><i class="fa fa-bookmark"/></a></li>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
|
|
||||||
|
<div t-name="FlectraLicense.contract_expire_panel" >
|
||||||
|
<div id="expiration-message">
|
||||||
|
<div>
|
||||||
|
<div id="inner-message" class="alert" t-att-style="'background:' + background + ';'">
|
||||||
|
<button type="button" class="close" data-dismiss="alert">&times;</button>
|
||||||
|
<span>
|
||||||
|
<span id="contract-message"><t t-esc="message"/></span>
|
||||||
|
<div id="register_contract" class="noselect">
|
||||||
|
<span id="btn_register_contract">Register</span>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="FlectraLicense.dialog_contract_registration" >
|
||||||
|
<div id="register_contract_form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="contract_id">Contract ID</label>
|
||||||
|
<input type="text" class="form-control" id="contract_id" placeholder="Contract ID"/>
|
||||||
|
<input type="hidden" value=""/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
|
@ -269,6 +269,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/backend_theme_customizer.js"/>
|
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/backend_theme_customizer.js"/>
|
||||||
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/customize_switcher.js"/>
|
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/customize_switcher.js"/>
|
||||||
|
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/DialogRegisterContract.js"/>
|
||||||
<script type="text/javascript" src="/web/static/src/js/chrome/bookmark.js"/>
|
<script type="text/javascript" src="/web/static/src/js/chrome/bookmark.js"/>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -11,6 +11,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import datetime,json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ from flectra.exceptions import AccessDenied, UserError
|
|||||||
from flectra.tools.parse_version import parse_version
|
from flectra.tools.parse_version import parse_version
|
||||||
from flectra.tools.misc import topological_sort
|
from flectra.tools.misc import topological_sort
|
||||||
from flectra.http import request
|
from flectra.http import request
|
||||||
|
from flectra.addons.web.models.crypt import *
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -293,6 +295,7 @@ class Module(models.Model):
|
|||||||
application = fields.Boolean('Application', readonly=True)
|
application = fields.Boolean('Application', readonly=True)
|
||||||
icon = fields.Char('Icon URL')
|
icon = fields.Char('Icon URL')
|
||||||
icon_image = fields.Binary(string='Icon', compute='_get_icon_image')
|
icon_image = fields.Binary(string='Icon', compute='_get_icon_image')
|
||||||
|
contract_certificate = fields.Char('Required Contract')
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
('name_uniq', 'UNIQUE (name)', 'The name of the module must be unique!'),
|
('name_uniq', 'UNIQUE (name)', 'The name of the module must be unique!'),
|
||||||
@ -433,6 +436,20 @@ class Module(models.Model):
|
|||||||
"- %s (%s)" % (module.shortdesc, labels[module.state])
|
"- %s (%s)" % (module.shortdesc, labels[module.state])
|
||||||
for module in modules
|
for module in modules
|
||||||
]))
|
]))
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
exp_date = ir_config.get_param('database.expiration_date')
|
||||||
|
reason = ir_config.get_param('database.expiration_reason')
|
||||||
|
set_param = ir_config.set_param
|
||||||
|
|
||||||
|
for mod in self:
|
||||||
|
if mod.contract_certificate and not (reason == 'contract_expire' and exp_date):
|
||||||
|
expire_date = datetime.datetime.now() + datetime.timedelta(days=15)
|
||||||
|
set_param('database.expiration_date', expire_date.replace(microsecond=0))
|
||||||
|
set_param('database.expiration_reason', 'contract_expire')
|
||||||
|
set_param('contract.validity',
|
||||||
|
base64.encodestring(
|
||||||
|
encrypt(json.dumps(str(expire_date.replace(microsecond=0))),
|
||||||
|
str(expire_date.replace(microsecond=0)))))
|
||||||
|
|
||||||
return dict(ACTION_DICT, name=_('Install'))
|
return dict(ACTION_DICT, name=_('Install'))
|
||||||
|
|
||||||
@ -573,6 +590,13 @@ class Module(models.Model):
|
|||||||
raise UserError(_("The `base` module cannot be uninstalled"))
|
raise UserError(_("The `base` module cannot be uninstalled"))
|
||||||
deps = self.downstream_dependencies()
|
deps = self.downstream_dependencies()
|
||||||
(self + deps).write({'state': 'to remove'})
|
(self + deps).write({'state': 'to remove'})
|
||||||
|
modules = self.env['ir.module.module'].search([('contract_certificate', '!=', False), ('state', '=', 'installed')])
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
set_param = ir_config.set_param
|
||||||
|
if len(modules) <= 0:
|
||||||
|
set_param('database.expiration_date', False)
|
||||||
|
set_param('database.expiration_reason', False)
|
||||||
|
set_param('contract.validity', False)
|
||||||
return dict(ACTION_DICT, name=_('Uninstall'))
|
return dict(ACTION_DICT, name=_('Uninstall'))
|
||||||
|
|
||||||
@assert_log_admin_access
|
@assert_log_admin_access
|
||||||
@ -697,6 +721,8 @@ class Module(models.Model):
|
|||||||
updated_values['state'] = 'uninstalled'
|
updated_values['state'] = 'uninstalled'
|
||||||
if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
|
if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
|
||||||
res[0] += 1
|
res[0] += 1
|
||||||
|
if terp.get('contract_certificate'):
|
||||||
|
mod.write({'contract_certificate': terp.get('contract_certificate') or False})
|
||||||
if updated_values:
|
if updated_values:
|
||||||
mod.write(updated_values)
|
mod.write(updated_values)
|
||||||
else:
|
else:
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
<group col="4">
|
<group col="4">
|
||||||
<field name="demo"/>
|
<field name="demo"/>
|
||||||
<field name="application"/>
|
<field name="application"/>
|
||||||
|
<field name="contract_certificate"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="Created Views" attrs="{'invisible':[('state','!=','installed')]}"/>
|
<group string="Created Views" attrs="{'invisible':[('state','!=','installed')]}"/>
|
||||||
|
@ -45,3 +45,4 @@ xlrd==1.0.0
|
|||||||
unittest2==1.1.0
|
unittest2==1.1.0
|
||||||
numpy==1.14.3
|
numpy==1.14.3
|
||||||
pypiwin32 ; sys_platform == 'win32'
|
pypiwin32 ; sys_platform == 'win32'
|
||||||
|
pycrypto==2.6.1
|
Loading…
Reference in New Issue
Block a user