LCOV - code coverage report
Current view: top level - pkg/shell - shell-modals.tsx Coverage Total Hit
Test: cockpit Lines: 34.3 % 143 49
Test Date: 2026-07-02 14:11:36

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2020 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6          123 : import cockpit from "cockpit";
       7          123 : import React, { useState } from "react";
       8              : import { AboutModal } from "@patternfly/react-core/dist/esm/components/AboutModal/index.js";
       9              : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
      10              : import { Divider } from "@patternfly/react-core/dist/esm/components/Divider/index.js";
      11              : import { Flex } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
      12              : import { Menu, MenuContent, MenuSearch, MenuSearchInput, MenuItem, MenuList } from "@patternfly/react-core/dist/esm/components/Menu/index.js";
      13              : import {
      14              :     Modal, ModalBody, ModalFooter, ModalHeader
      15              : } from '@patternfly/react-core/dist/esm/components/Modal/index.js';
      16              : import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput/index.js";
      17              : import { Content, ContentVariants } from "@patternfly/react-core/dist/esm/components/Content/index.js";
      18              : import { SearchIcon } from '@patternfly/react-icons';
      19              : 
      20              : import { useInit } from "hooks";
      21              : import { DialogResult } from "dialogs";
      22              : 
      23              : import { ShellState } from "./state";
      24              : 
      25              : import "menu-select-widget.scss";
      26              : 
      27          123 : const _ = cockpit.gettext;
      28              : 
      29              : interface Package {
      30              :     name: string;
      31              :     version: string;
      32              : }
      33              : 
      34            1 : export const AboutCockpitModal = ({
      35            1 :     dialogResult
      36            1 : } : {
      37              :     dialogResult: DialogResult<void>
      38            1 : }) => {
      39            1 :     const [packages, setPackages] = useState<Package[]>([]);
      40              : 
      41            1 :     useInit(() => {
      42            1 :         const packages: Package[] = [];
      43            1 :         const cmd = "(set +e; rpm -qa --qf '%{NAME} %{VERSION}\\n'; dpkg-query -f '${Package} ${Version}\n' --show; pacman -Q) 2> /dev/null | grep cockpit | sort";
      44            1 :         cockpit.spawn(["bash", "-c", cmd], { err: "message" })
      45            1 :                 .then(pkgs =>
      46            1 :                     pkgs.trim().split("\n")
      47            1 :                             .forEach(p => {
      48            1 :                                 const parts = p.split(" ");
      49            1 :                                 packages.push({ name: parts[0], version: parts[1] });
      50            1 :                             })
      51            1 :                 )
      52            0 :                 .catch(error => console.error("Could not read packages versions:", error))
      53            1 :                 .finally(() => setPackages(packages));
      54            1 :     });
      55              : 
      56            1 :     return (
      57            1 :         <AboutModal
      58            1 :             isOpen
      59            1 :             onClose={() => dialogResult.resolve()}
      60            1 :             id="about-cockpit-modal"
      61            1 :             aria-label="About Cockpit and its versions"
      62            1 :             trademark={_("Licensed under GNU LGPL version 2.1")}
      63            1 :             productName={_("Web Console")}
      64            1 :             brandImageSrc="../shell/images/cockpit-icon-gray.svg"
      65            1 :             brandImageAlt={_("Web console logo")}
      66              :         >
      67            1 :             <Content component={ContentVariants.p}>
      68            1 :                 {_("Cockpit is an interactive Linux server admin interface.")}
      69            1 :             </Content>
      70            1 :             <Content component={ContentVariants.p}>
      71            1 :                 <Content component={ContentVariants.a} href="https://cockpit-project.org" target="_blank" rel="noopener noreferrer">
      72            1 :                     {_("Project website")}
      73            1 :                 </Content>
      74            1 :             </Content>
      75            1 :             <Content component="dl">
      76            0 :                 {packages === null && <span>{_("Loading packages...")}</span>}
      77            1 :                 {packages !== null && packages.map(p =>
      78            1 :                     <React.Fragment key={p.name}>
      79            1 :                         <Content key={p.name} component="dt">{p.name}</Content>
      80            1 :                         <Content component="dd">{p.version}</Content>
      81            1 :                     </React.Fragment>
      82            1 :                 )}
      83            1 :             </Content>
      84            1 :         </AboutModal>
      85              :     );
      86            1 : };
      87              : 
      88              : export interface LangModalProps {
      89              :     dialogResult: DialogResult<void>;
      90              :     state: ShellState;
      91              : }
      92              : 
      93            0 : export const LangModal = ({
      94            0 :     dialogResult,
      95            0 :     state
      96            0 : }: LangModalProps) => {
      97            0 :     const language = document.cookie.replace(/(?:(?:^|.*;\s*)CockpitLang\s*=\s*([^;]*).*$)|^.*$/, "$1") || "en-us";
      98              : 
      99            0 :     const [selected, setSelected] = useState(language);
     100            0 :     const [searchInput, setSearchInput] = useState("");
     101              : 
     102            0 :     function onSelect() {
     103            0 :         if (!selected)
     104            0 :             return;
     105              : 
     106            0 :         const cookie = "CockpitLang=" + encodeURIComponent(selected) + "; path=/; expires=Sun, 16 Jul 3567 06:23:41 GMT";
     107            0 :         document.cookie = cookie;
     108            0 :         window.localStorage.setItem("cockpit.lang", selected);
     109            0 :         window.location.reload();
     110            0 :     }
     111              : 
     112            0 :     return (
     113            0 :         <Modal isOpen position="top" variant="small"
     114            0 :                id="display-language-modal"
     115            0 :                className="display-language-modal"
     116            0 :                onClose={() => dialogResult.resolve()}
     117              :         >
     118            0 :             <ModalHeader title={_("Display language")} />
     119            0 :             <ModalBody>
     120            0 :                 <Flex direction={{ default: 'column' }}>
     121            0 :                     <p>{_("Choose the language to be used in the application")}</p>
     122            0 :                     <Menu id="display-language-list"
     123            0 :                           isPlain
     124            0 :                           isScrollable
     125            0 :                           className="ct-menu-select-widget"
     126            0 :                           onSelect={(_, selected) => setSelected(selected as string)}
     127            0 :                           activeItemId={selected}
     128            0 :                           selected={selected}>
     129            0 :                         <MenuSearch>
     130            0 :                             <MenuSearchInput>
     131            0 :                                 <TextInput
     132            0 :                                     value={searchInput}
     133            0 :                                     aria-label={_("Filter menu items")}
     134            0 :                                     customIcon={<SearchIcon />}
     135            0 :                                     type="search"
     136            0 :                                     onChange={(_event, value) => setSearchInput(value)}
     137            0 :                                 />
     138            0 :                             </MenuSearchInput>
     139            0 :                         </MenuSearch>
     140            0 :                         <Divider />
     141            0 :                         <MenuContent>
     142            0 :                             <MenuList>
     143              :                                 {
     144            0 :                                     (() => {
     145            0 :                                         const locales = state.config.manifest.locales || {};
     146            0 :                                         const filteredLocales = Object.keys(locales)
     147            0 :                                                 .filter(key => !searchInput || locales[key].toLowerCase().includes(searchInput.toString().toLowerCase()));
     148              : 
     149            0 :                                         if (filteredLocales.length === 0) {
     150            0 :                                             return (
     151            0 :                                                 <MenuItem isDisabled>
     152            0 :                                                     {_("No languages match")}
     153            0 :                                                 </MenuItem>
     154              :                                             );
     155            0 :                                         }
     156            0 :                                         return filteredLocales.map(key => {
     157            0 :                                             return (
     158            0 :                                                 <MenuItem itemId={key} key={key} data-value={key}>
     159            0 :                                                     {locales[key]}
     160            0 :                                                 </MenuItem>
     161              :                                             );
     162            0 :                                         });
     163            0 :                                     })()
     164              :                                 }
     165            0 :                             </MenuList>
     166            0 :                         </MenuContent>
     167            0 :                     </Menu>
     168            0 :                 </Flex>
     169            0 :             </ModalBody>
     170            0 :             <ModalFooter>
     171            0 :                 <Button variant='primary' onClick={onSelect}>{_("Select")}</Button>
     172            0 :                 <Button variant='link' onClick={() => dialogResult.resolve()}>{_("Cancel")}</Button>
     173            0 :             </ModalFooter>
     174            0 :         </Modal>
     175              :     );
     176            0 : };
     177              : 
     178              : export interface OopsModalProps {
     179              :     dialogResult: DialogResult<void>;
     180              : }
     181              : 
     182            0 : export function OopsModal({ dialogResult }: OopsModalProps) {
     183            0 :     return (
     184            0 :         <Modal isOpen position="top" variant="medium"
     185            0 :                onClose={() => dialogResult.resolve()}
     186              :         >
     187            0 :             <ModalHeader title={_("Unexpected error")} />
     188            0 :             <ModalBody>
     189            0 :                 {_("Cockpit had an unexpected internal error.")}
     190            0 :                 <br />
     191            0 :                 <br />
     192            0 :                 <span>{("You can try restarting Cockpit by pressing refresh in your browser. The javascript console contains details about this error") + " ("}
     193            0 :                     <b>{_("Ctrl-Shift-I")}</b>
     194            0 :                     {" " + _("in most browsers") + ")."}
     195            0 :                 </span>
     196            0 :             </ModalBody>
     197            0 :             <ModalFooter>
     198            0 :                 <Button variant='secondary' onClick={() => dialogResult.resolve()}>{_("Close")}</Button>
     199            0 :             </ModalFooter>
     200            0 :         </Modal>
     201              :     );
     202            0 : }
        

Generated by: LCOV version 2.0-1