<commit_before>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
 */

import React from "react";
import moment from "moment";
import PropTypes from "prop-types";
import {
    Alert, Button,
    DescriptionList, DescriptionListTerm, DescriptionListGroup, DescriptionListDescription,
    Dropdown, DropdownItem, DropdownSeparator, KebabToggle,
    ExpandableSection,
    Tooltip, TooltipPosition,
    Card, CardBody, CardTitle, Text, TextVariants,
    Modal, Switch
} from "@patternfly/react-core";

import cockpit from "cockpit";
import { systemd_client, SD_MANAGER, SD_OBJ } from "./services.jsx";

import './service-details.scss';

const _ = cockpit.gettext;
const METRICS_POLL_DELAY = 30000; // 30s

/*
 * React template for showing basic dialog for confirming action
 * Required props:
 *  - title
 *     Title of the dialog
 *  - message
 *     Message in the dialog
 *  - close
 *     Action to be executed when Cancel button is selected.
 * Optional props:
 *  - confirmText
 *     Text of the button for confirming the action
 *  - confirmAction
 *     Action to be executed when the action is confirmed
 */
class ServiceConfirmDialog extends React.Component {
    render() {
        return (
            <Modal id={this.props.id} isOpen
                   position="top" variant="medium"
                   onClose={this.props.close}
                   title={this.props.title}
                   footer={
                       <>
                           { this.props.confirmText && this.props.confirmAction &&
                               <Button variant='danger' onClick={this.props.confirmAction}>
                                   {this.props.confirmText}
                               </Button>
                           }
                           <Button variant='link' className='btn-cancel' onClick={this.props.close}>
                               { _("Cancel") }
                           </Button>
                       </>
                   }>
                {this.props.message}
            </Modal>
        );
    }
}
ServiceConfirmDialog.propTypes = {
    title: PropTypes.string.isRequired,
    message: PropTypes.string.isRequired,
    close: PropTypes.func.isRequired,
    confirmText: PropTypes.string,
    confirmAction: PropTypes.func,
};

/*
 * React template for showing possible service action (in a kebab menu)
 * Required props:
 *  - masked
 *     Unit is masked
 *  - active
 *     Unit is active (running)
 *  - failed
 *     Unit has failed
 *  - canReload
 *      Unit can be reloaded
 *  - actionCallback
 *      Method for calling unit methods like `UnitStart`
 *  - fileActionCallback
 *      Method for calling unit file methods like `EnableUnitFiles`
 *  - disabled
 *      Button is disabled
 */
