[mv3] Add ability for admins to disable features

New managed setting:

"disabledFeatures": {
  "title": "User interface features to disable",
  "description": "A list of tokens, each of which correspond to a user interface feature to disable.",
  "type": "array",
  "items": { "type": "string" }
}

Supported tokens:
- `dashboard`: Prevent access to all dashboard settings
- `filteringMode`: Prevent changes to the default filtering mode,
  or the current filtering mode of any site

Related feedback:
https://github.com/uBlockOrigin/uBOL-home/discussions/35#discussioncomment-11326086
This commit is contained in:
Raymond Hill 2024-11-23 13:17:13 -05:00
parent 4979aa51f5
commit 346b5ded7c
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
9 changed files with 80 additions and 7 deletions

View File

@ -13,7 +13,7 @@
border-radius: 30% 15% / 15% 30%; border-radius: 30% 15% / 15% 30%;
height: 100%; height: 100%;
position: absolute; position: absolute;
width: 25%; width: calc(25% + 2px);
z-index: 10; z-index: 10;
} }
@ -39,12 +39,18 @@
.filteringModeSlider span[data-level] { .filteringModeSlider span[data-level] {
background-color: var(--accent-surface-1); background-color: var(--accent-surface-1);
border: 1px solid var(--accent-surface-1);
box-sizing: border-box;
display: inline-flex; display: inline-flex;
height: 30%; height: 30%;
margin-left: 1px; margin-left: 1px;
width: 25%; width: 25%;
} }
.filteringModeSlider span[data-level]:first-of-type {
margin-left: 0;
}
.filteringModeSlider.moving span[data-level] { .filteringModeSlider.moving span[data-level] {
pointer-events: none; pointer-events: none;
} }
@ -53,13 +59,13 @@
left: 0; left: 0;
} }
.filteringModeSlider[data-level="1"] .filteringModeButton { .filteringModeSlider[data-level="1"] .filteringModeButton {
left: 25%; left: calc(25% - 1px);
} }
.filteringModeSlider[data-level="2"] .filteringModeButton { .filteringModeSlider[data-level="2"] .filteringModeButton {
left: 50%; left: calc(50% - 1px);
} }
.filteringModeSlider[data-level="3"] .filteringModeButton { .filteringModeSlider[data-level="3"] .filteringModeButton {
left: 75%; left: calc(75% - 1px);
} }
[dir="rtl"] .filteringModeSlider[data-level="0"] .filteringModeButton { [dir="rtl"] .filteringModeSlider[data-level="0"] .filteringModeButton {
left: 75%; left: 75%;
@ -77,14 +83,17 @@
.filteringModeSlider[data-level="0"] span[data-level] { .filteringModeSlider[data-level="0"] span[data-level] {
background-color: var(--surface-2); background-color: var(--surface-2);
border-color: var(--surface-2);
} }
.filteringModeSlider[data-level="1"] span[data-level]:nth-of-type(1) ~ span[data-level] { .filteringModeSlider[data-level="1"] span[data-level]:nth-of-type(1) ~ span[data-level] {
background-color: var(--surface-2); background-color: var(--surface-2);
border-color: var(--surface-2);
} }
.filteringModeSlider[data-level="2"] span[data-level]:nth-of-type(2) ~ span[data-level] { .filteringModeSlider[data-level="2"] span[data-level]:nth-of-type(2) ~ span[data-level] {
background-color: var(--surface-2); background-color: var(--surface-2);
border-color: var(--surface-2);
} }
.filteringModeSlider[data-level]:not(.moving) span[data-level]:hover { .filteringModeSlider[data-level]:not(.moving) span[data-level]:hover {

View File

@ -63,6 +63,13 @@ hr {
font-weight: 600; font-weight: 600;
} }
body[data-forbid~="filteringMode"] .filteringModeSlider {
pointer-events: none;
}
body[data-forbid~="dashboard"] #gotoDashboard {
display: none;
}
#filteringModeText { #filteringModeText {
color: var(--ink-3); color: var(--ink-3);
margin: var(--default-gap-small); margin: var(--default-gap-small);

View File

@ -1,3 +1,6 @@
body.loading {
visibility: hidden;
}
body .firstRun { body .firstRun {
display: none; display: none;
} }
@ -13,6 +16,17 @@ p {
white-space: pre-line; white-space: pre-line;
} }
body[data-forbid~="dashboard"] #dashboard-nav [data-pane="settings"],
body[data-forbid~="dashboard"] section[data-pane="settings"],
body[data-forbid~="dashboard"] #dashboard-nav [data-pane="rulesets"],
body[data-forbid~="dashboard"] section[data-pane="rulesets"] {
display: none;
}
body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="defaultFilteringModeSectionLabel"]),
body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="filteringMode0Name"]) {
display: none;
}
#showBlockedCount:has(input[type="checkbox"][disabled]) { #showBlockedCount:has(input[type="checkbox"][disabled]) {
opacity: 0.5; opacity: 0.5;
} }

