systemd: Apply strict equality everywhere

Fixes on issue where we relied on type coercion in `hw-detect.js`.
This commit is contained in:
Jelle van der Waa 2026-10-20 11:20:56 +02:00 committed by Jelle van der Waa
parent 94e5358e0f
commit 95f63aabe6
22 changed files with 124 additions and 124 deletions

View file

@ -72,9 +72,9 @@ function findMemoryDevices(udevdb, info) {
}
let memoryRank = props[`MEMORY_DEVICE_${slot}_RANK`];
if (memoryRank == 1) {
if (memoryRank === "1") {
memoryRank = _("Single rank");
} else if (memoryRank == 2) {
} else if (memoryRank === "2") {
memoryRank = _("Dual rank");
} else {
memoryRank = _("Unknown");

View file

@ -164,7 +164,7 @@ const CPUSecurityMitigationsDialog = () => {
// this may either be an argument of its own, or part of mitigations=
const ma = cachedMitigations.mitigations_arg;
if (ma && ma.indexOf("nosmt") >= 0) {
const new_args = ma.split(',').filter(opt => opt != 'nosmt');
const new_args = ma.split(',').filter(opt => opt !== 'nosmt');
options = ['set', 'mitigations=' + new_args.join(',')];
} else {
options = ['remove', 'nosmt'];

View file

@ -79,7 +79,7 @@ export class LogEntry extends React.Component {
const cursor = cockpit.location.path[0];
journal.journalctl({ cursor, count: 1, follow: false })
.then(entries => {
if (entries.length >= 1 && entries[0].__CURSOR == cursor) {
if (entries.length >= 1 && entries[0].__CURSOR === cursor) {
this.setState({ entry: entries[0], loading: false, error: "" });
} else
this.setState({ entry: null, loading: false, error: _("Journal entry not found") });

View file

@ -65,7 +65,7 @@ const getPrioFilterOption = options => {
const getTimeFilterOption = options => {
function find_key(key, value) {
return timeFilterOptions.find(o => o.value.key == key && o.value.value == value)?.value;
return timeFilterOptions.find(o => o.value.key === key && o.value.value === value)?.value;
}
if (options.boot)
return find_key('boot', options.boot);
@ -105,7 +105,7 @@ export const LogsPage = () => {
const { options, path } = cockpit.location;
const full_grep = getGrepFiltersFromOptions({ options })[0];
if (path.length == 1) return;
if (path.length === 1) return;
setJournalPrio(getPrioFilterOption(options));
setIdentifiersFilter(options.tag || "all");
@ -117,7 +117,7 @@ export const LogsPage = () => {
return () => cockpit.removeEventListener("locationchanged", onNavigate);
}, []);
if (path.length == 1) {
if (path.length === 1) {
return <LogEntry />;
} else if (path.length > 1) { /* redirect */
console.warn("not a journal location: " + path);
@ -137,7 +137,7 @@ export const LogsPage = () => {
const onIdentifiersFilterChange = (value) => {
setUpdateIdentifiersList(false);
if (value == "all") {
if (value === "all") {
delete options.tag;
updateUrl(Object.assign(options));
} else {
@ -154,7 +154,7 @@ export const LogsPage = () => {
const onTimeFilterChange = (newTimeFilter) => {
setUpdateIdentifiersList(true);
if (newTimeFilter.key == 'boot' && newTimeFilter.value !== "0") // Don't follow if specific boot is picked
if (newTimeFilter.key === 'boot' && newTimeFilter.value !== "0") // Don't follow if specific boot is picked
setDataFollowing(false);
else if (options.boot && options.boot !== "0" && newTimeFilter.key !== "boot") // Start following is specific boot is removed
setDataFollowing(true);
@ -227,7 +227,7 @@ export const LogsPage = () => {
isDisabled={options.boot && options.boot !== "0"}
onClick={() => {
// Reset time filter if following mode is now selected but we are on a specific boot
if (!dataFollowing && timeFilter && timeFilter.key == "boot" && timeFilter.value !== "0") {
if (!dataFollowing && timeFilter && timeFilter.key === "boot" && timeFilter.value !== "0") {
setTimeFilter(undefined);
}
@ -285,7 +285,7 @@ const IdentifiersFilter = ({ identifiersFilter, onIdentifiersFilterChange, curre
selected={identifiersFilter}
selectedIsTrusted
onSelect={(e, selection) => { onIdentifiersFilterChange(selection) }}
onClearSelection={identifiersFilter != "all" && (() => { onIdentifiersFilterChange("all") })}
onClearSelection={identifiersFilter !== "all" && (() => { onIdentifiersFilterChange("all") })}
/>
);
};

View file

@ -22,7 +22,7 @@ export function checkJournalctlGrep(setShowTextSearch) {
export const getFilteredQuery = ({ match, options }) => {
const cmd = journal.build_cmd(match, options);
const filtered_cmd = cmd.filter(i => i !== "-q" && i !== "--output=json");
if (filtered_cmd[filtered_cmd.length - 1] == "--")
if (filtered_cmd[filtered_cmd.length - 1] === "--")
filtered_cmd.pop();
return filtered_cmd.join(" ");
@ -112,7 +112,7 @@ export const getOptionsFromTextInput = (value) => {
const map_keys = (key) => {
if (key === "identifier")
return "tag";
if (key == "service")
if (key === "service")
return "unit";
return key;
};

View file

@ -53,7 +53,7 @@ export class JournalBox extends React.Component {
}
componentDidUpdate(prevProps) {
if (prevProps.dataFollowing != this.props.dataFollowing) {
if (prevProps.dataFollowing !== this.props.dataFollowing) {
if (this.props.dataFollowing) {
const cursor = document.querySelector(".cockpit-logline");
if (cursor)
@ -191,7 +191,7 @@ export class JournalBox extends React.Component {
this.followingProcs.push(journal.journalctl(this.match, journalctlOptions)
.fail(this.queryError)
.stream(entries => {
if (entries[0].__CURSOR == cursor)
if (entries[0].__CURSOR === cursor)
entries.shift();
this.prependEntries(entries);
}));
@ -292,7 +292,7 @@ export class JournalBox extends React.Component {
const promise = journal.journalctl(this.match, journalctlOptions)
.fail(this.queryError)
.stream(entries => {
if (entries[0].__CURSOR == this.state.cursor)
if (entries[0].__CURSOR === this.state.cursor)
entries.shift();
count += entries.length;
this.appendEntries(entries);

View file

@ -261,7 +261,7 @@ const PageSystemInformationChangeHostname = () => {
return false;
}
const disabled = error.length || (init_hostname == hostname && init_pretty == pretty);
const disabled = error.length || (init_hostname === hostname && init_pretty === pretty);
return (
<Modal isOpen position="top" variant="medium"
onClose={Dialogs.close}

View file

@ -33,7 +33,7 @@ const generate_line = (host?: string, line?: string): string => {
const getFormattedDateTime = (time: number): string => {
const now = new Date();
const date = new Date(time);
if (date.getFullYear() == now.getFullYear()) {
if (date.getFullYear() === now.getFullYear()) {
return timeformat.dateTimeNoYear(date);
}
return timeformat.dateTime(date);
@ -59,7 +59,7 @@ const LastLogin = () => {
try {
const [reply] = await bridge.call("/LoginMessages", "cockpit.LoginMessages", "Get", []);
const obj = JSON.parse(reply as string);
if (obj.version == 1) {
if (obj.version === 1) {
setLoginMessages(obj);
} else {
// empty reply is okay -- older bridges just don't send that information

View file

@ -77,7 +77,7 @@ export const MotdCard = () => {
/* trim initial empty lines and trailing space, but keep initial spaces to not break ASCII art */
if (content)
content = content.trimEnd().replace(/^\s*\n/, '');
if (content && content != cockpit.localStorage.getItem('dismissed-motd')) {
if (content && content !== cockpit.localStorage.getItem('dismissed-motd')) {
setMotdText(content);
setMotdTag(tag);
setMotdVisible(true);

View file

@ -109,7 +109,7 @@ export class RealmdClient {
checkRealm(name) {
return this.dbus_realmd.call(MANAGER, PROVIDER, "Discover", [name, {}])
.then(([relevance, realms]) => {
if (realms.length == 0)
if (realms.length === 0)
return { result: false };
// the first realm
@ -138,7 +138,7 @@ export class RealmdClient {
return kerberosMembership.call("Join", [credentials, options])
.then(() => this.installWSCredentials(realm, user, password))
.catch(ex => {
if (ex.name == "org.freedesktop.realmd.Error.Cancelled")
if (ex.name === "org.freedesktop.realmd.Error.Cancelled")
return Promise.resolve();
ex.diagnostics = diagnostics;
return Promise.reject(ex);
@ -332,7 +332,7 @@ const JoinDialog = ({ realmd_client }) => {
setRealm(reply.realm);
// handle initial auto-detection
if (name == "") {
if (name === "") {
if (!address)
setAddress(reply.realm.Name);
}
@ -415,7 +415,7 @@ const JoinDialog = ({ realmd_client }) => {
<Form isHorizontal onSubmit={onJoin}>
<FormGroup label={ _("Domain address") } fieldId="realms-op-address" validated={addressValid}>
<TextInput id="realms-op-address" placeholder="domain.example.com"
data-discover={ (!addressValid || addressValid == "default") ? null : "done" }
data-discover={ (!addressValid || addressValid === "default") ? null : "done" }
autoComplete="url"
value={address} onChange={(_event, value) => validateAddress(value)} isDisabled={pending} />
</FormGroup>
@ -426,7 +426,7 @@ const JoinDialog = ({ realmd_client }) => {
<FormGroup label={ _("Domain administrator password") } fieldId="realms-op-admin-password">
<TextInput id="realms-op-admin-password" type="password" value={adminPassword} autoComplete="current-password" onChange={(_event, value) => setAdminPassword(value)} isDisabled={pending} />
</FormGroup>
<FormHelper fieldId="realms-op-address" helperText={domainHelperText} helperTextInvalid={addressValid == "error" && domainHelperText} icon={domainHelperIcon} />
<FormHelper fieldId="realms-op-address" helperText={domainHelperText} helperTextInvalid={addressValid === "error" && domainHelperText} icon={domainHelperIcon} />
</Form>
</ModalBody>
<ModalFooter>

View file

@ -63,7 +63,7 @@ export const ShutDownStatus = () => {
if (shutdownTime) {
const date = new Date(shutdownTime / 1000);
const now = new Date();
if (date.getFullYear() == now.getFullYear()) {
if (date.getFullYear() === now.getFullYear()) {
displayDate = timeformat.dateTimeNoYear(date);
} else {
displayDate = timeformat.dateTime(date);

View file

@ -37,7 +37,7 @@ function poll_tuned_state(tuned, tunedService) {
.catch((ex) => {
if (!tunedService.exists)
return ({ state: "not-installed" });
else if (tunedService.state != "running")
else if (tunedService.state !== "running")
return ({ state: "not-running" });
else
return Promise.reject(ex);
@ -59,17 +59,17 @@ export const TunedPerformanceProfile = () => {
const { state, active, recommended } = await poll_tuned_state(tuned, tunedService);
let status;
if (state == "not-installed")
if (state === "not-installed")
status = _("Tuned is not available");
else if (state == "not-running")
else if (state === "not-running")
status = _("Tuned is not running");
else if (active == "none")
else if (active === "none")
status = _("Tuned is off");
else if (active == recommended)
else if (active === recommended)
status = _("This system is using the recommended profile");
else
status = _("This system is using a custom profile");
setBtnText(state == "running" ? active : _("none"));
setBtnText(state === "running" ? active : _("none"));
setState(state);
setStatus(status);
} catch (ex) {
@ -100,7 +100,7 @@ export const TunedPerformanceProfile = () => {
return (
<Tooltip id="tuned-status-tooltip" content={status}>
<Button id="tuned-status-button"
isAriaDisabled={btnText == "error" || state == "not-installed" || !superuser.allowed}
isAriaDisabled={btnText === "error" || state === "not-installed" || !superuser.allowed}
isInline
onClick={showDialog}
variant='link'>
@ -131,7 +131,7 @@ const TunedDialog = ({
const setProfile = () => {
const setService = () => {
/* When the profile is none we disable tuned */
const enable = (selected != "none");
const enable = (selected !== "none");
const action = enable ? "start" : "stop";
return tunedDbus.call('/Tuned', 'com.redhat.tuned.control', action, [])
.then(results => {
@ -158,7 +158,7 @@ const TunedDialog = ({
let promise;
if (selected == "none") {
if (selected === "none") {
promise = tunedDbus.call("/Tuned", 'com.redhat.tuned.control', 'disable', [])
.then(results => {
/* Yup this is how tuned returns failures */
@ -197,13 +197,13 @@ const TunedDialog = ({
name = p[0];
desc = p[1];
}
if (name != "none") {
if (name !== "none") {
model.push({
name,
title: name,
description: desc,
active: name == active,
recommended: name == recommended,
active: name === active,
recommended: name === recommended,
});
}
});
@ -212,8 +212,8 @@ const TunedDialog = ({
name: "none",
title: _("None"),
description: _("Disable tuned"),
active: active == "none",
recommended: recommended == "none",
active: active === "none",
recommended: recommended === "none",
});
setProfiles(model);
@ -234,7 +234,7 @@ const TunedDialog = ({
return poll_tuned_state(tunedDbus, tunedService)
.then(res => {
const { state, active, recommended } = res;
if (state != "running") {
if (state !== "running") {
setError(_("Tuned has failed to start"));
return;
}

View file

@ -39,7 +39,7 @@ export const UncleanShutdownStatus = () => {
} else {
const epoch = started.getTime().toString();
setUncleanShutdownId(epoch);
setUncleanShutdownStatusVisible(epoch != cockpit.sessionStorage.getItem("dismissed-unclean-shutdown-id"));
setUncleanShutdownStatusVisible(epoch !== cockpit.sessionStorage.getItem("dismissed-unclean-shutdown-id"));
}
});
});

View file

@ -70,7 +70,7 @@ class OverviewPage extends React.Component {
const static_hostname = this.state.hostnameData.StaticHostname;
let str = this.state.hostnameData.Hostname;
if (pretty_hostname && static_hostname && static_hostname != pretty_hostname)
if (pretty_hostname && static_hostname && static_hostname !== pretty_hostname)
str = pretty_hostname + " (" + static_hostname + ")";
else if (static_hostname)
str = static_hostname;
@ -134,8 +134,8 @@ class OverviewPage extends React.Component {
);
const show_superuser = (
cockpit.transport.host && cockpit.transport.host != "localhost" &&
!(window.parent.name == "cockpit1" && window.parent.features &&
cockpit.transport.host && cockpit.transport.host !== "localhost" &&
!(window.parent.name === "cockpit1" && window.parent.features &&
window.parent.features.navbar_is_for_current_machine));
return (

View file

@ -25,13 +25,13 @@ import "./page-status.scss";
import { page_status } from "notifications";
function icon_for_type(type) {
if (type == "error")
if (type === "error")
return (
<Icon status="danger">
<ExclamationCircleIcon />
</Icon>
);
else if (type == "warning")
else if (type === "warning")
return (
<Icon status="warning">
<ExclamationTriangleIcon />
@ -47,15 +47,15 @@ function icon_for_type(type) {
function get_pficon(name) {
// set data-pficon for the tests
if (name == "security")
if (name === "security")
return <Icon isInline status="danger"><SecurityIcon data-pficon={name} /></Icon>;
if (name == "enhancement")
if (name === "enhancement")
return <Icon isInline status="custom"><EnhancementIcon data-pficon={name} /></Icon>;
if (name == "bug")
if (name === "bug")
return <Icon isInline className="pf-m-important"><BugIcon data-pficon={name} /></Icon>;
if (name == "check")
if (name === "check")
return <Icon isInline status="success"><CheckIcon data-pficon={name} /></Icon>;
if (name == "spinner")
if (name === "spinner")
return <Spinner diameter="1em" data-pficon={name} />;
throw new Error(`get_pficon(): unknown icon name ${name}`);

View file

@ -240,7 +240,7 @@ export class ServiceDetails extends React.Component {
this.doMemoryCurrentPolling = this.doMemoryCurrentPolling.bind(this);
// MemoryCurrent property does not emit a changed signal - do polling for this property
if (props.unit.ActiveState == "active") {
if (props.unit.ActiveState === "active") {
this.doMemoryCurrentPolling();
this.interval = setInterval(this.doMemoryCurrentPolling, METRICS_POLL_DELAY);
}
@ -305,7 +305,7 @@ export class ServiceDetails extends React.Component {
}
addUnitProperties(prop, value) {
if (prop == "MemoryCurrent" && this.state.unit_properties.MemoryCurrent !== value)
if (prop === "MemoryCurrent" && this.state.unit_properties.MemoryCurrent !== value)
this.setState(prevState => ({ unit_properties: { ...prevState.unit_properties, [prop]: value } }));
}
@ -346,7 +346,7 @@ export class ServiceDetails extends React.Component {
pinUnit() {
const newPinned = this.state.isPinned
? this.props.pinnedUnits.filter(unitId => unitId != this.props.unit.Id)
? this.props.pinnedUnits.filter(unitId => unitId !== this.props.unit.Id)
: [...this.props.pinnedUnits, this.props.unit.Id];
localStorage.setItem('systemd:pinnedUnits', JSON.stringify(newPinned));
@ -358,10 +358,10 @@ export class ServiceDetails extends React.Component {
this.setState({ waitsFileAction: true });
const args = [[this.props.unit.Names[0]], false];
if (force !== undefined)
args.push(force == "true");
args.push(force === "true");
const promise = systemd_client[this.props.owner].call(s_bus.O_MANAGER, s_bus.I_MANAGER, method, args)
.then(([results]) => {
if (results.length == 2 && !results[0])
if (results.length === 2 && !results[0])
this.show_note(_("This unit is not designed to be enabled explicitly."));
/* Executing daemon reload after file operations is necessary -
* see https://github.com/systemd/systemd/blob/main/src/systemctl/systemctl.c [enable_unit function]
@ -392,7 +392,7 @@ export class ServiceDetails extends React.Component {
}
const [cmd, args] = exec[0];
if (cmd !== "/bin/sh" || args.length !== 3 || args[1] != "-c") {
if (cmd !== "/bin/sh" || args.length !== 3 || args[1] !== "-c") {
console.warn(`ExecStart= entry is not of the form "/bin/sh -c CMD"`);
return null;
}
@ -476,7 +476,7 @@ export class ServiceDetails extends React.Component {
const failed = this.props.unit.ActiveState === "failed";
const masked = this.props.unit.LoadState === "masked";
const unit = this.state.unit_properties;
const showAction = this.props.permitted || this.props.owner == "user";
const showAction = this.props.permitted || this.props.owner === "user";
const isCustom = this.props.unit.FragmentPath.startsWith("/etc/systemd/system") && !masked;
const isTimer = (this.unitType === "timer");
const isQuadlet = this.props.unit.SourcePath?.includes("/containers/systemd/");

View file

@ -51,7 +51,7 @@ export function ServiceTabs({
<NavItem itemId={key}
key={key}
preventDefault
isActive={activeItem == key}>
isActive={activeItem === key}>
<Button variant="link" component="a">
{service_tabs.get(key)}
{tabErrors[key] ? <Icon status="danger"><ExclamationCircleIcon className="ct-exclamation-circle" /></Icon> : null}

View file

@ -23,7 +23,7 @@ import s_bus from "./busnames.js";
const _ = cockpit.gettext;
function debug() {
if (window.debugging == "all" || window.debugging?.includes("service-details")) // not-covered: debugging
if (window.debugging === "all" || window.debugging?.includes("service-details")) // not-covered: debugging
console.debug.apply(console, arguments); // not-covered: debugging
}
@ -120,13 +120,13 @@ export const Service = ({ dbusClient, owner, unitId, unitIsValid, addTimerProper
// resolve Alias name to primary ID
const cur_unit_id = unitProps.Id;
const unit_type = owner == "system" ? "UNIT" : "USER_UNIT";
const unit_type = owner === "system" ? "UNIT" : "USER_UNIT";
const match = [
"_SYSTEMD_" + unit_type + "=" + cur_unit_id, "+",
"COREDUMP_" + unit_type + "=" + cur_unit_id, "+",
unit_type + "=" + cur_unit_id,
];
const service_type = owner == "system" ? "service" : "user-service";
const service_type = owner === "system" ? "service" : "user-service";
const url = "/system/logs/#/?prio=debug&" + service_type + "=" + cur_unit_id;
const load_state = unitProps.LoadState;

View file

@ -56,7 +56,7 @@ const getServicesRow = ({ Id, shortId, AutomaticStartup, UnitFileState, LoadStat
const enabled = UnitFileState && UnitFileState.includes("enabled");
const disabled = UnitFileState && UnitFileState.includes("disabled");
const isStatic = UnitFileState && UnitFileState == "static";
const isStatic = UnitFileState && UnitFileState === "static";
const masked = LoadState && LoadState.includes("masked");
let unitFileState;
if (enabled || disabled)
@ -90,7 +90,7 @@ const getServicesRow = ({ Id, shortId, AutomaticStartup, UnitFileState, LoadStat
<ThumbtackIcon className='service-thumbtack-icon-color' />
</Tooltip>}
</Flex>
{props.Description != shortId && <div className='service-unit-description'>{props.Description}</div>}
{props.Description !== shortId && <div className='service-unit-description'>{props.Description}</div>}
</div>
)
},

View file

@ -52,7 +52,7 @@ let monotonic_timer_base = null; // µs
export const MAX_UINT64 = 2 ** 64 - 1;
function debug() {
if (window.debugging == "all" || window.debugging?.includes("services"))
if (window.debugging === "all" || window.debugging?.includes("services"))
console.debug.apply(console, arguments);
}
@ -252,10 +252,10 @@ class ServicesPageBody extends React.Component {
onOptionsChanged(options) {
const currentOptions = { ...cockpit.location.options, ...options };
if (!currentOptions.activestate || options.activestate == "[]")
if (!currentOptions.activestate || options.activestate === "[]")
delete currentOptions.activestate;
if (!currentOptions.filestate || options.filestate == "[]")
if (!currentOptions.filestate || options.filestate === "[]")
delete currentOptions.filestate;
if (!currentOptions.name)
@ -269,8 +269,8 @@ class ServicesPageBody extends React.Component {
this.systemd_subscription = systemd_client[this.props.owner].call(s_bus.O_MANAGER, s_bus.I_MANAGER, "Subscribe", null)
.finally(this.listUnits)
.catch(error => {
if (error.name != "org.freedesktop.systemd1.AlreadySubscribed" &&
error.name != "org.freedesktop.DBus.Error.FileExists")
if (error.name !== "org.freedesktop.systemd1.AlreadySubscribed" &&
error.name !== "org.freedesktop.DBus.Error.FileExists")
this.setState({ error: cockpit.format(_("Subscribing to systemd signals failed: $0"), error.toString()) });
});
})
@ -429,7 +429,7 @@ class ServicesPageBody extends React.Component {
isTemplate(id) {
const tp = id.indexOf("@");
const sp = id.lastIndexOf(".");
return (tp != -1 && (tp + 1 == sp || tp + 1 == id.length));
return (tp !== -1 && (tp + 1 === sp || tp + 1 === id.length));
}
listUnits() {
@ -523,9 +523,9 @@ class ServicesPageBody extends React.Component {
if (!unit_a || !unit_b)
return 0;
if (failed_a != failed_b)
if (failed_a !== failed_b)
return failed_b - failed_a;
else if (pinned_a != pinned_b)
else if (pinned_a !== pinned_b)
return pinned_b - pinned_a;
else
return unit_a_t[0].localeCompare(unit_b_t[0]);
@ -560,7 +560,7 @@ class ServicesPageBody extends React.Component {
/* Add some computed properties into a unit object - does not call setState */
updateComputedProperties(unit) {
unit.HasFailed = unit.ActiveState == "failed" || (
unit.HasFailed = unit.ActiveState === "failed" || (
unit.LoadState && unit.LoadState !== "loaded" && unit.LoadState !== "masked");
unit.CombinedState = this.activeState[unit.ActiveState] || unit.ActiveState;
@ -577,7 +577,7 @@ class ServicesPageBody extends React.Component {
const tabErrors = { };
Object.values(this.units).forEach(u => {
if (u.ActiveState == "failed" && u.LoadState != "not-found") {
if (u.ActiveState === "failed" && u.LoadState !== "not-found") {
const suffix = u.Id.substring(u.Id.lastIndexOf('.') + 1);
if (service_tabs_suffixes.includes(suffix)) {
tabErrors[suffix] = true;
@ -659,7 +659,7 @@ class ServicesPageBody extends React.Component {
/* Navigation: unit details page with a path, service list without;
* the details page does its own loading, we don't need to wait for isFullyLoaded */
const path = cockpit.location.path;
if (path.length == 1) {
if (path.length === 1) {
const unit_id = path[0];
return <Service unitIsValid={unitId => this.unit_files[unitId] || this.knownIds.has(unitId) }
@ -720,7 +720,7 @@ class ServicesPageBody extends React.Component {
/>
</CardHeader>
<ServicesList key={cockpit.format("$0-list", activeTab)}
isTimer={activeTab == 'timer'}
isTimer={activeTab === 'timer'}
onClearAllFilters={onClearAllFilters}
units={this.computeSelectedUnits()} />
</Card>
@ -769,9 +769,9 @@ const ServicesPageFilters = ({
};
const getFilterLabelKey = (typeLabel) => {
if (typeLabel == 'Active state')
if (typeLabel === 'Active state')
return 'activeState';
else if (typeLabel == 'File state')
else if (typeLabel === 'File state')
return 'fileState';
};
@ -899,7 +899,7 @@ const ServicesPage = () => {
return (
<WithDialogs>
<Page className='pf-m-no-sidebar'>
{cockpit.location.path.length == 0 &&
{cockpit.location.path.length === 0 &&
<PageSection hasBodyWrapper={false} className="services-header">
<Flex>
<ServiceTabs activeTab={activeTab}
@ -909,17 +909,17 @@ const ServicesPage = () => {
}} />
<FlexItem align={{ default: 'alignRight' }}>
{loggedUser && loggedUser !== 'root' && <ToggleGroup>
<ToggleGroupItem isSelected={owner == "system"}
<ToggleGroupItem isSelected={owner === "system"}
buttonId="system"
text={_("System")}
onChange={() => setOwner("system")} />
<ToggleGroupItem isSelected={owner == "user"}
<ToggleGroupItem isSelected={owner === "user"}
buttonId="user"
text={_("User")}
onChange={() => setOwner("user")} />
</ToggleGroup>}
</FlexItem>
{activeTab == "timer" && owner == "system" && superuser.allowed && <CreateTimerDialogButton isLoading={isLoading} owner={owner} />}
{activeTab === "timer" && owner === "system" && superuser.allowed && <CreateTimerDialogButton isLoading={isLoading} owner={owner} />}
</Flex>
</PageSection>}
<ServicesPageBody

View file

@ -158,25 +158,25 @@ export function create_timer({ name, description, command, delay, delayUnit, del
return `${month_str.padStart(2, '0')}-${day_str.padStart(2, '0')}`;
}
if (delay == "specific-time" && repeat == "no") {
if (delay === "specific-time" && repeat === "no") {
const today = new Date(clock_realtime_now);
timer_unit.OnCalendar = `OnCalendar=${today.getFullYear()}-${month_day_str(today)} ${specificTime}:00`;
} else if (repeat == "minutely") {
} else if (repeat === "minutely") {
timer_unit.repeat_second = repeat_array.map(item => Number(item.second));
timer_unit.OnCalendar = "OnCalendar=*-*-* *:*:" + timer_unit.repeat_second.join(",");
} else if (repeat == "hourly") {
} else if (repeat === "hourly") {
timer_unit.repeat_minute = repeat_array.map(item => Number(item.minute));
timer_unit.OnCalendar = "OnCalendar=*-*-* *:" + timer_unit.repeat_minute.join(",");
} else if (repeat == "daily") {
} else if (repeat === "daily") {
timer_unit.OnCalendar = repeat_array.map(item => `OnCalendar=*-*-* ${item.time}:00`);
} else if (repeat == "weekly") {
} else if (repeat === "weekly") {
timer_unit.OnCalendar = repeat_array.map(item => `OnCalendar=${item.day} *-*-* ${item.time}:00`);
} else if (repeat == "monthly") {
} else if (repeat === "monthly") {
timer_unit.OnCalendar = repeat_array.map(item => `OnCalendar=*-*-${item.day} ${item.time}:00`);
} else if (repeat == "yearly") {
} else if (repeat === "yearly") {
timer_unit.OnCalendar = repeat_array.map(item => `OnCalendar=*-${month_day_str(new Date(item.date))} ${item.time}:00`);
}
if (repeat != "hourly" && repeat != "minutely" && delay == "specific-time")
if (repeat !== "hourly" && repeat !== "minutely" && delay === "specific-time")
timer_unit.OnCalendar = timer_unit.OnCalendar.toString().replaceAll(",", "\n");
return create_timer_file({ timer_unit, delay, owner });
}
@ -188,7 +188,7 @@ function create_timer_file({ timer_unit, delay, owner }) {
const install = "[Install]\nWantedBy=timers.target\n";
const service_file = CockpitManagedMarker + unit + timer_unit.Description + service + timer_unit.Command + "\n";
let timer_file = CockpitManagedMarker;
if (delay == "system-boot") {
if (delay === "system-boot") {
const boottimer = timer + "OnBootSec=" + timer_unit.boot_time + timer_unit.boot_time_unit + "\n";
timer_file += unit + timer_unit.Description + boottimer;
} else if (timer_unit.OnCalendar) {

View file

@ -160,16 +160,16 @@ export const TimerDialog = ({ owner, timer }) => {
id="specific-time"
name="boot-or-specific-time"
onChange={() => setDelay("specific-time")}
isChecked={delay == "specific-time"}
isChecked={delay === "specific-time"}
label={_("At specific time")} />
<Radio value="system-boot"
id="system-boot"
name="boot-or-specific-time"
onChange={() => setDelay("system-boot")}
isChecked={delay == "system-boot"}
isChecked={delay === "system-boot"}
label={_("After system boot")} />
</Flex>
{ delay == "system-boot" &&
{ delay === "system-boot" &&
<FormGroup className="delay-group"
label={_("Delay")}>
<Flex>
@ -189,27 +189,27 @@ export const TimerDialog = ({ owner, timer }) => {
</Flex>
<FormHelper helperTextInvalid={submitted && validationFailed.delayNumber && _("Delay must be a number")} />
</FormGroup> }
{ delay == "specific-time" &&
{ delay === "specific-time" &&
<>
<FormGroup label={_("Repeat")}>
<FormSelect value={repeat}
id="drop-repeat"
onChange={(_, value) => {
if (value == repeat)
if (value === repeat)
return;
setRepeat(value);
if (value == "minutely")
if (value === "minutely")
setRepeatPatterns([{ key: 0, second: "0" }]);
else if (value == "hourly")
else if (value === "hourly")
setRepeatPatterns([{ key: 0, minute: "0" }]);
else if (value == "daily")
else if (value === "daily")
setRepeatPatterns([{ key: 0, time: "00:00" }]);
else if (value == "weekly")
else if (value === "weekly")
setRepeatPatterns([{ key: 0, day: "mon", time: "00:00" }]);
else if (value == "monthly")
else if (value === "monthly")
setRepeatPatterns([{ key: 0, day: 1, time: "00:00" }]);
else if (value == "yearly")
else if (value === "yearly")
setRepeatPatterns([{ key: 0, date: undefined, time: "00:00" }]);
}}
aria-label={_("Repeat")}>
@ -222,7 +222,7 @@ export const TimerDialog = ({ owner, timer }) => {
<FormSelectOption value="yearly" label={_("Yearly")} />
</FormSelect>
</FormGroup>
{repeat == "no" &&
{repeat === "no" &&
<FormGroup label={_("Run at")}>
<TimePicker className="create-timer-time-picker specific-no-repeat"
isOpen={isSpecificTimeOpen} setIsOpen={setSpecificTimeOpen}
@ -230,34 +230,34 @@ export const TimerDialog = ({ owner, timer }) => {
</FormGroup>}
{repeatPatterns.map((item, idx) => {
let label;
if (repeat == "minutely")
if (repeat === "minutely")
label = _("At second");
else if (repeat == "hourly")
else if (repeat === "hourly")
label = _("At minute");
else if (repeat == "daily")
else if (repeat === "daily")
label = _("Run at");
else if (repeat == "weekly" || repeat == "monthly" || repeat == "yearly")
else if (repeat === "weekly" || repeat === "monthly" || repeat === "yearly")
label = _("Run on");
let helperTextInvalid;
const min = repeatPatterns[idx].minute;
const validationFailedMinute = !(/^[0-9]+$/.test(min) && min <= 59 && min >= 0);
if (submitted && repeat == 'hourly' && validationFailedMinute) {
if (submitted && repeat === 'hourly' && validationFailedMinute) {
helperTextInvalid = _("Minute needs to be a number between 0-59");
}
const sec = repeatPatterns[idx].second;
const validationFailedSecond = !(/^[0-9]+$/.test(sec) && sec <= 59 && sec >= 0);
if (submitted && repeat == 'minutely' && validationFailedSecond) {
if (submitted && repeat === 'minutely' && validationFailedSecond) {
helperTextInvalid = _("Second needs to be a number between 0-59");
}
return (
<FormGroup label={label} key={item.key}>
<Flex className="specific-repeat-group" data-index={idx}>
{repeat == "minutely" &&
{repeat === "minutely" &&
<TextInput className='delay-number'
id={repeat}
value={repeatPatterns[idx].second}
@ -268,7 +268,7 @@ export const TimerDialog = ({ owner, timer }) => {
})}
validated={submitted && validationFailedSecond ? "error" : "default"} />
}
{repeat == "hourly" &&
{repeat === "hourly" &&
<TextInput className='delay-number'
value={repeatPatterns[idx].minute}
onChange={(_event, minute) => setRepeatPatterns(old => {
@ -278,8 +278,8 @@ export const TimerDialog = ({ owner, timer }) => {
})}
validated={submitted && validationFailedMinute ? "error" : "default"} />
}
{repeat == "daily" && timePicker(idx)}
{repeat == "weekly" && <>
{repeat === "daily" && timePicker(idx)}
{repeat === "weekly" && <>
<FormSelect value={repeatPatterns[idx].day}
className="week-days"
onChange={(_, day) => setRepeatPatterns(old => {
@ -298,7 +298,7 @@ export const TimerDialog = ({ owner, timer }) => {
</FormSelect>
{timePicker(idx)}
</>}
{repeat == "monthly" && <>
{repeat === "monthly" && <>
<FormSelect value={repeatPatterns[idx].day}
className="month-days"
onChange={(_, day) => setRepeatPatterns(old => {
@ -317,7 +317,7 @@ export const TimerDialog = ({ owner, timer }) => {
</FormSelect>
{timePicker(idx)}
</>}
{repeat == "yearly" && <>
{repeat === "yearly" && <>
<DatePicker aria-label={_("Pick date")}
buttonAriaLabel={_("Toggle date picker")}
locale={timeformat.dateFormatLang()}
@ -335,22 +335,22 @@ export const TimerDialog = ({ owner, timer }) => {
<InputGroup>
<Button icon={<MinusIcon />} aria-label={_("Remove")}
variant="secondary"
isDisabled={repeatPatterns.length == 1}
onClick={() => setRepeatPatterns(old => old.filter((item, item_idx) => idx != item_idx))} />
isDisabled={repeatPatterns.length === 1}
onClick={() => setRepeatPatterns(old => old.filter((item, item_idx) => idx !== item_idx))} />
<Button icon={<PlusIcon />} aria-label={_("Add")}
variant="secondary"
onClick={() => {
if (repeat == "minutely")
if (repeat === "minutely")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, second: "0" }]);
else if (repeat == "hourly")
else if (repeat === "hourly")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, minute: "0" }]);
else if (repeat == "daily")
else if (repeat === "daily")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, time: "00:00" }]);
else if (repeat == "weekly")
else if (repeat === "weekly")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, day: "mon", time: "00:00" }]);
else if (repeat == "monthly")
else if (repeat === "monthly")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, day: 1, time: "00:00" }]);
else if (repeat == "yearly")
else if (repeat === "yearly")
setRepeatPatterns(old => [...old, { key: repeatPatterns.length, date: undefined, time: "00:00" }]);
}} />
</InputGroup>