Merge branch 'flectra-app-store' into 'master'

[IMP] improvement on app store.

See merge request flectra-hq/flectra!162
This commit is contained in:
Parthiv Patel 2018-10-31 11:53:56 +00:00
commit c3cd4c4867
5 changed files with 94 additions and 70 deletions

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# 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 tarfile import tarfile
import uuid
import babel.messages.pofile import babel.messages.pofile
import base64 import base64
@ -570,7 +571,7 @@ class Home(http.Controller):
@http.route(['/web/app_action'], type='json', auth="user") @http.route(['/web/app_action'], type='json', auth="user")
def app_action(self, action='', module_name='', **kwargs): def app_action(self, action='', module_name='', **kwargs):
if request.env.user.has_group('base.group_system'): if request.env.user.has_group('base.group_system') and config.get('app_store') == 'install':
if module_name and action: if module_name and action:
module = request.env['ir.module.module'].search([('state', '=', 'installed'), ('name', '=', module_name)], limit=1) module = request.env['ir.module.module'].search([('state', '=', 'installed'), ('name', '=', module_name)], limit=1)
if module: if module:
@ -581,7 +582,7 @@ class Home(http.Controller):
@http.route(['/web/get_modules'], type='json', auth="user") @http.route(['/web/get_modules'], type='json', auth="user")
def get_modules(self, **kwargs): def get_modules(self, **kwargs):
if request.env.user.has_group('base.group_system'): if request.env.user.has_group('base.group_system') and config.get('app_store') in ['install', 'download']:
try: try:
modules = request.env['ir.module.module'].search_read([('state', '=', 'installed')], fields=['name']) modules = request.env['ir.module.module'].search_read([('state', '=', 'installed')], fields=['name'])
p = requests.post(server_url + '/flectrahq/get_modules', data=kwargs) p = requests.post(server_url + '/flectrahq/get_modules', data=kwargs)
@ -595,11 +596,11 @@ class Home(http.Controller):
return False return False
return False return False
@http.route(['/web/module_download/<int:id>'], type='http', auth="user", methods=['GET', 'POST']) @http.route(['/web/module_download/<string:id>'], type='http', auth="user", methods=['GET', 'POST'])
def app_download(self, id=None): def app_download(self, id=None, **kwargs):
if request.env.user.has_group('base.group_system'): if request.env.user.has_group('base.group_system') and config.get('app_store') in ['install', 'download']:
dbuuid = request.env['ir.config_parameter'].get_param('database.uuid') dbuuid = request.env['ir.config_parameter'].get_param('database.uuid')
p = requests.get(server_url + '/flectrahq/get_module_zip/' + str(id) + '/1', params={'dbuuid': dbuuid}) p = requests.get(server_url + '/flectrahq/get_module_zip/' + str(id), params={'dbuuid': dbuuid})
try: try:
data = json.loads(p.content.decode('utf-8')) data = json.loads(p.content.decode('utf-8'))
if data.get('error', False): if data.get('error', False):
@ -618,17 +619,12 @@ class Home(http.Controller):
return request.not_found() return request.not_found()
return request.not_found() return request.not_found()
@http.route(['/web/app_download_install/<int:id>'], type='json', auth="user") @http.route(['/web/app_download_install'], type='json', auth="user")
def app_download_install(self, id=None): def app_download_install(self, checksum=None, module_name=None, id=None, **kwargs):
if request.env.user.has_group('base.group_system'): if request.env.user.has_group('base.group_system') and config.get('app_store') == 'install':
IrModule = request.env['ir.module.module'] IrModule = request.env['ir.module.module']
try: try:
res_get_details = requests.get(server_url + '/flectrahq/get_module_zip/' + str(id) + '/0') res_download = requests.get(server_url + '/flectrahq/get_module_zip/' + str(id))
module_file_details = json.loads(res_get_details.content.decode('utf-8'))
except:
return {"error": "Internal Server Error"}
finally:
res_download = requests.get(server_url + '/flectrahq/get_module_zip/' + str(id) + '/1')
downloaded_file_checksum = hashlib.sha1(res_download.content or b'').hexdigest() downloaded_file_checksum = hashlib.sha1(res_download.content or b'').hexdigest()
if res_download.status_code == 200: if res_download.status_code == 200:
try: try:
@ -637,9 +633,8 @@ class Home(http.Controller):
return data return data
except: except:
pass pass
if checksum == downloaded_file_checksum:
if module_file_details['checksum'] == downloaded_file_checksum: path = os.path.join(tmp_dir_path, uuid.uuid4().hex)
path = os.path.join(tmp_dir_path, module_file_details['name'])
try: try:
with open(path, 'wb') as f: with open(path, 'wb') as f:
f.write(res_download.content) f.write(res_download.content)
@ -650,14 +645,15 @@ class Home(http.Controller):
finally: finally:
IrModule.update_list() IrModule.update_list()
root.load_addons() root.load_addons()
modules = IrModule.search([('name', '=', module_file_details['module_name'])], limit=1) if module_name:
modules = IrModule.search([('name', '=', module_name)], limit=1)
modules.button_immediate_install() modules.button_immediate_install()
os.remove(path) os.remove(path)
return {"success": "Module is successfully installed."} return {"success": "Module is successfully installed."}
else: else:
return {"error": "File Crash."} return {"error": "File crashed when downloading."}
except:
return {"error": "Internal Server Error."} return {"error": "Internal Server Error"}
return False return False

