Make support packet composable with plugins (#26403)

---------

Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2024-04-12 10:05:58 +02:00
коммит произвёл GitHub
родитель 165b5ea821
Коммит 92f11f8971
19 изменённых файлов: 505 добавлений и 158 удалений

Просмотреть файл

@@ -52,11 +52,7 @@ exports[`components/CommercialSupportModal should match snapshot 1`] = `
className="CommercialSupportModal"
>
<FormattedMarkdownMessage
defaultMessage="If you're experiencing issues, [submit a support ticket.](!{supportLink})
**Download Support Packet**
We recommend that you download additional environment details about your Mattermost environment to help with troubleshooting. Once downloaded, attach the packet to your support ticket to share with our Customer Support team."
defaultMessage="If you're experiencing issues, [submit a support ticket](!{supportLink}). To help with troubleshooting, it's recommended to download the Support Packet below that includes more details about your Mattermost environment."
id="commercial_support.description"
values={
Object {
@@ -64,26 +60,41 @@ We recommend that you download additional environment details about your Matterm
}
}
/>
<a
className="btn btn-primary DownloadSupportPacket"
href="/api/v4/system/support_packet"
rel="noopener noreferrer"
>
<MemoizedFormattedMessage
defaultMessage="Download Support Packet"
id="commercial_support.download_support_packet"
/>
</a>
<AlertBanner
message={
<FormattedMarkdownMessage
defaultMessage="Before downloading the support packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging)."
defaultMessage="Before downloading the Support Packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging)."
id="commercial_support.warning.banner"
/>
}
mode="info"
onDismiss={[Function]}
/>
<div
className="CommercialSupportModal__packet_contents_download"
>
<FormattedMarkdownMessage
defaultMessage="**Select your Support Packet contents to download**"
id="commercial_support.download_contents"
/>
</div>
<div
className="CommercialSupportModal__download"
>
<a
className="btn btn-primary DownloadSupportPacket"
onClick={[Function]}
rel="noopener noreferrer"
>
<i
className="icon icon-download-outline"
/>
<MemoizedFormattedMessage
defaultMessage="Download Support Packet"
id="commercial_support.download_support_packet"
/>
</a>
</div>
</div>
</ModalBody>
</Modal>

Просмотреть файл

@@ -1,5 +1,5 @@
.CommercialSupportModal {
padding: 20px 20px;
padding: 0 32px 32px 32px;
.DownloadSupportPacket {
margin-top: 20px;
@@ -8,4 +8,19 @@
.AlertBanner {
margin-top: 20px;
}
&__packet_contents_download {
margin-top: 20px;
margin-bottom: 12px;
}
&__options_checkbox_label {
padding-left: 12px;
font-weight: 500;
}
}
.modal-header .modal-title {
font-family: Metropolis, sans-serif;
font-weight: 600;
}

Просмотреть файл

@@ -17,6 +17,7 @@ describe('components/CommercialSupportModal', () => {
showBannerWarning={true}
isCloud={false}
currentUser={mockUser}
packetContents={[]}
/>,
);
expect(wrapper).toMatchSnapshot();

Просмотреть файл

@@ -1,16 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import moment from 'moment';
import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import type {SupportPacketContent} from '@mattermost/types/admin';
import type {UserProfile} from '@mattermost/types/users';
import {Client4} from 'mattermost-redux/client';
import AlertBanner from 'components/alert_banner';
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import LoadingSpinner from 'components/widgets/loading/loading_spinner';
import './commercial_support_modal.scss';
@@ -26,20 +29,25 @@ type Props = {
isCloud: boolean;
currentUser: UserProfile;
packetContents: SupportPacketContent[];
};
type State = {
show: boolean;
showBannerWarning: boolean;
packetContents: SupportPacketContent[];
loading: boolean;
};
export default class CommercialSupportModal extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
show: true,
showBannerWarning: props.showBannerWarning,
packetContents: props.packetContents,
loading: false,
};
}
@@ -61,6 +69,59 @@ export default class CommercialSupportModal extends React.PureComponent<Props, S
this.updateBannerWarning(false);
};
updateCheckStatus = (index: number) => {
this.setState({
packetContents: this.state.packetContents.map((content, currentIndex) => (
(currentIndex === index && !content.mandatory) ? {...content, selected: !content.selected} : content
)),
});
};
genereateDownloadURLWithParams = (): string => {
const url = new URL(Client4.getSystemRoute() + '/support_packet');
this.state.packetContents.forEach((content) => {
if (content.id === 'basic.server.logs') {
url.searchParams.set('basic_server_logs', String(content.selected));
} else if (!content.mandatory && content.selected) {
url.searchParams.append('plugin_packets', content.id);
}
});
return url.toString();
};
extractFilename = (input: string | null): string => {
// construct the expected filename in case of an error in the header
const formattedDate = (moment(new Date())).format('YYYY-MM-DD-HH-mm');
const presumedFileName = `mattermost_support_packet_${formattedDate}.zip`;
if (input === null) {
return presumedFileName;
}
const regex = /filename\*?=["']?((?:\\.|[^"'\s])+)(?=["']?)/g;
const matches = regex.exec(input!);
return matches ? matches[1] : presumedFileName;
};
downloadSupportPacket = async () => {
this.setState({loading: true});
const res = await fetch(this.genereateDownloadURLWithParams(), {
method: 'GET',
headers: {'Content-Type': 'application/zip'},
});
const blob = await res.blob();
this.setState({loading: false});
const href = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.setAttribute('download', this.extractFilename(res.headers.get('content-disposition')));
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
render() {
const {showBannerWarning} = this.state;
const {isCloud, currentUser} = this.props;
@@ -86,33 +147,71 @@ export default class CommercialSupportModal extends React.PureComponent<Props, S
<div className='CommercialSupportModal'>
<FormattedMarkdownMessage
id='commercial_support.description'
defaultMessage={'If you\'re experiencing issues, [submit a support ticket.](!{supportLink})\n \n**Download Support Packet**\n \nWe recommend that you download additional environment details about your Mattermost environment to help with troubleshooting. Once downloaded, attach the packet to your support ticket to share with our Customer Support team.'}
defaultMessage={'If you\'re experiencing issues, [submit a support ticket](!{supportLink}). To help with troubleshooting, it\'s recommended to download the Support Packet below that includes more details about your Mattermost environment.'}
values={{
supportLink,
}}
/>
<a
className='btn btn-primary DownloadSupportPacket'
href={`${Client4.getBaseRoute()}/system/support_packet`}
rel='noopener noreferrer'
>
<FormattedMessage
id='commercial_support.download_support_packet'
defaultMessage='Download Support Packet'
/>
</a>
{showBannerWarning &&
<AlertBanner
mode='info'
message={
<FormattedMarkdownMessage
id='commercial_support.warning.banner'
defaultMessage='Before downloading the support packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging).'
defaultMessage='Before downloading the Support Packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging).'
/>
}
onDismiss={this.hideBannerWarning}
/>
}
<div className='CommercialSupportModal__packet_contents_download'>
<FormattedMarkdownMessage
id='commercial_support.download_contents'
defaultMessage={'**Select your Support Packet contents to download**'}
/>
</div>
{this.state.packetContents.map((item, index) => (
<div
className='CommercialSupportModal__option'
key={item.id}
>
<input
className='CommercialSupportModal__options__checkbox'
id={item.id}
name={item.id}
type='checkbox'
checked={item.selected}
disabled={item.mandatory}
onChange={() => this.updateCheckStatus(index)}
/>
<FormattedMessage
id='mettormost.plugin.metrics.support.packet'
defaultMessage={item.label}
>
{(text) => (
<label
className='CommercialSupportModal__options_checkbox_label'
htmlFor={item.id}
>
{text}
</label>)
}
</FormattedMessage>
</div>
))}
<div className='CommercialSupportModal__download'>
<a
className='btn btn-primary DownloadSupportPacket'
onClick={this.downloadSupportPacket}
rel='noopener noreferrer'
>
{ this.state.loading ? <LoadingSpinner/> : <i className='icon icon-download-outline'/> }
<FormattedMessage
id='commercial_support.download_support_packet'
defaultMessage='Download Support Packet'
/>
</a>
</div>
</div>
</Modal.Body>
</Modal>

Просмотреть файл

@@ -16,11 +16,27 @@ function mapStateToProps(state: GlobalState) {
const isCloud = license.Cloud === 'true';
const currentUser = getCurrentUser(state);
const showBannerWarning = (config.EnableFile !== 'true' || config.FileLevel !== 'DEBUG') && !(isCloud);
const packetContents = [
{id: 'basic.contents', label: 'Basic contents', selected: true, mandatory: true},
{id: 'basic.server.logs', label: 'Server logs', selected: true, mandatory: false},
];
for (const [key, value] of Object.entries(state.entities.admin.plugins!)) {
if (value.active && value.props !== undefined && value.props.support_packet !== undefined) {
packetContents.push({
id: key,
label: value.props.support_packet,
selected: false,
mandatory: false,
});
}
}
return {
isCloud,
currentUser,
showBannerWarning,
packetContents,
};
}

Просмотреть файл

@@ -3345,10 +3345,11 @@
"combined_system_message.removed_from_team.one_you": "You were **removed from the team**.",
"combined_system_message.removed_from_team.two": "{firstUser} and {secondUser} were **removed from the team**.",
"combined_system_message.you": "You",
"commercial_support.description": "If you're experiencing issues, [submit a support ticket.](!{supportLink})\n \n**Download Support Packet**\n \nWe recommend that you download additional environment details about your Mattermost environment to help with troubleshooting. Once downloaded, attach the packet to your support ticket to share with our Customer Support team.",
"commercial_support.description": "If you're experiencing issues, [submit a support ticket](!{supportLink}). To help with troubleshooting, it's recommended to download the support packet below that includes more details about your Mattermost environment.",
"commercial_support.download_contents": "**Select your Support Packet contents to download**",
"commercial_support.download_support_packet": "Download Support Packet",
"commercial_support.title": "Commercial Support",
"commercial_support.warning.banner": "Before downloading the support packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging).",
"commercial_support.warning.banner": "Before downloading the Support Packet, set **Output Logs to File** to **true** and set **File Log Level** to **DEBUG** [here](!/admin_console/environment/logging).",
"confirm_modal.cancel": "Cancel",
"confirm_switch_to_yearly_modal.confirm": "Confirm",
"confirm_switch_to_yearly_modal.contact_sales": "Contact Sales",

Просмотреть файл

@@ -125,3 +125,11 @@ export type SchemaMigration = {
version: number;
name: string;
};
export type SupportPacketContent = {
id: string;
translation_id?: string;
label: string;
selected: boolean;
mandatory: boolean;
}