Commit 02839067 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch '947-advanced-upload-bug' into 'master'

Resolve "Advanced upload - bug?"

Closes #947

See merge request !1018
parents 837f6c95 ce1a334e
minerva (15.0.0~alpha.1) stable; urgency=medium
* Feature removal: when uploading complex map with submap there is no
possibility to change submap name (#947)
* Improvement: support for rendering colors in SBGNML added (#1009)
* Small improvement: sort order of tables in admin panel is preserved among
sessions (#836)
......@@ -11,6 +13,10 @@ minerva (15.0.0~alpha.1) stable; urgency=medium
upload (#820)
* Small improvement: items in context menu are reorganized (#787)
* Small improvement: CellDesigner files from version 2.5 should be parseable
* Small improvement: plugins in admin panel can be revalidated to update info
about the plugins (name, version)
* Small improvement: there is possibility to define default plugins loaded
with every opening of a map (#967)
* Bug fix: compartments limited by corner (left-top corner compartment, etc)
are parsed properly from CellDesigner files
* Bug fix: structural states of proteins are imported properly from SBGNML PD
......
......@@ -1394,6 +1394,29 @@ ServerConnector.removePlugin = function (params) {
return self.sendDeleteRequest(self.getPluginUrl(params));
};
/**
*
* @param {Object} params
* @param {string} params.hash
* @param {boolean} [params.isDefault]
* @param {boolean} [params.version]
* @param {boolean} [params.name]
*
* @returns {Promise<PluginData[]>}
*/
ServerConnector.updatePlugin = function (params) {
var self = this;
var content = {
plugin: {
hash: params.hash,
isDefault: params.isDefault,
version: params.version,
name: params.name
}
};
return self.sendPatchRequest(self.getPluginUrl(params), content);
};
/**
*
* @param {string} login
......
......@@ -10,6 +10,8 @@ var PanelControlElementType = require('./PanelControlElementType');
var logger = require('../logger');
var Functions = require('../Functions');
var Promise = require('bluebird');
/**
*
* @param {Object} params
......
......@@ -11,6 +11,7 @@ var PanelControlElementType = require('./PanelControlElementType');
// noinspection JSUnusedLocalSymbols
var logger = require('../logger');
var Promise = require('bluebird');
/**
*
......
......@@ -683,7 +683,7 @@ AddProjectDialog.prototype.createSubmapsTabContent = function () {
row[0] = entry.getFilename();
row[1] = "<input data='" + entry.getFilename() + "' name='submapName' value='" + entry.getData().name + "'/>";
row[1] = "<input data='" + entry.getFilename() + "' name='submapName' value='" + entry.getData().name + "' readonly disabled/>";
row[2] = typeSelect;
data.push(row);
}
......
......@@ -63,6 +63,10 @@ PluginAdminPanel.prototype._createGui = function () {
title: 'Version'
}, {
title: 'Url'
}, {
title: 'Default'
}, {
title: 'Re-Validate'
}, {
title: 'Remove',
orderable: false
......@@ -89,6 +93,73 @@ PluginAdminPanel.prototype._createGui = function () {
}).catch(GuiConnector.alert);
});
$(pluginsTable).on("click", "[name='re-validate-plugin']", function () {
var button = this;
var hash = $(button).attr("data");
var error = false;
var plugin;
return self.getServerConnector().getPluginData(hash).then(function (response) {
plugin = response;
return self.getServerConnector().sendRequest({
url: plugin.getUrls()[0],
description: "Loading plugin: " + plugin.getUrls()[0],
method: "GET"
}).catch(function (e) {
error = e;
});
}).then(function (content) {
var pluginRawData = undefined;
// noinspection JSUnusedLocalSymbols
var minervaDefine = function (pluginFunction) {
try {
if (typeof pluginFunction === "function") {
pluginRawData = pluginFunction();
} else {
pluginRawData = pluginFunction;
}
} catch (e) {
error = e;
}
};
content += "//# sourceURL=" + plugin.getUrls()[0];
eval(content);
if (error) {
return self.askConfirmRemoval({
title: "INFO",
content: "Plugin source file does not exist. Do you want to remove this plugin?",
input: false
}).then(function (param) {
if (param.status) {
return self.getServerConnector().removePlugin({hash: hash}).then(function () {
return self.onRefreshClicked();
});
}
});
} else {
return self.getServerConnector().updatePlugin({
hash: hash,
version: pluginRawData.getVersion(),
name: pluginRawData.getName()
}).then(function () {
return self.onRefreshClicked();
});
}
}).catch(GuiConnector.alert);
});
$(pluginsTable).on("click", "[name='edit-default-plugin']", function () {
var checkbox = this;
var hash = $(this).attr("data");
return self.getServerConnector().updatePlugin({
hash: hash,
isDefault: $(checkbox).is(':checked')
}).then(function () {
return self.onRefreshClicked();
}).catch(GuiConnector.alert);
});
pluginDiv.appendChild(self._createMenuRow());
};
......@@ -196,7 +267,15 @@ PluginAdminPanel.prototype.pluginToTableRow = function (plugin) {
row[0] = plugin.getName();
row[1] = plugin.getVersion();
row[2] = plugin.getUrls();
row[3] = "<button name='removePlugin' data='" + plugin.getHash() + "' ><i class='fa fa-trash-alt'></button>";
var checked = "";
if (plugin.isDefault()) {
checked = " checked ";
}
row[3] = "<input type='checkbox' name='edit-default-plugin' " + checked + " data='" + plugin.getHash() + "' />";
row[4] = "<button name='re-validate-plugin' data='" + plugin.getHash() + "' ><i class='fa fa-check-circle'></button>";
row[5] = "<button name='removePlugin' data='" + plugin.getHash() + "' ><i class='fa fa-trash-alt'></button>";
return row;
};
......
......@@ -12,6 +12,8 @@ var PublicationListDialog = require('./PublicationListDialog');
var logger = require('../../logger');
var Functions = require('../../Functions');
var Promise = require('bluebird');
/**
*
* @param {Object} params
......
......@@ -10,6 +10,7 @@ var Functions = require('../../Functions');
* @param {string} params.name
* @param {string} params.version
* @param {boolean} [params.isPublic=false]
* @param {boolean} [params.isDefault=false]
* @constructor
*/
function PluginData(params) {
......@@ -17,6 +18,7 @@ function PluginData(params) {
this.setUrls(params.urls);
this.setName(params.name);
this.setPublic(params.isPublic);
this.setDefault(params.isDefault);
this.setVersion(params.version);
}
......@@ -38,16 +40,25 @@ PluginData.prototype.getHash = function () {
/**
*
* @param {string|boolean} isPublic
* @param {?null|string|boolean} value
* @return {boolean}
*/
PluginData.prototype.setPublic = function (isPublic) {
if (isPublic === null || isPublic === undefined) {
this._public = false;
} else if (Functions.isString(isPublic)) {
this._public = (isPublic === "true");
function getBooleanValue(value) {
if (value === null || value === undefined) {
return false;
} else if (Functions.isString(value)) {
return (value === "true");
} else {
this._public = isPublic;
return value;
}
}
/**
*
* @param {?null|string|boolean} isPublic
*/
PluginData.prototype.setPublic = function (isPublic) {
this._public = getBooleanValue(isPublic);
};
/**
......@@ -58,6 +69,22 @@ PluginData.prototype.isPublic = function () {
return this._public;
};
/**
*
* @param {?null|string|boolean} isDefault
*/
PluginData.prototype.setDefault = function (isDefault) {
this._default = getBooleanValue(isDefault);
};
/**
*
* @returns {boolean}
*/
PluginData.prototype.isDefault = function () {
return this._default;
};
/**
*
* @param {string[]} urls
......
......@@ -3,6 +3,7 @@ var SubMenu = require('../../gui/SubMenu');
var Variant = require('./MolArtVariant');
var getUniprotSequence = require('./UniprotQuery');
var $ = require('jquery');
var Promise = require('bluebird');
/**
*
......
......@@ -11,6 +11,7 @@ var ConfigurationType = require('../../ConfigurationType');
var IdentifiedElement = require('../data/IdentifiedElement');
var Bounds = require('../canvas/Bounds');
var Point = require('../canvas/Point');
var Promise = require('bluebird');
/**
* Class representing overlay of the alias on the map relevant for a specific
......
......@@ -584,24 +584,38 @@ function create(params) {
return customMap.openSubmap(submapId);
}
}).then(function () {
var promises = [];
for (var i = 0; i < params.getPlugins().length; i++) {
promises.push(leftPanel.getPluginManager().addPlugin(params.getPlugins()[i]))
}
if (GuiConnector.getParams['plugins'] !== undefined) {
var hashes = GuiConnector.getParams['plugins'].split(",");
for (i = 0; i < hashes.length; i++) {
promises.push(ServerConnector.getPluginData(hashes[i]).then(function (plugin) {
if (plugin !== null) {
return leftPanel.getPluginManager().addPlugin({url: plugin.getUrls()[0]});
} else {
GuiConnector.warn("Plugin list contains invalid object.");
return ServerConnector.getPluginsData();
}).then(function (allPlugins) {
var promises = [], i;
for (i = 0; i < params.getPlugins().length; i++) {
promises.push(leftPanel.getPluginManager().addPlugin(params.getPlugins()[i]))
}
var hashes = [];
if (GuiConnector.getParams['plugins'] !== undefined) {
hashes = GuiConnector.getParams['plugins'].split(",");
}
for (i = 0; i < allPlugins.length; i++) {
if (hashes.indexOf(allPlugins[i].getHash()) < 0) {
if (allPlugins[i].isDefault()) {
hashes.push(allPlugins[i].getHash());
}
}))
}
}
for (i = 0; i < hashes.length; i++) {
if (hashes[i] !== '') {
promises.push(ServerConnector.getPluginData(hashes[i]).then(function (plugin) {
if (plugin !== null) {
return leftPanel.getPluginManager().addPlugin({url: plugin.getUrls()[0]});
} else {
GuiConnector.warn("Plugin list contains invalid object.");
}
}));
}
}
return Promise.all(promises);
}
return Promise.all(promises);
}).then(function () {
).then(function () {
return ServerConnector.getLoggedUser();
}).then(function (user) {
if (leftPanel.isGoogleLicenseConsentRequired()) {
......
......@@ -189,11 +189,13 @@ Plugin.prototype.load = function () {
Plugin.prototype.getMinWidth = function () {
var value;
var data = this.getLoadedPluginData();
if (data.minWidth !== undefined) {
if (typeof data.minWidth === "function") {
value = parseInt(data.minWidth());
} else {
value = parseInt(data.minWidth);
if (data !== undefined) {
if (data.minWidth !== undefined) {
if (typeof data.minWidth === "function") {
value = parseInt(data.minWidth());
} else {
value = parseInt(data.minWidth);
}
}
}
return value;
......
......@@ -187,6 +187,7 @@ PluginManager.prototype.adjustMinWidth = function () {
var i;
for (i = 0; i < self._plugins.length; i++) {
var plugin = self._plugins[i];
var value = plugin.getMinWidth();
if (value > minWidth) {
minWidth = value;
......
......@@ -53,6 +53,8 @@ public class Plugin implements Serializable {
* Is the plugin public.
*/
private boolean isPublic = false;
private boolean isDefault= false;
/**
* List of urls from which plugin can be downloaded.
......@@ -109,4 +111,12 @@ public class Plugin implements Serializable {
public void setPublic(boolean isPublic) {
this.isPublic = isPublic;
}
public boolean isDefault() {
return isDefault;
}
public void setDefault(boolean isDefault) {
this.isDefault = isDefault;
}
}
alter table plugin_table add column is_default boolean default false not null;
......@@ -11,8 +11,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import lcsb.mapviewer.api.BaseController;
import lcsb.mapviewer.api.ObjectNotFoundException;
import lcsb.mapviewer.api.*;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.interfaces.IUserService;
......@@ -34,15 +33,26 @@ public class PluginController extends BaseController {
this.userService = userService;
}
@PreAuthorize("not #isPublic or hasAuthority('IS_ADMIN')")
@PreAuthorize("(not #isPublic and #isDefault!=true) or hasAuthority('IS_ADMIN')")
@PostMapping(value = "/")
public Map<String, Object> createPlugin(
@RequestParam(value = "hash") String hash,
@RequestParam(value = "name") String name,
@RequestParam(value = "version") String version,
@RequestParam(value = "isPublic", defaultValue = "false") boolean isPublic,
@RequestParam(value = "isDefault", required = false) Boolean isDefault,
@RequestParam(value = "url", defaultValue = "") String url) {
return pluginRest.createPlugin(hash, name, version, url, isPublic);
return pluginRest.createPlugin(hash, name, version, url, isPublic, isDefault);
}
@PreAuthorize("hasAuthority('IS_ADMIN')")
@PatchMapping(value = "/{hash}")
public Map<String, Object> updatePlugin(
@PathVariable(value = "hash") String hash,
@RequestBody String body) throws QueryException {
Map<String, Object> node = parseBody(body);
Map<String, Object> data = getData(node, "plugin");
return pluginRest.updatePlugin(hash, data);
}
@GetMapping(value = "/")
......
......@@ -2,12 +2,13 @@ package lcsb.mapviewer.api.plugins;
import java.util.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lcsb.mapviewer.api.BaseRestImpl;
import lcsb.mapviewer.api.ObjectNotFoundException;
import lcsb.mapviewer.api.*;
import lcsb.mapviewer.model.plugin.Plugin;
import lcsb.mapviewer.model.plugin.PluginDataEntry;
import lcsb.mapviewer.model.user.User;
......@@ -18,6 +19,8 @@ import lcsb.mapviewer.persist.dao.plugin.PluginDataEntryDao;
@Service
public class PluginRestImpl extends BaseRestImpl {
Logger logger = LogManager.getLogger();
private PluginDao pluginDao;
private PluginDataEntryDao pluginDataEntryDao;
......@@ -28,11 +31,15 @@ public class PluginRestImpl extends BaseRestImpl {
this.pluginDataEntryDao = pluginDataEntryDao;
}
public Map<String, Object> createPlugin(String hash, String name, String version, String url, boolean isPublic) {
public Map<String, Object> createPlugin(String hash, String name, String version, String url, boolean isPublic,
Boolean isDefault) {
Plugin plugin = pluginDao.getByHash(hash);
if (plugin != null) {
plugin.getUrls().add(url);
plugin.setPublic(plugin.isPublic() || isPublic);
if (isDefault != null) {
plugin.setDefault(isDefault);
}
pluginDao.update(plugin);
} else {
plugin = new Plugin();
......@@ -40,6 +47,9 @@ public class PluginRestImpl extends BaseRestImpl {
plugin.setName(name);
plugin.setVersion(version);
plugin.setPublic(isPublic);
if (isDefault != null) {
plugin.setDefault(isDefault);
}
if (!url.isEmpty()) {
plugin.getUrls().add(url);
}
......@@ -54,6 +64,7 @@ public class PluginRestImpl extends BaseRestImpl {
result.put("name", plugin.getName());
result.put("version", plugin.getVersion());
result.put("isPublic", plugin.isPublic());
result.put("isDefault", plugin.isDefault());
List<String> urls = new ArrayList<>(plugin.getUrls());
Collections.sort(urls);
result.put("urls", urls);
......@@ -130,10 +141,69 @@ public class PluginRestImpl extends BaseRestImpl {
Plugin plugin = pluginDao.getByHash(hash);
if (plugin != null) {
plugin.setPublic(false);
plugin.setDefault(false);
pluginDao.update(plugin);
} else {
throw new ObjectNotFoundException("Plugin doesn't exist");
}
return getPlugin(hash);
}
public Map<String, Object> updatePlugin(String hash, Map<String, Object> data) throws QueryException {
Plugin plugin = pluginDao.getByHash(hash);
if (plugin == null) {
throw new ObjectNotFoundException("Plugin doesn't exist");
}
if (data == null) {
throw new QueryException("plugin data is not defined");
}
Set<String> fields = data.keySet();
for (String fieldName : fields) {
Object value = data.get(fieldName);
String stringValue = null;
Integer intValue = null;
Boolean boolValue = null;
if (value instanceof String) {
stringValue = (String) value;
}
if (value instanceof Integer) {
intValue = (Integer) value;
}
if (value instanceof Boolean) {
boolValue = (Boolean) value;
}
if (fieldName.equalsIgnoreCase("isPublic")) {
plugin.setPublic(boolValue);
} else if (fieldName.equalsIgnoreCase("isDefault")) {
plugin.setDefault(boolValue);
} else if (fieldName.equalsIgnoreCase("name")) {
plugin.setName(stringValue);
} else if (fieldName.equalsIgnoreCase("version")) {
plugin.setVersion(stringValue);
} else if (fieldName.equalsIgnoreCase("hash")) {
if (!stringValue.equals(hash)) {
throw new QueryException("plugin hash cannot be changed");
}
} else if (fieldName.equalsIgnoreCase("id")) {
if (intValue != 0 && intValue != plugin.getId()) {
throw new QueryException("plugin id cannot be changed");
}
} else if (fieldName.equalsIgnoreCase("urls")) {
if (value instanceof List) {
if (((List) value).size() > 0) {
plugin.getUrls().clear();
for (Object string : (List) value) {
plugin.getUrls().add(string.toString());
}
}
}
} else {
throw new QueryException("Unknown field: " + fieldName);
}
}
pluginDao.update(plugin);
return getPlugin(hash);
}
}
......@@ -9,7 +9,6 @@ import lcsb.mapviewer.api.convert.AllConvertTests;
import lcsb.mapviewer.api.files.AllFileTests;
import lcsb.mapviewer.api.genomics.AllGenomicsTests;
import lcsb.mapviewer.api.mesh.AllMeshTests;
import lcsb.mapviewer.api.plugins.AllPluginsTests;
import lcsb.mapviewer.api.projects.AllProjectTests;
import lcsb.mapviewer.api.users.AllUserTests;
......@@ -19,7 +18,6 @@ import lcsb.mapviewer.api.users.AllUserTests;
AllFileTests.class,
AllGenomicsTests.class,
AllMeshTests.class,
AllPluginsTests.class,
AllProjectTests.class,
AllUserTests.class,
})
......
package lcsb.mapviewer.api.plugins;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)