@using BTCPayServer.Configuration @using BTCPayServer.Plugins @using BTCPayServer.Abstractions.Contracts @model BTCPayServer.Controllers.UIServerController.ListPluginsViewModel @inject BTCPayServerOptions BTCPayServerOptions @{ Layout = "_Layout"; ViewData.SetActivePage(ServerNavPages.Plugins); var installed = Model.Installed.ToDictionary(plugin => plugin.Identifier.ToLowerInvariant(), plugin => plugin.Version); var availableAndNotInstalledx = Model.Available .Where(plugin => !installed.ContainsKey(plugin.Identifier.ToLowerInvariant())) .GroupBy(plugin => plugin.Identifier) .ToList(); var availableAndNotInstalled = new List(); foreach (var availableAndNotInstalledItem in availableAndNotInstalledx) { var ordered = availableAndNotInstalledItem.OrderByDescending(plugin => plugin.Version).ToArray(); availableAndNotInstalled.Add(ordered.FirstOrDefault(availablePlugin => DependenciesMet(availablePlugin.Dependencies)) ?? ordered.FirstOrDefault()); } bool DependentOn(string plugin) { foreach (var installedPlugin in Model.Installed) { if (installedPlugin.Dependencies.Any(dep => dep.Identifier.Equals(plugin, StringComparison.InvariantCultureIgnoreCase))) { return true; } } var pendingInstalls = Model.Commands.Where(tuple => tuple.command != "uninstall").Select(tuple => tuple.plugin).Distinct(); foreach (var pendingInstall in pendingInstalls) { if (Model.Available.Any(availablePlugin => availablePlugin.Identifier.Equals(pendingInstall, StringComparison.InvariantCultureIgnoreCase) && availablePlugin.Dependencies.Any(dep => dep.Identifier.Equals(plugin, StringComparison.InvariantCultureIgnoreCase)))) { return true; } } return false; } bool DependencyMet(IBTCPayServerPlugin.PluginDependency dependency) { var plugin = dependency.Identifier.ToLowerInvariant(); var versionReq = dependency.Condition; if (!installed.ContainsKey(plugin) && !versionReq.Equals("!")) { return false; } if (installed.ContainsKey(plugin) && versionReq.Equals("!")) { return false; } var versionConditions = versionReq.Split("||", StringSplitOptions.RemoveEmptyEntries); return versionConditions.Any(s => { s = s.Trim(); var v = s.Substring(1); if (s[1] == '=') { v = s.Substring(2); } var parsedV = Version.Parse(v); switch (s) { case { } xx when xx.StartsWith(">="): return installed[plugin] >= parsedV; case { } xx when xx.StartsWith("<="): return installed[plugin] <= parsedV; case { } xx when xx.StartsWith(">"): return installed[plugin] > parsedV; case { } xx when xx.StartsWith("<"): return installed[plugin] >= parsedV; case { } xx when xx.StartsWith("^"): return installed[plugin] >= parsedV && installed[plugin].Major == parsedV.Major; case { } xx when xx.StartsWith("~"): return installed[plugin] >= parsedV && installed[plugin].Major == parsedV.Major && installed[plugin].Minor == parsedV.Minor; case { } xx when xx.StartsWith("!="): return installed[plugin] != parsedV; case { } xx when xx.StartsWith("=="): default: return installed[plugin] == parsedV; } }); } bool DependenciesMet(IBTCPayServerPlugin.PluginDependency[] dependencies) { foreach (var dependency in dependencies) { if (!DependencyMet(dependency)) { return false; } } return true; } } @if (Model.Disabled.Any()) {
Some plugins were disabled due to fatal errors. They may be incompatible with this version of BTCPay Server.
} @if (Model.Commands.Any()) {
You need to restart BTCPay Server in order to update your active plugins. @if (Model.CanShowRestart) {
}
} @if (Model.Installed.Any()) {

Installed Plugins

@foreach (var plugin in Model.Installed.Where(i => !i.SystemPlugin)) { Model.DownloadedPluginsByIdentifier.TryGetValue(plugin.Identifier, out var downloadInfo); var matchedAvailable = Model.Available.Where(availablePlugin => availablePlugin.Identifier == plugin.Identifier && availablePlugin.Version > plugin.Version).OrderByDescending(availablePlugin => availablePlugin.Version).ToArray(); var x = matchedAvailable.FirstOrDefault(availablePlugin => DependenciesMet(availablePlugin.Dependencies)) ?? matchedAvailable.FirstOrDefault(); var updateAvailable = matchedAvailable.Any(); var tabId = plugin.Identifier.ToLowerInvariant().Replace(".", "_");

@plugin.Name

@if (!string.IsNullOrEmpty(downloadInfo?.Author)) { by @downloadInfo.Author }
@plugin.Version @if (updateAvailable && x != null) {
@x.Version available
}
@if (updateAvailable) { Show current info Show update info }

@plugin.Description

@if (plugin.Dependencies.Any()) {
Dependencies
    @foreach (var dependency in plugin.Dependencies) {
  • @dependency @if (!DependencyMet(dependency)) { }
  • }
}
@if (updateAvailable && x != null) {

@x.Description

@if (x.Dependencies.Any()) {
Dependencies
    @foreach (var dependency in x.Dependencies) {
  • @dependency @if (!DependencyMet(dependency)) { }
  • }
}
} @if (plugin != null) {
Resources
    @if (downloadInfo?.Source is not null) {
  • Sources
  • } @if (!string.IsNullOrEmpty(downloadInfo?.Documentation)) {
  • Documentation
  • } else {
  • No documentation
  • }
}
@{ var pendingAction = Model.Commands.Any(tuple => tuple.plugin.Equals(plugin.Identifier, StringComparison.InvariantCultureIgnoreCase)); } @if (pendingAction || (updateAvailable && x != null && DependenciesMet(x.Dependencies)) || !DependentOn(plugin.Identifier)) { }
}
} @if (availableAndNotInstalled.Any()) {

Available Plugins

@foreach (var plugin in availableAndNotInstalled) { var recommended = BTCPayServerOptions.RecommendedPlugins.Contains(plugin.Identifier.ToLowerInvariant()); var disabled = Model.Disabled?.Contains(plugin.Identifier) ?? false;

@plugin.Name

@if (!string.IsNullOrEmpty(plugin.Author)) { by @plugin.Author }
@plugin.Version @if (disabled) {
Disabled
} else if (recommended) {
Recommended
}

@plugin.Description

@if (plugin.Dependencies?.Any() is true) {
Dependencies
    @foreach (var dependency in plugin.Dependencies) {
  • @dependency @if (!DependencyMet(dependency)) { }
  • }
} @if (plugin != null) {
Resources
    @if (plugin.Source is not null) {
  • Sources
  • } @if (!string.IsNullOrEmpty(plugin.Documentation)) {
  • Documentation
  • } else {
  • No documentation
  • }
}
}
}

Upload Plugin

Add plugin manually

This is an extremely dangerous operation!
Only upload plugins from trusted sources.
@if (Model.Commands.Any()) {

Pending Action

Pending actions

    @foreach (var extComm in Model.Commands.GroupBy(tuple => tuple.plugin)) {
  • @extComm.Key
  • }
} @if (Model.Disabled.Any()) {

Disabled Plugins

Disabled plugins

    @foreach (var d in Model.Disabled) {
  • @d
  • }
}