View File

@ -99,27 +99,20 @@ var Apps = Widget.extend({
}, },
_openDialogAfterAction: function (data) { _openDialogAfterAction: function (data) {
if (!_.isEmpty(data)) { if (!_.isEmpty(data)) {
var buttons = [];
if (data.success) { if (data.success) {
buttons.push({
text: _t("Refresh"),
classes: 'btn-success',
click: function (e) {
window.location.reload(); window.location.reload();
}, return;
close: true
});
} }
buttons.push({ var buttons = [{
text: _t("Cancel"), text: _t("OK"),
classes: 'btn-warning', classes: 'btn-success',
close: true, close: true,
}); }];
var dialog = new Dialog(this, { var dialog = new Dialog(this, {
size: 'medium', size: 'medium',
buttons: buttons, buttons: buttons,
$content: $("<h4>" + (data.error || data.success) + "</h4>"), $content: $("<h4>" + data.error + "</h4>"),
title: _t(data.error ? "Error" : "Message"), title: _t("Error"),
}); });
dialog.open(); dialog.open();
} }
@ -152,8 +145,14 @@ var Apps = Widget.extend({
e.preventDefault(); e.preventDefault();
var self = this; var self = this;
var id = $(e.target).data("module-id"); var id = $(e.target).data("module-id");
var data = _.findWhere(this.all_app, {id: id});
self._rpc({ self._rpc({
route: '/web/app_download_install/' + id, route: '/web/app_download_install',
params: {
id: data['md5_val'],
checksum: data['checksum'],
module_name: data['technical_name']
}
}).then(function (data) { }).then(function (data) {
self._openDialogAfterAction(data); self._openDialogAfterAction(data);
}); });
@ -172,10 +171,11 @@ var Apps = Widget.extend({
params: { params: {
offset: this.context.categ[this.active_categ]['offset'], offset: this.context.categ[this.active_categ]['offset'],
categ: this.active_categ, categ: this.active_categ,
search: this.context.categ[self.active_categ]['search'] search: this.context.categ[this.active_categ]['search']
} }
}).done(function (data) { }).done(function (data) {
if (data) { if (data) {
self.all_app = self.all_app.concat(data.modules[self.active_categ]);
self.$el.find('#' + self.active_categ + " .module-kanban:last") self.$el.find('#' + self.active_categ + " .module-kanban:last")
.after(QWeb.render('AppStore.ModuleBoxContainer', { .after(QWeb.render('AppStore.ModuleBoxContainer', {
modules: data.modules[self.active_categ], modules: data.modules[self.active_categ],
@ -183,10 +183,15 @@ var Apps = Widget.extend({
mode: self.mode, mode: self.mode,
store_url: data.store_url store_url: data.store_url
})); }));
if (!_.isEmpty(data.modules[self.active_categ])) { if (!_.isEmpty(data.modules[self.active_categ]) && data.modules[self.active_categ].length == data.limit) {
self.$el.find('#' + self.active_categ + " .load-more").show(); self.$el.find('#' + self.active_categ + " .load-more").show();
} else { } else {
self.$el.find('#' + self.active_categ + " .load-more").hide(); var $rec = self.$el.find('#' + self.active_categ + " .module-kanban ");
var $load_more = self.$el.find('#' + self.active_categ + " .load-more");
$load_more.hide().next('h3').remove();
if (!$rec.length) {
$load_more.after('<h3>No such module(s) found.</h3>');
}
} }
} else { } else {
self.$el.html(QWeb.render('AppStore.TryError', {})); self.$el.html(QWeb.render('AppStore.TryError', {}));
@ -227,18 +232,25 @@ var Apps = Widget.extend({
} }
}).done(function (data) { }).done(function (data) {
if (data) { if (data) {
self.all_app = self.all_app.concat(data.modules[self.active_categ]);
self.$el.find('#' + self.active_categ).find('.module-kanban').remove(); self.$el.find('#' + self.active_categ).find('.module-kanban').remove();
self.context.categ[self.active_categ]['search'] = search; self.context.categ[self.active_categ]['search'] = search;
self.context.categ[self.active_categ]['offset'] = 0;
$(QWeb.render('AppStore.ModuleBoxContainer', { $(QWeb.render('AppStore.ModuleBoxContainer', {
modules: data.modules[self.active_categ], modules: data.modules[self.active_categ],
installed_modules: data.installed_modules, installed_modules: data.installed_modules,
mode: self.mode, mode: self.mode,
store_url: data.store_url store_url: data.store_url
})).prependTo(self.$el.find('#' + self.active_categ + " .o_kanban_view")); })).prependTo(self.$el.find('#' + self.active_categ + " .o_kanban_view"));
if (!_.isEmpty(data.modules[self.active_categ])) { if (!_.isEmpty(data.modules[self.active_categ]) && data.modules[self.active_categ].length == data.limit) {
self.$el.find('#' + self.active_categ + " .load-more").show().next('h3').remove(); self.$el.find('#' + self.active_categ + " .load-more").show().next('h3').remove();
} else { } else {
self.$el.find('#' + self.active_categ + " .load-more").hide().after('<h3>No such module(s) found.</h3>'); var $rec = self.$el.find('#' + self.active_categ + " .module-kanban ");
var $load_more = self.$el.find('#' + self.active_categ + " .load-more");
$load_more.hide().next('h3').remove();
if (!$rec.length) {
$load_more.after('<h3>No such module(s) found.</h3>');
}
} }
} else { } else {
self.$el.html(QWeb.render('AppStore.TryError', {})); self.$el.html(QWeb.render('AppStore.TryError', {}));

View File

@ -32,12 +32,15 @@
} }
li { li {
float: none; float: none;
> a:hover, > a:focus { > a {
white-space: nowrap;
&:hover, &:focus {
border-color: transparent; border-color: transparent;
border-radius: 0; border-radius: 0;
background-color: mix(@brand-primary, #ffffff, 50%); background-color: mix(@brand-primary, #ffffff, 50%);
} }
} }
}
@media (max-width: @screen-sm-max) { @media (max-width: @screen-sm-max) {
border-top: none; border-top: none;
} }
@ -70,13 +73,13 @@
visibility: visible; visibility: visible;
} }
.oe_module_desc { .oe_module_desc {
min-height: 130px;
.oe_module_action { .oe_module_action {
.o_module_tech_name {
display: inline-block;
width: 140px;
white-space: nowrap; white-space: nowrap;
overflow: hidden !important; overflow: hidden !important;
text-overflow: ellipsis; text-overflow: ellipsis;
.o_module_tech_name {
display: inline-block;
} }
} }
} }

View File

@ -1495,7 +1495,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<t t-set="is_install" t-value="installed_modules.includes(m.technical_name)"/> <t t-set="is_install" t-value="installed_modules.includes(m.technical_name)"/>
<li><a t-if="is_install and mode == 'install'" app-action="uninstall" t-att-data-module-id="m.id" href="#" class=" oe_kanban_action oe_kanban_action_a">Uninstall</a></li> <li><a t-if="is_install and mode == 'install'" app-action="uninstall" t-att-data-module-id="m.id" href="#" class=" oe_kanban_action oe_kanban_action_a">Uninstall</a></li>
<li><a app-action="download" t-att-data-module-id="m.id" t-att-href="m.price ? 'javascript:void(0);' : '/web/module_download/' + m.id" class=" oe_kanban_action oe_kanban_action_a">Download</a></li> <li><a app-action="download" t-att-data-module-id="m.id" t-att-href="m.price ? 'javascript:void(0);' : '/web/module_download/' + m.md5_val" class=" oe_kanban_action oe_kanban_action_a">Download</a></li>
<li><a app-action="view-info" t-att-data-module-id="m.id" href="#" class="oe_kanban_action oe_kanban_action_a">View Info</a></li> <li><a app-action="view-info" t-att-data-module-id="m.id" href="#" class="oe_kanban_action oe_kanban_action_a">View Info</a></li>
<li><a t-attf-href="{{ store_url }}/apps/{{ m.version }}/{{ m.technical_name }}" target="_blank" class=" oe_kanban_action oe_kanban_action_a">View More on Store</a></li> <li><a t-attf-href="{{ store_url }}/apps/{{ m.version }}/{{ m.technical_name }}" target="_blank" class=" oe_kanban_action oe_kanban_action_a">View More on Store</a></li>
</ul> </ul>
@ -1507,9 +1507,13 @@
<span><t t-esc="m.description or ''"/></span> <span><t t-esc="m.description or ''"/></span>
</p> </p>
<div class="oe_module_action"> <div class="oe_module_action">
<span class="pull-left"><t t-esc="m.price ? m.price + ' ' + m.currency[1] : 'Free'"/></span><br/> <span t-if="m.price">
<code class="pull-left o_module_tech_name"><small><span><t t-esc="m.technical_name"/></span></small></code> <t t-esc="m.currency[2] == 'after' ? m.price + ' ' + m.currency[1] : m.currency[1] + ' ' + m.price"/>
<div class="text-right"> </span>
<span t-else="m.price">Free</span>
<br/>
<code class="o_module_tech_name" t-att-title="m.technical_name"><small><span><t t-esc="m.technical_name"/></span></small></code>
<div class="text-right mt8">
<t t-if="is_install"> <t t-if="is_install">
<span>Installed</span> <span>Installed</span>
</t> </t>
@ -1521,7 +1525,7 @@
</t> </t>
<t t-else=""> <t t-else="">
<button t-if="mode == 'install'" app-action="install" t-att-data-module-id="m.id" class="btn btn-primary btn-sm oe_kanban_action oe_kanban_action_button">Install</button> <button t-if="mode == 'install'" app-action="install" t-att-data-module-id="m.id" class="btn btn-primary btn-sm oe_kanban_action oe_kanban_action_button">Install</button>
<a t-if="mode == 'download'" app-action="download" t-att-href="m.price ? 'javascript:void(0);' : '/web/module_download/' + m.id " t-att-data-module-id="m.id" class="btn btn-primary btn-sm oe_kanban_action oe_kanban_action_button"> <a t-if="mode == 'download'" app-action="download" t-att-href="m.price ? 'javascript:void(0);' : '/web/module_download/' + m.md5_val " t-att-data-module-id="m.id" class="btn btn-primary btn-sm oe_kanban_action oe_kanban_action_button">
<span><i class="fa fa-download"/></span> <span><i class="fa fa-download"/></span>
</a> </a>
</t> </t>
@ -1607,7 +1611,11 @@
<span><t t-esc="m.description or ''"/></span> <span><t t-esc="m.description or ''"/></span>
</p> </p>
<div class="oe_module_action"> <div class="oe_module_action">
<span><t t-esc="m.price ? m.price + ' ' + m.currency[1] : 'Free'"/></span><br/> <span t-if="m.price">
<t t-esc="m.currency[2] == 'after' ? m.price + ' ' + m.currency[1] : m.currency[1] + ' ' + m.price"/>
</span>
<span t-else="m.price">Free</span>
<br/>
<code><small><span><t t-esc="m.technical_name"/></span></small></code> <code><small><span><t t-esc="m.technical_name"/></span></small></code>
</div> </div>
</div> </div>

View File

@ -636,9 +636,11 @@ class configmanager(object):
except OSError: except OSError:
logging.getLogger(__name__).debug('Failed to create addons data dir %s', d) logging.getLogger(__name__).debug('Failed to create addons data dir %s', d)
try: try:
if self.get('db_name') and not os.listdir(os.path.join(d)): try:
from flectra.http import request, root
if request.session.db and not os.listdir(os.path.join(d)):
from flectra.sql_db import db_connect from flectra.sql_db import db_connect
with closing(db_connect(self.get('db_name')).cursor()) as cr: with closing(db_connect(request.session.db).cursor()) as cr:
if flectra.tools.table_exists(cr, 'ir_module_module'): if flectra.tools.table_exists(cr, 'ir_module_module'):
cr.execute("SELECT latest_version FROM ir_module_module WHERE name=%s", ('base',)) cr.execute("SELECT latest_version FROM ir_module_module WHERE name=%s", ('base',))
base_version = cr.fetchone() base_version = cr.fetchone()
@ -648,6 +650,9 @@ class configmanager(object):
s = os.path.join(add_dir, last_version) s = os.path.join(add_dir, last_version)
if float(last_version) < float(release.series) and os.listdir(os.path.join(s)): if float(last_version) < float(release.series) and os.listdir(os.path.join(s)):
self.copytree(s, d) self.copytree(s, d)
root.load_addons()
except:
pass
if self.get('app_store') == 'install': if self.get('app_store') == 'install':
if not os.access(d, os.W_OK): if not os.access(d, os.W_OK):