Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable and disable plugins via the API #4964

Merged
merged 17 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions InvenTree/plugin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,17 @@ def perform_create(self, serializer):


class PluginActivate(UpdateAPI):
"""Endpoint for activating a plugin."""
"""Endpoint for activating a plugin.

- PATCH: Activate a plugin

Pass a boolean value for the 'active' field.
If not provided, it is assumed to be True,
and the plugin will be activated.
"""

queryset = PluginConfig.objects.all()
serializer_class = PluginSerializers.PluginConfigEmptySerializer
serializer_class = PluginSerializers.PluginActivateSerializer
permission_classes = [IsSuperuser, ]

def get_object(self):
Expand All @@ -134,9 +141,8 @@ def get_object(self):

def perform_update(self, serializer):
"""Activate the plugin."""
instance = serializer.instance
instance.active = True
instance.save()

serializer.save()


class PluginSettingList(ListAPI):
Expand Down
20 changes: 20 additions & 0 deletions InvenTree/plugin/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ def save(self):

class PluginConfigEmptySerializer(serializers.Serializer):
"""Serializer for a PluginConfig."""
...


class PluginActivateSerializer(serializers.Serializer):
"""Serializer for activating or deactivating a plugin"""

model = PluginConfig

active = serializers.BooleanField(
required=False, default=True,
label=_('Activate Plugin'),
help_text=_('Activate this plugin')
)

def update(self, instance, validated_data):
"""Apply the new 'active' value to the plugin instance"""

instance.active = validated_data.get('active', True)
instance.save()
return instance


class PluginSettingSerializer(GenericReferencedSettingSerializer):
Expand Down
28 changes: 26 additions & 2 deletions InvenTree/templates/InvenTree/settings/plugin.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ <h4>{% trans "Plugins" %}</h4>
<div class='table-responsive'>
<table class='table table-striped table-condensed'>
<thead>
<col width='25'>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
<col width='25'>
{% endif %}
<tr>
<th></th>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
<th></th>
{% endif %}
<th>{% trans "Name" %}</th>
<th>{% trans "Key" %}</th>
<th>{% trans "Author" %}</th>
Expand All @@ -68,15 +76,31 @@ <h4>{% trans "Plugins" %}</h4>
<tbody>
{% plugin_list as pl_list %}
{% if pl_list %}
<tr><td colspan="6"><h6>{% trans 'Active plugins' %}</h6></td></tr>
<tr>
<td></td>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
<td></td>
{% endif %}
<td colspan="6">
<h5>{% trans 'Active plugins' %}</h5>
</td>
</tr>
{% for plugin_key, plugin in pl_list.items %}
{% include "InvenTree/settings/plugin_details.html" with plugin=plugin plugin_key=plugin_key %}
{% endfor %}
{% endif %}

{% inactive_plugin_list as in_pl_list %}
{% if in_pl_list %}
<tr><td colspan="6"><h6>{% trans 'Inactive plugins' %}</h6></td></tr>
<tr>
<td></td>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
<td></td>
{% endif %}
<td colspan="6">
<h5>{% trans 'Inactive plugins' %}</h5>
</td>
</tr>
{% for plugin_key, plugin in in_pl_list.items %}
{% include "InvenTree/settings/plugin_details.html" with plugin=plugin plugin_key=plugin_key %}
{% endfor %}
Expand Down
32 changes: 26 additions & 6 deletions InvenTree/templates/InvenTree/settings/plugin_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
{% else %}
<span class='fas fa-times-circle icon-red'></span>
{% endif %}
</td>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
<td>
{% url 'admin:plugin_pluginconfig_change' plugin.pk as url %}
{% include "admin_button.html" with url=url %}
</td>
{% endif %}
<td>

{% if plugin.human_name %}
{{ plugin.human_name }}
Expand Down Expand Up @@ -42,34 +50,46 @@
{% if plugin.website %}
<a href="{{ plugin.website }}"><span class="fas fa-globe"></span></a>
{% endif %}

