2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 13:05:42 +00:00

Enable and disable plugins via the API (#4964)

* Cleanup plugin settings page

- Template adjustments

* Activate plugin directly via API

* Update plugin activate endpoint

- Allow plugin to be deactivated also
- Default value = True if not provided

* Update front-end / js

- Allow same JS method to either enable or disable a plugin

* Hide info for plugins which are not active

* remove duplicated column

* Tweak serializer docstring

* Fix typo

* Add extra data to plugin serializer

- is_builtin
- is_sample

* Some backend cleanup

- Don't stringify null values
- Don't replace None with "Unavailable"

* front-end table for rendering plugins

* Change default sorting

- Show active plugins first

* Fix button callback

* Remove old template

* JS linting

* More linting
This commit is contained in:
Oliver
2023-06-05 12:19:56 +10:00
committed by GitHub
parent 0c47552199
commit 45ec7b9728
10 changed files with 276 additions and 120 deletions

View File

@ -52,37 +52,15 @@
</div>
{% endif %}
<div id='plugin-button-toolbar'>
<div class='button-toolbar container-fluid'>
<div class='btn-group' role='group'>
{% include "filter_list.html" with id="plugins" %}
</div>
</div>
</div>
<div class='table-responsive'>
<table class='table table-striped table-condensed'>
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Key" %}</th>
<th>{% trans "Author" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Version" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% plugin_list as pl_list %}
{% if pl_list %}
<tr><td colspan="6"><h6>{% trans 'Active plugins' %}</h6></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>
{% for plugin_key, plugin in in_pl_list.items %}
{% include "InvenTree/settings/plugin_details.html" with plugin=plugin plugin_key=plugin_key %}
{% endfor %}
{% endif %}
</tbody>
</table>
<table class='table table-striped table-condensed' id='plugin-table' data-toolbar='#plugin-button-toolbar'></table>
</div>
{% plugin_errors as pl_errors %}

View File

@ -1,75 +0,0 @@
{% load inventree_extras %}
{% load i18n %}
<tr>
<td>
{% if plugin.is_active %}
<span class='fas fa-check-circle icon-green'></span>
{% else %}
<span class='fas fa-times-circle icon-red'></span>
{% endif %}
{% if plugin.human_name %}
{{ plugin.human_name }}
{% elif plugin.title %}
{{ plugin.title }}
{% elif plugin.name %}
{{ plugin.name }}
{% endif %}
{% define plugin.registered_mixins as mixin_list %}
{% if mixin_list %}
{% for mixin in mixin_list %}
<a class='sidebar-selector' id='select-plugin-{{ plugin_key }}' data-bs-parent="#sidebar">
<span class='badge bg-dark badge-right rounded-pill'>{{ mixin.human_name }}</span>
</a>
{% endfor %}
{% endif %}
{% if plugin.is_builtin %}
<a class='sidebar-selector' id='select-plugin-{{ plugin_key }}' data-bs-parent='#sidebar'>
<span class='badge bg-success rounded-pill badge-right'>{% trans "Builtin" %}</span>
</a>
{% endif %}
{% if plugin.is_sample %}
<a class='sidebar-selector' id='select-plugin-{{ plugin_key }}' data-bs-parent="#sidebar">
<span class='badge bg-info rounded-pill badge-right'>{% trans "Sample" %}</span>
</a>
{% endif %}
{% 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 %}
<em>{{ no_info }}</em>
{% endif %}
</td>
<td>
{% if plugin.pub_date %}
{% render_date plugin.pub_date %}
{% else %}
<em>{{ no_info }}</em>
{% endif %}
</td>
<td>
{% if plugin.version %}
{{ plugin.version }}
{% else %}
<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 %}
{% endif %}
</td>
</tr>

View File

@ -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');

View File

@ -385,3 +385,21 @@ onPanelLoad('stocktake', function() {
});
{% endif %}
});
// Javascript for plugins panel
onPanelLoad('plugin', function() {
{% plugins_enabled as plug %}
loadPluginTable('#plugin-table', {
custom: {% js_bool plug %},
});
{% if plug %}
// Callback to install new plugin
$("#install-plugin").click(function() {
installPlugin();
});
{% endif %}
});