class ServiceActions extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dialogMaskedOpened: false,
        };
    }

    render() {
        const actions = [];

        // If masked, only show unmasking and nothing else
        if (this.props.masked) {
            actions.push(
                <DropdownItem key="unmask" onClick={() => this.props.fileActionCallback("UnmaskUnitFiles", undefined)}>{ _("Allow running (unmask)") }</DropdownItem>
            );
        } else { // All cases when not masked
            if (this.props.active) {
                if (this.props.canReload) {
                    actions.push(
                        <DropdownItem key="reload" onClick={() => this.props.actionCallback("ReloadUnit")}>{ _("Reload") }</DropdownItem>
                    );
                }
                actions.push(
                    <DropdownItem key="restart" onClick={() => this.props.actionCallback("RestartUnit")}>{ _("Restart") }</DropdownItem>
                );
                actions.push(
                    <DropdownItem key="stop" onClick={() => this.props.actionCallback("StopUnit")}>{ _("Stop") }</DropdownItem>,
                );
            } else {
                actions.push(
                    <DropdownItem key="start" onClick={() => this.props.actionCallback("StartUnit")}>{ _("Start") }</DropdownItem>
                );
            }

            if (actions.length > 0) {
                actions.push(
                    <DropdownSeparator key="divider" />
                );
            }

            if (this.props.failed)
                actions.push(
                    <DropdownItem key="reset" onClick={() => this.props.actionCallback("ResetFailedUnit", []) }>{ _("Clear 'Failed to start'") }</DropdownItem>
                );

            actions.push(
                <DropdownItem key="mask" onClick={() => this.setState({ dialogMaskedOpened: true }) }>{ _("Disallow running (mask)") }</DropdownItem>
            );
        }

        return (
            <>
                { this.state.dialogMaskedOpened &&
                    <ServiceConfirmDialog id="mask-service" title={ _("Mask service") }
                                          message={ _("Masking service prevents all dependent units from running. This can have bigger impact than anticipated. Please confirm that you want to mask this unit.")}
                                          close={() => this.setState({ dialogMaskedOpened: false }) }
                                          confirmText={ _("Mask service") }
                                          confirmAction={() => {
                                              this.props.fileActionCallback("MaskUnitFiles", false);
                                              this.props.actionCallback("ResetFailedUnit", []);
                                              this.setState({ dialogMaskedOpened: false });
                                          }} />
                }
                <Dropdown id="service-actions" title={ _("Additional actions") }
                          toggle={<KebabToggle isDisabled={this.props.disabled} onToggle={isActionOpen => this.setState({ isActionOpen })} />}
                          isOpen={this.state.isActionOpen}
                          isPlain
                          onSelect={() => this.setState({ isActionOpen: !this.state.isActionOpen })}
                          dropdownItems={actions} />
            </>
        );
    }
}
ServiceActions.propTypes = {
    masked: PropTypes.bool.isRequired,
    active: PropTypes.bool.isRequired,
    failed: PropTypes.bool.isRequired,
    canReload: PropTypes.bool,
    actionCallback: PropTypes.func.isRequired,
    fileActionCallback: PropTypes.func.isRequired,
    disabled: PropTypes.bool,
};

/*
 * React template for a service details<commit_msg>services: align the action dropdown horizontally to the right<commit_after>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
 */

import React from "react";
import moment from "moment";
import PropTypes from "prop-types";
import {
    Alert, Button,
    DescriptionList, DescriptionListTerm, DescriptionListGroup, DescriptionListDescription,
    Dropdown, DropdownItem, DropdownSeparator, KebabToggle,
    ExpandableSection,
    Tooltip, TooltipPosition,
    Card, CardBody, CardTitle, Text, TextVariants,
    Modal, Switch
} from "@patternfly/react-core";

import cockpit from "cockpit";
import { systemd_client, SD_MANAGER, SD_OBJ } from "./services.jsx";

import './service-details.scss';

const _ = cockpit.gettext;
const METRICS_POLL_DELAY = 30000; // 30s

/*
 * React template for showing basic dialog for confirming action
 * Required props:
 *  - title
 *     Title of the dialog
 *  - message
 *     Message in the dialog
 *  - close
 *     Action to be executed when Cancel button is selected.
 * Optional props:
 *  - confirmText
 *     Text of the button for confirming the action
 *  - confirmAction
 *     Action to be executed when the action is confirmed
 */
class ServiceConfirmDialog extends React.Component {
    render() {
        return (
            <Modal id={this.props.id} isOpen
                   position="top" variant="medium"
                   onClose={this.props.close}
                   title={this.props.title}
                   footer={
                       <>
                           { this.props.confirmText && this.props.confirmAction &&
                               <Button variant='danger' onClick={this.props.confirmAction}>
                                   {this.props.confirmText}
                               </Button>
                           }
                           <Button variant='link' className='btn-cancel' onClick={this.props.close}>
                               { _("Cancel") }
                           </Button>
                       </>
                   }>
                {this.props.message}
            </Modal>
        );
    }
}
ServiceConfirmDialog.propTypes = {
    title: PropTypes.string.isRequired,
    message: PropTypes.string.isRequired,
    close: PropTypes.func.isRequired,
    confirmText: PropTypes.string,
    confirmAction: PropTypes.func,
};

