Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue7
-rw-r--r--app/assets/javascripts/import_projects/components/bitbucket_status_table.vue74
-rw-r--r--app/assets/javascripts/import_projects/components/import_projects_table.vue7
-rw-r--r--app/assets/javascripts/import_projects/index.js1
-rw-r--r--app/assets/javascripts/import_projects/store/actions.js33
-rw-r--r--app/assets/javascripts/onboarding_issues/index.js118
-rw-r--r--app/assets/javascripts/pages/import/bitbucket/status/index.js19
-rw-r--r--app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue30
-rw-r--r--app/assets/javascripts/pages/import/bitbucket_server/status/index.js20
-rw-r--r--app/assets/javascripts/pages/import/fogbugz/status/index.js7
-rw-r--r--app/assets/javascripts/pages/import/gitlab/status/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue2
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/themes/_dark.scss3
-rw-r--r--app/controllers/import/base_controller.rb76
-rw-r--r--app/controllers/import/bitbucket_controller.rb53
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb77
-rw-r--r--app/controllers/import/fogbugz_controller.rb33
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/import/gitlab_controller.rb29
-rw-r--r--app/serializers/diffs_entity.rb12
-rw-r--r--app/serializers/import/base_provider_repo_entity.rb8
-rw-r--r--app/serializers/import/bitbucket_provider_repo_entity.rb15
-rw-r--r--app/serializers/import/bitbucket_server_provider_repo_entity.rb7
-rw-r--r--app/serializers/import/fogbugz_provider_repo_entity.rb17
-rw-r--r--app/serializers/import/githubish_provider_repo_entity.rb (renamed from app/serializers/provider_repo_entity.rb)12
-rw-r--r--app/serializers/import/gitlab_provider_repo_entity.rb19
-rw-r--r--app/serializers/import/provider_repo_serializer.rb23
-rw-r--r--app/serializers/paginated_diff_entity.rb10
-rw-r--r--app/serializers/provider_repo_serializer.rb5
-rw-r--r--app/views/import/_githubish_status.html.haml5
-rw-r--r--app/views/import/bitbucket/status.html.haml167
-rw-r--r--app/views/import/bitbucket_server/status.html.haml165
-rw-r--r--app/views/import/fogbugz/status.html.haml99
-rw-r--r--app/views/import/gitlab/status.html.haml95
37 files changed, 955 insertions, 311 deletions
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 0fb06dab635..6b9748bb725 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -12,6 +12,8 @@ import itemStats from './item_stats.vue';
import itemStatsValue from './item_stats_value.vue';
import itemActions from './item_actions.vue';
+import { showLearnGitLabGroupItemPopover } from '~/onboarding_issues';
+
export default {
directives: {
tooltip,
@@ -73,6 +75,11 @@ export default {
return GROUP_VISIBILITY_TYPE[this.group.visibility];
},
},
+ mounted() {
+ if (this.group.name === 'Learn GitLab') {
+ showLearnGitLabGroupItemPopover(this.group.id);
+ }
+ },
methods: {
onClickRowGroup(e) {
const NO_EXPAND_CLS = 'no-expand';
diff --git a/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue b/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
new file mode 100644
index 00000000000..1a9974db727
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
@@ -0,0 +1,74 @@
+<script>
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import ImportProjectsTable from './import_projects_table.vue';
+
+export default {
+ components: {
+ ImportProjectsTable,
+ GlAlert,
+ GlSprintf,
+ GlLink,
+ },
+ props: {
+ providerTitle: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isWarningDismissed: false,
+ };
+ },
+ computed: {
+ currentPage() {
+ return window.location.href;
+ },
+ },
+};
+</script>
+<template>
+ <import-projects-table provider-title="providerTitle">
+ <template #actions>
+ <slot name="actions"></slot>
+ </template>
+ <template #incompatible-repos-warning>
+ <gl-alert
+ v-if="!isWarningDismissed"
+ variant="warning"
+ class="gl-my-2"
+ @dismiss="isWarningDismissed = true"
+ >
+ <gl-sprintf
+ :message="
+ __(
+ 'One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.',
+ )
+ "
+ >
+ <template #provider>
+ {{ providerTitle }}
+ </template>
+ </gl-sprintf>
+ <gl-sprintf
+ :message="
+ __(
+ 'Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again.',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link
+ href="https://www.atlassian.com/git/tutorials/migrating-overview"
+ target="_blank"
+ >{{ content }}</gl-link
+ >
+ </template>
+ <template #linkToImportFlow>
+ <gl-link :href="currentPage">{{ __('import flow') }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
+ </template>
+ </import-projects-table>
+</template>
diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue
index f2ed16ba59f..6a467fb8c6a 100644
--- a/app/assets/javascripts/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue
@@ -24,6 +24,11 @@ export default {
type: String,
required: true,
},
+ filterable: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
@@ -114,7 +119,7 @@ export default {
{{ importAllButtonText }}
</gl-button>
<slot name="actions"></slot>
- <form class="gl-ml-auto" novalidate @submit.prevent>
+ <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent>
<input
:value="filter"
data-qa-selector="githubish_import_filter_field"
diff --git a/app/assets/javascripts/import_projects/index.js b/app/assets/javascripts/import_projects/index.js
index 49d38b56e40..68ba04aa9dd 100644
--- a/app/assets/javascripts/import_projects/index.js
+++ b/app/assets/javascripts/import_projects/index.js
@@ -30,6 +30,7 @@ export function initStoreFromElement(element) {
export function initPropsFromElement(element) {
return {
providerTitle: element.dataset.providerTitle,
+ filterable: parseBoolean(element.dataset.filterable),
};
}
diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js
index 6cf71126882..2422a1ed2e4 100644
--- a/app/assets/javascripts/import_projects/store/actions.js
+++ b/app/assets/javascripts/import_projects/store/actions.js
@@ -2,6 +2,7 @@ import Visibility from 'visibilityjs';
import * as types from './mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import Poll from '~/lib/utils/poll';
+import { visitUrl } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils';
@@ -9,6 +10,9 @@ import { jobsPathWithFilter, reposPathWithFilter } from './getters';
let eTagPoll;
+const hasRedirectInError = e => e?.response?.data?.error?.redirect;
+const redirectToUrlInError = e => visitUrl(e.response.data.error.redirect);
+
export const clearJobsEtagPoll = () => {
eTagPoll = null;
};
@@ -33,14 +37,18 @@ export const fetchRepos = ({ state, dispatch, commit }) => {
commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
)
.then(() => dispatch('fetchJobs'))
- .catch(() => {
- createFlash(
- sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
- provider,
- }),
- );
-
- commit(types.RECEIVE_REPOS_ERROR);
+ .catch(e => {
+ if (hasRedirectInError(e)) {
+ redirectToUrlInError(e);
+ } else {
+ createFlash(
+ sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
+ provider,
+ }),
+ );
+
+ commit(types.RECEIVE_REPOS_ERROR);
+ }
});
};
@@ -87,8 +95,13 @@ export const fetchJobs = ({ state, commit, dispatch }) => {
method: 'fetchJobs',
successCallback: ({ data }) =>
commit(types.RECEIVE_JOBS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
- errorCallback: () =>
- createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')),
+ errorCallback: e => {
+ if (hasRedirectInError(e)) {
+ redirectToUrlInError(e);
+ } else {
+ createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed'));
+ }
+ },
data: { filter },
});
diff --git a/app/assets/javascripts/onboarding_issues/index.js b/app/assets/javascripts/onboarding_issues/index.js
new file mode 100644
index 00000000000..afc3b0c2685
--- /dev/null
+++ b/app/assets/javascripts/onboarding_issues/index.js
@@ -0,0 +1,118 @@
+import $ from 'jquery';
+import { parseBoolean, getCookie, setCookie, removeCookie } from '~/lib/utils/common_utils';
+import { __ } from '~/locale';
+
+const COOKIE_NAME = 'onboarding_issues_settings';
+
+const POPOVER_LOCATIONS = {
+ GROUPS_SHOW: 'groups#show',
+ PROJECTS_SHOW: 'projects#show',
+ ISSUES_INDEX: 'issues#index',
+};
+
+const removeLearnGitLabCookie = () => {
+ removeCookie(COOKIE_NAME);
+};
+
+function disposePopover(event) {
+ event.preventDefault();
+ this.popover('dispose');
+ removeLearnGitLabCookie();
+}
+
+const showPopover = (el, path, footer, options) => {
+ // Cookie value looks like `{ 'groups#show': true, 'projects#show': true, 'issues#index': true }`. When it doesn't exist, don't show the popover.
+ const cookie = getCookie(COOKIE_NAME);
+ if (!cookie) return;
+
+ // When the popover action has already been taken, don't show the popover.
+ const settings = JSON.parse(cookie);
+ if (!parseBoolean(settings[path])) return;
+
+ const defaultOptions = {
+ boundary: 'window',
+ html: true,
+ placement: 'top',
+ template: `<div class="popover blue learn-gitlab d-none d-xl-block" role="tooltip">
+ <div class="arrow"></div>
+ <div class="close cursor-pointer gl-font-base text-white gl-opacity-10 p-2">&#10005</div>
+ <div class="popover-body gl-font-base gl-line-height-20 pb-0 px-3"></div>
+ <div class="bold text-right text-white p-2">${footer}</div>
+ </div>`,
+ };
+
+ // When one of the popovers is dismissed, remove the cookie.
+ const closeButton = () => document.querySelector('.learn-gitlab.popover .close');
+
+ // We still have to use jQuery, since Bootstrap's Popover is based on jQuery.
+ const jQueryEl = $(el);
+ const clickCloseButton = disposePopover.bind(jQueryEl);
+
+ jQueryEl
+ .popover({ ...defaultOptions, ...options })
+ .on('inserted.bs.popover', () => closeButton().addEventListener('click', clickCloseButton))
+ .on('hide.bs.dropdown', () => closeButton().removeEventListener('click', clickCloseButton))
+ .popover('show');
+
+ // The previous popover actions have been taken, don't show those popovers anymore.
+ Object.keys(settings).forEach(pathSetting => {
+ if (path !== pathSetting) {
+ settings[pathSetting] = false;
+ } else {
+ setCookie(COOKIE_NAME, settings);
+ }
+ });
+
+ // The final popover action will be taken on click, we then no longer need the cookie.
+ if (path === POPOVER_LOCATIONS.ISSUES_INDEX) {
+ el.addEventListener('click', removeLearnGitLabCookie);
+ }
+};
+
+export const showLearnGitLabGroupItemPopover = id => {
+ const el = document.querySelector(`#group-${id} .group-text a`);
+
+ if (!el) return;
+
+ const options = {
+ content: __(
+ 'Here are all your projects in your group, including the one you just created. To start, let’s take a look at your personalized learning project which will help you learn about GitLab at your own pace.',
+ ),
+ };
+
+ showPopover(el, POPOVER_LOCATIONS.GROUPS_SHOW, '1 / 2', options);
+};
+
+export const showLearnGitLabProjectPopover = () => {
+ // Do not show a popover if we are not viewing the 'Learn GitLab' project.
+ if (!window.location.pathname.includes('learn-gitlab')) return;
+
+ const el = document.querySelector('a.shortcuts-issues');
+
+ if (!el) return;
+
+ const options = {
+ content: __(
+ 'Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board.',
+ ),
+ };
+
+ showPopover(el, POPOVER_LOCATIONS.PROJECTS_SHOW, '2 / 2', options);
+};
+
+export const showLearnGitLabIssuesPopover = () => {
+ // Do not show a popover if we are not viewing the 'Learn GitLab' project.
+ if (!window.location.pathname.includes('learn-gitlab')) return;
+
+ const el = document.querySelector('a[data-qa-selector="issue_boards_link"]');
+
+ if (!el) return;
+
+ const options = {
+ content: __(
+ 'Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board.',
+ ),
+ };
+
+ showPopover(el, POPOVER_LOCATIONS.ISSUES_INDEX, '2 / 2', options);
+};
diff --git a/app/assets/javascripts/pages/import/bitbucket/status/index.js b/app/assets/javascripts/pages/import/bitbucket/status/index.js
new file mode 100644
index 00000000000..52b5adb79d1
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bitbucket/status/index.js
@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
+import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+ if (!mountElement) return undefined;
+
+ const store = initStoreFromElement(mountElement);
+ const props = initPropsFromElement(mountElement);
+
+ return new Vue({
+ el: mountElement,
+ store,
+ render(createElement) {
+ return createElement(BitbucketStatusTable, { props });
+ },
+ });
+});
diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue
new file mode 100644
index 00000000000..e01c7b80e1a
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue
@@ -0,0 +1,30 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
+
+export default {
+ components: {
+ BitbucketStatusTable,
+ GlButton,
+ },
+ props: {
+ providerTitle: {
+ type: String,
+ required: true,
+ },
+ reconfigurePath: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <bitbucket-status-table :provider-title="providerTitle">
+ <template #actions>
+ <gl-button variant="info" class="gl-ml-3" data-method="post" :href="reconfigurePath">{{
+ __('Reconfigure')
+ }}</gl-button>
+ </template>
+ </bitbucket-status-table>
+</template>
diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/index.js b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js
new file mode 100644
index 00000000000..88455c9b7b9
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js
@@ -0,0 +1,20 @@
+import Vue from 'vue';
+import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
+import BitbucketServerStatusTable from './components/bitbucket_server_status_table.vue';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+ if (!mountElement) return undefined;
+
+ const store = initStoreFromElement(mountElement);
+ const props = initPropsFromElement(mountElement);
+ const { reconfigurePath } = mountElement.dataset;
+
+ return new Vue({
+ el: mountElement,
+ store,
+ render(createElement) {
+ return createElement(BitbucketServerStatusTable, { props: { ...props, reconfigurePath } });
+ },
+ });
+});
diff --git a/app/assets/javascripts/pages/import/fogbugz/status/index.js b/app/assets/javascripts/pages/import/fogbugz/status/index.js
new file mode 100644
index 00000000000..dcd84f0faf9
--- /dev/null
+++ b/app/assets/javascripts/pages/import/fogbugz/status/index.js
@@ -0,0 +1,7 @@
+import mountImportProjectsTable from '~/import_projects';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+
+ mountImportProjectsTable(mountElement);
+});
diff --git a/app/assets/javascripts/pages/import/gitlab/status/index.js b/app/assets/javascripts/pages/import/gitlab/status/index.js
new file mode 100644
index 00000000000..dcd84f0faf9
--- /dev/null
+++ b/app/assets/javascripts/pages/import/gitlab/status/index.js
@@ -0,0 +1,7 @@
+import mountImportProjectsTable from '~/import_projects';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+
+ mountImportProjectsTable(mountElement);
+});
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index e8e0cda2139..a66b665d152 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -9,6 +9,7 @@ import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
import initIssuablesList from '~/issuables_list';
import initManualOrdering from '~/manual_ordering';
+import { showLearnGitLabIssuesPopover } from '~/onboarding_issues';
document.addEventListener('DOMContentLoaded', () => {
IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
@@ -24,4 +25,5 @@ document.addEventListener('DOMContentLoaded', () => {
initManualOrdering();
initIssuablesList();
+ showLearnGitLabIssuesPopover();
});
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index f29dbd92c46..3c44053e2b2 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -15,6 +15,7 @@ import leaveByUrl from '~/namespaces/leave_by_url';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
import initNamespaceStorageLimitAlert from '~/namespace_storage_limit_alert';
+import { showLearnGitLabProjectPopover } from '~/onboarding_issues';
document.addEventListener('DOMContentLoaded', () => {
initReadMore();
@@ -59,4 +60,6 @@ document.addEventListener('DOMContentLoaded', () => {
throw e;
});
}
+
+ showLearnGitLabProjectPopover();
});
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 893b32606f8..0e05f4a4622 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -134,7 +134,7 @@ export default {
addMultipleToDiscussionWarning() {
return sprintf(
__(
- '%{icon}You are about to add %{usersTag} people to the discussion. Proceed with caution.',
+ '%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification.',
),
{
icon: '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>',
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 44b95c40649..80218d25a1a 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -716,8 +716,8 @@ $accepting-mr-label-color: #69d100;
/*
* Issues
*/
-$issues-today-bg: #f3fff2;
-$issues-today-border: #e1e8d5;
+$issues-today-bg: #f3fff2 !default;
+$issues-today-border: #e1e8d5 !default;
$compare-display-color: #888;
/*
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index aa145f28093..23487a2d5f7 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -112,6 +112,9 @@ $popover-arrow-outer-color: $gray-800;
$secondary: $gray-600;
+$issues-today-bg: #333838;
+$issues-today-border: #333a40;
+
.gl-label {
filter: brightness(0.9) contrast(1.1);
}
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 04919a4b9d0..afdea4f7c9d 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -1,10 +1,86 @@
# frozen_string_literal: true
class Import::BaseController < ApplicationController
+ include ActionView::Helpers::SanitizeHelper
+
before_action :import_rate_limit, only: [:create]
+ def status
+ respond_to do |format|
+ format.json do
+ render json: { imported_projects: serialized_imported_projects,
+ provider_repos: serialized_provider_repos,
+ incompatible_repos: serialized_incompatible_repos,
+ namespaces: serialized_namespaces }
+ end
+ format.html
+ end
+ end
+
+ def realtime_changes
+ Gitlab::PollingInterval.set_header(response, interval: 3_000)
+
+ render json: already_added_projects.to_json(only: [:id], methods: [:import_status])
+ end
+
+ protected
+
+ def importable_repos
+ raise NotImplementedError
+ end
+
+ def incompatible_repos
+ []
+ end
+
+ def provider_name
+ raise NotImplementedError
+ end
+
+ def provider_url
+ raise NotImplementedError
+ end
+
private
+ def filter_attribute
+ :name
+ end
+
+ def sanitized_filter_param
+ @filter ||= sanitize(params[:filter])
+ end
+
+ def filtered(collection)
+ return collection unless sanitized_filter_param
+
+ collection.select { |item| item[filter_attribute].include?(sanitized_filter_param) }
+ end
+
+ def serialized_provider_repos
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(importable_repos, provider: provider_name, provider_url: provider_url)
+ end
+
+ def serialized_incompatible_repos
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(incompatible_repos, provider: provider_name, provider_url: provider_url)
+ end
+
+ def serialized_imported_projects
+ ProjectSerializer.new.represent(already_added_projects, serializer: :import, provider_url: provider_url)
+ end
+
+ def already_added_projects
+ @already_added_projects ||= filtered(find_already_added_projects(provider_name))
+ end
+
+ def serialized_namespaces
+ NamespaceSerializer.new.represent(namespaces)
+ end
+
+ def namespaces
+ current_user.manageable_groups_with_routes
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def find_already_added_projects(import_type)
current_user.created_projects.where(import_type: import_type).with_import_state
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index c37e799de62..4886aeb5e3f 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::BitbucketController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
include ActionView::Helpers::SanitizeHelper
before_action :verify_bitbucket_import_enabled
@@ -10,7 +12,7 @@ class Import::BitbucketController < Import::BaseController
rescue_from Bitbucket::Error::Unauthorized, with: :bitbucket_unauthorized
def callback
- response = client.auth_code.get_token(params[:code], redirect_uri: users_import_bitbucket_callback_url)
+ response = oauth_client.auth_code.get_token(params[:code], redirect_uri: users_import_bitbucket_callback_url)
session[:bitbucket_token] = response.token
session[:bitbucket_expires_at] = response.expires_at
@@ -22,9 +24,10 @@ class Import::BitbucketController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
+ return super if Feature.enabled?(:new_import_ui)
+
bitbucket_client = Bitbucket::Client.new(credentials)
repos = bitbucket_client.repos(filter: sanitized_filter_param)
-
@repos, @incompatible_repos = repos.partition { |repo| repo.valid? }
@already_added_projects = find_already_added_projects('bitbucket')
@@ -38,6 +41,10 @@ class Import::BitbucketController < Import::BaseController
render json: find_jobs('bitbucket')
end
+ def realtime_changes
+ super
+ end
+
def create
bitbucket_client = Bitbucket::Client.new(credentials)
@@ -59,7 +66,7 @@ class Import::BitbucketController < Import::BaseController
project = Gitlab::BitbucketImport::ProjectCreator.new(repo, project_name, target_namespace, current_user, credentials).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
@@ -68,16 +75,50 @@ class Import::BitbucketController < Import::BaseController
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ bitbucket_repos.reject { |repo| already_added_projects_names.include?(repo.full_name) || !repo.valid? }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :incompatible_repos
+ def incompatible_repos
+ bitbucket_repos.reject { |repo| repo.valid? }
+ end
+
+ override :provider_name
+ def provider_name
+ :bitbucket
+ end
+
+ override :provider_url
+ def provider_url
+ provider.url
+ end
+
private
- def client
- @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options)
+ def oauth_client
+ @oauth_client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options)
end
def provider
Gitlab::Auth::OAuth::Provider.config_for('bitbucket')
end
+ def client
+ @client ||= Bitbucket::Client.new(credentials)
+ end
+
+ def bitbucket_repos
+ @bitbucket_repos ||= client.repos(filter: sanitized_filter_param).to_a
+ end
+
def options
OmniAuth::Strategies::Bitbucket.default_options[:client_options].deep_symbolize_keys
end
@@ -91,7 +132,7 @@ class Import::BitbucketController < Import::BaseController
end
def go_to_bitbucket_for_permissions
- redirect_to client.auth_code.authorize_url(redirect_uri: users_import_bitbucket_callback_url)
+ redirect_to oauth_client.auth_code.authorize_url(redirect_uri: users_import_bitbucket_callback_url)
end
def bitbucket_unauthorized
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index 5fb7b5dccc5..9aa8110257d 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -1,12 +1,16 @@
# frozen_string_literal: true
class Import::BitbucketServerController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
include ActionView::Helpers::SanitizeHelper
before_action :verify_bitbucket_server_import_enabled
before_action :bitbucket_auth, except: [:new, :configure]
before_action :validate_import_params, only: [:create]
+ rescue_from BitbucketServer::Connection::ConnectionError, with: :bitbucket_connection_error
+
# As a basic sanity check to prevent URL injection, restrict project
# repository input and repository slugs to allowed characters. For Bitbucket:
#
@@ -24,7 +28,7 @@ class Import::BitbucketServerController < Import::BaseController
end
def create
- repo = bitbucket_client.repo(@project_key, @repo_slug)
+ repo = client.repo(@project_key, @repo_slug)
unless repo
return render json: { errors: _("Project %{project_repo} could not be found") % { project_repo: "#{@project_key}/#{@repo_slug}" } }, status: :unprocessable_entity
@@ -38,15 +42,13 @@ class Import::BitbucketServerController < Import::BaseController
project = Gitlab::BitbucketServerImport::ProjectCreator.new(@project_key, @repo_slug, repo, project_name, target_namespace, current_user, credentials).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
else
render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
- rescue BitbucketServer::Connection::ConnectionError => error
- render json: { errors: _("Unable to connect to server: %{error}") % { error: error } }, status: :unprocessable_entity
end
def configure
@@ -59,7 +61,9 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
- @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
+ return super if Feature.enabled?(:new_import_ui)
+
+ @collection = client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
@repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
# Use the import URL to filter beyond what BaseService#find_already_added_projects
@@ -67,10 +71,6 @@ class Import::BitbucketServerController < Import::BaseController
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
- rescue BitbucketServer::Connection::ConnectionError => error
- flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
- clear_session_data
- redirect_to new_import_bitbucket_server_path
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -78,6 +78,38 @@ class Import::BitbucketServerController < Import::BaseController
render json: find_jobs('bitbucket_server')
end
+ def realtime_changes
+ super
+ end
+
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ # Use the import URL to filter beyond what BaseService#find_already_added_projects
+ already_added_projects = filter_added_projects('bitbucket_server', bitbucket_repos.map(&:browse_url))
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ bitbucket_repos.reject { |repo| already_added_projects_names.include?(repo.browse_url) || !repo.valid? }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :incompatible_repos
+ def incompatible_repos
+ bitbucket_repos.reject { |repo| repo.valid? }
+ end
+
+ override :provider_name
+ def provider_name
+ :bitbucket_server
+ end
+
+ override :provider_url
+ def provider_url
+ session[bitbucket_server_url_key]
+ end
+
private
# rubocop: disable CodeReuse/ActiveRecord
@@ -86,8 +118,12 @@ class Import::BitbucketServerController < Import::BaseController
end
# rubocop: enable CodeReuse/ActiveRecord
- def bitbucket_client
- @bitbucket_client ||= BitbucketServer::Client.new(credentials)
+ def client
+ @client ||= BitbucketServer::Client.new(credentials)
+ end
+
+ def bitbucket_repos
+ @bitbucket_repos ||= client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param).to_a
end
def validate_import_params
@@ -153,4 +189,23 @@ class Import::BitbucketServerController < Import::BaseController
def sanitized_filter_param
sanitize(params[:filter])
end
+
+ def bitbucket_connection_error(error)
+ flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
+ clear_session_data
+
+ respond_to do |format|
+ format.json do
+ render json: {
+ error: {
+ message: _("Unable to connect to server: %{error}") % { error: error },
+ redirect: new_import_bitbucket_server_path
+ }
+ }, status: :unprocessable_entity
+ end
+ format.html do
+ redirect_to new_import_bitbucket_server_path
+ end
+ end
+ end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 4fb6efde7ff..91779a5d6cc 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::FogbugzController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
before_action :verify_fogbugz_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
before_action :verify_blocked_uri, only: :callback
@@ -48,6 +50,8 @@ class Import::FogbugzController < Import::BaseController
return redirect_to new_import_fogbugz_path
end
+ return super if Feature.enabled?(:new_import_ui)
+
@repos = client.repos
@already_added_projects = find_already_added_projects('fogbugz')
@@ -57,6 +61,10 @@ class Import::FogbugzController < Import::BaseController
end
# rubocop: enable CodeReuse/ActiveRecord
+ def realtime_changes
+ super
+ end
+
def jobs
render json: find_jobs('fogbugz')
end
@@ -69,12 +77,35 @@ class Import::FogbugzController < Import::BaseController
project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, current_user.namespace, current_user, umap).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ repos = client.repos
+
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ repos.reject { |repo| already_added_projects_names.include? repo.name }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :provider_name
+ def provider_name
+ :fogbugz
+ end
+
+ override :provider_url
+ def provider_url
+ session[:fogbugz_uri]
+ end
+
private
def client
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 4e8ceae75bd..097edcd6075 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -76,7 +76,7 @@ class Import::GithubController < Import::BaseController
def serialized_provider_repos
repos = client_repos.reject { |repo| already_added_project_names.include? repo.full_name }
- ProviderRepoSerializer.new(current_user: current_user).represent(repos, provider: provider, provider_url: provider_url)
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(repos, provider: provider, provider_url: provider_url)
end
def serialized_namespaces
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index 5ec8e9e6fc5..a95a67e208c 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::GitlabController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100
@@ -16,6 +18,8 @@ class Import::GitlabController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
+ return super if Feature.enabled?(:new_import_ui)
+
@repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
@already_added_projects = find_already_added_projects('gitlab')
@@ -37,7 +41,7 @@ class Import::GitlabController < Import::BaseController
project = Gitlab::GitlabImport::ProjectCreator.new(repo, target_namespace, current_user, access_params).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
@@ -46,6 +50,29 @@ class Import::GitlabController < Import::BaseController
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
+
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ repos.reject { |repo| already_added_projects_names.include? repo["path_with_namespace"] }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :provider_name
+ def provider_name
+ :gitlab
+ end
+
+ override :provider_url
+ def provider_url
+ 'https://gitlab.com'
+ end
+
private
def client
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index fb4fbe57130..5905ec5cb76 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -69,11 +69,9 @@ class DiffsEntity < Grape::Entity
expose :diff_files do |diffs, options|
submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
- code_navigation_path =
- Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs.head_sha)
DiffFileEntity.represent(diffs.diff_files,
- options.merge(submodule_links: submodule_links, code_navigation_path: code_navigation_path))
+ options.merge(submodule_links: submodule_links, code_navigation_path: code_navigation_path(diffs)))
end
expose :merge_request_diffs, using: MergeRequestDiffEntity, if: -> (_, options) { options[:merge_request_diffs]&.any? } do |diffs|
@@ -81,7 +79,7 @@ class DiffsEntity < Grape::Entity
end
expose :definition_path_prefix, if: -> (diff_file) { Feature.enabled?(:code_navigation, merge_request.project) } do |diffs|
- project_blob_path(merge_request.project, diffs.diff_refs.head_sha)
+ project_blob_path(merge_request.project, diffs.diff_refs&.head_sha)
end
def merge_request
@@ -90,6 +88,12 @@ class DiffsEntity < Grape::Entity
private
+ def code_navigation_path(diffs)
+ return unless Feature.enabled?(:code_navigation, merge_request.project)
+
+ Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs&.head_sha)
+ end
+
def commit_ids
@commit_ids ||= merge_request.recent_commits.map(&:id)
end
diff --git a/app/serializers/import/base_provider_repo_entity.rb b/app/serializers/import/base_provider_repo_entity.rb
new file mode 100644
index 00000000000..88a831a1686
--- /dev/null
+++ b/app/serializers/import/base_provider_repo_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class Import::BaseProviderRepoEntity < Grape::Entity
+ expose :id
+ expose :full_name
+ expose :sanitized_name
+ expose :provider_link
+end
diff --git a/app/serializers/import/bitbucket_provider_repo_entity.rb b/app/serializers/import/bitbucket_provider_repo_entity.rb
new file mode 100644
index 00000000000..e8c647d407e
--- /dev/null
+++ b/app/serializers/import/bitbucket_provider_repo_entity.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class Import::BitbucketProviderRepoEntity < Import::BaseProviderRepoEntity
+ expose :id, override: true do |repo|
+ repo.full_name
+ end
+
+ expose :sanitized_name, override: true do |repo|
+ repo.name.gsub(/[^\s\w.-]/, '')
+ end
+
+ expose :provider_link, override: true do |repo, options|
+ repo.clone_url
+ end
+end
diff --git a/app/serializers/import/bitbucket_server_provider_repo_entity.rb b/app/serializers/import/bitbucket_server_provider_repo_entity.rb
new file mode 100644
index 00000000000..d818cac46cd
--- /dev/null
+++ b/app/serializers/import/bitbucket_server_provider_repo_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Import::BitbucketServerProviderRepoEntity < Import::BitbucketProviderRepoEntity
+ expose :provider_link, override: true do |repo, options|
+ repo.browse_url
+ end
+end
diff --git a/app/serializers/import/fogbugz_provider_repo_entity.rb b/app/serializers/import/fogbugz_provider_repo_entity.rb
new file mode 100644
index 00000000000..d420de141e1
--- /dev/null
+++ b/app/serializers/import/fogbugz_provider_repo_entity.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class Import::FogbugzProviderRepoEntity < Import::BaseProviderRepoEntity
+ include ImportHelper
+
+ expose :full_name, override: true do |repo|
+ repo.name
+ end
+
+ expose :sanitized_name, override: true do |repo|
+ repo.safe_name
+ end
+
+ expose :provider_link, override: true do |repo, options|
+ provider_project_link_url(options[:provider_url], repo.path)
+ end
+end
diff --git a/app/serializers/provider_repo_entity.rb b/app/serializers/import/githubish_provider_repo_entity.rb
index d70aaa91324..d3e323053f9 100644
--- a/app/serializers/provider_repo_entity.rb
+++ b/app/serializers/import/githubish_provider_repo_entity.rb
@@ -1,19 +1,13 @@
# frozen_string_literal: true
-class ProviderRepoEntity < Grape::Entity
+class Import::GithubishProviderRepoEntity < Import::BaseProviderRepoEntity
include ImportHelper
- expose :id
- expose :full_name
- expose :owner_name do |provider_repo, options|
- owner_name(provider_repo, options[:provider])
- end
-
- expose :sanitized_name do |provider_repo|
+ expose :sanitized_name, override: true do |provider_repo|
sanitize_project_name(provider_repo[:name])
end
- expose :provider_link do |provider_repo, options|
+ expose :provider_link, override: true do |provider_repo, options|
provider_project_link_url(options[:provider_url], provider_repo[:full_name])
end
diff --git a/app/serializers/import/gitlab_provider_repo_entity.rb b/app/serializers/import/gitlab_provider_repo_entity.rb
new file mode 100644
index 00000000000..5fecd0a1cd3
--- /dev/null
+++ b/app/serializers/import/gitlab_provider_repo_entity.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class Import::GitlabProviderRepoEntity < Import::BaseProviderRepoEntity
+ expose :id, override: true do |repo|
+ repo["id"]
+ end
+
+ expose :full_name, override: true do |repo|
+ repo["path_with_namespace"]
+ end
+
+ expose :sanitized_name, override: true do |repo|
+ repo["path"]
+ end
+
+ expose :provider_link, override: true do |repo|
+ repo["web_url"]
+ end
+end
diff --git a/app/serializers/import/provider_repo_serializer.rb b/app/serializers/import/provider_repo_serializer.rb
new file mode 100644
index 00000000000..5a9549d79aa
--- /dev/null
+++ b/app/serializers/import/provider_repo_serializer.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class Import::ProviderRepoSerializer < BaseSerializer
+ def represent(repo, opts = {})
+ entity =
+ case opts[:provider]
+ when :fogbugz
+ Import::FogbugzProviderRepoEntity
+ when :github, :gitea
+ Import::GithubishProviderRepoEntity
+ when :bitbucket
+ Import::BitbucketProviderRepoEntity
+ when :bitbucket_server
+ Import::BitbucketServerProviderRepoEntity
+ when :gitlab
+ Import::GitlabProviderRepoEntity
+ else
+ raise NotImplementedError
+ end
+
+ super(repo, opts, entity)
+ end
+end
diff --git a/app/serializers/paginated_diff_entity.rb b/app/serializers/paginated_diff_entity.rb
index a31c9d70d4b..0ab077d9377 100644
--- a/app/serializers/paginated_diff_entity.rb
+++ b/app/serializers/paginated_diff_entity.rb
@@ -10,11 +10,9 @@ class PaginatedDiffEntity < Grape::Entity
expose :diff_files do |diffs, options|
submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
- code_navigation_path =
- Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs.head_sha)
DiffFileEntity.represent(diffs.diff_files,
- options.merge(submodule_links: submodule_links, code_navigation_path: code_navigation_path))
+ options.merge(submodule_links: submodule_links, code_navigation_path: code_navigation_path(diffs)))
end
expose :pagination do
@@ -38,6 +36,12 @@ class PaginatedDiffEntity < Grape::Entity
private
+ def code_navigation_path(diffs)
+ return unless Feature.enabled?(:code_navigation, merge_request.project)
+
+ Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs&.head_sha)
+ end
+
%i[current_page next_page total_pages].each do |method|
define_method method do
pagination_data[method]
diff --git a/app/serializers/provider_repo_serializer.rb b/app/serializers/provider_repo_serializer.rb
deleted file mode 100644
index 8a73f6fe6df..00000000000
--- a/app/serializers/provider_repo_serializer.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-class ProviderRepoSerializer < BaseSerializer
- entity ProviderRepoEntity
-end
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index b05c039c85c..9bf1f0c61bb 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -1,4 +1,6 @@
- provider = local_assigns.fetch(:provider)
+- extra_data = local_assigns.fetch(:extra_data, {})
+- filterable = local_assigns.fetch(:filterable, true)
- provider_title = Gitlab::ImportSources.title(provider)
#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
@@ -6,4 +8,5 @@
ci_cd_only: has_ci_cd_only_params?.to_s,
repos_path: url_for([:status, :import, provider, format: :json]),
jobs_path: url_for([:realtime_changes, :import, provider, format: :json]),
- import_path: url_for([:import, provider, format: :json]) } }
+ import_path: url_for([:import, provider, format: :json]),
+ filterable: filterable.to_s }.merge(extra_data) }
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 7399ff937ce..d405acef75c 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -5,90 +5,93 @@
%i.fa.fa-bitbucket
= _('Import projects from Bitbucket')
-- if @repos.any?
- %p.light
- = _('Select projects you want to import.')
- %p
- - if @incompatible_repos.any?
- = button_tag class: 'btn btn-import btn-success js-import-all' do
- = _('Import all compatible projects')
- = icon('spinner spin', class: 'loading-icon')
- - else
- = button_tag class: 'btn btn-import btn-success js-import-all' do
- = _('Import all projects')
- = icon('spinner spin', class: 'loading-icon')
+- if Feature.enabled?(:new_import_ui)
+ = render 'import/githubish_status', provider: 'bitbucket'
+- else
+ - if @repos.any?
+ %p.light
+ = _('Select projects you want to import.')
+ %p
+ - if @incompatible_repos.any?
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all compatible projects')
+ = icon('spinner spin', class: 'loading-icon')
+ - else
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all projects')
+ = icon('spinner spin', class: 'loading-icon')
-.position-relative.ms-no-clear.d-flex.flex-fill.float-right.append-bottom-10
- = form_tag status_import_bitbucket_path, method: 'get' do
- = text_field_tag :filter, @filter, class: 'form-control pr-5', placeholder: _('Filter projects'), size: 40, autofocus: true, 'aria-label': _('Search')
- .position-absolute.position-top-0.d-flex.align-items-center.text-muted.position-right-0.h-100
- .border-left
- %button{ class: 'btn btn-transparent btn-secondary', 'aria-label': _('Search Button'), type: 'submit' }
- %i{ class: 'fa fa-search', 'aria-hidden': true }
+ .position-relative.ms-no-clear.d-flex.flex-fill.float-right.append-bottom-10
+ = form_tag status_import_bitbucket_path, method: 'get' do
+ = text_field_tag :filter, @filter, class: 'form-control pr-5', placeholder: _('Filter projects'), size: 40, autofocus: true, 'aria-label': _('Search')
+ .position-absolute.position-top-0.d-flex.align-items-center.text-muted.position-right-0.h-100
+ .border-left
+ %button{ class: 'btn btn-transparent btn-secondary', 'aria-label': _('Search Button'), type: 'submit' }
+ %i{ class: 'fa fa-search', 'aria-hidden': true }
-.table-responsive
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _('From Bitbucket')
- %th= _('To GitLab')
- %th= _('Status')
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
- %td
- = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
- %td
- = link_to project.full_path, [project.namespace.becomes(Namespace), project]
- %td.job-status
- - case project.import_status
- - when 'finished'
- %span
- %i.fa.fa-check
- = _('done')
- - when 'started'
- %i.fa.fa-spinner.fa-spin
- = _('started')
- - else
- = project.human_import_status_name
+ .table-responsive
+ %table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
+ %thead
+ %tr
+ %th= _('From Bitbucket')
+ %th= _('To GitLab')
+ %th= _('Status')
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %td
+ = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
+ %td
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - case project.import_status
+ - when 'finished'
+ %span
+ %i.fa.fa-check
+ = _('done')
+ - when 'started'
+ %i.fa.fa-spinner.fa-spin
+ = _('started')
+ - else
+ = project.human_import_status_name
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
- %td
- = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
- %td.import-target
- %fieldset.row
- .input-group
- .project-path.input-group-prepend
- - if current_user.can_select_namespace?
- - selected = params[:namespace_id] || :current_user
- - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner, path: repo.owner) } : {}
- = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
- - else
- = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
- %span.input-group-prepend
- .input-group-text /
- = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
- %td.import-actions.job-status
- = button_tag class: 'btn btn-import js-add-to-import' do
- = _('Import')
- = icon('spinner spin', class: 'loading-icon')
- - @incompatible_repos.each do |repo|
- %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
- %td
- = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
- %td.import-target
- %td.import-actions-job-status
- = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
+ - @repos.each do |repo|
+ %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
+ %td
+ = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
+ %td.import-target
+ %fieldset.row
+ .input-group
+ .project-path.input-group-prepend
+ - if current_user.can_select_namespace?
+ - selected = params[:namespace_id] || :current_user
+ - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner, path: repo.owner) } : {}
+ = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
+ - else
+ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
+ %span.input-group-prepend
+ .input-group-text /
+ = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ %td.import-actions.job-status
+ = button_tag class: 'btn btn-import js-add-to-import' do
+ = _('Import')
+ = icon('spinner spin', class: 'loading-icon')
+ - @incompatible_repos.each do |repo|
+ %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
+ %td
+ = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
+ %td.import-target
+ %td.import-actions-job-status
+ = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
-- if @incompatible_repos.any?
- %p
- = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
- - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
- - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
- = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
+ - if @incompatible_repos.any?
+ %p
+ = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
+ - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
+ = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
-.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
+ .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml
index 1aaf5883bf4..7523b8f7b1c 100644
--- a/app/views/import/bitbucket_server/status.html.haml
+++ b/app/views/import/bitbucket_server/status.html.haml
@@ -5,91 +5,94 @@
%i.fa.fa-bitbucket-square
= _('Import projects from Bitbucket Server')
-- if @repos.any?
- %p.light
- = _('Select projects you want to import.')
- .btn-group
- - if @incompatible_repos.any?
- = button_tag class: 'btn btn-import btn-success js-import-all' do
- = _('Import all compatible projects')
- = icon('spinner spin', class: 'loading-icon')
- - else
- = button_tag class: 'btn btn-import btn-success js-import-all' do
- = _('Import all projects')
- = icon('spinner spin', class: 'loading-icon')
+- if Feature.enabled?(:new_import_ui)
+ = render 'import/githubish_status', provider: 'bitbucket_server', extra_data: { reconfigure_path: configure_import_bitbucket_server_path }
+- else
+ - if @repos.any?
+ %p.light
+ = _('Select projects you want to import.')
+ .btn-group
+ - if @incompatible_repos.any?
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all compatible projects')
+ = icon('spinner spin', class: 'loading-icon')
+ - else
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all projects')
+ = icon('spinner spin', class: 'loading-icon')
-.btn-group
- = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
+ .btn-group
+ = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
-.input-btn-group.float-right
- = form_tag status_import_bitbucket_server_path, :method => 'get' do
- = text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true
+ .input-btn-group.float-right
+ = form_tag status_import_bitbucket_server_path, :method => 'get' do
+ = text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true
-.table-responsive.prepend-top-10
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _('From Bitbucket Server')
- %th= _('To GitLab')
- %th= _('Status')
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
- %td
- = link_to project.import_source, project.import_source, target: '_blank', rel: 'noopener noreferrer'
- %td
- = link_to project.full_path, [project.namespace.becomes(Namespace), project]
- %td.job-status
- - case project.import_status
- - when 'finished'
- = icon('check', text: 'Done')
- - when 'started'
- = icon('spin', text: 'started')
- - else
- = project.human_import_status_name
+ .table-responsive.prepend-top-10
+ %table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
+ %thead
+ %tr
+ %th= _('From Bitbucket Server')
+ %th= _('To GitLab')
+ %th= _('Status')
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %td
+ = link_to project.import_source, project.import_source, target: '_blank', rel: 'noopener noreferrer'
+ %td
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - case project.import_status
+ - when 'finished'
+ = icon('check', text: 'Done')
+ - when 'started'
+ = icon('spin', text: 'started')
+ - else
+ = project.human_import_status_name
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo.project_key}___#{repo.slug}", data: { project: repo.project_key, repository: repo.slug } }
- %td
- = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
- %td.import-target
- %fieldset.row
- .input-group
- .project-path.input-group-prepend
- - if current_user.can_select_namespace?
- - selected = params[:namespace_id] || :extra_group
- - opts = current_user.can_create_group? ? { extra_group: Group.new(name: sanitize_project_name(repo.project_key), path: sanitize_project_name(repo.project_key)) } : {}
- = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
- - else
- = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
- %span.input-group-prepend
- .input-group-text /
- = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true
- %td.import-actions.job-status
- = button_tag class: 'btn btn-import js-add-to-import' do
- Import
- = icon('spinner spin', class: 'loading-icon')
- - @incompatible_repos.each do |repo|
- %tr{ id: "repo_#{repo.project_key}___#{repo.slug}" }
- %td
- = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
- %td.import-target
- %td.import-actions-job-status
- = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
+ - @repos.each do |repo|
+ %tr{ id: "repo_#{repo.project_key}___#{repo.slug}", data: { project: repo.project_key, repository: repo.slug } }
+ %td
+ = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
+ %td.import-target
+ %fieldset.row
+ .input-group
+ .project-path.input-group-prepend
+ - if current_user.can_select_namespace?
+ - selected = params[:namespace_id] || :extra_group
+ - opts = current_user.can_create_group? ? { extra_group: Group.new(name: sanitize_project_name(repo.project_key), path: sanitize_project_name(repo.project_key)) } : {}
+ = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
+ - else
+ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
+ %span.input-group-prepend
+ .input-group-text /
+ = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true
+ %td.import-actions.job-status
+ = button_tag class: 'btn btn-import js-add-to-import' do
+ Import
+ = icon('spinner spin', class: 'loading-icon')
+ - @incompatible_repos.each do |repo|
+ %tr{ id: "repo_#{repo.project_key}___#{repo.slug}" }
+ %td
+ = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
+ %td.import-target
+ %td.import-actions-job-status
+ = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
-- if @incompatible_repos.any?
- %p
- One or more of your Bitbucket Server projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert
- = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
- and go through the
- = link_to 'import flow', status_import_bitbucket_server_path
- again.
+ - if @incompatible_repos.any?
+ %p
+ One or more of your Bitbucket Server projects cannot be imported into GitLab
+ directly because they use Subversion or Mercurial for version control,
+ rather than Git. Please convert
+ = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
+ and go through the
+ = link_to 'import flow', status_import_bitbucket_server_path
+ again.
-= paginate_without_count(@collection)
+ = paginate_without_count(@collection)
-.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
+ .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index eca67582d6f..75529487aa4 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -4,56 +4,63 @@
%i.fa.fa-bug
= _('Import projects from FogBugz')
-- if @repos.any?
- %p.light
- = _('Select projects you want to import.')
+- if Feature.enabled?(:new_import_ui)
%p.light
- link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
= _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr
- %p
- = button_tag class: 'btn btn-import btn-success js-import-all' do
- = _('Import all projects')
- = icon("spinner spin", class: "loading-icon")
+ = render 'import/githubish_status', provider: 'fogbugz', filterable: false
+- else
+ - if @repos.any?
+ %p.light
+ = _('Select projects you want to import.')
+ %p.light
+ - link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
+ = _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
+ %hr
+ %p
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all projects')
+ = icon("spinner spin", class: "loading-icon")
-.table-responsive
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _("From FogBugz")
- %th= _("To GitLab")
- %th= _("Status")
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
- %td
- = project.import_source
- %td
- = link_to project.full_path, [project.namespace.becomes(Namespace), project]
- %td.job-status
- - case project.import_status
- - when 'finished'
- %span
- %i.fa.fa-check
- = _("done")
- - when 'started'
- %i.fa.fa-spinner.fa-spin
- = _("started")
- - else
- = project.human_import_status_name
+ .table-responsive
+ %table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
+ %thead
+ %tr
+ %th= _("From FogBugz")
+ %th= _("To GitLab")
+ %th= _("Status")
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %td
+ = project.import_source
+ %td
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - case project.import_status
+ - when 'finished'
+ %span
+ %i.fa.fa-check
+ = _("done")
+ - when 'started'
+ %i.fa.fa-spinner.fa-spin
+ = _("started")
+ - else
+ = project.human_import_status_name
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo.id}" }
- %td
- = repo.name
- %td.import-target
- #{current_user.username}/#{repo.name}
- %td.import-actions.job-status
- = button_tag class: "btn btn-import js-add-to-import" do
- = _("Import")
- = icon("spinner spin", class: "loading-icon")
+ - @repos.each do |repo|
+ %tr{ id: "repo_#{repo.id}" }
+ %td
+ = repo.name
+ %td.import-target
+ #{current_user.username}/#{repo.name}
+ %td.import-actions.job-status
+ = button_tag class: "btn btn-import js-add-to-import" do
+ = _("Import")
+ = icon("spinner spin", class: "loading-icon")
-.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
+ .js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index a5fa12fe7df..a12b69ae5f9 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -4,52 +4,55 @@
%i.fa.fa-heart
= _('Import projects from GitLab.com')
-%p.light
- = _('Select projects you want to import.')
-%hr
-%p
- = button_tag class: "btn btn-import btn-success js-import-all" do
- = _('Import all projects')
- = icon("spinner spin", class: "loading-icon")
+- if Feature.enabled?(:new_import_ui)
+ = render 'import/githubish_status', provider: 'gitlab', filterable: false
+- else
+ %p.light
+ = _('Select projects you want to import.')
+ %hr
+ %p
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ = _('Import all projects')
+ = icon("spinner spin", class: "loading-icon")
-.table-responsive
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _('From GitLab.com')
- %th= _('To this GitLab instance')
- %th= _('Status')
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
- %td
- = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
- %td
- = link_to project.full_path, [project.namespace.becomes(Namespace), project]
- %td.job-status
- - case project.import_status
- - when 'finished'
- %span
- %i.fa.fa-check
- = _('done')
- - when 'started'
- %i.fa.fa-spinner.fa-spin
- = _('started')
- - else
- = project.human_import_status_name
+ .table-responsive
+ %table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
+ %thead
+ %tr
+ %th= _('From GitLab.com')
+ %th= _('To this GitLab instance')
+ %th= _('Status')
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %td
+ = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
+ %td
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - case project.import_status
+ - when 'finished'
+ %span
+ %i.fa.fa-check
+ = _('done')
+ - when 'started'
+ %i.fa.fa-spinner.fa-spin
+ = _('started')
+ - else
+ = project.human_import_status_name
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo["id"]}" }
- %td
- = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank", rel: 'noopener noreferrer'
- %td.import-target
- = import_project_target(repo['namespace']['path'], repo['name'])
- %td.import-actions.job-status
- = button_tag class: "btn btn-import js-add-to-import" do
- = _('Import')
- = icon("spinner spin", class: "loading-icon")
+ - @repos.each do |repo|
+ %tr{ id: "repo_#{repo["id"]}" }
+ %td
+ = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank", rel: 'noopener noreferrer'
+ %td.import-target
+ = import_project_target(repo['namespace']['path'], repo['name'])
+ %td.import-actions.job-status
+ = button_tag class: "btn btn-import js-add-to-import" do
+ = _('Import')
+ = icon("spinner spin", class: "loading-icon")
-.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }
+ .js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }