diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2018-03-12 10:48:44 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-03-26 20:20:57 +0200 |
commit | ea341da5bb721a4d833c3dd324162bbb0bc04642 (patch) | |
tree | 856834be64b032fdcff68abbbc939a7b1c2f28ba /server/sonar-web/src/main/js/app | |
parent | 9ac78350022bfadca4845a42cb600dfa762fb66b (diff) | |
download | sonarqube-ea341da5bb721a4d833c3dd324162bbb0bc04642.tar.gz sonarqube-ea341da5bb721a4d833c3dd324162bbb0bc04642.zip |
VSTS-141 Move vsts integration to it's own module
Diffstat (limited to 'server/sonar-web/src/main/js/app')
11 files changed, 0 insertions, 1305 deletions
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx deleted file mode 100644 index 6afeb86b3a4..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx +++ /dev/null @@ -1,194 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 LoginForm from './LoginForm'; -import ProjectSelector from './ProjectSelector'; -import { Component, searchProjects } from '../../../../api/components'; -import { - Settings, - VSTSWidgetSettings, - VSTSConfigurationContext, - serializeWidgetSettings, - parseWidgetSettings -} from '../utils'; -import { getCurrentUser } from '../../../../api/users'; -import { CurrentUser } from '../../../types'; - -interface Props { - contribution: string; - widgetHelpers: any; -} - -interface State { - currentUser?: CurrentUser; - loading: boolean; - projects: Component[]; - settings: Settings; - selectedProject?: Component; - widgetConfigurationContext?: VSTSConfigurationContext; -} - -declare const VSS: { - register: (contributionId: string, callback: Function) => void; - resize: Function; -}; - -const PAGE_SIZE = 10; - -export default class Configuration extends React.PureComponent<Props, State> { - mounted = false; - state: State = { loading: true, projects: [], settings: { project: '' } }; - - componentDidMount() { - this.mounted = true; - VSS.register(this.props.contribution, () => { - return { load: this.load, onSave: this.onSave }; - }); - } - - componentDidUpdate() { - VSS.resize(); - } - - componentWillUnmount() { - this.mounted = false; - } - - load = ( - widgetSettings: VSTSWidgetSettings, - widgetConfigurationContext: VSTSConfigurationContext - ) => { - const settings = parseWidgetSettings(widgetSettings); - if (this.mounted) { - this.setState({ settings: settings || {}, widgetConfigurationContext }); - this.fetchInitialData(); - } - return this.props.widgetHelpers.WidgetStatusHelper.Success(); - }; - - onSave = () => { - const { settings } = this.state; - if (!settings.project) { - return this.props.widgetHelpers.WidgetConfigurationSave.Invalid(); - } - return this.props.widgetHelpers.WidgetConfigurationSave.Valid( - serializeWidgetSettings(settings) - ); - }; - - fetchInitialData = () => { - this.setState({ loading: true }); - getCurrentUser() - .then(currentUser => { - this.setState({ currentUser }); - const params: { ps: number; filter?: string } = { ps: PAGE_SIZE }; - if (currentUser.isLoggedIn) { - params.filter = 'isFavorite'; - } - return searchProjects(params); - }) - .then(this.handleSearchProjectsResult, this.stopLoading); - }; - - handleReload = () => { - this.fetchInitialData(); - }; - - handleProjectChange = ( - event: React.ChangeEvent<HTMLSelectElement> | React.FocusEvent<HTMLSelectElement> - ) => { - const { value } = event.currentTarget; - this.setState( - ({ settings }) => ({ settings: { ...settings, project: value } }), - this.notifyChange - ); - }; - - notifyChange = ({ settings, widgetConfigurationContext } = this.state) => { - const { widgetHelpers } = this.props; - if (widgetConfigurationContext && widgetConfigurationContext.notify) { - const eventName = widgetHelpers.WidgetEvent.ConfigurationChange; - const eventArgs = widgetHelpers.WidgetEvent.Args(serializeWidgetSettings(settings)); - widgetConfigurationContext.notify(eventName, eventArgs); - } - }; - - handleProjectSearch = (query: string) => { - const searchParams: { ps: number; filter?: string } = { ps: PAGE_SIZE }; - if (query) { - searchParams.filter = query; - } - return searchProjects(searchParams).then(this.handleSearchProjectsResult, this.stopLoading); - }; - - handleProjectSelect = (project: Component) => { - this.setState( - ({ settings }) => ({ - selectedProject: project, - settings: { ...settings, project: project.key } - }), - this.notifyChange - ); - }; - - handleSearchProjectsResult = ({ components }: { components: Component[] }) => { - if (this.mounted) { - this.setState({ loading: false, projects: components }); - } - }; - - stopLoading = () => { - this.setState({ loading: false }); - }; - - render() { - const { currentUser, projects, loading, selectedProject, settings } = this.state; - if (loading) { - return ( - <div className="vsts-loading"> - <i className="spinner global-loading-spinner" /> - </div> - ); - } - - const isLoggedIn = Boolean(currentUser && currentUser.isLoggedIn); - const selected = selectedProject || projects.find(project => project.key === settings.project); - return ( - <div className="widget-configuration vsts-configuration bowtie"> - <div className="dropdown config-settings-field" id="sonarcloud-project"> - <label>SonarCloud project</label> - <ProjectSelector - isLoggedIn={isLoggedIn} - onQueryChange={this.handleProjectSearch} - onSelect={this.handleProjectSelect} - projects={projects} - selected={selected} - /> - </div> - {!isLoggedIn && ( - <div className="config-settings-field"> - <label>You must be logged in to see your private projects :</label> - <LoginForm onReload={this.handleReload} /> - </div> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx deleted file mode 100644 index 10367598a92..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 LoginLink from './LoginLink'; -import SonarCloudIcon from './SonarCloudIcon'; -import * as theme from '../../../../app/theme'; -import { IdentityProvider } from '../../../types'; -import { getIdentityProviders } from '../../../../api/users'; -import { getTextColor } from '../../../../helpers/colors'; -import { getBaseUrl } from '../../../../helpers/urls'; - -interface Props { - onReload: () => void; - title?: string; -} - -interface State { - identityProviders?: IdentityProvider[]; -} - -export default class LoginForm extends React.PureComponent<Props, State> { - mounted = false; - state: State = {}; - - componentDidMount() { - this.mounted = true; - getIdentityProviders().then( - identityProvidersResponse => { - if (this.mounted) { - this.setState({ - identityProviders: identityProvidersResponse.identityProviders - }); - } - }, - () => {} - ); - } - - componentWillUnmount() { - this.mounted = false; - } - - render() { - const { onReload, title } = this.props; - const { identityProviders } = this.state; - const vstsProvider = - identityProviders && identityProviders.find(provider => provider.key === 'microsoft'); - - return ( - <div className="vsts-widget-login"> - {title && <SonarCloudIcon size={32} />} - {title && <p className="login-message-text">{title}</p>} - {identityProviders && ( - <section className="oauth-providers"> - {vstsProvider && ( - <LoginLink - onReload={onReload} - sessionUrl={`sessions/init/${vstsProvider.key}`} - style={{ - backgroundColor: vstsProvider.backgroundColor, - color: getTextColor(vstsProvider.backgroundColor, theme.secondFontColor) - }}> - <img - alt={vstsProvider.name} - height="20" - src={getBaseUrl() + vstsProvider.iconPath} - width="20" - /> - <span>{vstsProvider.name} log in</span> - </LoginLink> - )} - </section> - )} - - <div className="text-center"> - <LoginLink onReload={onReload} sessionUrl={'sessions/new'}> - {vstsProvider ? 'More options' : 'Log in on SonarCloud'} - </LoginLink> - </div> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx deleted file mode 100644 index a4dc36adc92..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 { getBaseUrl } from '../../../../helpers/urls'; - -interface Props { - className?: string; - children: React.ReactNode; - onReload: () => void; - style?: React.CSSProperties; - sessionUrl: string; -} - -export default class LoginLink extends React.PureComponent<Props> { - handleLoginClick = (event: React.MouseEvent<HTMLAnchorElement>) => { - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.blur(); - - (window as any).authenticationDone = () => { - this.props.onReload(); - }; - - const returnTo = encodeURIComponent(window.location.pathname + '?type=authenticated'); - window.open( - `${getBaseUrl()}/${this.props.sessionUrl}?return_to=${returnTo}`, - 'Login on SonarCloud', - 'toolbar=0,status=0,width=377,height=380' - ); - }; - - render() { - return ( - <a - className={this.props.className} - href="#" - onClick={this.handleLoginClick} - style={this.props.style}> - {this.props.children} - </a> - ); - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx deleted file mode 100644 index d28fa4dc504..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx +++ /dev/null @@ -1,264 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 * as classNames from 'classnames'; -import { debounce } from 'lodash'; -import ProjectSelectorItem from './ProjectSelectorItem'; -import { Component } from '../../../../api/components'; - -interface Props { - isLoggedIn: boolean; - onQueryChange: (query: string) => Promise<void>; - onSelect: (component: Component) => void; - projects: Component[]; - selected?: Component; -} - -interface State { - activeIdx: number; - activeKey?: string; - favorite: boolean; - open: boolean; - search: string; - searching: boolean; -} - -export default class ProjectSelector extends React.PureComponent<Props, State> { - node?: HTMLElement | null; - debouncedHandleSearch: () => void; - - constructor(props: Props) { - super(props); - const firstProject = props.projects[0]; - this.state = { - activeIdx: firstProject ? 0 : -1, - activeKey: firstProject && firstProject.key, - favorite: props.isLoggedIn, - open: false, - search: '', - searching: false - }; - this.debouncedHandleSearch = debounce(this.handleSearch, 250); - } - - componentDidMount() { - window.addEventListener('click', this.handleClickOutside); - if (this.node) { - this.node.addEventListener('keydown', this.handleKeyDown, true); - } - } - - componentWillReceiveProps(nextProps: Props) { - if (this.props.projects !== nextProps.projects) { - let activeIdx = nextProps.projects.findIndex(project => project.key === this.state.activeKey); - activeIdx = activeIdx >= 0 ? activeIdx : 0; - this.setState({ activeIdx, activeKey: this.getActiveKey(activeIdx) }); - } - } - - componentWillUnmount() { - window.removeEventListener('click', this.handleClickOutside); - if (this.node) { - this.node.removeEventListener('keydown', this.handleKeyDown); - } - } - - getActiveKey = (idx: number) => { - const { projects } = this.props; - return projects[idx] && projects[idx].key; - }; - - getEmptyMessage = () => { - const { favorite, search } = this.state; - if (search) { - return 'No project matching your search.'; - } else if (favorite) { - return "You don't have any favorite projects yet."; - } - return 'No project have been found'; - }; - - handleClickOutside = (event: Event) => { - if (!this.node || !this.node.contains(event.target as HTMLElement)) { - this.setState({ open: false }); - } - }; - - handleFilterAll = () => { - this.setState({ favorite: false, searching: true }, this.handleSearch); - }; - - handleFilterFavorite = () => { - this.setState({ favorite: true, searching: true }, this.handleSearch); - }; - - handleItemHover = (item: Component) => { - let activeIdx = this.props.projects.findIndex(project => project.key === item.key); - activeIdx = activeIdx >= 0 ? activeIdx : 0; - this.setState({ activeIdx, activeKey: this.getActiveKey(activeIdx) }); - }; - - handleKeyDown = (evt: KeyboardEvent) => { - switch (evt.keyCode) { - case 40: // down - evt.stopPropagation(); - evt.preventDefault(); - this.setState(this.selectNextItem); - break; - case 38: // up - evt.stopPropagation(); - evt.preventDefault(); - this.setState(this.selectPreviousItem); - break; - case 37: // left - case 39: // right - evt.stopPropagation(); - break; - case 13: // enter - if (this.state.activeIdx >= 0) { - this.handleSelect(this.props.projects[this.state.activeIdx]); - } - break; - case 27: // escape - this.setState({ open: false }); - break; - } - }; - - handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => { - this.setState( - { search: event.currentTarget.value, searching: true }, - this.debouncedHandleSearch - ); - }; - - handleSearch = () => { - const filter = []; - if (this.state.favorite) { - filter.push('isFavorite'); - } - if (this.state.search) { - filter.push(`query = "${this.state.search}"`); - } - this.props.onQueryChange(filter.join(' and ')).then(this.stopSearching, this.stopSearching); - }; - - handleSelect = (project: Component) => { - this.props.onSelect(project); - this.setState({ open: false }); - }; - - selectNextItem = ({ activeIdx }: State, { projects }: Props) => { - let newActiveIdx = activeIdx + 1; - if (activeIdx < 0 || activeIdx >= projects.length - 1) { - newActiveIdx = 0; - } - return { activeIdx: newActiveIdx, activeKey: this.getActiveKey(newActiveIdx) }; - }; - - selectPreviousItem = ({ activeIdx }: State, { projects }: Props) => { - let newActiveIdx = activeIdx - 1; - if (activeIdx <= 0) { - newActiveIdx = projects.length - 1; - } - return { activeIdx: newActiveIdx, activeKey: this.getActiveKey(newActiveIdx) }; - }; - - stopSearching = () => { - this.setState({ searching: false }); - }; - - toggleOpen = () => { - this.setState(({ open }) => ({ open: !open })); - }; - - render() { - const { isLoggedIn, projects, selected } = this.props; - const { activeIdx, favorite, open, search, searching } = this.state; - return ( - <div className="project-picker" ref={node => (this.node = node)}> - <div - className="filtered-list-dropdown-menu" - onClick={this.toggleOpen} - role="button" - tabIndex={0}> - <span className="selected-item-text"> - {selected ? selected.name : 'Select a project...'} - </span> - <span className="drop-icon bowtie-icon bowtie-chevron-down-light" /> - </div> - {open && ( - <div className="filtered-list-popup" role="dialog"> - <div className="filtered-list-control bowtie-filtered-list"> - <div className="filter-container"> - {isLoggedIn && ( - <div className="views"> - <ul className="pivot-view" role="tablist"> - <li - className={classNames('filtered-list-tab', { selected: favorite })} - role="presentation"> - <a onClick={this.handleFilterFavorite} role="tab" tabIndex={0}> - My Projects - </a> - </li> - <li - className={classNames('filtered-list-tab', { selected: !favorite })} - role="presentation"> - <a onClick={this.handleFilterAll} role="tab" tabIndex={-1}> - All - </a> - </li> - </ul> - </div> - )} - <div className="filtered-list-search-container bowtie-style"> - <input - autoFocus={true} - className="filtered-list-search" - onChange={this.handleSearchChange} - placeholder="Search by project name" - type="text" - value={search} - /> - {searching && <i className="spinner" />} - </div> - </div> - <ul className="filtered-list"> - {projects.map((project, idx) => ( - <ProjectSelectorItem - isActive={activeIdx === idx} - isSelected={Boolean(selected && selected.key === project.key)} - key={project.key} - onHover={this.handleItemHover} - onSelect={this.handleSelect} - project={project} - /> - ))} - {projects.length <= 0 && ( - <li className="filtered-list-message">{this.getEmptyMessage()}</li> - )} - </ul> - </div> - </div> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx deleted file mode 100644 index 6dd56d58056..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 * as classNames from 'classnames'; -import { Component } from '../../../../api/components'; - -interface Props { - isActive: boolean; - isSelected: boolean; - onHover: (project: Component) => void; - onSelect: (project: Component) => void; - project: Component; -} - -export default class ProjectSelectorItem extends React.PureComponent<Props> { - handleClick = () => { - this.props.onSelect(this.props.project); - }; - - handleHover = () => { - this.props.onHover(this.props.project); - }; - - render() { - return ( - <li - className={classNames('filtered-list-item', { - 'current-item': this.props.isSelected, - 'active-item': this.props.isActive - })} - onClick={this.handleClick} - onFocus={this.handleHover} - onMouseOver={this.handleHover}> - {this.props.project.name} - </li> - ); - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx deleted file mode 100644 index 51a08eec4ce..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 * as classNames from 'classnames'; -import SonarCloudIcon from './SonarCloudIcon'; -import { MeasureComponent } from '../../../../api/measures'; -import { getPathUrlAsString, getProjectUrl } from '../../../../helpers/urls'; - -interface Props { - component: MeasureComponent; -} - -const QG_LEVELS: { [level: string]: string } = { - ERROR: 'Failed', - WARN: 'Warning', - OK: 'Passed', - NONE: 'None' -}; - -export default function QGWidget({ component }: Props) { - const qgMeasure = component && component.measures.find(m => m.metric === 'alert_status'); - - if (!qgMeasure || !qgMeasure.value) { - return <p>Project Quality Gate not computed.</p>; - } - - return ( - <div className={classNames('widget dark-widget clickable', 'level-' + qgMeasure.value)}> - <a href={getPathUrlAsString(getProjectUrl(component.key))} target="_blank"> - <h2 className="title truncated-text-ellipsis">{component.name}</h2> - <div className="big-value truncated-text-ellipsis">{QG_LEVELS[qgMeasure.value]}</div> - <div className="footer truncated-text-ellipsis"> - <SonarCloudIcon fill="#FFF" /> Quality Gate - </div> - </a> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx deleted file mode 100644 index 17cbb789252..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 { IconProps } from '../../../../components/icons-components/types'; - -export default function SonarCloudIcon({ className, fill = '#f3702a', size = 22 }: IconProps) { - return ( - <svg - className={className} - height={size} - version="1.1" - viewBox="0 0 22 22" - width={size} - xmlSpace="preserve" - xmlnsXlink="http://www.w3.org/1999/xlink"> - <path - d="M20.24 10.65l.15.29.14.3.12.3.1.32.09.32.07.33.04.34.03.34.02.35-.06.8-.16.76-.25.73-.34.68-.42.62-.5.57-.56.5-.63.42-.68.34-.72.25-.77.16-.8.05-.39-.01-.39-.04-.38-.06-.37-.09-.37-.11-.35-.13-.34-.16-.33-.17-.31-.2-.31-.21-.28-.23-.28-.25-.27.25-.29.24-.3.22-.32.2-.33.17-.35.16-.35.14-.37.11-.37.09-.39.06-.39.04-.4.02-.8-.06-.76-.15-.73-.26-.68-.34-.63-.42-.56-.5-.5-.56-.42-.63-.34-.68-.25-.72-.16-.77-.05-.8.03-.64.1-.63.17-.6.23-.57.28-.55.34-.51.39-.47.43-.42.48-.38.52-.34.55-.28.59-.22v-.08l.05-.8.16-.76.25-.73.34-.68.43-.63.49-.56.57-.5.63-.42.67-.34.73-.25.77-.16L11 2l.79.05.77.16.73.25.68.34.62.42.57.5.49.56.43.63.34.68.25.73.16.76.05.8v.07l.04.01.27.09.27.11.26.12.25.13.24.14.24.15.23.17.22.17.21.19.21.19.19.21.19.21v.01l-.01-.01.19.26.19.27.17.28zm-3.77-.64l-.11.27-.12.25-.13.25-.15.24-.16.24-.17.23-.18.21-.19.21-.2.2-.21.2-.22.18-.22.17-.03.02-.03.02-.02.01-.03.02-.03.01-.03.02-.04.01-.03.01-.03.01h-.04l-.03.01h-.03l-.04.01h-.03l-.09-.01-.08-.01-.08-.03-.08-.03-.07-.04-.07-.05-.06-.05-.05-.06-.05-.07-.04-.07-.03-.07-.02-.08-.02-.09v-.19l.01-.05.02-.05.01-.05.02-.05.02-.04.03-.05.03-.04.03-.04.03-.04.04-.03.04-.03.04-.03.01-.01.23-.18.22-.2.21-.2.19-.22.18-.24.16-.24.15-.26.12-.27.12-.27.09-.29.07-.29.05-.3.04-.31.01-.31-.03-.51-.09-.5-.14-.48-.19-.45-.24-.42-.28-.39-.32-.36-.36-.33-.39-.28-.43-.23-.45-.2-.48-.14-.49-.08-.51-.03-.5.03-.49.08-.46.13-.44.18-.42.23-.39.27-.35.3-.32.35-.29.37-.24.41-.2.43-.15.46-.1.48-.05.49H7.04l.16.01.15.01.15.01.15.02.15.02.14.03.15.02.14.03.14.04.14.04.14.04.13.04.13.05.02.01.08.02.07.03.07.03.07.03.07.03.07.04.07.03.06.04.07.04.06.04.06.04.07.04.06.04.06.05.03.03.03.03.03.03.03.04.02.04.03.03.02.04.01.05.02.04.01.04.01.05.01.04v.05l.01.05-.01.08-.01.09-.03.08-.03.07-.04.08-.05.06-.05.06-.06.06-.07.04-.07.04-.07.04-.08.02-.09.01-.08.01h-.04l-.04-.01h-.04l-.04-.01-.04-.01-.03-.01-.04-.01-.03-.01-.03-.02-.04-.02-.03-.01-.03-.02-.03-.03-.03-.02.01.01-.03-.02-.03-.02-.03-.02-.04-.02-.03-.02-.03-.02-.04-.02-.03-.01-.04-.02-.04-.01-.03-.02-.04-.01-.04-.02-.04-.01h-.01l.03.01-.03-.01-.08-.03-.1-.03-.11-.03-.11-.03-.11-.03-.1-.03-.12-.02-.11-.02-.11-.01-.11-.02-.12-.01h-.11L7 9.17h-.11l-.52.03-.49.09-.48.14-.45.19-.42.23-.4.29-.36.32-.32.36-.28.39-.24.42-.19.46-.14.47-.09.5-.02.51.02.51.09.5.14.47.19.45.24.43.28.39.32.36.36.32.4.28.42.24.45.19.48.14.49.09.52.03.26-.01.27-.02.26-.04.25-.06.25-.06.24-.09.24-.09.23-.11.22-.12.22-.14.2-.14.2-.16.18-.17.18-.18-.1-.16-.1-.2-.1-.2-.09-.2-.08-.21-.07-.22-.07-.21-.06-.22-.04-.22-.05-.23-.03-.23-.02-.23-.01-.23-.01-.24v-.01l.01-.08.01-.09.02-.08.04-.07.04-.07.04-.07.06-.06.06-.06.06-.04.08-.04.07-.03.08-.03.09-.01.08-.01.09.01.08.01.08.03.08.03.07.04.06.04.07.06.05.06.05.07.04.07.03.07.02.08.02.09v.08l.03.52.09.49.14.48.19.45.24.42.28.39.32.36.36.33.4.28.42.24.45.19.48.14.49.08.52.03-.01-.01.52-.03.49-.09.48-.14.45-.19.42-.24.4-.28.36-.32.32-.36.28-.39.24-.43.19-.45.14-.47.08-.5.03-.51-.01-.4-.06-.39-.08-.38-.12-.36-.15-.35-.18-.34-.2-.32-.23-.29-.25-.28-.28-.26-.3-.23-.32-.2-.34-.18-.35-.15-.01.04-.09.27-.1.27zm-6.36 6.6l-.02-.03.02.03z" - style={{ fill }} - /> - </svg> - ); -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx deleted file mode 100644 index 276633320a8..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 QGWidget from './QGWidget'; -import LoginForm from './LoginForm'; -import { getMeasuresAndMeta, MeasureComponent } from '../../../../api/measures'; -import { Metric } from '../../../types'; -import { Settings } from '../utils'; - -interface Props { - settings: Settings; -} - -interface State { - component?: MeasureComponent; - loading: boolean; - metrics?: Metric[]; - unauthorized: boolean; -} -export default class Widget extends React.PureComponent<Props, State> { - mounted = false; - state: State = { loading: true, unauthorized: false }; - - componentDidMount() { - this.mounted = true; - const { settings } = this.props; - if (settings.project) { - this.fetchProjectMeasures(settings.project); - } else { - this.setState({ loading: false }); - } - } - - componentWillReceiveProps(nextProps: Props) { - const { project } = nextProps.settings; - if (project !== this.props.settings.project) { - if (project) { - this.fetchProjectMeasures(project); - } else { - this.setState({ component: undefined }); - } - } - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchProjectMeasures = (project: string) => { - this.setState({ loading: true }); - getMeasuresAndMeta(project, ['alert_status'], { additionalFields: 'metrics' }).then( - ({ component, metrics }) => { - if (this.mounted) { - this.setState({ component, loading: false, metrics, unauthorized: false }); - } - }, - response => { - if (response && response.response.status === 403) { - this.setState({ loading: false, unauthorized: true }); - } else { - this.setState({ loading: false }); - } - } - ); - }; - - handleReload = () => { - const { settings } = this.props; - if (settings.project) { - this.fetchProjectMeasures(settings.project); - } - }; - - render() { - const { component, loading, metrics, unauthorized } = this.state; - if (loading) { - return ( - <div className="vsts-loading"> - <i className="spinner global-loading-spinner" /> - </div> - ); - } - - if (unauthorized) { - return ( - <div className="widget"> - <LoginForm onReload={this.handleReload} title="Authentication on SonarCloud required" /> - </div> - ); - } - - if (!component || !metrics) { - return ( - <div className="vsts-widget-configure widget"> - <h2 className="title">Code Quality</h2> - <div className="content"> - <div>Configure widget</div> - <img - alt="" - src="https://cdn.vsassets.io/v/20180301T143409/_content/Dashboards/unconfigured-small.png" - /> - </div> - </div> - ); - } - - return <QGWidget component={component} />; - } -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/index.js b/server/sonar-web/src/main/js/app/integration/vsts/index.js deleted file mode 100644 index 468f5190698..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 { parse } from 'querystring'; -import React from 'react'; -import { render } from 'react-dom'; -import Configuration from './components/Configuration'; -import Widget from './components/Widget'; -import { parseWidgetSettings } from './utils'; -import './vsts.css'; - -const container = document.getElementById('content'); -const query = parse(window.location.search.replace('?', '')); - -if (query.type === 'authenticated') { - if (window.opener && window.opener.authenticationDone) { - window.opener.authenticationDone(); - } - window.close(); -} else if (VSS && query.contribution && VSS.init && VSS.require) { - VSS.init({ - explicitNotifyLoaded: true, - usePlatformStyles: true - }); - - VSS.require('TFS/Dashboards/WidgetHelpers', WidgetHelpers => { - WidgetHelpers.IncludeWidgetStyles(); - WidgetHelpers.IncludeWidgetConfigurationStyles(); - - if (query.type === 'configuration') { - render( - <Configuration contribution={query.contribution} widgetHelpers={WidgetHelpers} />, - container - ); - } else { - VSS.register(query.contribution, () => { - const loadFunction = loadVSTSWidget(WidgetHelpers); - return { load: loadFunction, reload: loadFunction }; - }); - } - VSS.notifyLoadSucceeded(); - }); -} - -function loadVSTSWidget(WidgetHelpers) { - return widgetSettings => { - try { - render(<Widget settings={parseWidgetSettings(widgetSettings)} />, container); - } catch (error) { - return WidgetHelpers.WidgetStatusHelper.Failure(error); - } - - return WidgetHelpers.WidgetStatusHelper.Success(); - }; -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/utils.ts b/server/sonar-web/src/main/js/app/integration/vsts/utils.ts deleted file mode 100644 index ec9bc4688b8..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ - -export interface VSTSConfigurationContext { - notify: Function; -} - -export interface VSTSCustomSettings { - data: string; -} - -export interface VSTSWidgetSettings { - customSettings: VSTSCustomSettings; -} - -export interface Settings { - project?: string; -} - -export function parseWidgetSettings(widgetSettings: VSTSWidgetSettings): Settings { - try { - return JSON.parse(widgetSettings.customSettings.data) || {}; - } catch (e) { - return {}; - } -} - -export function serializeWidgetSettings(parsedSettings: Settings): VSTSCustomSettings { - return { data: JSON.stringify(parsedSettings) }; -} diff --git a/server/sonar-web/src/main/js/app/integration/vsts/vsts.css b/server/sonar-web/src/main/js/app/integration/vsts/vsts.css deleted file mode 100644 index 634a9bd345a..00000000000 --- a/server/sonar-web/src/main/js/app/integration/vsts/vsts.css +++ /dev/null @@ -1,294 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 '../../styles/components/spinner.css'; -@import '../../styles/components/global-loading.css'; - -#content { - height: 100%; -} - -.vsts-loading { - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.vsts-widget-configure { - display: block; - position: relative; - width: 100%; - height: 100%; - padding: 10px 14px; - font-size: 16px; -} - -.vsts-widget-configure .title { - color: #333; - font-weight: normal; -} - -.vsts-widget-configure .content { - padding-top: 10%; - text-align: center; - color: #666; -} - -.vsts-widget-configure img { - height: 40px; - margin-top: 10px; -} - -.widget.dark-widget.clickable > a { - color: white; -} - -.widget .footer { - display: flex; - align-items: center; -} - -.widget .footer svg { - margin-right: 8px; -} - -.vsts-widget-login { - text-align: center; - padding-top: 4px; -} - -.vsts-widget-login .login-message-text { - color: #666; - margin: 0; -} - -.vsts-widget-login .oauth-providers { - margin-top: 8px; - margin-bottom: 8px; -} -.vsts-widget-login .oauth-providers a { - display: inline-block; - line-height: 22px; - padding: 4px 6px; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 2px; - box-sizing: border-box; - background-color: var(--darkBlue); - color: #fff; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.vsts-widget-login .oauth-providers a:hover, -.vsts-widget-login .oauth-providers a:focus { - box-shadow: 0 0 16px rgba(0, 0, 0, 0.2); -} - -.vsts-widget-login .oauth-providers span { - padding-left: 4px; -} - -.vsts-widget-login .oauth-providers img { - vertical-align: top; -} - -.vsts-configuration { - min-height: 540px; -} - -.vsts-configuration .config-settings-field { - margin-bottom: 20px; -} - -.big-value { - font-size: 36px; - line-height: 68px; - margin: 20px 0 10px 0; - font-weight: 300; -} - -.level-OK { - background-color: var(--green); -} - -.level-WARN { - background-color: var(--orange); -} - -.level-ERROR { - background-color: var(--red); -} - -.level-NONE { - background-color: var(--gray71); -} - -.Select { - width: 100%; -} - -.project-picker { - position: relative; - width: 100%; - height: 32px; -} - -.filtered-list-dropdown-menu { - white-space: nowrap; - position: relative; - cursor: pointer; - padding: 6px; - border: 1px solid #c8c8c8; -} - -.filtered-list-dropdown-menu .drop-icon { - float: right; - position: relative; - overflow: hidden; - vertical-align: middle; -} - -.filtered-list-dropdown-menu .selected-item-text { - width: 90%; - padding-left: 5px; - padding-right: 5px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-wrap: normal; - vertical-align: middle; - display: inline-block; -} - -.filtered-list-popup { - position: absolute; - display: block; - width: 100%; - top: 30px; - left: 0; - z-index: 20000; - overflow-y: auto; - max-height: 400px; - font-size: 12px; - background-color: #fff; - border: 1px solid #c8c8c8; - box-shadow: 0 2.5px 5px rgba(0, 0, 0, 0.4); -} - -.filtered-list-popup .filtered-list-control .pivot-view { - margin-left: 0; -} - -.filtered-list-control.bowtie-filtered-list .filtered-list-tab > a { - outline: none; -} - -.filtered-list-tab.selected::before { - content: ''; - position: absolute; - bottom: 0; - left: 10px; - right: 10px; - height: 2px; - background-color: #0078d7; -} - -.filtered-list-tab:first-child.selected::before { - left: 0; -} - -.filtered-list-control .filter-container { - position: relative; -} - -.filtered-list-search::placeholder { - font-size: 12px; -} - -.filtered-list-search-container .spinner { - position: absolute; - right: 16px; - bottom: 18px; -} - -.filtered-list-control.bowtie-filtered-list .filter-container { - padding: 10px; - width: 100%; -} - -.filtered-list-control.bowtie-filtered-list .filtered-list { - padding: 0; - margin: 0 0 4px 0; - max-height: 300px; - overflow: auto; -} - -.filtered-list-control .filtered-list > li { - list-style-type: none; - padding: 5px 10px; - border: none; - margin: 0; - height: 30px; - line-height: 20px; - cursor: pointer; - position: relative; - vertical-align: middle; - outline: none; - overflow: hidden; - text-overflow: ellipsis; - word-wrap: normal; - white-space: pre; -} - -.filtered-list-control .filtered-list > li.filtered-list-message { - white-space: normal; - color: #666; - cursor: default; - overflow: visible; -} - -.filtered-list-control .filtered-list > li.filtered-list-item.current-item { - font-weight: 700; - color: #212121; - background-color: #f4f4f4; -} - -.filtered-list-control .filtered-list > li.filtered-list-item.active-item { - font-weight: normal; - color: #212121; - background-color: #eff6fc; -} - -.filtered-list-control .filtered-list > li.filtered-list-item.active-item::after { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - border: 1px solid #a6a6a6; -} - -.filtered-list-control .filtered-list > li.filtered-list-item.current-item.active-item { - font-weight: 700; -} |