View File

@ -2,17 +2,149 @@
{% load inventree_extras %}
/* globals
addCachedAlert,
constructForm,
showMessage,
inventreeGet,
inventreePut,
loadTableFilters,
makeIconButton,
renderDate,
setupFilterList,
showApiError,
showModalSpinner,
wrapButtons,
*/
/* exported
activatePlugin,
installPlugin,
loadPluginTable,
locateItemOrLocation
*/
/*
* Load the plugin table
*/
function loadPluginTable(table, options={}) {
options.params = options.params || {};
let filters = loadTableFilters('plugins', options.params);
setupFilterList('plugins', $(table), '#filter-list-plugins');
$(table).inventreeTable({
url: '{% url "api-plugin-list" %}',
name: 'plugins',
original: options.params,
queryParams: filters,
sortable: true,
formatNoMatches: function() {
return '{% trans "No plugins found" %}';
},
columns: [
{
field: 'active',
title: '',
sortable: true,
formatter: function(value, row) {
if (row.active) {
return `<span class='fa fa-check-circle icon-green' title='{% trans "This plugin is active" %}'></span>`;
} else {
return `<span class='fa fa-times-circle icon-red' title ='{% trans "This plugin is not active" %}'></span>`;
}
}
},
{
field: 'name',
title: '{% trans "Plugin Description" %}',
sortable: true,
formatter: function(value, row) {
let html = '';
if (row.active) {
html += `<strong>${value}</strong>`;
if (row.meta && row.meta.description) {
html += ` - <small>${row.meta.description}</small>`;
}
} else {
html += `<em>${value}</em>`;
}
if (row.is_builtin) {
html += `<span class='badge bg-success rounded-pill badge-right'>{% trans "Builtin" %}</span>`;
}
if (row.is_sample) {
html += `<span class='badge bg-info rounded-pill badge-right'>{% trans "Sample" %}</span>`;
}
return html;
}
},
{
field: 'meta.version',
title: '{% trans "Version" %}',
formatter: function(value, row) {
if (value) {
let html = value;
if (row.meta.pub_date) {
html += `<span class='badge rounded-pill bg-dark float-right'>${renderDate(row.meta.pub_date)}</span>`;
}
return html;
} else {
return '-';
}
}
},
{
field: 'meta.author',
title: '{% trans "Author" %}',
},
{
field: 'actions',
title: '',
formatter: function(value, row) {
let buttons = '';
// Check if custom plugins are enabled for this instance
if (options.custom && !row.is_builtin) {
if (row.active) {
buttons += makeIconButton('fa-stop-circle icon-red', 'btn-plugin-disable', row.pk, '{% trans "Disable Plugin" %}');
} else {
buttons += makeIconButton('fa-play-circle icon-green', 'btn-plugin-enable', row.pk, '{% trans "Enable Plugin" %}');
}
}
return wrapButtons(buttons);
}
},
]
});
if (options.custom) {
// Callback to activate a plugin
$(table).on('click', '.btn-plugin-enable', function() {
let pk = $(this).attr('pk');
activatePlugin(pk, true);
});
// Callback to deactivate a plugin
$(table).on('click', '.btn-plugin-disable', function() {
let pk = $(this).attr('pk');
activatePlugin(pk, false);
});
}
}
/*
* Install a new plugin via the API
*/
function installPlugin() {
constructForm(`/api/plugins/install/`, {
method: 'POST',
@ -30,6 +162,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) {

View File

@ -426,6 +426,17 @@ function getPartTestTemplateFilters() {
}
// Return a dictionary of filters for the "plugins" table
function getPluginTableFilters() {
return {
active: {
type: 'bool',
title: '{% trans "Active" %}',
},
};
}
// Return a dictionary of filters for the "build" table
function getBuildTableFilters() {
@ -774,6 +785,8 @@ function getAvailableTableFilters(tableKey) {
return getPartTableFilters();
case 'parttests':
return getPartTestTemplateFilters();
case 'plugins':
return getPluginTableFilters();
case 'purchaseorder':
return getPurchaseOrderFilters();
case 'purchaseorderlineitem':