/*
 * React template for showing possible service action (in a kebab menu)
 * Required props:
 *  - masked
 *     Unit is masked
 *  - active
 *     Unit is active (running)
 *  - failed
 *     Unit has failed
 *  - canReload
 *      Unit can be reloaded
 *  - actionCallback
 *      Method for calling unit methods like `UnitStart`
 *  - fileActionCallback
 *      Method for calling unit file methods like `EnableUnitFiles`
 *  - disabled
 *      Button is disabled
 */
class ServiceActions extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dialogMaskedOpened: false,
        };
    }

    render() {
        const actions = [];

        // If masked, only show unmasking and nothing else
        if (this.props.masked) {
            actions.push(
                <DropdownItem key="unmask" onClick={() => this.props.fileActionCallback("UnmaskUnitFiles", undefined)}>{ _("Allow running (unmask)") }</DropdownItem>
            );
        } else { // All cases when not masked
            if (this.props.active) {
                if (this.props.canReload) {
                    actions.push(
                        <DropdownItem key="reload" onClick={() => this.props.actionCallback("ReloadUnit")}>{ _("Reload") }</DropdownItem>
                    );
                }
                actions.push(
                    <DropdownItem key="restart" onClick={() => this.props.actionCallback("RestartUnit")}>{ _("Restart") }</DropdownItem>
                );
                actions.push(
                    <DropdownItem key="stop" onClick={() => this.props.actionCallback("StopUnit")}>{ _("Stop") }</DropdownItem>,
                );
            } else {
                actions.push(
                    <DropdownItem key="start" onClick={() => this.props.actionCallback("StartUnit")}>{ _("Start") }</DropdownItem>
                );
            }

            if (actions.length > 0) {
                actions.push(
                    <DropdownSeparator key="divider" />
                );
            }

            if (this.props.failed)
                actions.push(
                    <DropdownItem key="reset" onClick={() => this.props.actionCallback("ResetFailedUnit", []) }>{ _("Clear 'Failed to start'") }</DropdownItem>
                );

            actions.push(
                <DropdownItem key="mask" onClick={() => this.setState({ dialogMaskedOpened: true }) }>{ _("Disallow running (mask)") }</DropdownItem>
            );
        }

        return (
            <>
                { this.state.dialogMaskedOpened &&
                    <ServiceConfirmDialog id="mask-service" title={ _("Mask service") }
                                          message={ _("Masking service prevents all dependent units from running. This can have bigger impact than anticipated. Please confirm that you want to mask this unit.")}
                                          close={() => this.setState({ dialogMaskedOpened: false }) }
                                          confirmText={ _("Mask service") }
                                          confirmAction={() => {
                                              this.props.fileActionCallback("MaskUnitFiles", false);
                                              this.props.actionCallback("ResetFailedUnit", []);
                                              this.setState({ dialogMaskedOpened: false });
                                          }} />
                }
                <Dropdown id="service-actions" title={ _("Additional actions") }
                          toggle={<KebabToggle isDisabled={this.props.disabled} onToggle={isActionOpen => this.setState({ isActionOpen })} />}
                          isOpen={this.state.isActionOpen}
                          isPlain
                          onSelect={() => this.setState({ isActionOpen: !this.state.isActionOpen })}
                          position='right'
                          dropdownItems={actions} />
            </>
        );
    }
}
ServiceActions.propTypes = {
    masked: PropTypes.bool.isRequired,
    active: PropTypes.bool.isRequired,
    failed: PropTypes.bool.isRequired,
    canReload: PropTypes.bool,
    actionCallback: PropTypes.func.isRequired,
    fileActionCallback: PropTypes.func.isRequired,
    disabled: PropTypes.bool,
};

/*
 * React template for a service details
{'fraction_non_alphanumeric': 0.10646233132162473, 'fraction_numerical': 0.0018986912592391673, 'mean_word_length': 1.7669793621013132, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4879112', 'n_tokens_mistral': 3688, 'n_tokens_neox': 3588, 'n_words': 994}