Merge branch 'fix/pmgrPipConfig' into devel

Conflicts:
	src/octoprint/plugins/pluginmanager/__init__.py
	src/octoprint/util/pip.py
This commit is contained in:
Gina Häußge 2015-09-22 11:39:02 +02:00
commit 2a4ee5072c
4 changed files with 169 additions and 15 deletions

View file

@ -88,9 +88,17 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
)
def on_settings_save(self, data):
old_pip = self._settings.get(["pip"])
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
new_pip = self._settings.get(["pip"])
self._repository_cache_ttl = self._settings.get_int(["repository_ttl"]) * 60
self._pip_caller.refresh = True
if old_pip != new_pip:
self._pip_caller.configured = new_pip
try:
self._pip_caller.trigger_refresh()
except:
self._pip_caller
##~~ AssetPlugin
@ -178,9 +186,17 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
self._repository_available = self._refresh_repository()
return jsonify(plugins=self._get_plugins(),
repository=dict(available=self._repository_available, plugins=self._repository_plugins),
repository=dict(
available=self._repository_available,
plugins=self._repository_plugins
),
os=self._get_os(),
octoprint=self._get_octoprint_version_string())
octoprint=self._get_octoprint_version_string(),
pip=dict(
available=self._pip_caller.available,
command=self._pip_caller.command,
version=str(self._pip_caller.version)
))
def on_api_command(self, command, data):
if not admin_permission.can():
@ -645,7 +661,8 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
pending_enable=(not plugin.enabled and plugin.key in self._pending_enable),
pending_disable=(plugin.enabled and plugin.key in self._pending_disable),
pending_install=(plugin.key in self._pending_install),
pending_uninstall=(plugin.key in self._pending_uninstall)
pending_uninstall=(plugin.key in self._pending_uninstall),
origin=plugin.origin.type
)
__plugin_name__ = "Plugin Manager"

View file