View File

@ -16,7 +16,7 @@
<link rel="shortcut icon" type="image/png" href="img/icon_64.png"/> <link rel="shortcut icon" type="image/png" href="img/icon_64.png"/>
</head> </head>
<body data-pane="settings"> <body data-pane="settings" class="loading">
<!-- -------- --> <!-- -------- -->
<div id="dashboard-nav"> <div id="dashboard-nav">
<span class="logo"><img data-i18n-title="extName" src="img/ublock.svg" alt="uBO Lite"></span><!-- <span class="logo"><img data-i18n-title="extName" src="img/ublock.svg" alt="uBO Lite"></span><!--

View File

@ -40,6 +40,11 @@ import {
windows, windows,
} from './ext.js'; } from './ext.js';
import {
adminReadEx,
getAdminRulesets,
} from './admin.js';
import { import {
enableRulesets, enableRulesets,
getEnabledRulesetsDetails, getEnabledRulesetsDetails,
@ -62,7 +67,6 @@ import {
} from './config.js'; } from './config.js';
import { broadcastMessage } from './utils.js'; import { broadcastMessage } from './utils.js';
import { getAdminRulesets } from './admin.js';
import { registerInjectables } from './scripting-manager.js'; import { registerInjectables } from './scripting-manager.js';
/******************************************************************************/ /******************************************************************************/
@ -189,6 +193,7 @@ function onMessage(request, sender, callback) {
getRulesetDetails(), getRulesetDetails(),
dnr.getEnabledRulesets(), dnr.getEnabledRulesets(),
getAdminRulesets(), getAdminRulesets(),
adminReadEx('disabledFeatures'),
]).then(results => { ]).then(results => {
const [ const [
defaultFilteringMode, defaultFilteringMode,
@ -196,6 +201,7 @@ function onMessage(request, sender, callback) {
rulesetDetails, rulesetDetails,
enabledRulesets, enabledRulesets,
adminRulesets, adminRulesets,
disabledFeatures,
] = results; ] = results;
callback({ callback({
defaultFilteringMode, defaultFilteringMode,
@ -210,6 +216,7 @@ function onMessage(request, sender, callback) {
firstRun: process.firstRun, firstRun: process.firstRun,
isSideloaded, isSideloaded,
developerMode: rulesetConfig.developerMode, developerMode: rulesetConfig.developerMode,
disabledFeatures,
}); });
process.firstRun = false; process.firstRun = false;
}); });
@ -251,6 +258,7 @@ function onMessage(request, sender, callback) {
hasOmnipotence(), hasOmnipotence(),
hasGreatPowers(request.origin), hasGreatPowers(request.origin),
getEnabledRulesetsDetails(), getEnabledRulesetsDetails(),
adminReadEx('disabledFeatures'),
]).then(results => { ]).then(results => {
callback({ callback({
level: results[0], level: results[0],
@ -260,6 +268,7 @@ function onMessage(request, sender, callback) {
rulesetDetails: results[3], rulesetDetails: results[3],
isSideloaded, isSideloaded,
developerMode: rulesetConfig.developerMode, developerMode: rulesetConfig.developerMode,
disabledFeatures: results[4],
}); });
}); });
return true; return true;
@ -416,6 +425,11 @@ async function start() {
} }
toggleDeveloperMode(rulesetConfig.developerMode); toggleDeveloperMode(rulesetConfig.developerMode);
// Required to ensure the up to date property is available when needed
if ( process.wakeupRun === false ) {
adminReadEx('disabledFeatures');
}
} }
// https://github.com/uBlockOrigin/uBOL-home/issues/199 // https://github.com/uBlockOrigin/uBOL-home/issues/199

