diff --git a/app/views/extension/index.phtml b/app/views/extension/index.phtml
index ba789bebd..706d238e5 100644
--- a/app/views/extension/index.phtml
+++ b/app/views/extension/index.phtml
@@ -9,25 +9,33 @@
= _t('admin.extensions.title') ?>
- = _t('admin.extensions.system') ?>
+
+
= _t('gen.action.manage') ?>
extension->getConfigureView();
diff --git a/app/views/helpers/extension/details.phtml b/app/views/helpers/extension/details.phtml
index f6b361c6f..08226e86c 100644
--- a/app/views/helpers/extension/details.phtml
+++ b/app/views/helpers/extension/details.phtml
@@ -1,22 +1,24 @@
-
- -
- ext_details->getType() === 'user' || FreshRSS_Auth::hasAccess('admin')) { ?>
- ext_details->getName()); ?>
-
-
- = _t('admin.extensions.system.no_rights') ?>
-
-
- - = $this->ext_details->getName() ?>
-
+
+ext_details->getName());
+ $ext_enabled = $this->ext_details->isEnabled();
+ if ($ext_enabled) {
+ $button_class = ' active';
+ $name_class = '';
+ $action = 'disable';
+ $title = _t('gen.action.disable');
+ } else {
+ $button_class = '';
+ $name_class = ' disabled';
+ $action = 'enable';
+ $title = _t('gen.action.enable');
+ }
+ if ($this->ext_details->getType() === 'user' || FreshRSS_Auth::hasAccess('admin')) {?>
+
+ = _i('configure') ?>
+ = $this->ext_details->getName() ?>
+
+
+ = $this->ext_details->getName() ?>
+
diff --git a/p/themes/Alternative-Dark/adark.css b/p/themes/Alternative-Dark/adark.css
index 52f24b915..8106b3a46 100644
--- a/p/themes/Alternative-Dark/adark.css
+++ b/p/themes/Alternative-Dark/adark.css
@@ -205,6 +205,15 @@ a.btn {
box-shadow: none;
}
+/*=== switches */
+.switch.active {
+ background-color: #0062b7;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Alternative-Dark/adark.rtl.css b/p/themes/Alternative-Dark/adark.rtl.css
index 9d949f76a..f1939a7be 100644
--- a/p/themes/Alternative-Dark/adark.rtl.css
+++ b/p/themes/Alternative-Dark/adark.rtl.css
@@ -205,6 +205,15 @@ a.btn {
box-shadow: none;
}
+/*=== switches */
+.switch.active {
+ background-color: #0062b7;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Alternative-Dark/icons/disabled-light.svg b/p/themes/Alternative-Dark/icons/disabled-light.svg
new file mode 100644
index 000000000..8542db156
--- /dev/null
+++ b/p/themes/Alternative-Dark/icons/disabled-light.svg
@@ -0,0 +1,3 @@
+
diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css
index f94a26b2b..56b293f80 100644
--- a/p/themes/Dark/dark.css
+++ b/p/themes/Dark/dark.css
@@ -238,6 +238,20 @@ a.btn {
background: #801;
}
+/*=== switches */
+.switch {
+ background-color: #333;
+}
+
+.switch.active {
+ background-color: #6986b2;
+}
+
+.switch::before,
+.switch:hover::before {
+ background-color: black;
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Dark/dark.rtl.css b/p/themes/Dark/dark.rtl.css
index fa7adfb06..1019c1c48 100644
--- a/p/themes/Dark/dark.rtl.css
+++ b/p/themes/Dark/dark.rtl.css
@@ -238,6 +238,20 @@ a.btn {
background: #801;
}
+/*=== switches */
+.switch {
+ background-color: #333;
+}
+
+.switch.active {
+ background-color: #6986b2;
+}
+
+.switch::before,
+.switch:hover::before {
+ background-color: black;
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Flat/flat.css b/p/themes/Flat/flat.css
index 3822c7c35..52200ab88 100644
--- a/p/themes/Flat/flat.css
+++ b/p/themes/Flat/flat.css
@@ -249,6 +249,14 @@ a.btn {
background: #c0392b;
}
+.switch.active {
+ background-color: #2980b9;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Flat/flat.rtl.css b/p/themes/Flat/flat.rtl.css
index ff9836b6d..7fa4b4ab2 100644
--- a/p/themes/Flat/flat.rtl.css
+++ b/p/themes/Flat/flat.rtl.css
@@ -249,6 +249,14 @@ a.btn {
background: #c0392b;
}
+.switch.active {
+ background-color: #2980b9;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
/*=== Navigation */
.nav-list .nav-header,
.nav-list .item {
diff --git a/p/themes/Flat/icons/disabled-light.svg b/p/themes/Flat/icons/disabled-light.svg
new file mode 100644
index 000000000..c46f71801
--- /dev/null
+++ b/p/themes/Flat/icons/disabled-light.svg
@@ -0,0 +1,3 @@
+
diff --git a/p/themes/Screwdriver/screwdriver.css b/p/themes/Screwdriver/screwdriver.css
index f06dd9e8a..53fc593e7 100644
--- a/p/themes/Screwdriver/screwdriver.css
+++ b/p/themes/Screwdriver/screwdriver.css
@@ -1039,6 +1039,10 @@ a.btn {
text-shadow: 0 0 10px #666;
}
+.switch.active {
+ background-color: #d18114;
+}
+
/*=== Navigation menu (for articles) */
#nav_entries {
background: linear-gradient(180deg, #222 0%, #171717 100%) #222;
diff --git a/p/themes/Screwdriver/screwdriver.rtl.css b/p/themes/Screwdriver/screwdriver.rtl.css
index 6c512caf6..7ec1e75d0 100644
--- a/p/themes/Screwdriver/screwdriver.rtl.css
+++ b/p/themes/Screwdriver/screwdriver.rtl.css
@@ -1039,6 +1039,10 @@ a.btn {
text-shadow: 0 0 10px #666;
}
+.switch.active {
+ background-color: #d18114;
+}
+
/*=== Navigation menu (for articles) */
#nav_entries {
background: linear-gradient(-180deg, #222 0%, #171717 100%) #222;
diff --git a/p/themes/Swage/icons/disabled-light.svg b/p/themes/Swage/icons/disabled-light.svg
new file mode 100644
index 000000000..c46f71801
--- /dev/null
+++ b/p/themes/Swage/icons/disabled-light.svg
@@ -0,0 +1,3 @@
+
diff --git a/p/themes/Swage/swage.css b/p/themes/Swage/swage.css
index ab31b5de0..03b7be3b5 100644
--- a/p/themes/Swage/swage.css
+++ b/p/themes/Swage/swage.css
@@ -38,6 +38,14 @@ select:invalid {
clear: both;
}
+.switch.active {
+ background-color: #0062be;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
#nav_entries, .notification, #new-article, .aside, .header > .item.title, .stick.configure-feeds {
width: 231px;
}
diff --git a/p/themes/Swage/swage.rtl.css b/p/themes/Swage/swage.rtl.css
index ea673b91d..65aa1f3a8 100644
--- a/p/themes/Swage/swage.rtl.css
+++ b/p/themes/Swage/swage.rtl.css
@@ -38,6 +38,14 @@ select:invalid {
clear: both;
}
+.switch.active {
+ background-color: #0062be;
+}
+
+.switch.active:hover {
+ background-image: url('./icons/disabled-light.svg');
+}
+
#nav_entries, .notification, #new-article, .aside, .header > .item.title, .stick.configure-feeds {
width: 231px;
}
diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css
index b6111788e..0a03a80d4 100644
--- a/p/themes/base-theme/template.css
+++ b/p/themes/base-theme/template.css
@@ -298,6 +298,102 @@ a.btn {
font-weight: bold;
}
+/*=== switch */
+.switch {
+ margin: 0 0.5em;
+ padding: revert;
+ position: relative;
+ width: 3.5em;
+ height: 1.75em;
+ border: 0;
+ border-radius: 1em;
+ background-color: #ccc;
+ cursor: pointer;
+ box-sizing: content-box;
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-image: url('../icons/disabled.svg');
+ transition: background-position 0.5s;
+}
+
+.switch:not([disabled]):hover {
+ background-image: url('../icons/enabled.svg');
+ background-repeat: no-repeat;
+ background-position: right 7px center;
+}
+
+.switch.active {
+ background-color: rgb(133, 216, 133);
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-image: url('../icons/enabled.svg');
+}
+
+.switch.active:not([disabled]):hover {
+ background-position: left 7px center;
+ background-repeat: no-repeat;
+ background-image: url('../icons/disabled.svg');
+}
+
+@supports selector(.switch::before) {
+ .switch {
+ background-image: none;
+ }
+
+ .switch.active {
+ background-image: none;
+ }
+}
+
+/* ::before = circle */
+.switch::before {
+ content: "";
+ position: absolute;
+ left: 5px;
+ right: unset;
+ top: 0.2em;
+ width: 1.5em;
+ height: 1.5em;
+ background-color: #fff;
+ background-image: url('../icons/disabled.svg');
+ background-repeat: no-repeat;
+ background-position: center center;
+ border-radius: 50%;
+ transition: left 0.6s, right 0.6s;
+}
+
+.switch:not([disabled]):hover::before {
+ background-color: #eee;
+}
+
+.switch.active::before {
+ background-image: url('../icons/enabled.svg');
+ background-position: center center;
+ left: unset;
+ right: 5px;
+}
+
+.switch.active:not([disabled]):hover::before {
+ right: 8px;
+}
+
+/* ::after = background */
+.switch::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ right: 8px;
+ width: 12px;
+ height: 12px;
+ transform: translateY(-50%);
+}
+
+.switch.active::after {
+ width: 14px;
+ height: 14px;
+ left: 8px;
+}
+
.btn:focus-visible,
input[type="checkbox"]:focus-visible {
outline: 2px solid #ccc;
@@ -337,6 +433,23 @@ input[type="checkbox"]:focus-visible {
display: table-cell;
}
+/*=== manage-list */
+.manage-list {
+ list-style: none;
+}
+
+.manage-list li {
+ line-height: 2;
+}
+
+.manage-list li * {
+ vertical-align: middle;
+}
+
+.manage-list .disabled {
+ font-style: italic;
+}
+
/*=== Dropdown */
.dropdown {
position: relative;
diff --git a/p/themes/base-theme/template.rtl.css b/p/themes/base-theme/template.rtl.css
index eeb4c69f5..fdf094b9c 100644
--- a/p/themes/base-theme/template.rtl.css
+++ b/p/themes/base-theme/template.rtl.css
@@ -298,6 +298,102 @@ a.btn {
font-weight: bold;
}
+/*=== switch */
+.switch {
+ margin: 0 0.5em;
+ padding: revert;
+ position: relative;
+ width: 3.5em;
+ height: 1.75em;
+ border: 0;
+ border-radius: 1em;
+ background-color: #ccc;
+ cursor: pointer;
+ box-sizing: content-box;
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-image: url('../icons/disabled.svg');
+ transition: background-position 0.5s;
+}
+
+.switch:not([disabled]):hover {
+ background-image: url('../icons/enabled.svg');
+ background-repeat: no-repeat;
+ background-position: left 7px center;
+}
+
+.switch.active {
+ background-color: rgb(133, 216, 133);
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-image: url('../icons/enabled.svg');
+}
+
+.switch.active:not([disabled]):hover {
+ background-position: right 7px center;
+ background-repeat: no-repeat;
+ background-image: url('../icons/disabled.svg');
+}
+
+@supports selector(.switch::before) {
+ .switch {
+ background-image: none;
+ }
+
+ .switch.active {
+ background-image: none;
+ }
+}
+
+/* ::before = circle */
+.switch::before {
+ content: "";
+ position: absolute;
+ right: 5px;
+ left: unset;
+ top: 0.2em;
+ width: 1.5em;
+ height: 1.5em;
+ background-color: #fff;
+ background-image: url('../icons/disabled.svg');
+ background-repeat: no-repeat;
+ background-position: center center;
+ border-radius: 50%;
+ transition: right 0.6s, left 0.6s;
+}
+
+.switch:not([disabled]):hover::before {
+ background-color: #eee;
+}
+
+.switch.active::before {
+ background-image: url('../icons/enabled.svg');
+ background-position: center center;
+ right: unset;
+ left: 5px;
+}
+
+.switch.active:not([disabled]):hover::before {
+ left: 8px;
+}
+
+/* ::after = background */
+.switch::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 8px;
+ width: 12px;
+ height: 12px;
+ transform: translateY(-50%);
+}
+
+.switch.active::after {
+ width: 14px;
+ height: 14px;
+ right: 8px;
+}
+
.btn:focus-visible,
input[type="checkbox"]:focus-visible {
outline: 2px solid #ccc;
@@ -337,6 +433,23 @@ input[type="checkbox"]:focus-visible {
display: table-cell;
}
+/*=== manage-list */
+.manage-list {
+ list-style: none;
+}
+
+.manage-list li {
+ line-height: 2;
+}
+
+.manage-list li * {
+ vertical-align: middle;
+}
+
+.manage-list .disabled {
+ font-style: italic;
+}
+
/*=== Dropdown */
.dropdown {
position: relative;
diff --git a/p/themes/icons/disabled.svg b/p/themes/icons/disabled.svg
new file mode 100644
index 000000000..cc16ff83d
--- /dev/null
+++ b/p/themes/icons/disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/p/themes/icons/enabled.svg b/p/themes/icons/enabled.svg
new file mode 100644
index 000000000..dbdcfdbc6
--- /dev/null
+++ b/p/themes/icons/enabled.svg
@@ -0,0 +1,3 @@
+