forked from CGM_Public/pretix_original
Add search and links to plugin settings (#4854)
* Show links to plugin views and settings in plugin list and in success message after activating a plugin * Fix menu highlighting in payment provider settings * Specify settings_links and navigation_links for built-in plugins * Add link to payment plugins from payment settings * Add client-side search and "View only active plugins" for plugins page
This commit is contained in:
@@ -43,7 +43,6 @@ from django.dispatch import receiver
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
@@ -286,7 +285,7 @@ class OrderChangedSplit(OrderChangeLogEntryType):
|
||||
_('Position #{posid} ({old_item}, {old_price}) split into new order: {order}'),
|
||||
old_item=escape(old_item),
|
||||
posid=data.get('positionid', '?'),
|
||||
order=format_html(mark_safe('<a href="{}">{}</a>'), url, data['new_order']),
|
||||
order=format_html('<a href="{}">{}</a>', url, data['new_order']),
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
)
|
||||
|
||||
@@ -303,7 +302,7 @@ class OrderChangedSplitFrom(OrderLogEntryType):
|
||||
})
|
||||
return format_html(
|
||||
_('This order has been created by splitting the order {order}'),
|
||||
order=format_html(mark_safe('<a href="{}">{}</a>'), url, data['original_order']),
|
||||
order=format_html('<a href="{}">{}</a>', url, data['original_order']),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings.payment',
|
||||
'active': url.url_name in ('event.settings.payment', 'event.settings.payment.provider'),
|
||||
},
|
||||
{
|
||||
'label': _('Plugins'),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p>{{ plugin.description|safe }}</p>
|
||||
<p class="plugin-description">{{ plugin.description|safe }}</p>
|
||||
{% if plugin.restricted and plugin.module not in request.event.settings.allowed_restricted_plugins %}
|
||||
<p class="text-muted">
|
||||
<span class="fa fa-info-circle" aria-hidden="true"></span>
|
||||
|
||||
@@ -48,19 +48,19 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<td colspan="4">
|
||||
<br>
|
||||
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
|
||||
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
|
||||
There are no payment providers available. Please go to the
|
||||
<a {{ plugin_settings_href }}>plugin settings</a> and activate one or more payment plugins.
|
||||
{% endblocktrans %}
|
||||
<a href="{{ plugin_settings_url }}#tab-0-1-open" class="btn btn-default">
|
||||
<i class="fa fa-plus"></i> {% trans "Enable additional payment plugins" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Deadlines" %}</legend>
|
||||
|
||||
@@ -10,20 +10,40 @@
|
||||
software functionality, connect your event to third-party services, or apply other forms of customizations.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if "success" in request.GET %}
|
||||
<div class="alert alert-success">
|
||||
{% trans "Your changes have been saved." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-lg-10">
|
||||
<p><input type="search" id="plugin_search_input" class="form-control" placeholder="{% trans "Search" %}"></p>
|
||||
</div>
|
||||
<div class="col-lg-2 text-right">
|
||||
<p class="btn-group btn-group-flex" data-toggle="buttons">
|
||||
<label class="btn btn-primary-if-active active"><input type="radio" name="plugin_state_filter" value="all" checked> {% trans "All" %}</label>
|
||||
<label class="btn btn-primary-if-active"><input type="radio" name="plugin_state_filter" value="active"> {% trans "Active" %}</label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form action="" method="post" class="form-horizontal form-plugins">
|
||||
{% csrf_token %}
|
||||
{% if "success" in request.GET %}
|
||||
<div class="alert alert-success">
|
||||
{% trans "Your changes have been saved." %}
|
||||
<div id="plugin_search_results" class="panel panel-default collapse">
|
||||
<div class="panel-heading">
|
||||
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
{% trans "Search results" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="tabbed-form">
|
||||
<div class="panel-body">
|
||||
<div class="plugin-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="plugin_tabs"><div class="tabbed-form">
|
||||
{% for cat, catlabel, plist, has_pictures in plugins %}
|
||||
<fieldset>
|
||||
<fieldset data-plugin-category="{{ cat }}" data-plugin-category-label="{{ catlabel }}">
|
||||
<legend>{{ catlabel }}</legend>
|
||||
<div class="plugin-list">
|
||||
{% for plugin in plist %}
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}">
|
||||
{% for plugin, is_active, settings_links, navigation_links in plist %}
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}" data-plugin-module="{{ plugin.module }}" data-plugin-name="{{ plugin.name }}">
|
||||
{% if plugin.featured %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
@@ -49,8 +69,8 @@
|
||||
{% if show_meta %}
|
||||
<span class="text-muted text-sm">{{ plugin.version }}</span>
|
||||
{% endif %}
|
||||
{% if plugin.module in plugins_active %}
|
||||
<span class="label label-success">
|
||||
{% if is_active %}
|
||||
<span class="label label-success" data-is-active>
|
||||
<span class="fa fa-check" aria-hidden="true"></span>
|
||||
{% trans "Active" %}
|
||||
</span>
|
||||
@@ -66,8 +86,32 @@
|
||||
<div class="plugin-action">
|
||||
<span class="text-muted">{% trans "Not available" %}</span>
|
||||
</div>
|
||||
{% elif plugin.module in plugins_active %}
|
||||
{% elif is_active %}
|
||||
<div class="plugin-action flip">
|
||||
{% if navigation_links %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle{% if plugin.featured %} btn-lg{% endif %}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{% trans "Open plugin settings" %}">
|
||||
<span class="fa fa-compass"></span> {% trans "Go to" %} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% for link in navigation_links %}
|
||||
<li><a href="{{ link.0 }}">{{ link.1 }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if settings_links %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle{% if plugin.featured %} btn-lg{% endif %}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{% trans "Open plugin settings" %}">
|
||||
<span class="fa fa-cog"></span> {% trans "Settings" %} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% for link in settings_links %}
|
||||
<li><a href="{{ link.0 }}">{{ link.1 }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-default{% if plugin.featured %} btn-lg{% endif %}" name="plugin:{{ plugin.module }}"
|
||||
value="disable">{% trans "Disable" %}</button>
|
||||
</div>
|
||||
@@ -86,6 +130,7 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div></div>
|
||||
</form>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/plugins.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import operator
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
@@ -62,8 +63,9 @@ from django.http import (
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.html import conditional_escape, format_html
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
||||
from django.views.generic import FormView, ListView
|
||||
@@ -109,6 +111,8 @@ from ...helpers.format import (
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
from . import CreateView, PaginationMixin, UpdateView
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EventSettingsViewMixin:
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -339,12 +343,29 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
return self.request.event
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
def available_plugins(self, event):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return (p for p in get_all_plugins(event) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True))
|
||||
|
||||
def prepare_links(self, pluginmeta, key):
|
||||
links = getattr(pluginmeta, key, [])
|
||||
try:
|
||||
return [
|
||||
(
|
||||
reverse(urlname, kwargs={"organizer": self.request.organizer.slug, "event": self.request.event.slug, **kwargs}),
|
||||
" > ".join(map(str, linktext)) if isinstance(linktext, tuple) else linktext,
|
||||
) for linktext, urlname, kwargs in links
|
||||
]
|
||||
except:
|
||||
logger.exception('Failed to resolve settings links.')
|
||||
return []
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
plugins = [p for p in get_all_plugins(self.object) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True)]
|
||||
plugins = list(self.available_plugins(self.object))
|
||||
|
||||
order = [
|
||||
'FEATURE',
|
||||
'PAYMENT',
|
||||
@@ -375,12 +396,18 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
)
|
||||
plugins_grouped = [(c, list(plist)) for c, plist in plugins_grouped]
|
||||
|
||||
active_plugins = self.object.get_plugins()
|
||||
|
||||
def plugin_details(plugin):
|
||||
is_active = plugin.module in active_plugins
|
||||
settings_links = self.prepare_links(plugin, 'settings_links') if is_active else None
|
||||
navigation_links = self.prepare_links(plugin, 'navigation_links') if is_active else None
|
||||
return (plugin, is_active, settings_links, navigation_links)
|
||||
context['plugins'] = sorted([
|
||||
(c, labels.get(c, c), plist, any(getattr(p, 'picture', None) for p in plist))
|
||||
(c, labels.get(c, c), map(plugin_details, plist), any(getattr(p, 'picture', None) for p in plist))
|
||||
for c, plist
|
||||
in plugins_grouped
|
||||
], key=lambda c: (order.index(c[0]), c[1]) if c[0] in order else (999, str(c[1])))
|
||||
context['plugins_active'] = self.object.get_plugins()
|
||||
context['show_meta'] = settings.PRETIX_PLUGINS_SHOW_META
|
||||
return context
|
||||
|
||||
@@ -390,13 +417,10 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
self.object = self.get_object()
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins(self.object)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
p.module: p for p in self.available_plugins(self.object)
|
||||
}
|
||||
|
||||
with transaction.atomic():
|
||||
@@ -404,19 +428,38 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
if key.startswith("plugin:"):
|
||||
module = key.split(":")[1]
|
||||
if value == "enable" and module in plugins_available:
|
||||
if getattr(plugins_available[module], 'restricted', False):
|
||||
pluginmeta = plugins_available[module]
|
||||
if getattr(pluginmeta, 'restricted', False):
|
||||
if module not in request.event.settings.allowed_restricted_plugins:
|
||||
continue
|
||||
|
||||
self.request.event.log_action('pretix.event.plugins.enabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.enable_plugin(module, allow_restricted=request.event.settings.allowed_restricted_plugins)
|
||||
|
||||
links = self.prepare_links(pluginmeta, 'settings_links')
|
||||
if links:
|
||||
info = [
|
||||
'<p>',
|
||||
format_html(_('The plugin {} is now active, you can configure it here:'),
|
||||
format_html("<strong>{}</strong>", pluginmeta.name)),
|
||||
'</p><p>',
|
||||
] + [
|
||||
format_html('<a href="{}" class="btn btn-default">{}</a> ', url, text)
|
||||
for url, text in links
|
||||
] + ['</p>']
|
||||
else:
|
||||
info = [
|
||||
format_html(_('The plugin {} is now active.'),
|
||||
format_html("<strong>{}</strong>", pluginmeta.name)),
|
||||
]
|
||||
messages.success(self.request, mark_safe("".join(info)))
|
||||
else:
|
||||
self.request.event.log_action('pretix.event.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.disable_plugin(module)
|
||||
messages.success(self.request, _('The plugin has been disabled.'))
|
||||
self.object.save()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
|
||||
@@ -38,6 +38,9 @@ class AutoCheckinApp(AppConfig):
|
||||
description = _(
|
||||
"Automatically check-in specific tickets after they have been sold."
|
||||
)
|
||||
navigation_links = [
|
||||
((_("Check-in"), _("Auto check-in")), "plugins:autocheckin:index", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -37,6 +37,9 @@ class BadgesApp(AppConfig):
|
||||
featured = True
|
||||
description = _("Automatically generate badges or name tags for your attendees. You can download the badges in the "
|
||||
"backend or automatically print them with our check-in apps.")
|
||||
settings_links = [
|
||||
(_("Badges"), "plugins:badges:index", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -38,6 +38,13 @@ class BankTransferApp(AppConfig):
|
||||
version = version
|
||||
description = _("Accept payments from your customers using classical wire transfer methods with your own "
|
||||
"bank account.")
|
||||
settings_links = [
|
||||
((_("Payment"), _("Bank transfer")), "control:event.settings.payment.provider", {"provider": "banktransfer"}),
|
||||
]
|
||||
navigation_links = [
|
||||
((_("Bank transfer"), _("Import bank data")), "plugins:banktransfer:import", {}),
|
||||
((_("Bank transfer"), _("Export refunds")), "plugins:banktransfer:refunds.list", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -35,3 +35,6 @@ class ManualPaymentApp(AppConfig):
|
||||
version = version
|
||||
category = 'PAYMENT'
|
||||
description = _("A fully customizable payment method for manual processing.")
|
||||
settings_links = [
|
||||
((_("Payment"), _("Manual payment")), "control:event.settings.payment.provider", {"provider": "manual"}),
|
||||
]
|
||||
|
||||
@@ -41,6 +41,9 @@ class Paypal2App(AppConfig):
|
||||
"also offer payments in a variety of local payment methods such as eps, iDEAL, and "
|
||||
"many more to your customers - they don't even need a PayPal account. PayPal is one of the "
|
||||
"most popular payment methods world-wide.")
|
||||
settings_links = [
|
||||
((_("Payment"), _("PayPal")), "control:event.settings.payment.provider", {"provider": "paypal_settings"}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -36,6 +36,9 @@ class ReturnURLApp(AppConfig):
|
||||
category = 'API'
|
||||
description = _("This plugin allows to link to payments and redirect back afterwards. This is useful in "
|
||||
"combination with our API.")
|
||||
settings_links = [
|
||||
((_("Settings"), _("Redirection")), "plugins:returnurl:settings", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -35,6 +35,9 @@ class StatisticsApp(AppConfig):
|
||||
version = version
|
||||
category = 'FEATURE'
|
||||
description = _("Get a birds-eye view of your event sales with graphical statistics.")
|
||||
navigation_links = [
|
||||
((_("Orders"), _("Statistics")), "plugins:statistics:index", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -40,6 +40,9 @@ class StripeApp(AppConfig):
|
||||
description = _("Accept payments via Stripe, a globally popular payment service provider. Stripe supports "
|
||||
"payments via credit cards as well as many local payment methods such as iDEAL, Alipay,"
|
||||
"and many more.")
|
||||
settings_links = [
|
||||
((_("Payment"), _("Stripe")), "control:event.settings.payment.provider", {"provider": "stripe_settings"}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals, tasks # NOQA
|
||||
|
||||
@@ -51,6 +51,9 @@ class TicketOutputPdfApp(AppConfig):
|
||||
featured = True
|
||||
description = _("Issue tickets as PDF files, usable on any device. Our drag-and-drop editor allows you to "
|
||||
"customize the layout of the PDF files to your brand.")
|
||||
settings_links = [
|
||||
((_("Settings"), _("Tickets")), "control:event.settings.tickets", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -36,6 +36,9 @@ class WebCheckinApp(AppConfig):
|
||||
experimental = True
|
||||
category = "FEATURE"
|
||||
description = _("Turn your browser into a check-in device to perform access control.")
|
||||
navigation_links = [
|
||||
((_("Check-in"), _("Web Check-in")), "plugins:webcheckin:index", {}),
|
||||
]
|
||||
|
||||
def ready(self):
|
||||
from . import signals # NOQA
|
||||
|
||||
@@ -114,6 +114,10 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
background: transparent;
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
.btn-group-flex { display: flex; }
|
||||
.btn-group-flex > .btn { flex: 1; }
|
||||
|
||||
.panel-heading {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
84
src/pretix/static/pretixcontrol/js/ui/plugins.js
Normal file
84
src/pretix/static/pretixcontrol/js/ui/plugins.js
Normal file
@@ -0,0 +1,84 @@
|
||||
$(function() {
|
||||
var plugins = $(".plugin-container").toArray().map(function(el) {
|
||||
return {
|
||||
sortName: el.getAttribute('data-plugin-name').toLowerCase().replace(/pretix /g, ''),
|
||||
name: el.getAttribute('data-plugin-name').toLowerCase(),
|
||||
module: el.getAttribute('data-plugin-module').toLowerCase(),
|
||||
description: $(el).find('.plugin-description').text().toLowerCase(),
|
||||
html: el.outerHTML,
|
||||
category: $(el).closest('[data-plugin-category]').attr('data-plugin-category'),
|
||||
categoryLabel: $(el).closest('[data-plugin-category]').attr('data-plugin-category-label'),
|
||||
active: !!$(el).has('[data-is-active]').length,
|
||||
}
|
||||
});
|
||||
function SearchMatcher(term, fields) {
|
||||
this.searchFor = term.toLowerCase().split(/\s+/);
|
||||
this.fields = fields;
|
||||
}
|
||||
function inStringRanked(haystack, needle) {
|
||||
let pos = -1, rank = 0;
|
||||
do {
|
||||
pos = haystack.indexOf(needle, pos + 1);
|
||||
if (pos !== -1) rank = 10;
|
||||
if (pos === 0 || haystack.charCodeAt(pos - 1) <= 47)
|
||||
return 15; // string start or word start (=char before match is special char)
|
||||
} while (pos !== -1);
|
||||
return rank;
|
||||
}
|
||||
SearchMatcher.prototype.isMatch = function(obj) {
|
||||
let rank = 0;
|
||||
for(let j = 0; j < this.searchFor.length; j++) {
|
||||
var searchFor = this.searchFor[j];
|
||||
for(let i = this.fields.length - 1; i >= 0; i--) {
|
||||
var result = inStringRanked(obj[this.fields[i]], searchFor);
|
||||
if (result) {
|
||||
rank += (i + 1) * result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rank;
|
||||
}
|
||||
function strcmp(a, b) {
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
}
|
||||
var $results_box = $("#plugin_search_results");
|
||||
var $plugin_tabs = $("#plugin_tabs");
|
||||
var $results = $("#plugin_search_results .plugin-list");
|
||||
function search() {
|
||||
$results.html("");
|
||||
var value = $("#plugin_search_input").val();
|
||||
var only_active = $("input[name=plugin_state_filter][value=active]").prop("checked");
|
||||
if (!value && !only_active) {
|
||||
$results_box.hide(); $plugin_tabs.show();
|
||||
return;
|
||||
}
|
||||
$results_box.show(); $plugin_tabs.hide();
|
||||
var matcher = new SearchMatcher(value, ["description", "module", "name"]);
|
||||
var matches = [];
|
||||
for(const plugin of plugins) {
|
||||
if (only_active && !plugin.active) continue;
|
||||
var rank = matcher.isMatch(plugin);
|
||||
if (!rank) continue;
|
||||
matches.push([rank, plugin]);
|
||||
}
|
||||
matches.sort(function (a,b) { return (b[0]-a[0]) || strcmp(a[1].sortName, b[1].sortName); })
|
||||
$results.append(matches.map(function(res) { return $(res[1].html).prepend('<span class="pull-right">' + res[1].categoryLabel + '</span>'); }))
|
||||
$results.find(".panel-body, .panel, .featured-plugin, .btn-lg").removeClass("panel-body panel featured-plugin btn-lg");
|
||||
if (matches.length === 0) {
|
||||
$results.append(gettext("No results"));
|
||||
}
|
||||
}
|
||||
$("#plugin_search_input").on("input", search);
|
||||
$("input[name=plugin_state_filter]").on("change", search);
|
||||
$results_box.find("button.close").on("click", function() {
|
||||
$("input[name=plugin_state_filter][value=all]").prop("checked", true).trigger("click");
|
||||
$("#plugin_search_input").val("").trigger("input");
|
||||
});
|
||||
if (location.search) {
|
||||
var search = new URLSearchParams(location.search);
|
||||
if (search.has('q')) {
|
||||
$("#plugin_search_input").val(search.get("q")).trigger("input");
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -770,6 +770,9 @@ h1 .label {
|
||||
flex-shrink: 0;
|
||||
padding-top: 15px;
|
||||
}
|
||||
.plugin-container:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
.plugin-container:not(.featured-plugin) + .plugin-container {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user