PLT-7468 Moved more error pages to use predefined error types (#7378)
* PLT-7468 Moved more errors to use error types * PLT-7468 Moved 404 error page to use error types * Made helper function for rendering external links on error page
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
6ca443a255
Коммит
e30e4cfe3d
@@ -392,9 +392,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
code := r.URL.Query().Get("code")
|
code := r.URL.Query().Get("code")
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
err := model.NewAppError("completeOAuth", "api.oauth.complete_oauth.missing_code.app_error", map[string]interface{}{"service": strings.Title(service)}, "URL: "+r.URL.String(), http.StatusBadRequest)
|
http.Redirect(w, r, c.GetSiteURLHeader()+"/error?type=oauth_missing_code&service="+strings.Title(service), http.StatusTemporaryRedirect)
|
||||||
err.Translate(c.T)
|
|
||||||
http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+err.Message, http.StatusTemporaryRedirect)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := r.ParseMultipartForm(*utils.Cfg.FileSettings.MaxFileSize); err != nil {
|
if err := r.ParseMultipartForm(*utils.Cfg.FileSettings.MaxFileSize); err != nil {
|
||||||
c.Err = model.NewAppError("uploadProfileImage", "api.user.upload_profile_user.parse.app_error", nil, "", http.StatusInternalServerError)
|
c.Err = model.NewAppError("uploadProfileImage", "api.user.upload_profile_user.parse.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
i18n/en.json
12
i18n/en.json
@@ -1397,10 +1397,6 @@
|
|||||||
"id": "api.oauth.authorize_oauth.missing.app_error",
|
"id": "api.oauth.authorize_oauth.missing.app_error",
|
||||||
"translation": "Missing one or more of response_type, client_id, or redirect_uri"
|
"translation": "Missing one or more of response_type, client_id, or redirect_uri"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "api.oauth.complete_oauth.missing_code.app_error",
|
|
||||||
"translation": "The service provider {{.service}} did not provide an authorization code in the redirect URL.\n\nFor [Google Apps](https://docs.mattermost.com/deployment/sso-google.html) make sure your administrator enabled the Google+ API.\n\nFor [Office 365](https://docs.mattermost.com/deployment/sso-office.html) make sure the administrator of your Microsoft organization has enabled the Mattermost app.\n\nFor [GitLab](https://docs.mattermost.com/deployment/sso-gitlab.html) please make sure you followed the setup instructions.\n\nIf you reviewed the above and are still having trouble with configuration, you may post in our [Troubleshooting forum](https://forum.mattermost.org/c/general/trouble-shoot) where we'll be happy to help with issues during setup."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "api.oauth.delete.permissions.app_error",
|
"id": "api.oauth.delete.permissions.app_error",
|
||||||
"translation": "Invalid permissions to delete the OAuth2 App"
|
"translation": "Invalid permissions to delete the OAuth2 App"
|
||||||
@@ -2231,14 +2227,6 @@
|
|||||||
"id": "api.templates.email_organization",
|
"id": "api.templates.email_organization",
|
||||||
"translation": "Sent by "
|
"translation": "Sent by "
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "api.templates.error.link",
|
|
||||||
"translation": "Go back to Mattermost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "api.templates.error.title",
|
|
||||||
"translation": "{{ .SiteName }} needs your help:"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "api.templates.find_teams_body.found",
|
"id": "api.templates.find_teams_body.found",
|
||||||
"translation": "Your request to find teams associated with your email found the following:"
|
"translation": "Your request to find teams associated with your email found the following:"
|
||||||
|
|||||||
12
utils/api.go
12
utils/api.go
@@ -35,13 +35,8 @@ func GetOriginChecker(r *http.Request) OriginCheckerProc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) {
|
func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) {
|
||||||
T, _ := GetTranslationsAndLocale(w, r)
|
|
||||||
|
|
||||||
title := T("api.templates.error.title", map[string]interface{}{"SiteName": ClientCfg["SiteName"]})
|
|
||||||
message := err.Message
|
message := err.Message
|
||||||
details := err.DetailedError
|
details := err.DetailedError
|
||||||
link := "/"
|
|
||||||
linkMessage := T("api.templates.error.link")
|
|
||||||
|
|
||||||
status := http.StatusTemporaryRedirect
|
status := http.StatusTemporaryRedirect
|
||||||
if err.StatusCode != http.StatusInternalServerError {
|
if err.StatusCode != http.StatusInternalServerError {
|
||||||
@@ -51,10 +46,7 @@ func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request)
|
|||||||
http.Redirect(
|
http.Redirect(
|
||||||
w,
|
w,
|
||||||
r,
|
r,
|
||||||
"/error?title="+url.QueryEscape(title)+
|
"/error?message="+url.QueryEscape(message)+
|
||||||
"&message="+url.QueryEscape(message)+
|
"&details="+url.QueryEscape(details),
|
||||||
"&details="+url.QueryEscape(details)+
|
|
||||||
"&link="+url.QueryEscape(link)+
|
|
||||||
"&linkmessage="+url.QueryEscape(linkMessage),
|
|
||||||
status)
|
status)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ import {stopPeriodicStatusUpdates} from 'actions/status_actions.jsx';
|
|||||||
import * as WebsocketActions from 'actions/websocket_actions.jsx';
|
import * as WebsocketActions from 'actions/websocket_actions.jsx';
|
||||||
import {trackEvent} from 'actions/diagnostics_actions.jsx';
|
import {trackEvent} from 'actions/diagnostics_actions.jsx';
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
import {ActionTypes, Constants, ErrorPageTypes} from 'utils/constants.jsx';
|
||||||
const ActionTypes = Constants.ActionTypes;
|
|
||||||
import EventTypes from 'utils/event_types.jsx';
|
import EventTypes from 'utils/event_types.jsx';
|
||||||
|
|
||||||
import WebSocketClient from 'client/web_websocket_client.jsx';
|
import WebSocketClient from 'client/web_websocket_client.jsx';
|
||||||
@@ -146,18 +145,7 @@ export function emitPostFocusEvent(postId, onSuccess) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let link = `${TeamStore.getCurrentTeamRelativeUrl()}/channels/`;
|
browserHistory.push('/error?type=' + ErrorPageTypes.PERMALINK_NOT_FOUND);
|
||||||
const channel = ChannelStore.getCurrent();
|
|
||||||
if (channel) {
|
|
||||||
link += channel.name;
|
|
||||||
} else {
|
|
||||||
link += 'town-square';
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = encodeURIComponent(Utils.localizeMessage('permalink.error.access', 'Permalink belongs to a deleted message or to a channel to which you do not have access.'));
|
|
||||||
const title = encodeURIComponent(Utils.localizeMessage('permalink.error.title', 'Message Not Found'));
|
|
||||||
|
|
||||||
browserHistory.push('/error?message=' + message + '&title=' + title + '&link=' + encodeURIComponent(link));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {FormattedMessage} from 'react-intl';
|
|||||||
import {Link} from 'react-router/es6';
|
import {Link} from 'react-router/es6';
|
||||||
|
|
||||||
import {ErrorPageTypes} from 'utils/constants.jsx';
|
import {ErrorPageTypes} from 'utils/constants.jsx';
|
||||||
import * as TextFormatting from 'utils/text_formatting.jsx';
|
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
export default class ErrorPage extends React.Component {
|
export default class ErrorPage extends React.Component {
|
||||||
@@ -16,14 +15,6 @@ export default class ErrorPage extends React.Component {
|
|||||||
location: PropTypes.object.isRequired
|
location: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.renderTitle = this.renderTitle.bind(this);
|
|
||||||
this.renderMessage = this.renderMessage.bind(this);
|
|
||||||
this.renderLink = this.renderLink.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
$('body').attr('class', 'sticky error');
|
$('body').attr('class', 'sticky error');
|
||||||
}
|
}
|
||||||
@@ -32,18 +23,29 @@ export default class ErrorPage extends React.Component {
|
|||||||
$('body').attr('class', '');
|
$('body').attr('class', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
linkFilter(link) {
|
renderTitle = () => {
|
||||||
return link.startsWith('https://docs.mattermost.com') || link.startsWith('https://forum.mattermost.org');
|
switch (this.props.location.query.type) {
|
||||||
}
|
case ErrorPageTypes.LOCAL_STORAGE:
|
||||||
|
|
||||||
renderTitle() {
|
|
||||||
if (this.props.location.query.type === ErrorPageTypes.LOCAL_STORAGE) {
|
|
||||||
return (
|
return (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='error.local_storage.title'
|
id='error.local_storage.title'
|
||||||
defaultMessage='Cannot Load Mattermost'
|
defaultMessage='Cannot Load Mattermost'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case ErrorPageTypes.PERMALINK_NOT_FOUND:
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='permalink.error.title'
|
||||||
|
defaultMessage='Message Not Found'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case ErrorPageTypes.PAGE_NOT_FOUND:
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.not_found.title'
|
||||||
|
defaultMessage='Message Not Found'
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.location.query.title) {
|
if (this.props.location.query.title) {
|
||||||
@@ -53,8 +55,9 @@ export default class ErrorPage extends React.Component {
|
|||||||
return Utils.localizeMessage('error.generic.title', 'Error');
|
return Utils.localizeMessage('error.generic.title', 'Error');
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMessage() {
|
renderMessage = () => {
|
||||||
if (this.props.location.query.type === ErrorPageTypes.LOCAL_STORAGE) {
|
switch (this.props.location.query.type) {
|
||||||
|
case ErrorPageTypes.LOCAL_STORAGE:
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -83,45 +86,108 @@ export default class ErrorPage extends React.Component {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
case ErrorPageTypes.PERMALINK_NOT_FOUND:
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='permalink.error.access'
|
||||||
|
defaultMessage='Permalink belongs to a deleted message or to a channel to which you do not have access.'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
case ErrorPageTypes.OAUTH_MISSING_CODE:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.oauth_missing_code'
|
||||||
|
defaultMessage='The service provider {service} did not provide an authorization code in the redirect URL.'
|
||||||
|
values={{
|
||||||
|
service: this.props.location.query.service
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.oauth_missing_code.google'
|
||||||
|
defaultMessage='For {link} make sure your administrator enabled the Google+ API.'
|
||||||
|
values={{
|
||||||
|
link: this.renderLink('https://docs.mattermost.com/deployment/sso-google.html', 'error.oauth_missing_code.google.link', 'Google Apps')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.oauth_missing_code.office365'
|
||||||
|
defaultMessage='For {link} make sure the administrator of your Microsoft organization has enabled the Mattermost app.'
|
||||||
|
values={{
|
||||||
|
link: this.renderLink('https://docs.mattermost.com/deployment/sso-office.html', 'error.oauth_missing_code.office365.link', 'Office 365')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.oauth_missing_code.gitlab'
|
||||||
|
defaultMessage='For {link} please make sure you followed the setup instructions.'
|
||||||
|
values={{
|
||||||
|
link: this.renderLink('https://docs.mattermost.com/deployment/sso-gitlab.html', 'error.oauth_missing_code.gitlab.link', 'GitLab')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.oauth_missing_code.forum'
|
||||||
|
defaultMessage="If you reviewed the above and are still having trouble with configuration, you may post in our {link} where we'll be happy to help with issues during setup."
|
||||||
|
values={{
|
||||||
|
link: this.renderLink('https://forum.mattermost.org/c/trouble-shoot', 'error.oauth_missing_code.forum.link', 'Troubleshooting forum')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case ErrorPageTypes.PAGE_NOT_FOUND:
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.not_found.message'
|
||||||
|
defaultMessage='The page you were trying to reach does not exist'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = this.props.location.query.message;
|
if (this.props.location.query.message) {
|
||||||
if (!message) {
|
return <p>{this.props.location.query.message}</p>;
|
||||||
message = Utils.localizeMessage('error.generic.message', 'An error has occoured.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div dangerouslySetInnerHTML={{__html: TextFormatting.formatText(message, {linkFilter: this.linkFilter})}}/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLink() {
|
|
||||||
let link = this.props.location.query.link;
|
|
||||||
if (link) {
|
|
||||||
link = link.trim();
|
|
||||||
} else {
|
|
||||||
link = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!link.startsWith('/')) {
|
|
||||||
// Only allow relative links
|
|
||||||
link = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
let linkMessage = this.props.location.query.linkmessage;
|
|
||||||
if (!linkMessage) {
|
|
||||||
linkMessage = Utils.localizeMessage('error.generic.link_message', 'Back to Mattermost');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={link}>
|
<p>
|
||||||
{linkMessage}
|
<FormattedMessage
|
||||||
</Link>
|
id='error.generic.message'
|
||||||
|
defaultMessage='An error has occurred.'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLink = (url, id, defaultMessage) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={url}
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id={id}
|
||||||
|
defaultMessage={defaultMessage}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const title = this.renderTitle();
|
const title = this.renderTitle();
|
||||||
const message = this.renderMessage();
|
const message = this.renderMessage();
|
||||||
const link = this.renderLink();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
@@ -129,9 +195,16 @@ export default class ErrorPage extends React.Component {
|
|||||||
<div className='error__icon'>
|
<div className='error__icon'>
|
||||||
<i className='fa fa-exclamation-triangle'/>
|
<i className='fa fa-exclamation-triangle'/>
|
||||||
</div>
|
</div>
|
||||||
<h2>{title}</h2>
|
<h2>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
{message}
|
{message}
|
||||||
{link}
|
<Link to='/'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='error.generic.link'
|
||||||
|
defaultMessage='Back to Mattermost'
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1440,6 +1440,8 @@
|
|||||||
"emoji_picker.recent": "Recently Used",
|
"emoji_picker.recent": "Recently Used",
|
||||||
"emoji_picker.search": "Search",
|
"emoji_picker.search": "Search",
|
||||||
"emoji_picker.symbols": "Symbols",
|
"emoji_picker.symbols": "Symbols",
|
||||||
|
"error.generic.link": "Back to Mattermost",
|
||||||
|
"error.generic.message": "An error has occurred.",
|
||||||
"error.local_storage.help1": "Enable cookies",
|
"error.local_storage.help1": "Enable cookies",
|
||||||
"error.local_storage.help2": "Turn off private browsing",
|
"error.local_storage.help2": "Turn off private browsing",
|
||||||
"error.local_storage.help3": "Use a supported browser (IE 11, Chrome 43+, Firefox 38+, Safari 9, Edge)",
|
"error.local_storage.help3": "Use a supported browser (IE 11, Chrome 43+, Firefox 38+, Safari 9, Edge)",
|
||||||
@@ -1447,6 +1449,15 @@
|
|||||||
"error.not_found.link_message": "Back to Mattermost",
|
"error.not_found.link_message": "Back to Mattermost",
|
||||||
"error.not_found.message": "The page you were trying to reach does not exist",
|
"error.not_found.message": "The page you were trying to reach does not exist",
|
||||||
"error.not_found.title": "Page not found",
|
"error.not_found.title": "Page not found",
|
||||||
|
"error.oauth_missing_code": "The service provider {service} did not provide an authorization code in the redirect URL.",
|
||||||
|
"error.oauth_missing_code.forum": "If you reviewed the above and are still having trouble with configuration, you may post in our {link} where we'll be happy to help with issues during setup.",
|
||||||
|
"error.oauth_missing_code.forum.link": "Troubleshooting forum",
|
||||||
|
"error.oauth_missing_code.gitlab": "For {link} please make sure you followed the setup instructions.",
|
||||||
|
"error.oauth_missing_code.gitlab.link": "GitLab",
|
||||||
|
"error.oauth_missing_code.google": "For {link} make sure your administrator enabled the Google+ API.",
|
||||||
|
"error.oauth_missing_code.google.link": "Google Apps",
|
||||||
|
"error.oauth_missing_code.office365": "For {link} make sure the administrator of your Microsoft organization has enabled the Mattermost app.",
|
||||||
|
"error.oauth_missing_code.office365.link": "Office 365",
|
||||||
"error_bar.expired": "Enterprise license is expired and some features may be disabled. <a href='{link}' target='_blank'>Please renew</a>.",
|
"error_bar.expired": "Enterprise license is expired and some features may be disabled. <a href='{link}' target='_blank'>Please renew</a>.",
|
||||||
"error_bar.expiring": "Enterprise license expires on {date}. <a href='{link}' target='_blank'>Please renew</a>.",
|
"error_bar.expiring": "Enterprise license expires on {date}. <a href='{link}' target='_blank'>Please renew</a>.",
|
||||||
"error_bar.past_grace": "Enterprise license is expired and some features may be disabled. Please contact your System Administrator for details.",
|
"error_bar.past_grace": "Enterprise license is expired and some features may be disabled. Please contact your System Administrator for details.",
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export default {
|
|||||||
{
|
{
|
||||||
path: 'error',
|
path: 'error',
|
||||||
getComponents: (location, callback) => {
|
getComponents: (location, callback) => {
|
||||||
System.import('components/error_page.jsx').then(RouteUtils.importComponentSuccess(callback));
|
System.import('components/error_page').then(RouteUtils.importComponentSuccess(callback));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import * as Utils from 'utils/utils.jsx';
|
|
||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
import {ErrorPageTypes} from 'utils/constants.jsx';
|
||||||
|
|
||||||
export function importComponentSuccess(callback) {
|
export function importComponentSuccess(callback) {
|
||||||
return (comp) => callback(null, comp.default);
|
return (comp) => callback(null, comp.default);
|
||||||
@@ -13,10 +13,7 @@ export function createGetChildComponentsFunction(arrayOfComponents) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const notFoundParams = {
|
export const notFoundParams = {
|
||||||
title: Utils.localizeMessage('error.not_found.title', 'Page not found'),
|
type: ErrorPageTypes.PAGE_NOT_FOUND
|
||||||
message: Utils.localizeMessage('error.not_found.message', 'The page you were trying to reach does not exist'),
|
|
||||||
link: '/',
|
|
||||||
linkmessage: Utils.localizeMessage('error.not_found.link_message', 'Back to Mattermost')
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mfaPaths = [
|
const mfaPaths = [
|
||||||
|
|||||||
@@ -303,7 +303,10 @@ export const StatTypes = keyMirror({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ErrorPageTypes = {
|
export const ErrorPageTypes = {
|
||||||
LOCAL_STORAGE: 'local_storage'
|
LOCAL_STORAGE: 'local_storage',
|
||||||
|
OAUTH_MISSING_CODE: 'oauth_missing_code',
|
||||||
|
PAGE_NOT_FOUND: 'page_not_found',
|
||||||
|
PERMALINK_NOT_FOUND: 'permalink_not_found'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const JobTypes = {
|
export const JobTypes = {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user