View File

@ -44,6 +44,14 @@ function normalizedHostname(hn) {
/******************************************************************************/ /******************************************************************************/
function renderAdminRules() {
const { disabledFeatures: forbid = [] } = popupPanelData;
if ( forbid.length === 0 ) { return; }
dom.body.dataset.forbid = forbid.join(' ');
}
/******************************************************************************/
const BLOCKING_MODE_MAX = 3; const BLOCKING_MODE_MAX = 3;
function setFilteringMode(level, commit = false) { function setFilteringMode(level, commit = false) {
@ -325,6 +333,8 @@ async function init() {
} }
} }
renderAdminRules();
setFilteringMode(popupPanelData.level); setFilteringMode(popupPanelData.level);
dom.text('#hostname', punycode.toUnicode(tabHostname)); dom.text('#hostname', punycode.toUnicode(tabHostname));

View File

@ -36,6 +36,17 @@ function hashFromIterable(iter) {
/******************************************************************************/ /******************************************************************************/
function renderAdminRules() {
const { disabledFeatures: forbid = [] } = cachedRulesetData;
if ( forbid.length === 0 ) { return; }
dom.body.dataset.forbid = forbid.join(' ');
if ( forbid.includes('dashboard') ) {
dom.body.dataset.pane = 'about';
}
}
/******************************************************************************/
function renderWidgets() { function renderWidgets() {
if ( cachedRulesetData.firstRun ) { if ( cachedRulesetData.firstRun ) {
dom.cl.add(dom.body, 'firstRun'); dom.cl.add(dom.body, 'firstRun');
@ -256,8 +267,10 @@ sendMessage({
if ( !data ) { return; } if ( !data ) { return; }
cachedRulesetData = data; cachedRulesetData = data;
try { try {
renderAdminRules();
renderFilterLists(cachedRulesetData); renderFilterLists(cachedRulesetData);
renderWidgets(); renderWidgets();
dom.cl.remove(dom.body, 'loading');
} catch(ex) { } catch(ex) {
} }
listen(); listen();

View File

@ -16,6 +16,12 @@
"description": "Prefix a ruleset id with '+' to add, or '-' to remove. Use '-*' to disable all non-default lists.", "description": "Prefix a ruleset id with '+' to add, or '-' to remove. Use '-*' to disable all non-default lists.",
"type": "array", "type": "array",
"items": { "type": "string" } "items": { "type": "string" }
},
"disabledFeatures": {
"title": "User interface features to disable",
"description": "A list of tokens, each of which correspond to a user interface feature to disable.",
"type": "array",
"items": { "type": "string" }
} }
} }
} }

View File

@ -32,7 +32,7 @@
<span></span> <span></span>
<span id="showMatchedRules" class="fa-icon tool" tabindex="0" title="Show matched rules">list-alt<span class="caption">Show matched rules</span></span> <span id="showMatchedRules" class="fa-icon tool" tabindex="0" title="Show matched rules">list-alt<span class="caption">Show matched rules</span></span>
<span id="reportFilterIssue" class="fa-icon tool enabled" data-i18n-title="popupTipReport">comment-alt<span class="caption" data-i18n="popupTipReport"></span></span> <span id="reportFilterIssue" class="fa-icon tool enabled" data-i18n-title="popupTipReport">comment-alt<span class="caption" data-i18n="popupTipReport"></span></span>
<span class="fa-icon tool enabled" tabindex="0" data-i18n-title="popupTipDashboard">cogs<span class="caption" data-i18n="popupTipDashboard"></span></span> <span id="gotoDashboard" class="fa-icon tool enabled" tabindex="0" data-i18n-title="popupTipDashboard">cogs<span class="caption" data-i18n="popupTipDashboard"></span></span>
</div> </div>
<!-- -------- --> <!-- -------- -->
<div id="rulesetStats" data-section="a"> <div id="rulesetStats" data-section="a">