...@@ -236,6 +236,7 @@ export default { ...@@ -236,6 +236,7 @@ export default {
</gl-dropdown> </gl-dropdown>
<div class="filtered-search-input-container flex-fill"> <div class="filtered-search-input-container flex-fill">
<gl-form-input <gl-form-input
v-model="errorSearchQuery"
class="pl-2 filtered-search" class="pl-2 filtered-search"
:disabled="loading" :disabled="loading"
:placeholder="__('Search or filter results…')" :placeholder="__('Search or filter results…')"
... ...
......
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import { escapeFileUrl } from '~/lib/utils/url_utility';
import { decorateData, sortTree } from '../stores/utils'; import { decorateData, sortTree } from '../stores/utils';
export const splitParent = path => { export const splitParent = path => {
...@@ -48,7 +47,7 @@ export const decorateFiles = ({ ...@@ -48,7 +47,7 @@ export const decorateFiles = ({
id: path, id: path,
name, name,
path, path,
url: `/${projectId}/tree/${branchId}/-/${escapeFileUrl(path)}/`, url: `/${projectId}/tree/${branchId}/-/${path}/`,
type: 'tree', type: 'tree',
parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`, parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
tempFile, tempFile,
...@@ -85,7 +84,7 @@ export const decorateFiles = ({ ...@@ -85,7 +84,7 @@ export const decorateFiles = ({
id: path, id: path,
name, name,
path, path,
url: `/${projectId}/blob/${branchId}/-/${escapeFileUrl(path)}`, url: `/${projectId}/blob/${branchId}/-/${path}`,
type: 'blob', type: 'blob',
parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`, parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
tempFile, tempFile,
... ...
......
import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants'; import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants';
import { escapeFileUrl } from '~/lib/utils/url_utility';
export const dataStructure = () => ({ export const dataStructure = () => ({
id: '', id: '',
...@@ -220,9 +219,7 @@ export const mergeTrees = (fromTree, toTree) => { ...@@ -220,9 +219,7 @@ export const mergeTrees = (fromTree, toTree) => {
export const replaceFileUrl = (url, oldPath, newPath) => { export const replaceFileUrl = (url, oldPath, newPath) => {
// Add `/-/` so that we don't accidentally replace project path // Add `/-/` so that we don't accidentally replace project path
const result = url.replace(`/-/${escapeFileUrl(oldPath)}`, `/-/${escapeFileUrl(newPath)}`); return url.replace(`/-/${oldPath}`, `/-/${newPath}`);
return result;
}; };
export const swapInStateArray = (state, arr, key, entryPath) => export const swapInStateArray = (state, arr, key, entryPath) =>
... ...
......
...@@ -26,6 +26,10 @@ export default (resolvers = {}, config = {}) => { ...@@ -26,6 +26,10 @@ export default (resolvers = {}, config = {}) => {
headers: { headers: {
[csrf.headerKey]: csrf.token, [csrf.headerKey]: csrf.token,
}, },
// fetch won’t send cookies in older browsers, unless you set the credentials init option.
// We set to `same-origin` which is default value in modern browsers.
// See https://github.com/whatwg/fetch/pull/585 for more information.
credentials: 'same-origin',
}; };
return new ApolloClient({ return new ApolloClient({
... ...
......
<script> <script>
import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from '../../locale'; import { __ } from '../../locale';
import Icon from '../../vue_shared/components/icon.vue'; import Icon from '../../vue_shared/components/icon.vue';
import getRefMixin from '../mixins/get_ref'; import getRefMixin from '../mixins/get_ref';
...@@ -102,12 +103,12 @@ export default { ...@@ -102,12 +103,12 @@ export default {
.filter(p => p !== '') .filter(p => p !== '')
.reduce( .reduce(
(acc, name, i) => { (acc, name, i) => {
const path = `${i > 0 ? acc[i].path : ''}/${name}`; const path = joinPaths(i > 0 ? acc[i].path : '', encodeURIComponent(name));
return acc.concat({ return acc.concat({
name, name,
path, path,
to: `/-/tree/${escape(this.ref)}${escape(path)}`, to: `/-/tree/${joinPaths(escape(this.ref), path)}`,
}); });
}, },
[ [
... ...
......
...@@ -79,7 +79,7 @@ export default { ...@@ -79,7 +79,7 @@ export default {
return this.$apollo.queries.commit.loading; return this.$apollo.queries.commit.loading;
}, },
showCommitId() { showCommitId() {
return this.commit.sha.substr(0, 8); return this.commit?.sha?.substr(0, 8);
}, },
}, },
watch: { watch: {
... ...
......
...@@ -91,7 +91,9 @@ export default { ...@@ -91,7 +91,9 @@ export default {
}, },
computed: { computed: {
routerLinkTo() { routerLinkTo() {
return this.isFolder ? { path: `/-/tree/${escape(this.ref)}/${escape(this.path)}` } : null; return this.isFolder
? { path: `/-/tree/${escape(this.ref)}/${encodeURIComponent(this.path)}` }
: null;
}, },
iconName() { iconName() {
return `fa-${getIconName(this.type, this.path)}`; return `fa-${getIconName(this.type, this.path)}`;
...@@ -141,6 +143,7 @@ export default { ...@@ -141,6 +143,7 @@ export default {
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i> <i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component <component
:is="linkComponent" :is="linkComponent"
ref="link"
:to="routerLinkTo" :to="routerLinkTo"
:href="url" :href="url"
class="str-truncated" class="str-truncated"
... ...
......
...@@ -27,7 +27,7 @@ export function fetchLogsTree(client, path, offset, resolver = null) { ...@@ -27,7 +27,7 @@ export function fetchLogsTree(client, path, offset, resolver = null) {
fetchpromise = axios fetchpromise = axios
.get( .get(
`${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${escape( `${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${encodeURIComponent(
path.replace(/^\//, ''), path.replace(/^\//, ''),
)}`, )}`,
{ {
... ...
......
<script> <script>
import FileHeader from '~/vue_shared/components/file_row_header.vue'; import FileHeader from '~/vue_shared/components/file_row_header.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue';
import { escapeFileUrl } from '~/lib/utils/url_utility';
export default { export default {
name: 'FileRow', name: 'FileRow',
...@@ -94,7 +95,7 @@ export default { ...@@ -94,7 +95,7 @@ export default {
hasUrlAtCurrentRoute() { hasUrlAtCurrentRoute() {
if (!this.$router || !this.$router.currentRoute) return true; if (!this.$router || !this.$router.currentRoute) return true;
return this.$router.currentRoute.path === `/project${this.file.url}`; return this.$router.currentRoute.path === `/project${escapeFileUrl(this.file.url)}`;
}, },
}, },
}; };
... ...
......
...@@ -1006,6 +1006,14 @@ pre.light-well { ...@@ -1006,6 +1006,14 @@ pre.light-well {
} }
} }
&:not(.with-pipeline-status) {
.icon-wrapper:first-of-type {
@include media-breakpoint-up(lg) {
margin-left: $gl-padding-32;
}
}
}
.ci-status-link { .ci-status-link {
display: inline-flex; display: inline-flex;
} }
... ...
......
...@@ -17,12 +17,6 @@ ...@@ -17,12 +17,6 @@
.tree-controls { .tree-controls {
text-align: right; text-align: right;
> .btn,
.project-action-button > .btn,
.git-clone-holder > .btn {
margin-left: 8px;
}
.control { .control {
float: left; float: left;
margin-left: 10px; margin-left: 10px;
... ...
......
...@@ -45,6 +45,12 @@ ...@@ -45,6 +45,12 @@
.border-bottom-color-default { border-bottom-color: $border-color; } .border-bottom-color-default { border-bottom-color: $border-color; }
.box-shadow-default { box-shadow: 0 2px 4px 0 $black-transparent; } .box-shadow-default { box-shadow: 0 2px 4px 0 $black-transparent; }
.gl-children-ml-sm-3 > * {
@include media-breakpoint-up(sm) {
@include gl-ml-3;
}
}
.mh-50vh { max-height: 50vh; } .mh-50vh { max-height: 50vh; }
.font-size-inherit { font-size: inherit; } .font-size-inherit { font-size: inherit; }
... ...
......
...@@ -52,8 +52,10 @@ class BroadcastMessage < ApplicationRecord ...@@ -52,8 +52,10 @@ class BroadcastMessage < ApplicationRecord
end end
def cache def cache
::Gitlab::SafeRequestStore.fetch(:broadcast_message_json_cache) do
Gitlab::JsonCache.new(cache_key_with_version: false) Gitlab::JsonCache.new(cache_key_with_version: false)
end end
end
def cache_expires_in def cache_expires_in
2.weeks 2.weeks
...@@ -68,9 +70,9 @@ class BroadcastMessage < ApplicationRecord ...@@ -68,9 +70,9 @@ class BroadcastMessage < ApplicationRecord
now_or_future = messages.select(&:now_or_future?) now_or_future = messages.select(&:now_or_future?)
# If there are cached entries but none are to be displayed we'll purge the # If there are cached entries but they don't match the ones we are
# cache so we don't keep running this code all the time. # displaying we'll refresh the cache so we don't need to keep filtering.
cache.expire(cache_key) if now_or_future.empty? cache.expire(cache_key) if now_or_future != messages
now_or_future.select(&:now?).select { |message| message.matches_current_path(current_path) } now_or_future.select(&:now?).select { |message| message.matches_current_path(current_path) }
end end
... ...
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
class ImportExportUploader < AttachmentUploader class ImportExportUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[tar.gz gz].freeze EXTENSION_WHITELIST = %w[tar.gz gz].freeze
def self.workhorse_local_upload_path
File.join(options.storage_path, 'uploads', TMP_UPLOAD_PATH)
end
def extension_whitelist def extension_whitelist
EXTENSION_WHITELIST EXTENSION_WHITELIST
end end
... ...
......
= render 'shared/projects/list', projects: @projects, user: current_user = render 'shared/projects/list', projects: @projects, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true), user: current_user
- is_explore_page = defined?(explore_page) && explore_page - is_explore_page = defined?(explore_page) && explore_page
= render 'shared/projects/list', projects: projects, user: current_user, explore_page: is_explore_page = render 'shared/projects/list', projects: projects, user: current_user, explore_page: is_explore_page, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true)
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
- else - else
= link_to title, project_tree_path(@project, tree_join(@ref, path)) = link_to title, project_tree_path(@project, tree_join(@ref, path))
.tree-controls< .tree-controls.gl-children-ml-sm-3<
= render 'projects/find_file_link' = render 'projects/find_file_link'
-# only show normal/blame view links for text files -# only show normal/blame view links for text files
- if blob.readable_text? - if blob.readable_text?
... ...
......
...@@ -75,7 +75,8 @@ ...@@ -75,7 +75,8 @@
= link_to new_project_tag_path(@project) do = link_to new_project_tag_path(@project) do
#{ _('New tag') } #{ _('New tag') }
.tree-controls{ class: ("gl-font-size-0" if vue_file_list_enabled?) }< .tree-controls
.d-block.d-sm-flex.flex-wrap.align-items-start.gl-children-ml-sm-3<
= render_if_exists 'projects/tree/lock_link' = render_if_exists 'projects/tree/lock_link'
- if vue_file_list_enabled? - if vue_file_list_enabled?
#js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } } #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } }
...@@ -101,8 +102,8 @@ ...@@ -101,8 +102,8 @@
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref
.project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0>
= render "shared/mobile_clone_panel"
.project-clone-holder.d-none.d-md-inline-block> .project-clone-holder.d-none.d-md-inline-block>
= render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right'
.project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-sm-2>
= render "shared/mobile_clone_panel"
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- cache_key = project_list_cache_key(project, pipeline_status: pipeline_status) - cache_key = project_list_cache_key(project, pipeline_status: pipeline_status)
- updated_tooltip = time_ago_with_tooltip(project.last_activity_date) - updated_tooltip = time_ago_with_tooltip(project.last_activity_date)
- show_pipeline_status_icon = pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project)
- css_controls_class = compact_mode ? [] : ["flex-lg-row", "justify-content-lg-between"] - css_controls_class = compact_mode ? [] : ["flex-lg-row", "justify-content-lg-between"]
- css_controls_class << "with-pipeline-status" if show_pipeline_status_icon
- avatar_container_class = project.creator && use_creator_avatar ? '' : 'rect-avatar' - avatar_container_class = project.creator && use_creator_avatar ? '' : 'rect-avatar'
%li.project-row.d-flex{ class: css_class } %li.project-row.d-flex{ class: css_class }
...@@ -60,6 +62,11 @@ ...@@ -60,6 +62,11 @@
.controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0.text-secondary{ class: css_controls_class.join(" ") } .controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0.text-secondary{ class: css_controls_class.join(" ") }
.icon-container.d-flex.align-items-center .icon-container.d-flex.align-items-center
- if show_pipeline_status_icon
- pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
%span.icon-wrapper.pipeline-status
= render 'ci/status/icon', status: project.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path
= render_if_exists 'shared/projects/archived', project: project = render_if_exists 'shared/projects/archived', project: project
- if stars - if stars
= link_to project_starrers_path(project), = link_to project_starrers_path(project),
... ...
......
---
title: Fixed regression when URL was encoded in a loop
merge_request: 25849
author:
type: fixed