@@ -21,12 +21,13 @@ import * as React from 'react'; | |||
import DocLink from '../../components/common/DocLink'; | |||
import InstanceMessage from '../../components/common/InstanceMessage'; | |||
import Link from '../../components/common/Link'; | |||
import AppVersionStatus from '../../components/shared/AppVersionStatus'; | |||
import { Alert } from '../../components/ui/Alert'; | |||
import { getEdition } from '../../helpers/editions'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { AppState } from '../../types/appstate'; | |||
import withAppStateContext from './app-state/withAppStateContext'; | |||
import GlobalFooterBranding from './GlobalFooterBranding'; | |||
import withAppStateContext from './app-state/withAppStateContext'; | |||
export interface GlobalFooterProps { | |||
hideLoggedInInfo?: boolean; | |||
@@ -55,26 +56,26 @@ export function GlobalFooter({ hideLoggedInInfo, appState }: GlobalFooterProps) | |||
)} | |||
{!hideLoggedInInfo && appState?.version && ( | |||
<li className="page-footer-menu-item"> | |||
{translateWithParameters('footer.version_x', appState.version)} | |||
<AppVersionStatus /> | |||
</li> | |||
)} | |||
<li className="page-footer-menu-item"> | |||
<a | |||
href="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
<Link | |||
to="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
> | |||
{translate('footer.license')} | |||
</a> | |||
</Link> | |||
</li> | |||
<li className="page-footer-menu-item"> | |||
<a | |||
href="https://community.sonarsource.com/c/help/sq" | |||
<Link | |||
to="https://community.sonarsource.com/c/help/sq" | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
> | |||
{translate('footer.community')} | |||
</a> | |||
</Link> | |||
</li> | |||
<li className="page-footer-menu-item"> | |||
<DocLink to="/">{translate('footer.documentation')}</DocLink> |
@@ -17,29 +17,29 @@ exports[`should display the sq version 1`] = ` | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
footer.version_x.6.4-SNAPSHOT | |||
<AppVersionStatus /> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
> | |||
footer.license | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://community.sonarsource.com/c/help/sq" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://community.sonarsource.com/c/help/sq" | |||
> | |||
footer.community | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
@@ -84,24 +84,24 @@ exports[`should not render the only logged in information 1`] = ` | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
> | |||
footer.license | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://community.sonarsource.com/c/help/sq" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://community.sonarsource.com/c/help/sq" | |||
> | |||
footer.community | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
@@ -142,29 +142,29 @@ exports[`should render the only logged in information 1`] = ` | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
footer.version_x.1.0 | |||
<AppVersionStatus /> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://www.gnu.org/licenses/lgpl-3.0.txt" | |||
> | |||
footer.license | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" | |||
> | |||
<a | |||
href="https://community.sonarsource.com/c/help/sq" | |||
<ForwardRef(Link) | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="https://community.sonarsource.com/c/help/sq" | |||
> | |||
footer.community | |||
</a> | |||
</ForwardRef(Link)> | |||
</li> | |||
<li | |||
className="page-footer-menu-item" |
@@ -28,5 +28,6 @@ export const DEFAULT_APP_STATE = { | |||
qualifiers: [], | |||
settings: {}, | |||
version: '', | |||
versionEOL: '', | |||
}; | |||
export const AppStateContext = React.createContext<AppState>(DEFAULT_APP_STATE); |
@@ -13,6 +13,7 @@ exports[`should set value correctly 1`] = ` | |||
"sonar.lf.logoUrl": "whatevs/", | |||
}, | |||
"version": "1.0", | |||
"versionEOL": "2022-01-01", | |||
} | |||
} | |||
/> |
@@ -43,3 +43,7 @@ export default function withAppStateContext<P>( | |||
} | |||
}; | |||
} | |||
export function useAppState() { | |||
return React.useContext(AppStateContext); | |||
} |
@@ -11,6 +11,7 @@ exports[`should render React extensions correctly 1`] = ` | |||
], | |||
"settings": {}, | |||
"version": "1.0", | |||
"versionEOL": "2022-01-01", | |||
} | |||
} | |||
currentUser={ | |||
@@ -73,6 +74,7 @@ exports[`should render React extensions correctly 2`] = ` | |||
], | |||
"settings": {}, | |||
"version": "1.0", | |||
"versionEOL": "2022-01-01", | |||
} | |||
} | |||
currentUser={ |
@@ -12,6 +12,7 @@ exports[`should render correctly and start polling if issue sync is needed 1`] = | |||
], | |||
"settings": {}, | |||
"version": "1.0", | |||
"versionEOL": "2022-01-01", | |||
} | |||
} | |||
> |
@@ -20,6 +20,7 @@ | |||
import * as React from 'react'; | |||
import withAppStateContext from '../../../app/components/app-state/withAppStateContext'; | |||
import { ClipboardButton } from '../../../components/controls/clipboard'; | |||
import AppVersionStatus from '../../../components/shared/AppVersionStatus'; | |||
import { Alert } from '../../../components/ui/Alert'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { translate } from '../../../helpers/l10n'; | |||
@@ -79,7 +80,9 @@ export function PageHeader(props: Props) { | |||
<th> | |||
<strong>{translate('system.version')}</strong> | |||
</th> | |||
<td>{version}</td> | |||
<td> | |||
<AppVersionStatus /> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> |
@@ -85,7 +85,7 @@ exports[`should render correctly 3`] = ` | |||
</strong> | |||
</th> | |||
<td> | |||
7.7.0.1234 | |||
<AppVersionStatus /> | |||
</td> | |||
</tr> | |||
</tbody> | |||
@@ -160,7 +160,7 @@ exports[`should render correctly: on embedded database 1`] = ` | |||
</strong> | |||
</th> | |||
<td> | |||
7.7.0.1234 | |||
<AppVersionStatus /> | |||
</td> | |||
</tr> | |||
</tbody> |
@@ -0,0 +1,48 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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 3 of the License, or (at your option) any later version. | |||
* | |||
* This program 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 this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { useAppState } from '../../app/components/app-state/withAppStateContext'; | |||
import { now, parseDate } from '../../helpers/dates'; | |||
import { translate } from '../../helpers/l10n'; | |||
import DocLink from '../common/DocLink'; | |||
export default function AppVersionStatus() { | |||
const { version, versionEOL } = useAppState(); | |||
const isActive = parseDate(versionEOL) > now(); | |||
return ( | |||
<FormattedMessage | |||
id="footer.version" | |||
defaultMessage={translate('footer.version')} | |||
values={{ | |||
version, | |||
status: ( | |||
<DocLink | |||
to="/setup-and-upgrade/upgrade-the-server/active-versions/" | |||
className="little-spacer-left" | |||
> | |||
{translate(`footer.version.status.${isActive ? 'active' : 'inactive'}`)} | |||
</DocLink> | |||
), | |||
}} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,50 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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 3 of the License, or (at your option) any later version. | |||
* | |||
* This program 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 this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { screen } from '@testing-library/react'; | |||
import React from 'react'; | |||
import AppStateContextProvider from '../../../app/components/app-state/AppStateContextProvider'; | |||
import AppVersionStatus from '../../../components/shared/AppVersionStatus'; | |||
import { mockAppState } from '../../../helpers/testMocks'; | |||
import { renderComponent } from '../../../helpers/testReactTestingUtils'; | |||
jest.mock('../../../helpers/dates', () => ({ | |||
...jest.requireActual('../../../helpers/dates'), | |||
now: () => new Date('2022-01-01'), | |||
})); | |||
it('should render active version if it has not reached EOL', () => { | |||
renderAppVersionStatus(mockAppState({ versionEOL: '2022-01-02' })); | |||
expect(screen.getByRole('link', { name: /footer.version.status.active/ })).toBeInTheDocument(); | |||
}); | |||
it('should render inactive version if it has reached EOL', () => { | |||
renderAppVersionStatus(mockAppState({ versionEOL: '2021-12-30' })); | |||
expect(screen.getByRole('link', { name: /footer.version.status.inactive/ })).toBeInTheDocument(); | |||
}); | |||
const renderAppVersionStatus = (appState = mockAppState()) => { | |||
return renderComponent( | |||
<AppStateContextProvider appState={appState}> | |||
<AppVersionStatus /> | |||
</AppStateContextProvider> | |||
); | |||
}; |
@@ -76,6 +76,7 @@ export function mockAppState(overrides: Partial<AppState> = {}): AppState { | |||
qualifiers: ['TRK'], | |||
settings: {}, | |||
version: '1.0', | |||
versionEOL: '2022-01-01', | |||
...overrides, | |||
}; | |||
} |
@@ -35,4 +35,5 @@ export interface AppState { | |||
standalone?: boolean; | |||
version: string; | |||
webAnalyticsJsPath?: string; | |||
versionEOL: string; | |||
} |
@@ -3531,7 +3531,9 @@ footer.security=Security | |||
footer.status=Status | |||
footer.terms=Terms | |||
footer.twitter=Twitter | |||
footer.version_x=Version {0} | |||
footer.version=v{version}{status} | |||
footer.version.status.active=ACTIVE | |||
footer.version.status.inactive=NO LONGER ACTIVE | |||
footer.web_api=Web API | |||