Merge Admin Plugins and Packages into single management page

This commit is contained in:
djmaze 2021-07-16 13:57:04 +02:00
parent d69743cee5
commit 79945d45b4
20 changed files with 142 additions and 322 deletions

View file

@ -38,13 +38,6 @@ class RemoteAdminFetch extends AbstractFetchRemote {
});
}
/**
* @param {?Function} fCallback
*/
pluginList(fCallback) {
this.defaultRequest(fCallback, 'AdminPluginList');
}
/**
* @param {?Function} fCallback
*/

View file

@ -7,7 +7,6 @@ import { DomainsAdminSettings } from 'Settings/Admin/Domains';
import { LoginAdminSettings } from 'Settings/Admin/Login';
import { ContactsAdminSettings } from 'Settings/Admin/Contacts';
import { SecurityAdminSettings } from 'Settings/Admin/Security';
import { PluginsAdminSettings } from 'Settings/Admin/Plugins';
import { PackagesAdminSettings } from 'Settings/Admin/Packages';
import { AboutAdminSettings } from 'Settings/Admin/About';
import { BrandingAdminSettings } from 'Settings/Admin/Branding';
@ -38,7 +37,6 @@ export class SettingsAdminScreen extends AbstractSettingsScreen {
[BrandingAdminSettings, 'Branding'],
[ContactsAdminSettings, 'Contacts'],
[SecurityAdminSettings, 'Security'],
[PluginsAdminSettings, 'Plugins'],
[PackagesAdminSettings, 'Packages'],
[AboutAdminSettings, 'About'],
].forEach(item =>

View file

@ -6,6 +6,10 @@ import { getNotification } from 'Common/Translator';
import { PackageAdminStore } from 'Stores/Admin/Package';
import Remote from 'Remote/Admin/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { PluginPopupView } from 'View/Popup/Plugin';
import { SettingsGet } from 'Common/Globals';
export class PackagesAdminSettings {
constructor() {
this.packagesError = ko.observable('');
@ -13,24 +17,42 @@ export class PackagesAdminSettings {
this.packages = PackageAdminStore;
this.packagesCurrent = ko.computed(() =>
PackageAdminStore.filter(item => item && item.installed && !item.compare)
PackageAdminStore.filter(item => item && item.installed && !item.canBeUpdated)
);
this.packagesAvailableForUpdate = ko.computed(() =>
PackageAdminStore.filter(item => item && item.installed && !!item.compare)
PackageAdminStore.filter(item => item && item.installed && !!item.canBeUpdated)
);
this.packagesAvailableForInstallation = ko.computed(() =>
PackageAdminStore.filter(item => item && !item.installed)
);
this.visibility = ko.computed(() => (PackageAdminStore.loading() ? 'visible' : 'hidden'));
this.enabledPlugins = ko.observable(!!SettingsGet('EnabledPlugins'));
this.enabledPlugins.subscribe(value =>
Remote.saveAdminConfig(null, {
EnabledPlugins: value ? 1 : 0
})
);
}
onShow() {
this.packagesError('');
}
onBuild() {
onBuild(oDom) {
PackageAdminStore.fetch();
oDom.addEventListener('click', event => {
// configurePlugin
let el = event.target.closestWithin('.package-configure', oDom),
data = el ? ko.dataFor(el) : 0;
data && Remote.plugin((iError, data) => iError || showScreenPopup(PluginPopupView, [data.Result]), data.name)
// disablePlugin
el = event.target.closestWithin('.package-active', oDom);
data = el ? ko.dataFor(el) : 0;
data && this.disablePlugin(data);
});
}
requestHelper(packageToRequest, install) {
@ -70,4 +92,20 @@ export class PackagesAdminSettings {
Remote.packageInstall(this.requestHelper(packageToInstall, true), packageToInstall);
}
}
disablePlugin(plugin) {
let b = !plugin.enabled();
plugin.enabled(b);
Remote.pluginDisable((iError, data) => {
if (iError) {
this.packagesError(
(Notification.UnsupportedPluginPackage === iError && data && data.ErrorMessage)
? data.ErrorMessage
: getNotification(iError)
);
}
// PackageAdminStore.fetch();
}, plugin.name, !b);
}
}

View file

@ -1,63 +0,0 @@
import ko from 'ko';
import { Notification } from 'Common/Enums';
import { SettingsGet } from 'Common/Globals';
import { getNotification } from 'Common/Translator';
import { showScreenPopup } from 'Knoin/Knoin';
import { PluginAdminStore } from 'Stores/Admin/Plugin';
import Remote from 'Remote/Admin/Fetch';
import { PluginPopupView } from 'View/Popup/Plugin';
export class PluginsAdminSettings
{
constructor() {
this.enabledPlugins = ko.observable(!!SettingsGet('EnabledPlugins'));
this.plugins = PluginAdminStore;
this.pluginsError = PluginAdminStore.error;
this.enabledPlugins.subscribe(value =>
Remote.saveAdminConfig(null, {
EnabledPlugins: value ? 1 : 0
})
);
}
disablePlugin(plugin) {
let b = !plugin.disabled();
plugin.disabled(b);
Remote.pluginDisable((iError, data) => {
if (iError) {
PluginAdminStore.error(
(Notification.UnsupportedPluginPackage === iError && data && data.ErrorMessage)
? data.ErrorMessage
: getNotification(iError)
);
}
PluginAdminStore.fetch();
}, plugin.name, b);
}
configurePlugin(plugin) {
Remote.plugin((iError, data) => iError || showScreenPopup(PluginPopupView, [data.Result]), plugin.name);
}
onBuild(oDom) {
oDom.addEventListener('click', event => {
let el = event.target.closestWithin('.configure-plugin-action', oDom);
el && ko.dataFor(el) && this.configurePlugin(ko.dataFor(el));
el = event.target.closestWithin('.disabled-plugin', oDom);
el && ko.dataFor(el) && this.disablePlugin(ko.dataFor(el));
});
}
onShow() {
PluginAdminStore.error('');
PluginAdminStore.fetch();
}
}

View file

@ -8,35 +8,34 @@ PackageAdminStore.real = ko.observable(true);
PackageAdminStore.loading = ko.observable(false);
//PackageAdminStore.error = ko.observable('');
PackageAdminStore.fetch = () => {
PackageAdminStore.loading(true);
Remote.packagesList((iError, data) => {
PackageAdminStore.loading(false);
if (!iError) {
if (iError) {
PackageAdminStore.real(false);
} else {
PackageAdminStore.real(!!data.Result.Real);
let list = [];
const loading = {};
PackageAdminStore.forEach(item => {
if (item && item.loading()) {
loading[item.file] = item;
}
});
let list = [];
if (isArray(data.Result.List)) {
list = data.Result.List.map(item => {
if (item) {
item.loading = ko.observable(loading[item.file] !== undefined);
return 'core' === item.type && !item.canBeInstalled ? null : item;
}
return null;
}).filter(v => v);
list = data.Result.List.filter(v => v).map(item => {
item.loading = ko.observable(loading[item.file] !== undefined);
item.enabled = ko.observable(item.enabled);
return item;
});
}
PackageAdminStore(list);
} else {
PackageAdminStore.real(false);
}
});
};

View file

@ -1,24 +0,0 @@
import ko from 'ko';
import Remote from 'Remote/Admin/Fetch';
export const PluginAdminStore = ko.observableArray();
PluginAdminStore.loading = ko.observable(false);
PluginAdminStore.error = ko.observable('');
PluginAdminStore.fetch = () => {
PluginAdminStore.loading(true);
Remote.pluginList((iError, data) => {
PluginAdminStore.loading(false);
if (!iError) {
PluginAdminStore(
data.Result.map(item => ({
name: item.Name,
disabled: ko.observable(!item.Enabled),
configured: ko.observable(!!item.Configured)
}))
);
}
});
};

View file

@ -35,7 +35,6 @@
@import "AdminDomains.less";
@import "AdminDomain.less";
@import "AdminPackages.less";
@import "AdminPlugins.less";
@import "AdminPlugin.less";
@import "AdminAbout.less";

View file

@ -5,6 +5,11 @@
width: 650px;
}
.disabled table,
table .disabled {
opacity: 0.5;
}
}
.b-admin-packages-list-table {
@ -30,4 +35,9 @@
.package-actions {
vertical-align: middle;
}
.package-configure, .package-active {
cursor: pointer;
margin-right: 1em;
}
}

View file

@ -540,103 +540,75 @@ trait Admin
$bReal = \is_array($aRep) && 0 < \count($aRep);
}
$aResult = array();
if (\is_array($aRep))
{
foreach ($aRep as $oItem)
{
if ($oItem && isset($oItem->type, $oItem->id, $oItem->name,
$oItem->version, $oItem->release, $oItem->file, $oItem->description))
{
if (!empty($oItem->required) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->required, '<'))
{
continue;
}
if (!empty($oItem->depricated) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->depricated, '>='))
{
continue;
}
if ('plugin' === $oItem->type)
{
$aResult[] = array(
'type' => $oItem->type,
'id' => $oItem->id,
'name' => $oItem->name,
'installed' => '',
'version' => $oItem->version,
'file' => $oItem->file,
'release' => $oItem->release,
'desc' => $oItem->description
);
}
}
}
}
return $aResult;
return \is_array($aRep) ? $aRep : [];
}
private function getRepositoryData(bool &$bReal, bool &$bRainLoopUpdatable) : array
{
$bRainLoopUpdatable = $this->rainLoopUpdatable();
$aResult = $this->getRepositoryDataByUrl($this->snappyMailRepo(), $bReal);
$aSub = array();
foreach ($aResult as $aItem)
{
if ('plugin' === $aItem['type'])
$aResult = array();
foreach ($this->getRepositoryDataByUrl($this->snappyMailRepo(), $bReal) as $oItem) {
if ($oItem && isset($oItem->type, $oItem->id, $oItem->name,
$oItem->version, $oItem->release, $oItem->file, $oItem->description))
{
$aSub[] = $aItem;
}
}
$aResult = $aSub;
unset($aSub);
if (!empty($oItem->required) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->required, '<')) {
continue;
}
$aInstalled = $this->Plugins()->InstalledPlugins();
foreach ($aResult as &$aItem)
{
if ('plugin' === $aItem['type'])
{
foreach ($aInstalled as &$aSubItem)
{
if (\is_array($aSubItem) && isset($aSubItem[0]) && $aSubItem[0] === $aItem['id'])
{
$aSubItem[2] = true;
$aItem['installed'] = $aSubItem[1];
}
if (!empty($oItem->depricated) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->depricated, '>=')) {
continue;
}
if ('plugin' === $oItem->type) {
$aResult[$oItem->id] = array(
'type' => $oItem->type,
'id' => $oItem->id,
'name' => $oItem->name,
'installed' => '',
'enabled' => true,
'version' => $oItem->version,
'file' => $oItem->file,
'release' => $oItem->release,
'desc' => $oItem->description,
'canBeDeleted' => false,
'canBeUpdated' => true
);
}
}
}
foreach ($aInstalled as $aSubItemSec)
{
if ($aSubItemSec && !isset($aSubItemSec[2]))
{
\array_push($aResult, array(
'type' => 'plugin',
'id' => $aSubItemSec[0],
'name' => $aSubItemSec[0],
'installed' => $aSubItemSec[1],
'version' => '',
'file' => '',
'release' => '',
'desc' => ''
));
$aEnabledPlugins = \array_map('trim',
\explode(',', \strtolower($this->Config()->Get('plugins', 'enabled_list', '')))
);
$aInstalled = $this->Plugins()->InstalledPlugins();
foreach ($aInstalled as $aItem) {
if ($aItem) {
if (isset($aResult[$aItem[0]])) {
$aResult[$aItem[0]]['installed'] = $aItem[1];
$aResult[$aItem[0]]['enabled'] = \in_array(\strtolower($aItem[0]), $aEnabledPlugins);
$aResult[$aItem[0]]['canBeDeleted'] = true;
$aResult[$aItem[0]]['canBeUpdated'] = \version_compare($aItem[1], $aResult[$aItem[0]]['version'], '<');
} else {
\array_push($aResult, array(
'type' => 'plugin',
'id' => $aItem[0],
'name' => $aItem[0],
'installed' => $aItem[1],
'enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins),
'version' => '',
'file' => '',
'release' => '',
'desc' => '',
'canBeDeleted' => true,
'canBeUpdated' => false
));
}
}
}
foreach ($aResult as &$aItem)
{
$aItem['compare'] = \version_compare($aItem['installed'], $aItem['version'], '<');
$aItem['canBeDeleted'] = '' !== $aItem['installed'] && 'plugin' === $aItem['type'];
$aItem['canBeUpdated'] = $aItem['compare'];
$aItem['canBeInstalled'] = true;
}
return $aResult;
return \array_values($aResult);
}
public function DoAdminPackagesList() : array
@ -647,6 +619,8 @@ trait Admin
$bRainLoopUpdatable = false;
$aList = $this->getRepositoryData($bReal, $bRainLoopUpdatable);
// \uksort($aList, function($a, $b){return \strcasecmp($a['name'], $b['name']);});
return $this->DefaultResponse(__FUNCTION__, array(
'Real' => $bReal,
'MainUpdatable' => $bRainLoopUpdatable,
@ -773,29 +747,6 @@ trait Admin
('plugin' !== $sType ? array('Reload' => true) : true) : false);
}
public function DoAdminPluginList() : array
{
$this->IsAdminLoggined();
$aResult = array();
$sEnabledPlugins = $this->Config()->Get('plugins', 'enabled_list', '');
$aEnabledPlugins = \explode(',', \strtolower($sEnabledPlugins));
$aEnabledPlugins = \array_map('trim', $aEnabledPlugins);
$aList = $this->Plugins()->InstalledPlugins();
foreach ($aList as $aItem)
{
$aResult[] = array(
'Name' => $aItem[0],
'Enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins),
'Configured' => false
);
}
return $this->DefaultResponse(__FUNCTION__, $aResult);
}
private function pluginEnable(string $sName, bool $bEnable = true) : bool
{
if (0 === \strlen($sName))

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "Verlangen, dass das verwendete SSL-Zertifikat beglaubigt ist (IMAP\/SMTP)",
"LABEL_ALLOW_SELF_SIGNED": "Selbst-signierte Zertifikate erlauben"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Plugins",
"LABEL_ENABLE_PLUGINS": "Plugins aktivieren",
"ALERT_NO_PLUGINS": "Es wurden noch keine Plugins installiert.",
"LINK_INSTALL_NEW": "Hier klicken, um Plugins zu installieren!",
"HINT_CLICK_NAME": "Klicken Sie auf den Namen, um das Plugin zu konfigurieren."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Plugins aktivieren",
"LEGEND_AVAILABLE_FOR_UPDATE": "Zur Aktualisierung verfügbar",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Zur Installation verfügbar",
"LEGEND_INSTALLED_PACKAGES": "Installierte Pakete",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used (IMAP\/SMTP)",
"LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Plugins",
"LABEL_ENABLE_PLUGINS": "Enable plugins",
"ALERT_NO_PLUGINS": "No plugins have yet been installed.",
"LINK_INSTALL_NEW": "Click here to install new!",
"HINT_CLICK_NAME": "Click on the name to configure the plugin."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Enable plugins",
"LEGEND_AVAILABLE_FOR_UPDATE": "Available for Update",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Available for Installation",
"LEGEND_INSTALLED_PACKAGES": "Installed Packages",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "Requerir verificación del certificado SSL (IMAP \/ SMTP)",
"LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-firmados"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Complementos",
"LABEL_ENABLE_PLUGINS": "Activar Plugins",
"ALERT_NO_PLUGINS": "No hay plugins instalados.",
"LINK_INSTALL_NEW": "¡Haz click aquí para instalar uno nuevo!",
"HINT_CLICK_NAME": "Haz click en el nombre para configurar el plugin."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Activar Plugins",
"LEGEND_AVAILABLE_FOR_UPDATE": "Actualización disponible",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Instalación disponible",
"LEGEND_INSTALLED_PACKAGES": "Paquetes instalados",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "S'assurer qu'un certificat SSL (IMAP\/SMTP) est utilisé",
"LABEL_ALLOW_SELF_SIGNED": "Autoriser les certificats auto-signés"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Plugins",
"LABEL_ENABLE_PLUGINS": "Activer les plugins",
"ALERT_NO_PLUGINS": "Aucun plugin n'a été installé.",
"LINK_INSTALL_NEW": "Cliquez ici pour installer un nouveau plugin !",
"HINT_CLICK_NAME": "Cliquez sur le nom pour configurer le plugin."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Activer les plugins",
"LEGEND_AVAILABLE_FOR_UPDATE": "Disponible pour la mise à jour",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Disponible pour l'installation",
"LEGEND_INSTALLED_PACKAGES": "Paquets installés",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "A használt (IMAP\/SMTP) tanúsítvány ellenőrzés megkövetelése",
"LABEL_ALLOW_SELF_SIGNED": "Saját aláírt tanúsítványok engedélyezése"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Beépülők",
"LABEL_ENABLE_PLUGINS": "Beépülők engedélyezése",
"ALERT_NO_PLUGINS": "Még nincsenek telepítve beépülők.",
"LINK_INSTALL_NEW": "Új telepítéséhez kattints ide!",
"HINT_CLICK_NAME": "A beépülő beállításához kattints a nevére."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Beépülők engedélyezése",
"LEGEND_AVAILABLE_FOR_UPDATE": "Frissíthető",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Telepíthető",
"LEGEND_INSTALLED_PACKAGES": "Telepített csomagok",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "Verificatie van SSL certificaten (IMAP\/SMTP)",
"LABEL_ALLOW_SELF_SIGNED": "Sta zelf ondertekende certificaten toe"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Invoegtoepassingen",
"LABEL_ENABLE_PLUGINS": "Invoegtoepassingen inschakelen",
"ALERT_NO_PLUGINS": "Er zijn nog geen invoegtoepassingen geïnstalleerd",
"LINK_INSTALL_NEW": "Klik hier om een invoegtoepassing te installeren!",
"HINT_CLICK_NAME": "Klik op de naam om de invoegtoepassing te configuren."
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Invoegtoepassingen inschakelen",
"LEGEND_AVAILABLE_FOR_UPDATE": "Update beschikbaar",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Gereed voor installatie",
"LEGEND_INSTALLED_PACKAGES": "Geïnstalleerde pakketten",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "Kräver verifiering av SSL-certifikat som används (IMAP\/SMTP)",
"LABEL_ALLOW_SELF_SIGNED": "Låt självsignerade certifikat"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "Plugin",
"LABEL_ENABLE_PLUGINS": "Aktivera Plugin",
"ALERT_NO_PLUGINS": "Inga plugin har ännu blivit installerade",
"LINK_INSTALL_NEW": "Klicka här för att installera nya",
"HINT_CLICK_NAME": "Klicka på namnet för att konfigurera pluginet"
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "Aktivera Plugin",
"LEGEND_AVAILABLE_FOR_UPDATE": "Tillåt uppdateringar",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "Tillåt installation",
"LEGEND_INSTALLED_PACKAGES": "installerade paket",

View file

@ -87,14 +87,8 @@
"LABEL_REQUIRE_VERIFICATION": "要求验证 SSL 证书IMAP\/SMTP",
"LABEL_ALLOW_SELF_SIGNED": "允许自签名证书"
},
"TAB_PLUGINS": {
"LEGEND_PLUGINS": "插件",
"LABEL_ENABLE_PLUGINS": "启用插件",
"ALERT_NO_PLUGINS": "当前还未安装插件。",
"LINK_INSTALL_NEW": "点击此处安装新插件!",
"HINT_CLICK_NAME": "点击名称来配置插件。"
},
"TAB_PACKAGES": {
"LABEL_ENABLE_PLUGINS": "启用插件",
"LEGEND_AVAILABLE_FOR_UPDATE": "有更新可用",
"LEGEND_AVAILABLE_FOR_INSTALLATION": "可用于安装",
"LEGEND_INSTALLED_PACKAGES": "已安装的插件包",

View file

@ -1,5 +1,10 @@
<div class="b-admin-packages">
<div class="legend">
<span data-i18n="TABS_LABELS/LABEL_PACKAGES_NAME"></span>
<i class="icon-spinner" style="margin: 5px" data-bind="visible: packages.loading"></i>
</div>
<div class="alert" style="margin-top: 10px;" data-bind="visible: !packages.real() && !packages.loading()"
data-i18n="TAB_PACKAGES/ALERT_CANNOT_ACCESS_REPOSITORY"></div>
@ -8,6 +13,13 @@
<span data-bind="text: packagesError"></span>
</div>
<div data-bind="component: {
name: 'Checkbox',
params: { value: enabledPlugins, label: 'TAB_PACKAGES/LABEL_ENABLE_PLUGINS' }
}"></div>
<br />
<div data-bind="visible: 0 < packagesAvailableForUpdate().length">
<div class="legend">
<span data-i18n="TAB_PACKAGES/LEGEND_AVAILABLE_FOR_UPDATE"></span>&nbsp;
@ -17,7 +29,7 @@
<br />
</div>
<div data-bind="visible: 0 < packagesCurrent().length">
<div id="packages-installed" data-bind="visible: 0 < packagesCurrent().length, css: {'disabled': !enabledPlugins()}">
<div class="legend" data-i18n="TAB_PACKAGES/LEGEND_INSTALLED_PACKAGES"></div>
<div data-bind="template: { name: 'AdminSettingsPackagesTable', data: {f: packagesCurrent} }"></div>
<br />

View file

@ -2,20 +2,18 @@
<colgroup>
<col />
<col style="width: 100px" />
<!-- <col style="width: 100px" /> -->
<col style="width: 80px" />
</colgroup>
<tbody data-bind="foreach: f">
<tr>
<tr data-bind="css: {'disabled': !enabled() }">
<td>
<span class="package-name" data-bind="text: name"></span>
<span class="package-installed pull-right" data-bind="text: installed"></span>
<span class="package-active pull-right fontastic" data-bind="visible: installed, text: enabled() ? '☑' : '☐'"></span>
<span class="package-configure pull-right fontastic" data-bind="visible: installed"></span>
<div class="package-desc" data-bind="text: desc"></div>
</td>
<td class="package-version" data-bind="text: version"></td>
<!--
<td class="package-release" data-bind="text: release"></td>
-->
<td class="package-actions">
<i class="icon-download e-action button-download" data-bind="visible: !loading() && canBeUpdated, click: function () { $root.installPackage($data); }"></i>
&nbsp;

View file

@ -1,43 +0,0 @@
<div class="b-admin-plugins g-ui-user-select-none">
<div class="alert" style="margin-top: 10px;" data-bind="visible: '' !== pluginsError()">
<button type="button" class="close" data-bind="click: function () { pluginsError('') }">×</button>
<span data-bind="text: pluginsError"></span>
</div>
<div class="legend">
<span data-i18n="TAB_PLUGINS/LEGEND_PLUGINS"></span>
<i class="icon-spinner" style="margin: 5px" data-bind="visible: plugins.loading"></i>
</div>
<div data-bind="component: {
name: 'Checkbox',
params: { value: enabledPlugins, label: 'TAB_PLUGINS/LABEL_ENABLE_PLUGINS' }
}"></div>
<br />
<div class="alert alert-info" data-bind="visible: 0 === plugins().length">
<span data-i18n="TAB_PLUGINS/ALERT_NO_PLUGINS"></span>
<br />
<br />
<strong><a href="#/packages" data-i18n="TAB_PLUGINS/LINK_INSTALL_NEW"></a></strong>
</div>
<div data-bind="visible: 0 < plugins().length">
<blockquote><p class="muted" data-i18n="TAB_PLUGINS/HINT_CLICK_NAME"></p></blockquote>
</div>
<table class="table table-hover b-admin-plugins-list-table" data-bind="i18nUpdate: plugins, visible: 0 < plugins().length, css: {'disabled': !enabledPlugins()}">
<colgroup>
<col />
<col style="width: 30px" />
</colgroup>
<tbody data-bind="foreach: plugins">
<tr data-bind="css: {'disabled': disabled }">
<td class="configure-plugin-action e-action plugin-name" data-bind="text: name"></td>
<td class="disabled-plugin e-action fontastic" data-bind="text: disabled() ? '☐' : '☑'"></td>
</tr>
</tbody>
</table>
</div>