</td>
<td>{{ plugin_key }}</td>
{% trans "Unavailable" as no_info %}
<td>
{% if plugin.author %}
{{ plugin.author }}
{% else %}
{% elif plugin.is_active %}
<em>{{ no_info }}</em>
{% endif %}
</td>
<td>
{% if plugin.pub_date %}
{% render_date plugin.pub_date %}
{% else %}
{% elif plugin.is_active %}
<em>{{ no_info }}</em>
{% endif %}
</td>
<td>
{% if plugin.version %}
{{ plugin.version }}
{% else %}
{% elif plugin.is_active %}
<em>{{ no_info }}</em>
{% endif %}
</td>
<td>
{% if user.is_staff and perms.plugin.change_pluginconfig %}
{% url 'admin:plugin_pluginconfig_change' plugin.pk as url %}
{% include "admin_button.html" with url=url %}

{% plugins_enabled as plug %}
{% if plug %}
{% if not plugin.is_builtin %}
{% if plugin.is_active %}
<button type='button' class='btn btn-danger btn-plugin-disable' pk='{{ plugin.pk }}' title='{% trans "Disable plugin" %}'>
<span class='fas fa-stop-circle'></span>
</button>
{% else %}
<button type='button' class='btn btn-success btn-plugin-enable' pk='{{ plugin.pk }}' title='{% trans "Enable plugin" %}'>
<span class='fas fa-play-circle'></span>
</button>
{% endif %}
{% endif %}
{% endif %}
</td>
</tr>
6 changes: 0 additions & 6 deletions InvenTree/templates/InvenTree/settings/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@

{% if user.is_staff %}
{% include "InvenTree/settings/settings_staff_js.html" %}
{% plugins_enabled as plug %}
{% if plug %}
$("#install-plugin").click(function() {
installPlugin();
});
{% endif %}
{% endif %}

enableSidebar('settings');
Expand Down
26 changes: 26 additions & 0 deletions InvenTree/templates/InvenTree/settings/settings_staff_js.html
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,29 @@
});
{% endif %}
});

// Javascript for plugins panel
onPanelLoad('plugin', function() {

{% plugins_enabled as plug %}

{% if plug %}
// Callback to install new plugin
$("#install-plugin").click(function() {
installPlugin();
});

// Callback to activate a plugin
$(".btn-plugin-enable").click(function() {
let pk = $(this).attr('pk');
activatePlugin(pk, true);
});

// Callback to deactivate a plugin
$(".btn-plugin-disable").click(function() {
let pk = $(this).attr('pk');
activatePlugin(pk, false);
});

{% endif %}
});
50 changes: 50 additions & 0 deletions InvenTree/templates/js/translated/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

/* exported
installPlugin,
activatePlugin,
locateItemOrLocation
*/

Expand All @@ -30,6 +31,55 @@ function installPlugin() {
}


/*
* Activate a specific plugin via the API
*/
function activatePlugin(plugin_id, active=true) {

let url = `{% url "api-plugin-list" %}${plugin_id}/activate/`;

let html = active ? `
<span class='alert alert-block alert-info'>
{% trans "Are you sure you want to enable this plugin?" %}
</span>
` : `
<span class='alert alert-block alert-danger'>
{% trans "Are you sure you want to disable this plugin?" %}
</span>
`;

constructForm(null, {
title: active ? '{% trans "Enable Plugin" %}' : '{% trans "Disable Plugin" %}',
preFormContent: html,
confirm: true,
submitText: active ? '{% trans "Enable" %}' : '{% trans "Disable" %}',
submitClass: active ? 'success' : 'danger',
onSubmit: function(_fields, opts) {
showModalSpinner(opts.modal);

inventreePut(
url,
{
active: active,
},
{
method: 'PATCH',
success: function() {
$(opts.modal).modal('hide');
addCachedAlert('{% trans "Plugin updated" %}', {style: 'success'});
location.reload();
},
error: function(xhr) {
$(opts.modal).modal('hide');
showApiError(xhr, url);
}
}
)
}
});
}


function locateItemOrLocation(options={}) {

if (!options.item && !options.location) {
Expand Down