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/import_entities/import_groups/components/import_table.vue17
-rw-r--r--app/assets/javascripts/import_entities/import_groups/index.js2
-rw-r--r--app/assets/javascripts/issuable/issuable_form.js3
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue5
-rw-r--r--app/assets/javascripts/repository/constants.js21
-rw-r--r--app/assets/javascripts/users_select/index.js18
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss3
-rw-r--r--app/assets/stylesheets/themes/_dark.scss2
-rw-r--r--app/models/wiki.rb86
-rw-r--r--app/views/import/bulk_imports/status.html.haml1
-rw-r--r--app/views/layouts/_head.html.haml47
-rw-r--r--app/views/notify/issue_due_email.html.haml2
-rw-r--r--app/views/registrations/welcome/show.html.haml2
13 files changed, 160 insertions, 49 deletions
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
index 028197ec9b1..ca04824c663 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
@@ -71,6 +71,10 @@ export default {
type: String,
required: true,
},
+ historyPath: {
+ type: String,
+ required: true,
+ },
},
data() {
@@ -485,12 +489,15 @@ export default {
<template>
<div>
- <h1
- class="gl-my-0 gl-py-4 gl-font-size-h1 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex"
+ <div
+ class="gl-display-flex gl-align-items-center gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1"
>
- <img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
- {{ s__('BulkImport|Import groups from GitLab') }}
- </h1>
+ <h1 class="gl-my-0 gl-py-4 gl-font-size-h1gl-display-flex">
+ <img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
+ {{ s__('BulkImport|Import groups from GitLab') }}
+ </h1>
+ <gl-link :href="historyPath" class="gl-ml-auto">{{ s__('BulkImport|History') }}</gl-link>
+ </div>
<gl-alert
v-if="unavailableFeatures.length > 0 && unavailableFeaturesAlertVisible"
variant="warning"
diff --git a/app/assets/javascripts/import_entities/import_groups/index.js b/app/assets/javascripts/import_entities/import_groups/index.js
index 67a7258d504..02af0db7f9a 100644
--- a/app/assets/javascripts/import_entities/import_groups/index.js
+++ b/app/assets/javascripts/import_entities/import_groups/index.js
@@ -17,6 +17,7 @@ export function mountImportGroupsApp(mountElement) {
jobsPath,
sourceUrl,
groupPathRegex,
+ historyPath,
} = mountElement.dataset;
const apolloProvider = new VueApollo({
defaultClient: createApolloClient({
@@ -38,6 +39,7 @@ export function mountImportGroupsApp(mountElement) {
sourceUrl,
jobsPath,
groupPathRegex: new RegExp(`^(${groupPathRegex})$`),
+ historyPath,
},
});
},
diff --git a/app/assets/javascripts/issuable/issuable_form.js b/app/assets/javascripts/issuable/issuable_form.js
index 018cadad50f..8e76a33c7dd 100644
--- a/app/assets/javascripts/issuable/issuable_form.js
+++ b/app/assets/javascripts/issuable/issuable_form.js
@@ -65,7 +65,8 @@ export default class IssuableForm {
this.gfmAutoComplete = new GfmAutoComplete(
gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
).setup();
- this.usersSelect = new UsersSelect();
+ const autoAssignToMe = form.get(0).id === 'new_merge_request';
+ this.usersSelect = new UsersSelect(undefined, undefined, { autoAssignToMe });
this.reviewersSelect = new UsersSelect(undefined, '.js-reviewer-search');
this.zenMode = new ZenMode();
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index c9e4aab1db1..ea64f06e1c1 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -17,7 +17,7 @@ import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
import userInfoQuery from '../queries/user_info.query.graphql';
import applicationInfoQuery from '../queries/application_info.query.graphql';
-import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE } from '../constants';
+import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE, LEGACY_FILE_TYPES } from '../constants';
import BlobButtonGroup from './blob_button_group.vue';
import ForkSuggestion from './fork_suggestion.vue';
import { loadViewer } from './blob_viewers';
@@ -132,7 +132,8 @@ export default {
return this.shouldLoadLegacyViewer ? null : loadViewer(fileType, this.isUsingLfs);
},
shouldLoadLegacyViewer() {
- return this.viewer.fileType === TEXT_FILE_TYPE && !this.glFeatures.highlightJs;
+ const isTextFile = this.viewer.fileType === TEXT_FILE_TYPE && !this.glFeatures.highlightJs;
+ return isTextFile || LEGACY_FILE_TYPES.includes(this.blobInfo.fileType);
},
legacyViewerLoaded() {
return (
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index bb9d3180be8..2cafeed2ef4 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -86,3 +86,24 @@ export const DEFAULT_BLOB_INFO = {
export const TEXT_FILE_TYPE = 'text';
export const LFS_STORAGE = 'lfs';
+
+/**
+ * We have some features (like linking to external dependencies) that our frontend highlighter
+ * do not yet support.
+ * These are file types that we want the legacy (backend) syntax highlighter to highlight.
+ */
+export const LEGACY_FILE_TYPES = [
+ 'package_json',
+ 'gemfile',
+ 'gemspec',
+ 'composer_json',
+ 'podfile',
+ 'podspec',
+ 'podspec_json',
+ 'cartfile',
+ 'godeps_json',
+ 'requirements_txt',
+ 'cargo_toml',
+ 'go_mod',
+ 'go_sum',
+];
diff --git a/app/assets/javascripts/users_select/index.js b/app/assets/javascripts/users_select/index.js
index f7a5589af90..e1e5cc565c6 100644
--- a/app/assets/javascripts/users_select/index.js
+++ b/app/assets/javascripts/users_select/index.js
@@ -35,7 +35,7 @@ function UsersSelect(currentUser, els, options = {}) {
}
}
- const { handleClick } = options;
+ const { handleClick, autoAssignToMe } = options;
const userSelect = this;
$els.each((i, dropdown) => {
@@ -172,10 +172,7 @@ function UsersSelect(currentUser, els, options = {}) {
});
};
- $assignToMeLink.on('click', (e) => {
- e.preventDefault();
- $(e.currentTarget).hide();
-
+ const onAssignToMeClick = () => {
if ($dropdown.data('multiSelect')) {
assignYourself();
checkMaxSelect();
@@ -194,8 +191,19 @@ function UsersSelect(currentUser, els, options = {}) {
.text(gon.current_user_fullname)
.removeClass('is-default');
}
+ };
+
+ $assignToMeLink.on('click', (e) => {
+ e.preventDefault();
+ $(e.currentTarget).hide();
+ onAssignToMeClick();
});
+ if (autoAssignToMe) {
+ $assignToMeLink.hide();
+ onAssignToMeClick();
+ }
+
$block.on('click', '.js-assign-yourself', (e) => {
e.preventDefault();
return assignTo(userSelect.currentUser.id);
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 8e5ff794f28..d8d0eda9478 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -12,6 +12,7 @@ body.gl-dark {
--gl-text-color: #fafafa;
--border-color: #4f4f4f;
--black: #fff;
+ --nav-active-bg: rgba(255, 255, 255, 0.08);
}
:root {
--white: #333;
@@ -1787,6 +1788,7 @@ body.gl-dark {
--white: #333;
--black: #fff;
--svg-status-bg: #333;
+ --nav-active-bg: rgba(255, 255, 255, 0.08);
}
.nav-sidebar li a {
color: var(--gray-600);
@@ -2023,6 +2025,7 @@ body.gl-dark {
--white: #333;
--black: #fff;
--svg-status-bg: #333;
+ --nav-active-bg: rgba(255, 255, 255, 0.08);
}
.tab-width-8 {
-moz-tab-size: 8;
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index 550e3981401..6a9e96c3ac5 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -99,6 +99,7 @@ $white-normal: #333;
$white-dark: #444;
$border-color: #4f4f4f;
+$nav-active-bg: rgba(255, 255, 255, 0.08);
body.gl-dark {
--gray-10: #{$gray-10};
@@ -199,6 +200,7 @@ body.gl-dark {
--black: #{$black};
--svg-status-bg: #{$white};
+ --nav-active-bg: #{$nav-active-bg};
.gl-button.gl-button,
.gl-button.gl-button.btn-block {
diff --git a/app/models/wiki.rb b/app/models/wiki.rb
index b3f09b20463..b29c43bcfe1 100644
--- a/app/models/wiki.rb
+++ b/app/models/wiki.rb
@@ -13,43 +13,65 @@ class Wiki
markdown: {
name: 'Markdown',
default_extension: :md,
+ extension_regex: Regexp.new('md|mkdn?|mdown|markdown', 'i'),
created_by_user: true
},
rdoc: {
name: 'RDoc',
default_extension: :rdoc,
+ extension_regex: Regexp.new('rdoc', 'i'),
created_by_user: true
},
asciidoc: {
name: 'AsciiDoc',
default_extension: :asciidoc,
+ extension_regex: Regexp.new('adoc|asciidoc', 'i'),
created_by_user: true
},
org: {
name: 'Org',
default_extension: :org,
+ extension_regex: Regexp.new('org', 'i'),
created_by_user: true
},
textile: {
name: 'Textile',
- default_extension: :textile
+ default_extension: :textile,
+ extension_regex: Regexp.new('textile', 'i')
},
creole: {
name: 'Creole',
- default_extension: :creole
+ default_extension: :creole,
+ extension_regex: Regexp.new('creole', 'i')
},
rest: {
name: 'reStructuredText',
- default_extension: :rst
+ default_extension: :rst,
+ extension_regex: Regexp.new('re?st(\.txt)?', 'i')
},
mediawiki: {
name: 'MediaWiki',
- default_extension: :mediawiki
+ default_extension: :mediawiki,
+ extension_regex: Regexp.new('(media)?wiki', 'i')
+ },
+ pod: {
+ name: 'Pod',
+ default_extension: :pod,
+ extension_regex: Regexp.new('pod', 'i')
+ },
+ plaintext: {
+ name: 'Plain Text',
+ default_extension: :txt,
+ extension_regex: Regexp.new('txt', 'i')
}
}.freeze unless defined?(MARKUPS)
VALID_USER_MARKUPS = MARKUPS.select { |_, v| v[:created_by_user] }.freeze unless defined?(VALID_USER_MARKUPS)
+ unless defined?(ALLOWED_EXTENSIONS_REGEX)
+ ALLOWED_EXTENSIONS_REGEX = Regexp.union(MARKUPS.map { |key, value| value[:extension_regex] }).freeze
+ end
+
CouldNotCreateWikiError = Class.new(StandardError)
HOMEPAGE = 'home'
@@ -205,15 +227,36 @@ class Wiki
end
def create_page(title, content, format = :markdown, message = nil)
- commit = commit_details(:created, message, title)
+ if Feature.enabled?(:gitaly_replace_wiki_create_page, container, default_enabled: :yaml)
+ with_valid_format(format) do |default_extension|
+ if file_exists_by_regex?(title)
+ raise_duplicate_page_error!
+ end
+
+ capture_git_error(:created) do
+ create_wiki_repository unless repository_exists?
+ sanitized_path = sluggified_full_path(title, default_extension)
+ repository.create_file(user, sanitized_path, content, **multi_commit_options(:created, message, title))
+ repository.expire_status_cache if repository.empty?
+ after_wiki_activity
- wiki.write_page(title, format.to_sym, content, commit)
- repository.expire_status_cache if repository.empty?
- after_wiki_activity
+ true
+ rescue Gitlab::Git::Index::IndexError
+ raise_duplicate_page_error!
+ end
+ end
+ else
+ commit = commit_details(:created, message, title)
+
+ wiki.write_page(title, format.to_sym, content, commit)
+ repository.expire_status_cache if repository.empty?
+ after_wiki_activity
- true
+ true
+ end
rescue Gitlab::Git::Wiki::DuplicatePageError => e
- @error_message = "Duplicate page: #{e.message}"
+ @error_message = _("Duplicate page: %{error_message}" % { error_message: e.message })
+
false
end
@@ -393,12 +436,33 @@ class Wiki
yield default_extension
end
+ def file_exists_by_regex?(title)
+ return false unless repository_exists?
+
+ escaped_title = Regexp.escape(sluggified_title(title))
+ regex = Regexp.new("^#{escaped_title}\.#{ALLOWED_EXTENSIONS_REGEX}$", 'i')
+
+ repository.ls_files('HEAD').any? { |s| s =~ regex }
+ end
+
+ def raise_duplicate_page_error!
+ raise Gitlab::Git::Wiki::DuplicatePageError, _('A page with that title already exists')
+ end
+
def sluggified_full_path(title, extension)
sluggified_title(title) + '.' + extension
end
def sluggified_title(title)
- Gitlab::EncodingHelper.encode_utf8_no_detect(title).tr(' ', '-')
+ utf8_encoded_title = Gitlab::EncodingHelper.encode_utf8_no_detect(title)
+
+ sanitized_title(utf8_encoded_title).tr(' ', '-')
+ end
+
+ def sanitized_title(title)
+ clean_absolute_path = File.expand_path(title, '/')
+
+ Pathname.new(clean_absolute_path).relative_path_from('/').to_s
end
end
diff --git a/app/views/import/bulk_imports/status.html.haml b/app/views/import/bulk_imports/status.html.haml
index 2aae8811678..71866bab30b 100644
--- a/app/views/import/bulk_imports/status.html.haml
+++ b/app/views/import/bulk_imports/status.html.haml
@@ -8,4 +8,5 @@
jobs_path: realtime_changes_import_bulk_imports_path(format: :json),
source_url: @source_url,
group_path_regex: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
+ history_path: history_import_bulk_imports_path,
group_url_error_message: group_url_error_message } }
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 15cd9bece71..48b9015ccc0 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -3,32 +3,14 @@
%head{ prefix: "og: http://ogp.me/ns#" }
%meta{ charset: "utf-8" }
+ %title= page_title(site_name)
+
= render 'layouts/loading_hints'
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
= render 'layouts/startup_js'
- -# Open Graph - http://ogp.me/
- %meta{ property: 'og:type', content: "object" }
- %meta{ property: 'og:site_name', content: site_name }
- %meta{ property: 'og:title', content: page_title }
- %meta{ property: 'og:description', content: page_description }
- %meta{ property: 'og:image', content: page_image }
- %meta{ property: 'og:image:width', content: '64' }
- %meta{ property: 'og:image:height', content: '64' }
- %meta{ property: 'og:url', content: request.base_url + request.fullpath }
-
- -# Twitter Card - https://dev.twitter.com/cards/types/summary
- %meta{ property: 'twitter:card', content: "summary" }
- %meta{ property: 'twitter:title', content: page_title }
- %meta{ property: 'twitter:description', content: page_description }
- %meta{ property: 'twitter:image', content: page_image }
- = page_card_meta_tags
-
- %title= page_title(site_name)
- %meta{ name: "description", content: page_description }
-
- if page_canonical_link
%link{ rel: 'canonical', href: page_canonical_link }
@@ -67,13 +49,32 @@
= yield :project_javascripts
- = csrf_meta_tags
- = csp_meta_tag
- = action_cable_meta_tag
+ -# Open Graph - http://ogp.me/
+ %meta{ property: 'og:type', content: "object" }
+ %meta{ property: 'og:site_name', content: site_name }
+ %meta{ property: 'og:title', content: page_title }
+ %meta{ property: 'og:description', content: page_description }
+ %meta{ property: 'og:image', content: page_image }
+ %meta{ property: 'og:image:width', content: '64' }
+ %meta{ property: 'og:image:height', content: '64' }
+ %meta{ property: 'og:url', content: request.base_url + request.fullpath }
+
+ -# Twitter Card - https://dev.twitter.com/cards/types/summary
+ %meta{ property: 'twitter:card', content: "summary" }
+ %meta{ property: 'twitter:title', content: page_title }
+ %meta{ property: 'twitter:description', content: page_description }
+ %meta{ property: 'twitter:image', content: page_image }
+ = page_card_meta_tags
+
+ %meta{ name: "description", content: page_description }
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
%meta{ name: 'theme-color', content: user_theme_primary_color }
+ = csrf_meta_tags
+ = csp_meta_tag
+ = action_cable_meta_tag
+
-# Apple Safari/iOS home screen icons
= favicon_link_tag 'touch-icon-iphone.png', rel: 'apple-touch-icon'
= favicon_link_tag 'touch-icon-ipad.png', rel: 'apple-touch-icon', sizes: '76x76'
diff --git a/app/views/notify/issue_due_email.html.haml b/app/views/notify/issue_due_email.html.haml
index 3208d061928..9dd501022dd 100644
--- a/app/views/notify/issue_due_email.html.haml
+++ b/app/views/notify/issue_due_email.html.haml
@@ -1,5 +1,5 @@
%p.details
- = sprintf(s_("Notify|%{author_link}'s issue %{issue_reference_link} is due soon."), { author_link: link_to(@issue.author_name, user_url(@issue.author)), issue_reference_link: issue_reference_link(@issue) })
+ = sprintf(s_("Notify|%{author_link}'s issue %{issue_reference_link} is due soon."), { author_link: link_to(@issue.author_name, user_url(@issue.author)), issue_reference_link: issue_reference_link(@issue) }).html_safe
- if @issue.assignees.any?
%p
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index 6235cce5d80..1f6976d4f38 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -27,7 +27,7 @@
- if Feature.enabled?(:user_other_role_details)
.row
.form-group.col-sm-12.js-other-role-group.hidden
- = f.label :other_role, _('What is your job title? (optional)'), class: 'form-check-label gl-mb-3'
+ = f.label :other_role, _('What is your job title? (optional)')
= f.text_field :other_role, class: 'form-control'
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
= render_if_exists "registrations/welcome/setup_for_company", f: f