/* * This file is part of Cockpit. * * Copyright (C) 2021 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ import cockpit from "cockpit"; import React from 'react'; import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js"; import { Select, SelectOption } from "@patternfly/react-core/dist/esm/deprecated/components/Select/index.js"; import { Modal } from "@patternfly/react-core/dist/esm/components/Modal/index.js"; import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js"; import { Flex } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js"; import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js"; import { Divider } from "@patternfly/react-core/dist/esm/components/Divider/index.js"; import { TextArea } from "@patternfly/react-core/dist/esm/components/TextArea/index.js"; import { DatePicker } from "@patternfly/react-core/dist/esm/components/DatePicker/index.js"; import { TimePicker } from "@patternfly/react-core/dist/esm/components/TimePicker/index.js"; import { ServerTime } from 'serverTime.js'; import * as timeformat from "timeformat.js"; import { DialogsContext } from "dialogs.jsx"; import { FormHelper } from "cockpit-components-form-helper"; import "cockpit-components-shutdown.scss"; const _ = cockpit.gettext; export class ShutdownModal extends React.Component { static contextType = DialogsContext; constructor(props) { super(props); this.date_spawn = null; this.state = { error: "", dateError: "", message: "", isOpen: false, selected: "1", dateObject: undefined, startDate: undefined, date: "", time: "", when: "+1", formFilled: false, }; this.onSubmit = this.onSubmit.bind(this); this.updateDate = this.updateDate.bind(this); this.updateTime = this.updateTime.bind(this); this.calculate = this.calculate.bind(this); this.dateRangeValidator = this.dateRangeValidator.bind(this); this.server_time = new ServerTime(); } componentDidMount() { this.server_time.wait() .then(() => { const dateObject = this.server_time.utc_fake_now; const date = timeformat.dateShort(dateObject); const hour = this.server_time.utc_fake_now.getUTCHours(); const minute = this.server_time.utc_fake_now.getUTCMinutes(); this.setState({ dateObject, date, startDate: new Date(dateObject.toDateString()), time: hour.toString().padStart(2, "0") + ":" + minute.toString().padStart(2, "0"), }); }) .always(() => this.setState({ formFilled: true })); } updateDate(value, dateObject) { this.setState({ date: value, dateObject }, this.calculate); } updateTime(value, hour, minute) { this.setState({ time: value, hour, minute }, this.calculate); } calculate() { if (this.date_spawn) this.date_spawn.close("cancelled"); if (this.state.selected != "x") { this.setState(prevState => ({ when: "+" + prevState.selected, error: "", dateError: "", })); return; } const time_error = this.state.hour === null || this.state.minute === null; const date_error = !this.state.dateObject; if (time_error && date_error) { this.setState({ dateError: _("Invalid date format and invalid time format") }); return; } else if (time_error) { this.setState({ dateError: _("Invalid time format") }); return; } else if (date_error) { this.setState({ dateError: _("Invalid date format") }); return; } const cmd = ["date", "--date=" + (new Intl.DateTimeFormat('en-us').format(this.state.dateObject)) + " " + this.state.time, "+%s"]; this.date_spawn = cockpit.spawn(cmd, { err: "message" }); this.date_spawn.then(data => { const input_timestamp = parseInt(data, 10); const server_timestamp = parseInt(this.server_time.now.getTime() / 1000, 10); let offset = Math.ceil((input_timestamp - server_timestamp) / 60); /* If the time in minutes just changed, make it happen now */ if (offset === -1) { offset = 0; } else if (offset < 0) { // Otherwise it is a failure this.setState({ dateError: _("Cannot schedule event in the past") }); return; } this.setState({ when: "+" + offset, error: "", dateError: "", }); }); this.date_spawn.catch(e => { if (e.problem == "cancelled") return; this.setState({ error: e.message }); }); this.date_spawn.finally(() => { this.date_spawn = null }); } onSubmit(event) { const Dialogs = this.context; const arg = this.props.shutdown ? "--poweroff" : "--reboot"; if (!this.props.shutdown) cockpit.hint("restart"); cockpit.spawn(["shutdown", arg, this.state.when, this.state.message], { superuser: true, err: "message" }) .then(this.props.onClose || Dialogs.close) .catch(e => this.setState({ error: e })); event.preventDefault(); return false; } dateRangeValidator(date) { if (this.state.startDate && date < this.state.startDate) { return _("Cannot schedule event in the past"); } return ''; } render() { const Dialogs = this.context; const options = [ {_("No delay")}, , {_("1 minute")}, {_("5 minutes")}, {_("20 minutes")}, {_("40 minutes")}, {_("60 minutes")}, , {_("Specific time")} ]; return ( } > <>