@ -6,6 +6,12 @@ $(function() {
self.settingsViewModel = parameters[1];
self.printerState = parameters[2];
self.config_repositoryUrl = ko.observable();
self.config_repositoryTtl = ko.observable();
self.config_pipCommand = ko.observable();
self.configurationDialog = $("#settings_plugin_pluginmanager_configurationdialog");
self.plugins = new ItemListHelper(
"plugin.pluginmanager.installedplugins",
{
@ -72,6 +78,10 @@ $(function() {
self.followDependencyLinks = ko.observable(false);
self.pipAvailable = ko.observable(false);
self.pipCommand = ko.observable();
self.pipVersion = ko.observable();
self.working = ko.observable(false);
self.workingTitle = ko.observable();
self.workingDialog = undefined;
@ -86,11 +96,15 @@ $(function() {
};
self.enableUninstall = function(data) {
return self.enableManagement() && !data.bundled && data.key != 'pluginmanager' && !data.pending_uninstall;
return self.enableManagement()
&& (data.origin != "entry_point" || self.pipAvailable())
&& !data.bundled
&& data.key != 'pluginmanager'
&& !data.pending_uninstall;
};
self.enableRepoInstall = function(data) {
return self.enableManagement() && self.isCompatible(data);
return self.enableManagement() && self.pipAvailable() && self.isCompatible(data);
};
self.invalidUrl = ko.computed(function() {
@ -100,7 +114,7 @@ $(function() {
self.enableUrlInstall = ko.computed(function() {
var url = self.installUrl();
return self.enableManagement() && url !== undefined && url.trim() != "" && !self.invalidUrl();
return self.enableManagement() && self.pipAvailable() && url !== undefined && url.trim() != "" && !self.invalidUrl();
});
self.invalidArchive = ko.computed(function() {
@ -110,7 +124,7 @@ $(function() {
self.enableArchiveInstall = ko.computed(function() {
var name = self.uploadFilename();
return self.enableManagement() && name !== undefined && name.trim() != "" && !self.invalidArchive();
return self.enableManagement() && self.pipAvailable() && name !== undefined && name.trim() != "" && !self.invalidArchive();
});
self.uploadElement.fileupload({
@ -167,7 +181,8 @@ $(function() {
self.fromResponse = function(data) {
self._fromPluginsResponse(data.plugins);
self._fromRepositoryResponse(data.repository)
self._fromRepositoryResponse(data.repository);
self._fromPipResponse(data.pip);
};
self._fromPluginsResponse = function(data) {
@ -188,6 +203,17 @@ $(function() {
}
};
self._fromPipResponse = function(data) {
self.pipAvailable(data.available);
if (data.available) {
self.pipCommand(data.command);
self.pipVersion(data.version);
} else {
self.pipCommand(undefined);
self.pipVersion(undefined);
}
};
self.requestData = function(includeRepo) {
if (!self.loginState.isAdmin()) {
return;
@ -343,6 +369,51 @@ $(function() {
self.requestData(true);
};
self.showPluginSettings = function() {
self._copyConfig();
self.configurationDialog.modal();
};
self.savePluginSettings = function() {
var pipCommand = self.config_pipCommand();
if (pipCommand != undefined && pipCommand.trim() == "") {
pipCommand = null;
}
var repository = self.config_repositoryUrl();
if (repository != undefined && repository.trim() == "") {
repository = null;
}
var repositoryTtl;
try {
repositoryTtl = parseInt(self.config_repositoryTtl());
} catch (ex) {
repositoryTtl = null;
}
var data = {
plugins: {
pluginmanager: {
repository: repository,
repository_ttl: repositoryTtl,
pip: pipCommand
}
}
};
self.settingsViewModel.saveData(data, function() {
self.configurationDialog.modal("hide");
self._copyConfig();
self.refreshRepository();
});
};
self._copyConfig = function() {
self.config_repositoryUrl(self.settingsViewModel.settings.plugins.pluginmanager.repository());
self.config_repositoryTtl(self.settingsViewModel.settings.plugins.pluginmanager.repository_ttl());
self.config_pipCommand(self.settingsViewModel.settings.plugins.pluginmanager.pip());
};
self.installed = function(data) {
return _.includes(self.installedPlugins(), data.id);
};

View file

@ -4,7 +4,20 @@
</div>
{% endmacro %}
{% macro pluginmanager_nopip() %}
<div class="alert" data-bind="visible: !pipAvailable()">{% trans %}
The <code>pip</code> command could not be detected automatically,
please configure it manually. No installation and uninstallation of plugin
packages is possible while <code>pip</code> is unavailable.
{% endtrans %}</div>
{% endmacro %}
{{ pluginmanager_printing() }}
{{ pluginmanager_nopip() }}
<div class="pull-right">
<button class="btn btn-small" data-bind="click: function() { $root.showPluginSettings(); }" title="{{ _('Plugin Configuration') }}"><i class="icon-wrench"></i></button>
</div>
<h3>{{ _('Installed Plugins') }}</h3>
@ -47,6 +60,10 @@
<button class="btn btn-block" data-bind="click: $root.showRepository">{{ _('Get More...') }}</button>
<p class="muted" data-bind="visible: pipAvailable()">
<small>Using pip at "<span data-bind="text: pipCommand"></span>" (Version <span data-bind="text: pipVersion"></span>)</small>
</p>
<div id="settings_plugin_pluginmanager_workingdialog" class="modal hide fade">
<div class="modal-header">
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
@ -67,6 +84,7 @@
</div>
<div class="modal-body">
{{ pluginmanager_printing() }}
{{ pluginmanager_nopip() }}
<h4 style="position: relative">
{{ _('... from the <a href="%(url)s" target="_blank">Plugin Repository</a>', url='http://plugins.octoprint.org') }}
<a class="dropdown-toggle pull-right" data-toggle="dropdown" href="#">
@ -167,3 +185,41 @@
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Close') }}</button>
</div>
</div>
<div id="settings_plugin_pluginmanager_configurationdialog" class="modal hide fade">
<div class="modal-header">
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
<h3>{{ _('Plugin Configuration') }}</h3>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="control-group" title="{{ _('pip command to use for managing plugins. You might have to configure this if auto detection fails.') }}">
<label class="control-label">{{ _('pip command') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: config_pipCommand" placeholder="{{ _('Autodetect') }}">
<span class="help-inline">{{ _('<strong>Only</strong> set this if OctoPrint cannot autodetect the path to <code>pip</code> to use for managing plugins.') }}</span>
</div>
</div>
<div class="control-group" title="{{ _('URL of the Plugin Repository to use. You should normally not have to change this.') }}">
<label class="control-label">{{ _('Repository URL') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: config_repositoryUrl">
</div>
</div>
<div class="control-group" title="{{ _('How long to cache repository data, in minutes. You should normally not have to change this.') }}">
<label class="control-label">{{ _('Repository cache TTL') }}</label>
<div class="controls">
<div class="input-append">
<input type="number" class="input-mini" data-bind="value: config_repositoryTtl">
<span class="add-on">min</span>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Cancel') }}</button>
<button class="btn btn-primary" data-bind="click: savePluginSettings" aria-hidden="true">{{ _('Save') }}</button>
</div>
</div>

View file

@ -27,14 +27,16 @@ class PipCaller(CommandlineCaller):
CommandlineCaller.__init__(self)
self._logger = logging.getLogger(__name__)
self._configured = configured
self.configured = configured
self._command = None
self._version = None
self._command, self._version = self._find_pip()
self.trigger_refresh()
self.refresh = False
self.on_log_call = lambda *args, **kwargs: None
self.on_log_stdout = lambda *args, **kwargs: None
self.on_log_stderr = lambda *args, **kwargs: None
def __le__(self, other):
return self.version is not None and self.version <= other
@ -60,10 +62,18 @@ class PipCaller(CommandlineCaller):
def available(self):
return self._command is not None
def trigger_refresh(self):
try:
self._command, self._version = self._find_pip()
except:
self._logger.exception("Error while discovering pip command")
self._command = None
self._version = None
self.refresh = False
def execute(self, *args):
if self.refresh:
self._command, self._version = self._find_pip()
self.refresh = False
self.trigger_refresh()
if self._command is None:
raise UnknownPip()
@ -81,7 +91,7 @@ class PipCaller(CommandlineCaller):
return self.call(command)
def _find_pip(self):
pip_command = self._configured
pip_command = self.configured
pip_version = None
if pip_command is None: