Line data Source code
1 : /*
2 : * Copyright (C) 2019 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 :
6 35 : import React, { useState } from 'react';
7 :
8 : import { Alert, AlertActionCloseButton } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
9 : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
10 : import {
11 : Modal, ModalBody, ModalFooter, ModalHeader
12 : } from '@patternfly/react-core/dist/esm/components/Modal/index.js';
13 : import { Stack } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
14 : import { TextArea } from "@patternfly/react-core/dist/esm/components/TextArea/index.js";
15 : import { EditIcon } from '@patternfly/react-icons';
16 : import { ModalError } from 'cockpit-components-inline-notification.jsx';
17 : import { superuser } from "superuser";
18 : import { useDialogs } from "dialogs.jsx";
19 : import { useInit } from "hooks";
20 :
21 35 : import cockpit from "cockpit";
22 :
23 : import './motdCard.scss';
24 :
25 35 : const _ = cockpit.gettext;
26 :
27 1 : const MotdEditDialog = ({ text, expectedTag }) => {
28 1 : const Dialogs = useDialogs();
29 1 : const [value, setValue] = useState(text);
30 1 : const [error, setError] = useState(null);
31 1 : const [errorDetail, setErrorDetail] = useState(null);
32 :
33 1 : return (
34 1 : <Modal position="top"
35 1 : variant="medium" isOpen
36 1 : id="motd-box-edit-modal"
37 1 : onClose={Dialogs.close}>
38 :
39 1 : <ModalHeader title={_("Edit /etc/motd")} />
40 1 : <ModalBody>
41 1 : <Stack hasGutter>
42 1 : {error &&
43 0 : <ModalError dialogError={error}
44 0 : dialogErrorDetail={errorDetail} />}
45 1 : <TextArea resizeOrientation="vertical"
46 1 : value={value}
47 1 : onChange={(_event, value) => setValue(value)} />
48 1 : </Stack>
49 1 : </ModalBody>
50 1 : <ModalFooter>
51 1 : <Button variant='primary'
52 1 : onClick={() => cockpit.file("/etc/motd", { superuser: "try", err: "message" })
53 1 : .replace(value, expectedTag)
54 1 : .then(Dialogs.close)
55 0 : .catch(exc => {
56 0 : setError(_("Failed to save changes in /etc/motd"));
57 0 : setErrorDetail(exc.message);
58 0 : })}>
59 1 : {_("Save changes")}
60 1 : </Button>
61 1 : <Button variant='link'
62 1 : onClick={Dialogs.close}>
63 1 : {_("Cancel")}
64 1 : </Button>
65 1 : </ModalFooter>
66 1 : </Modal>);
67 1 : };
68 :
69 35 : export const MotdCard = () => {
70 35 : const Dialogs = useDialogs();
71 35 : const [motdText, setMotdText] = useState("");
72 35 : const [motdTag, setMotdTag] = useState(null);
73 35 : const [motdVisible, setMotdVisible] = useState(false);
74 :
75 35 : useInit(() => {
76 30 : cockpit.file("/etc/motd").watch((content, tag) => {
77 : /* trim initial empty lines and trailing space, but keep initial spaces to not break ASCII art */
78 30 : if (content)
79 6 : content = content.trimEnd().replace(/^\s*\n/, '');
80 6 : if (content && content != cockpit.localStorage.getItem('dismissed-motd')) {
81 6 : setMotdText(content);
82 6 : setMotdTag(tag);
83 6 : setMotdVisible(true);
84 6 : } else {
85 30 : setMotdVisible(false);
86 30 : }
87 30 : });
88 35 : });
89 :
90 0 : function hideAlert() {
91 0 : setMotdVisible(false);
92 0 : cockpit.localStorage.setItem('dismissed-motd', motdText);
93 0 : }
94 :
95 35 : if (!motdVisible)
96 35 : return null;
97 :
98 6 : const actionClose = <>
99 6 : {superuser.allowed &&
100 6 : <Button icon={<EditIcon />} variant="plain"
101 6 : id="motd-box-edit"
102 1 : onClick={() => Dialogs.show(<MotdEditDialog text={motdText} expectedTag={motdTag} />)}
103 6 : aria-label={_("Edit motd")} />}
104 35 : <AlertActionCloseButton onClose={hideAlert} />
105 35 : </>;
106 :
107 35 : return <Alert id="motd-box" isInline className="motd-box"
108 35 : variant="custom"
109 35 : title={<pre id="motd">{motdText}</pre>}
110 35 : actionClose={actionClose} />;
111 35 : };
|