Commit 811c0255 authored by Nick Thomas's avatar Nick Thomas

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-07-28

parents 745db030 e09a5a90
...@@ -146,6 +146,8 @@ import AuditLogs from './audit_logs'; ...@@ -146,6 +146,8 @@ import AuditLogs from './audit_logs';
.init(); .init();
} }
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
switch (page) { switch (page) {
case 'profiles:preferences:show': case 'profiles:preferences:show':
initExperimentalFlags(); initExperimentalFlags();
...@@ -162,7 +164,7 @@ import AuditLogs from './audit_logs'; ...@@ -162,7 +164,7 @@ import AuditLogs from './audit_logs';
break; break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
case 'projects:issues:index': case 'projects:issues:index':
if (gl.FilteredSearchManager && document.querySelector('.filtered-search')) { if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests'); const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
filteredSearchManager.setup(); filteredSearchManager.setup();
} }
...@@ -190,11 +192,17 @@ import AuditLogs from './audit_logs'; ...@@ -190,11 +192,17 @@ import AuditLogs from './audit_logs';
break; break;
case 'dashboard:issues': case 'dashboard:issues':
case 'dashboard:merge_requests': case 'dashboard:merge_requests':
case 'groups:issues':
case 'groups:merge_requests': case 'groups:merge_requests':
new ProjectSelect(); new ProjectSelect();
initLegacyFilters(); initLegacyFilters();
break; break;
case 'groups:issues':
if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager('issues');
filteredSearchManager.setup();
}
new ProjectSelect();
break;
case 'dashboard:todos:index': case 'dashboard:todos:index':
new Todos(); new Todos();
break; break;
......
...@@ -11,6 +11,16 @@ const Ajax = { ...@@ -11,6 +11,16 @@ const Ajax = {
if (!self.destroyed) self.hook.list[config.method].call(self.hook.list, data); if (!self.destroyed) self.hook.list[config.method].call(self.hook.list, data);
}, },
preprocessing: function preprocessing(config, data) {
let results = data;
if (config.preprocessing && !data.preprocessed) {
results = config.preprocessing(data);
AjaxCache.override(config.endpoint, results);
}
return results;
},
init: function init(hook) { init: function init(hook) {
var self = this; var self = this;
self.destroyed = false; self.destroyed = false;
...@@ -31,7 +41,8 @@ const Ajax = { ...@@ -31,7 +41,8 @@ const Ajax = {
dynamicList.outerHTML = loadingTemplate.outerHTML; dynamicList.outerHTML = loadingTemplate.outerHTML;
} }
AjaxCache.retrieve(config.endpoint) return AjaxCache.retrieve(config.endpoint)
.then(self.preprocessing.bind(null, config))
.then((data) => self._loadData(data, config, self)) .then((data) => self._loadData(data, config, self))
.catch(config.onError); .catch(config.onError);
}, },
......
...@@ -6,7 +6,7 @@ import './filtered_search_dropdown'; ...@@ -6,7 +6,7 @@ import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown { class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(options = {}) { constructor(options = {}) {
const { input, endpoint, symbol } = options; const { input, endpoint, symbol, preprocessing } = options;
super(options); super(options);
this.symbol = symbol; this.symbol = symbol;
this.config = { this.config = {
...@@ -14,6 +14,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { ...@@ -14,6 +14,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
endpoint, endpoint,
method: 'setData', method: 'setData',
loadingTemplate: this.loadingTemplate, loadingTemplate: this.loadingTemplate,
preprocessing,
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occured fetching the dropdown data.');
......
...@@ -75,6 +75,66 @@ class DropdownUtils { ...@@ -75,6 +75,66 @@ class DropdownUtils {
return updatedItem; return updatedItem;
} }
static mergeDuplicateLabels(dataMap, newLabel) {
const updatedMap = dataMap;
const key = newLabel.title;
const hasKeyProperty = Object.prototype.hasOwnProperty.call(updatedMap, key);
if (!hasKeyProperty) {
updatedMap[key] = newLabel;
} else {
const existing = updatedMap[key];
if (!existing.multipleColors) {
existing.multipleColors = [existing.color];
}
existing.multipleColors.push(newLabel.color);
}
return updatedMap;
}
static duplicateLabelColor(labelColors) {
const colors = labelColors;
const spacing = 100 / colors.length;
// Reduce the colors to 4
colors.length = Math.min(colors.length, 4);
const color = colors.map((c, i) => {
const percentFirst = Math.floor(spacing * i);
const percentSecond = Math.floor(spacing * (i + 1));
return `${c} ${percentFirst}%, ${c} ${percentSecond}%`;
}).join(', ');
return `linear-gradient(${color})`;
}
static duplicateLabelPreprocessing(data) {
const results = [];
const dataMap = {};
data.forEach(DropdownUtils.mergeDuplicateLabels.bind(null, dataMap));
Object.keys(dataMap)
.forEach((key) => {
const label = dataMap[key];
if (label.multipleColors) {
label.color = DropdownUtils.duplicateLabelColor(label.multipleColors);
label.text_color = '#000000';
}
results.push(label);
});
results.preprocessed = true;
return results;
}
static setDataValueIfSelected(filter, selected) { static setDataValueIfSelected(filter, selected) {
const dataValue = selected.getAttribute('data-value'); const dataValue = selected.getAttribute('data-value');
......
...@@ -58,6 +58,7 @@ class FilteredSearchDropdownManager { ...@@ -58,6 +58,7 @@ class FilteredSearchDropdownManager {
extraArguments: { extraArguments: {
endpoint: `${this.baseEndpoint}/labels.json`, endpoint: `${this.baseEndpoint}/labels.json`,
symbol: '~', symbol: '~',
preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing,
}, },
element: this.container.querySelector('#js-dropdown-label'), element: this.container.querySelector('#js-dropdown-label'),
}, },
......
...@@ -28,13 +28,13 @@ class FilteredSearchManager { ...@@ -28,13 +28,13 @@ class FilteredSearchManager {
allowedKeys: this.filteredSearchTokenKeys.getKeys(), allowedKeys: this.filteredSearchTokenKeys.getKeys(),
}); });
this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown'); this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
const projectPath = this.searchHistoryDropdownElement ? const fullPath = this.searchHistoryDropdownElement ?
this.searchHistoryDropdownElement.dataset.projectFullPath : 'project'; this.searchHistoryDropdownElement.dataset.fullPath : 'project';
let recentSearchesPagePrefix = 'issue-recent-searches'; let recentSearchesPagePrefix = 'issue-recent-searches';
if (this.page === 'merge_requests') { if (this.page === 'merge_requests') {
recentSearchesPagePrefix = 'merge-request-recent-searches'; recentSearchesPagePrefix = 'merge-request-recent-searches';
} }
const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`; const recentSearchesKey = `${fullPath}-${recentSearchesPagePrefix}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey); this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
} }
......
...@@ -58,29 +58,54 @@ class FilteredSearchVisualTokens { ...@@ -58,29 +58,54 @@ class FilteredSearchVisualTokens {
`; `;
} }
static setTokenStyle(tokenContainer, backgroundColor, textColor) {
const token = tokenContainer;
// Labels with linear gradient should not override default background color
if (backgroundColor.indexOf('linear-gradient') === -1) {
token.style.backgroundColor = backgroundColor;
}
token.style.color = textColor;
if (textColor === '#FFFFFF') {
const removeToken = token.querySelector('.remove-token');
removeToken.classList.add('inverted');
}
return token;
}
static preprocessLabel(labelsEndpoint, labels) {
let processed = labels;
if (!labels.preprocessed) {
processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels);
AjaxCache.override(labelsEndpoint, processed);
processed.preprocessed = true;
}
return processed;
}
static updateLabelTokenColor(tokenValueContainer, tokenValue) { static updateLabelTokenColor(tokenValueContainer, tokenValue) {
const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search'); const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
const baseEndpoint = filteredSearchInput.dataset.baseEndpoint; const baseEndpoint = filteredSearchInput.dataset.baseEndpoint;
const labelsEndpoint = `${baseEndpoint}/labels.json`; const labelsEndpoint = `${baseEndpoint}/labels.json`;
return AjaxCache.retrieve(labelsEndpoint) return AjaxCache.retrieve(labelsEndpoint)
.then((labels) => { .then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue); .then((labels) => {
const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
if (!matchingLabel) {
return;
}
const tokenValueStyle = tokenValueContainer.style; if (!matchingLabel) {
tokenValueStyle.backgroundColor = matchingLabel.color; return;
tokenValueStyle.color = matchingLabel.text_color; }
if (matchingLabel.text_color === '#FFFFFF') { FilteredSearchVisualTokens
const removeToken = tokenValueContainer.querySelector('.remove-token'); .setTokenStyle(tokenValueContainer, matchingLabel.color, matchingLabel.text_color);
removeToken.classList.add('inverted'); })
} .catch(() => new Flash('An error occurred while fetching label colors.'));
})
.catch(() => new Flash('An error occurred while fetching label colors.'));
} }
static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) { static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
/* global ListLabel */ /* global ListLabel */
import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import DropdownUtils from './filtered_search/dropdown_utils';
(function() { (function() {
this.LabelsSelect = (function() { this.LabelsSelect = (function() {
...@@ -218,18 +219,7 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; ...@@ -218,18 +219,7 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
} }
} }
if (label.duplicate) { if (label.duplicate) {
spacing = 100 / label.color.length; color = gl.DropdownUtils.duplicateLabelColor(label.color);
// Reduce the colors to 4
label.color = label.color.filter(function(color, i) {
return i < 4;
});
color = _.map(label.color, function(color, i) {
var percentFirst, percentSecond;
percentFirst = Math.floor(spacing * i);
percentSecond = Math.floor(spacing * (i + 1));
return color + " " + percentFirst + "%," + color + " " + percentSecond + "% ";
}).join(',');
color = "linear-gradient(" + color + ")";
} }
else { else {
if (label.color != null) { if (label.color != null) {
......
...@@ -6,6 +6,10 @@ class AjaxCache extends Cache { ...@@ -6,6 +6,10 @@ class AjaxCache extends Cache {
this.pendingRequests = { }; this.pendingRequests = { };
} }
override(endpoint, data) {
this.internalStorage[endpoint] = data;
}
retrieve(endpoint, forceRetrieve) { retrieve(endpoint, forceRetrieve) {
if (this.hasData(endpoint) && !forceRetrieve) { if (this.hasData(endpoint) && !forceRetrieve) {
return Promise.resolve(this.get(endpoint)); return Promise.resolve(this.get(endpoint));
......
...@@ -143,10 +143,19 @@ $new-sidebar-width: 220px; ...@@ -143,10 +143,19 @@ $new-sidebar-width: 220px;
white-space: nowrap; white-space: nowrap;
a { a {
display: block; display: flex;
align-items: center;
padding: 12px 16px; padding: 12px 16px;
color: $inactive-color; color: $inactive-color;
} }
svg {
fill: $inactive-color;
}
}
.nav-item-name {
flex: 1;
} }
li.active { li.active {
...@@ -156,11 +165,25 @@ $new-sidebar-width: 220px; ...@@ -156,11 +165,25 @@ $new-sidebar-width: 220px;
color: $active-color; color: $active-color;
font-weight: 700; font-weight: 700;
} }
svg {
fill: $active-color;
}
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
left: (-$new-sidebar-width); left: (-$new-sidebar-width);
} }
.nav-icon-container {
display: flex;
margin-right: 8px;
svg {
height: 16px;
width: 16px;
}
}
} }
.with-performance-bar .nav-sidebar { .with-performance-bar .nav-sidebar {
...@@ -173,7 +196,7 @@ $new-sidebar-width: 220px; ...@@ -173,7 +196,7 @@ $new-sidebar-width: 220px;
> li { > li {
a { a {
padding: 8px 16px 8px 24px; padding: 8px 16px 8px 50px;
&:hover, &:hover,
&:focus { &:focus {
...@@ -198,7 +221,6 @@ $new-sidebar-width: 220px; ...@@ -198,7 +221,6 @@ $new-sidebar-width: 220px;
.sidebar-top-level-items { .sidebar-top-level-items {
> li { > li {
.badge { .badge {
float: right;
background-color: $inactive-badge-background; background-color: $inactive-badge-background;
color: $inactive-color; color: $inactive-color;
} }
...@@ -220,6 +242,10 @@ $new-sidebar-width: 220px; ...@@ -220,6 +242,10 @@ $new-sidebar-width: 220px;
background-color: $hover-background; background-color: $hover-background;
color: $hover-color; color: $hover-color;
svg {
fill: $hover-color;
}
.badge { .badge {
background-color: $indigo-500; background-color: $indigo-500;
color: $hover-color; color: $hover-color;
......
...@@ -10,8 +10,10 @@ module EE ...@@ -10,8 +10,10 @@ module EE
private private
def search_multiple_assignees?(type) def search_multiple_assignees?(type)
context = @project.presence || @group
type == :issues && type == :issues &&
@project.feature_available?(:multiple_issue_assignees) context.feature_available?(:multiple_issue_assignees)
end end
end end
end end
...@@ -137,15 +137,23 @@ module SearchHelper ...@@ -137,15 +137,23 @@ module SearchHelper
end end
def search_filter_input_options(type) def search_filter_input_options(type)
{ opts = {
id: "filtered-search-#{type}", id: "filtered-search-#{type}",
placeholder: 'Search or filter results...', placeholder: 'Search or filter results...',
data: { data: {
'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username])
'username-params' => @users.to_json(only: [:id, :username]),
'base-endpoint' => project_path(@project)
} }
} }
if @project.present?
opts[:data]['project-id'] = @project.id
opts[:data]['base-endpoint'] = project_path(@project)
else
# Group context
opts[:data]['base-endpoint'] = group_canonical_path(@group)
end
opts
end end
# Sanitize a HTML field for search display. Most tags are stripped out and the # Sanitize a HTML field for search display. Most tags are stripped out and the
......
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues") = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'filtered_search'
= webpack_bundle_tag 'issues'
- if show_new_nav? && group_issues_exists - if show_new_nav? && group_issues_exists
- content_for :breadcrumbs_extra do - content_for :breadcrumbs_extra do
= link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do = link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do
...@@ -20,7 +25,7 @@ ...@@ -20,7 +25,7 @@
Subscribe Subscribe
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue" = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
= render 'shared/issuable/filter', type: :issues = render 'shared/issuable/search_bar', type: :issues
.row-content-block.second-block .row-content-block.second-block
Only issues from the Only issues from the
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
%span .nav-icon-container
= custom_icon('overview')
%span.nav-item-name
Overview Overview
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -45,7 +47,9 @@ ...@@ -45,7 +47,9 @@
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles audit_logs)) do = nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles audit_logs)) do
= link_to admin_conversational_development_index_path, title: 'Monitoring' do = link_to admin_conversational_development_index_path, title: 'Monitoring' do
%span .nav-icon-container
= custom_icon('monitoring')
%span.nav-item-name
Monitoring Monitoring
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -80,67 +84,93 @@ ...@@ -80,67 +84,93 @@
= nav_link(controller: :broadcast_messages) do = nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do = link_to admin_broadcast_messages_path, title: 'Messages' do
%span .nav-icon-container
= custom_icon('messages')
%span.nav-item-name
Messages Messages
= nav_link(controller: [:hooks, :hook_logs]) do = nav_link(controller: [:hooks, :hook_logs]) do
= link_to admin_hooks_path, title: 'Hooks' do = link_to admin_hooks_path, title: 'Hooks' do
%span .nav-icon-container
= custom_icon('system_hooks')
%span.nav-item-name
System Hooks System Hooks
= nav_link(controller: :applications) do = nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do = link_to admin_applications_path, title: 'Applications' do
%span .nav-icon-container
= custom_icon('applications')
%span.nav-item-name
Applications Applications
= nav_link(controller: :abuse_reports) do = nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse Reports" do = link_to admin_abuse_reports_path, title: "Abuse Reports" do
%span .nav-icon-container
= custom_icon('abuse_reports')
%span.nav-item-name
Abuse Reports Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all)) %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :licenses) do = nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License' do = link_to admin_license_path, title: 'License' do
.nav-icon-container
= custom_icon('license')
%span %span
License License
- if akismet_enabled? - if akismet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
%span .nav-icon-container
= custom_icon('mr_bold')
%span.nav-item-name
Spam Logs Spam Logs
= nav_link(controller: :push_rules) do = nav_link(controller: :push_rules) do
= link_to admin_push_rule_path, title: 'Push Rules' do = link_to admin_push_rule_path, title: 'Push Rules' do
.nav-icon-container
= custom_icon('push_rules')
%span %span
Push Rules Push Rules
= nav_link(controller: :geo_nodes) do = nav_link(controller: :geo_nodes) do
= link_to admin_geo_nodes_path, title: 'Geo Nodes' do = link_to admin_geo_nodes_path, title: 'Geo Nodes' do
.nav-icon-container
= custom_icon('geo_nodes')
%span %span
Geo Nodes Geo Nodes
= nav_link(controller: :deploy_keys) do = nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
%span .nav-icon-container
= custom_icon('key')
%span.nav-item-name
Deploy Keys Deploy Keys
= nav_link(controller: :services) do = nav_link(controller: :services) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do = link_to admin_application_settings_services_path, title: 'Service Templates' do
%span .nav-icon-container
= custom_icon('service_templates')
%span.nav-item-name
Service Templates Service Templates
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
= link_to admin_labels_path, title: 'Labels' do = link_to admin_labels_path, title: 'Labels' do
%span .nav-icon-container
= custom_icon('labels')
%span.nav-item-name
Labels Labels
= nav_link(controller: :appearances) do = nav_link(controller: :appearances) do
= link_to admin_appearances_path, title: 'Appearances' do = link_to admin_appearances_path, title: 'Appearances' do
%span .nav-icon-container
= custom_icon('appearance')
%span.nav-item-name
Appearance Appearance
%li.divider %li.divider
= nav_link(controller: :application_settings) do = nav_link(controller: :application_settings) do
= link_to admin_application_settings_path, title: 'Settings' do = link_to admin_application_settings_path, title: 'Settings' do
%span .nav-icon-container
= custom_icon('settings')
%span.nav-item-name
Settings Settings
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'About group' do = link_to group_path(@group), title: 'About group' do
%span .nav-icon-container
= custom_icon('project')
%span.nav-item-name
About About
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -33,10 +35,12 @@ ...@@ -33,10 +35,12 @@
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
= link_to issues_group_path(@group), title: 'Issues' do = link_to issues_group_path(@group), title: 'Issues' do
%span .nav-icon-container
Issues = custom_icon('issues')
%span.nav-item-name
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count) Issues
%span.badge.count= number_with_delimiter(issues.count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
...@@ -56,18 +60,24 @@ ...@@ -56,18 +60,24 @@
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%span .nav-icon-container
Merge Requests = custom_icon('mr_bold')
%span.nav-item-name
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count) Merge Requests
%span.badge.count= number_with_delimiter(merge_requests.count)
= nav_link(path: 'group_members#index') do = nav_link(path: 'group_members#index') do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
%span .nav-icon-container
= custom_icon('members')
%span.nav-item-name
Members Members
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do
= link_to edit_group_path(@group), title: 'Settings' do = link_to edit_group_path(@group), title: 'Settings' do
%span .nav-icon-container
= custom_icon('settings')
%span.nav-item-name
Settings Settings
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: 'groups#edit') do = nav_link(path: 'groups#edit') do
......
...@@ -10,42 +10,60 @@ ...@@ -10,42 +10,60 @@
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
%span .nav-icon-container
= custom_icon('profile')
%span.nav-item-name
Profile Profile
= nav_link(controller: [:accounts, :two_factor_auths]) do = nav_link(controller: [:accounts, :two_factor_auths]) do
= link_to profile_account_path, title: 'Account' do = link_to profile_account_path, title: 'Account' do
%span .nav-icon-container
= custom_icon('account')
%span.nav-item-name
Account Account
- if current_application_settings.user_oauth_applications? - if current_application_settings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do = nav_link(controller: 'oauth/applications') do
= link_to applications_profile_path, title: 'Applications' do = link_to applications_profile_path, title: 'Applications' do
%span .nav-icon-container
= custom_icon('applications')
%span.nav-item-name
Applications Applications
= nav_link(controller: :chat_names) do = nav_link(controller: :chat_names) do
= link_to profile_chat_names_path, title: 'Chat' do = link_to profile_chat_names_path, title: 'Chat' do
%span .nav-icon-container
= custom_icon('chat')
%span.nav-item-name
Chat Chat
= nav_link(controller: :personal_access_tokens) do = nav_link(controller: :personal_access_tokens) do
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do = link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
%span .nav-icon-container
= custom_icon('access_tokens')
%span.nav-item-name
Access Tokens Access Tokens
= nav_link(controller: :emails) do = nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do = link_to profile_emails_path, title: 'Emails' do
%span .nav-icon-container
= custom_icon('emails')
%span.nav-item-name
Emails Emails
- unless current_user.ldap_user? - unless current_user.ldap_user?
= nav_link(controller: :passwords) do = nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do = link_to edit_profile_password_path, title: 'Password' do
%span .nav-icon-container
= custom_icon('lock')
%span.nav-item-name
Password Password
= nav_link(controller: :notifications) do = nav_link(controller: :notifications) do
= link_to profile_notifications_path, title: 'Notifications' do = link_to profile_notifications_path, title: 'Notifications' do
%span .nav-icon-container
= custom_icon('notifications')
%span.nav-item-name
Notifications Notifications
= nav_link(controller: :keys) do = nav_link(controller: :keys) do
= link_to profile_keys_path, title: 'SSH Keys' do = link_to profile_keys_path, title: 'SSH Keys' do
%span .nav-icon-container
= custom_icon('key')
%span.nav-item-name
SSH Keys SSH Keys
= nav_link(controller: :gpg_keys) do = nav_link(controller: :gpg_keys) do
= link_to profile_gpg_keys_path, title: 'GPG Keys' do = link_to profile_gpg_keys_path, title: 'GPG Keys' do
...@@ -53,9 +71,13 @@ ...@@ -53,9 +71,13 @@
GPG Keys GPG Keys
= nav_link(controller: :preferences) do = nav_link(controller: :preferences) do
= link_to profile_preferences_path, title: 'Preferences' do = link_to profile_preferences_path, title: 'Preferences' do
%span .nav-icon-container
= custom_icon('preferences')
%span.nav-item-name
Preferences Preferences
= nav_link(path: 'profiles#audit_log') do = nav_link(path: 'profiles#audit_log') do
= link_to audit_log_profile_path, title: 'Authentication log' do = link_to audit_log_profile_path, title: 'Authentication log' do
%span .nav-icon-container
= custom_icon('authentication_log')
%span.nav-item-name
Authentication log Authentication log
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do = nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
= link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do
%span .nav-icon-container
= custom_icon('project')
%span.nav-item-name
About About
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -32,7 +34,9 @@ ...@@ -32,7 +34,9 @@
- if project_nav_tab? :files - if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
= link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do = link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do
%span .nav-icon-container
= custom_icon('doc_text')
%span.nav-item-name
Repository Repository
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -71,16 +75,20 @@ ...@@ -71,16 +75,20 @@
- if project_nav_tab? :container_registry - if project_nav_tab? :container_registry
= nav_link(controller: %w[projects/registry/repositories]) do = nav_link(controller: %w[projects/registry/repositories]) do
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do = link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
%span .nav-icon-container
= custom_icon('mr_bold')
%span.nav-item-name
Registry Registry
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do = nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
%span .nav-icon-container
- if @project.issues_enabled? = custom_icon('issues')
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.nav-item-name
Issues Issues
- if @project.issues_enabled?
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
- if project_nav_tab?(:issues) && !current_controller?(:merge_requests) - if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
...@@ -115,14 +123,18 @@ ...@@ -115,14 +123,18 @@
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span .nav-icon-container
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) = custom_icon('mr_bold')
%span.nav-item-name
Merge Requests Merge Requests
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
%span .nav-icon-container
= custom_icon('pipeline')
%span.nav-item-name
Pipelines Pipelines
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -159,19 +171,25 @@ ...@@ -159,19 +171,25 @@
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
%span .nav-icon-container
= custom_icon('wiki')
%span.nav-item-name
Wiki Wiki
- if project_nav_tab? :snippets - if project_nav_tab? :snippets
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
%span .nav-icon-container
= custom_icon('snippets')
%span.nav-item-name
Snippets Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span .nav-icon-container
= custom_icon('settings')
%span.nav-item-name
Settings Settings
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
...@@ -207,9 +225,11 @@ ...@@ -207,9 +225,11 @@
- else - else
= nav_link(path: %w[members#show]) do = nav_link(path: %w[members#show]) do
= link_to project_settings_members_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
.nav-icon-container
= custom_icon('members')
%span %span
Settings Members
-# Shortcut to Project > Activity -# Shortcut to Project > Activity
%li.hidden %li.hidden
......
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><path d="m13 2h-10c-1.7 0-3 1.3-3 3v6c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-6c0-1.7-1.3-3-3-3m1 9c0 .6-.4 1-1 1h-10c-.6 0-1-.4-1-1v-6c0-.6.4-1 1-1h10c.6 0 1 .4 1 1v6"/><circle cx="4" cy="8" r="1"/><circle cx="8" cy="8" r="1"/><circle cx="12" cy="8" r="1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><path d="m6.8 8c-.3 0-.5 0-.8 0-5 0-6 2.7-6 4.5s.1 2.5 6 2.5c.6 0 1.1 0 1.5 0-1-1.1-1.5-2.5-1.5-4 0-1.1.3-2.1.8-3"/><circle cx="6" cy="4" r="3"/><path d="m15.9 11.5l-.9-.6c0-.3-.1-.7-.2-.9l.6-.9c.1-.1.1-.2 0-.3l-.4-.5c-.1-.1-.2-.1-.3-.1l-.9.4c-.3-.2-.5-.3-.9-.4l-.3-1c0-.1-.1-.2-.2-.2h-.6c-.1 0-.2.1-.2.2l-.3 1c-.3.1-.6.2-.9.4l-1.1-.4c-.1 0-.2 0-.3.1l-.4.5c0 .1 0 .2 0 .3l.6.9c-.1.3-.2.6-.2.9l-.9.5c-.1.1-.1.2-.1.3l.1.6c0 .1.1.2.2.2l1.1.1c.1.2.3.4.5.6l-.2 1.2c0 .1 0 .2.1.3l.6.3c.1 0 .2 0 .3-.1l.9-.9c.2 0 .4 0 .6 0l.9.9c.1.1.2.1.3 0l.6-.3c.1 0 .2-.2.1-.3l-.1-1.1c.2-.2.4-.4.5-.6l1.1-.1c.1 0 .2-.1.2-.2l.1-.6c.1-.1.1-.2 0-.2m-3.9.5c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858-.411.022-.744.026-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023.172-.009.332-.02.478-.035-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M5.414 12l-3.707 3.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M3 4a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm0-2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3z"/><path d="M3.212 4L8 8.31 12.788 4H3.212zm6.126 5.796a2 2 0 0 1-2.676 0L.183 3.965A3.001 3.001 0 0 1 3 2h10c1.293 0 2.395.818 2.817 1.965l-6.48 5.83z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><path d="m9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4"/><path d="m7.3 12.7c.4.4 1 .3 1.4-.1 2.9-3.1 4.3-5.6 4.3-7.3 0-2.9-2.2-5.3-5-5.3s-5 2.4-5 5.3c0 1.7 1.4 4.2 4.3 7.4m.7-10.7c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2 0-1.9 1.4-3.3 3-3.3"/><circle cx="8" cy="5" r="1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></svg>
<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16" class="gitlab-icon">
<path fill="#7E7C7C" d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2"></path>
<path fill="#7E7C7C" d="M7.1597,4 L8.8887,4 L8.8887,8 L7.1107,8 L7.1597,4 Z M7.1597,9.6667 L8.8887,9.6667 L8.8887,11.4447 L7.1107,11.4447 L7.1597,9.6667 Z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M7.574 6.689a4.002 4.002 0 0 1 6.275-4.861 4 4 0 0 1-4.86 6.275l-2.21 2.21.706.707a1 1 0 0 1-1.414 1.415l-.707-.708-.707.708.707.707a1 1 0 0 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.415-1.414l5.746-5.746zm2.033-.618a2 2 0 1 0 2.828-2.829 2 2 0 0 0-2.828 2.829z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667z"/><path d="M.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><path d="m8 9c-.6 0-1 .4-1 1v1c0 .6.4 1 1 1s1-.4 1-1v-1c0-.6-.4-1-1-1"/><path d="m12 5v-1c0-2.2-1.8-4-4-4s-4 1.8-4 4v1c-1.7 0-3 1.3-3 3v5c0 1.7 1.3 3 3 3h8c1.7 0 3-1.3 3-3v-5c0-1.7-1.3-3-3-3m-6-1c0-1.1.9-2 2-2s2 .9 2 2v1h-4v-1m7 9c0 .6-.4 1-1 1h-8c-.6 0-1-.4-1-1v-5c0-.6.4-1 1-1h8c.6 0 1 .4 1 1v5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M2 2v3h3V2H2zm0-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm9 2v3h3V2h-3zm0-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zM2 11v3h3v-3H2zm0-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm9 2v3h3v-3h-3zm0-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><path d="m8 0c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8m0 14c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6"/><circle cx="12.5" cy="9.5" r=".5"/><circle cx="12.5" cy="6.5" r=".5"/><circle cx="10.5" cy="12.5" r=".5"/><circle cx="10.5" cy="3.5" r=".5"/><circle cx="5.5" cy="12.5" r=".5"/><circle cx="5.5" cy="3.5" r=".5"/><circle cx="3.5" cy="9.5" r=".5"/><circle cx="3.5" cy="6.5" r=".5"/><path d="m9 7.2c0 0 0-.1 0-.2v-1.9c0-.1 0-.1-.1-.2l-.8-.8c0 0-.1 0-.1 0l-.9.8c-.1.1-.1.1-.1.2v1.9c0 .1 0 .2 0 .2-.6.4-1 1-1 1.8 0 1.1.9 2 2 2s2-.9 2-2c0-.8-.4-1.4-1-1.8m-1 2.8c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2zm10 5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zm-5 5h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2z" fill-rule="evenodd"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></svg>
<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
<path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E" fill-rule="evenodd"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M5.268 9a2 2 0 0 1 3.464 0H10a1 1 0 0 1 0 2H8.732a2 2 0 0 1-3.464 0H4a1 1 0 0 1 0-2h1.268zM6 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 6 5.515V2zM13.749.094A3.001 3.001 0 0 1 16 3v10a3.001 3.001 0 0 1-2.251 2.906A3.989 3.989 0 0 0 15 13V3c0-1.144-.48-2.177-1.251-2.906zM3 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918c.594.181 1.15.452 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 1 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 1 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 8 6.191V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></svg>
- type = local_assigns.fetch(:type) - type = local_assigns.fetch(:type)
- board = local_assigns.fetch(:board, nil) - board = local_assigns.fetch(:board, nil)
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : '' - block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
- full_path = @project.present? ? @project.full_path : @group.full_path
.issues-filters .issues-filters
.issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal } .issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
...@@ -22,7 +23,7 @@ ...@@ -22,7 +23,7 @@
dropdown_class: "filtered-search-history-dropdown", dropdown_class: "filtered-search-history-dropdown",
content_class: "filtered-search-history-dropdown-content", content_class: "filtered-search-history-dropdown-content",
title: "Recent searches" }) do title: "Recent searches" }) do
.js-filtered-search-history-dropdown{ data: { project_full_path: @project.full_path } } .js-filtered-search-history-dropdown{ data: { full_path: full_path } }
.filtered-search-box-input-container.droplab-dropdown .filtered-search-box-input-container.droplab-dropdown
.scroll-container .scroll-container
%ul.tokens-container.list-unstyled %ul.tokens-container.list-unstyled
......
---
title: Fix accessing individual files on Object Storage
merge_request:
author:
---
title: Add icons to contextual sidebars
merge_request:
author:
...@@ -426,8 +426,6 @@ Here are some things to keep in mind regarding test performance: ...@@ -426,8 +426,6 @@ Here are some things to keep in mind regarding test performance:
- `FactoryGirl.build(...)` and `.build_stubbed` are faster than `.create`. - `FactoryGirl.build(...)` and `.build_stubbed` are faster than `.create`.
- Don't `create` an object when `build`, `build_stubbed`, `attributes_for`, - Don't `create` an object when `build`, `build_stubbed`, `attributes_for`,
`spy`, or `double` will do. Database persistence is slow! `spy`, or `double` will do. Database persistence is slow!
- Use `create(:empty_project)` instead of `create(:project)` when you don't need
the underlying Git repository. Filesystem operations are slow!
- Don't mark a feature as requiring JavaScript (through `@javascript` in - Don't mark a feature as requiring JavaScript (through `@javascript` in
Spinach or `:js` in RSpec) unless it's _actually_ required for the test Spinach or `:js` in RSpec) unless it's _actually_ required for the test
to be valid. Headless browser testing is slow! to be valid. Headless browser testing is slow!
......
...@@ -57,6 +57,10 @@ of that milestone and the issues/merge requests count that it shares across the ...@@ -57,6 +57,10 @@ of that milestone and the issues/merge requests count that it shares across the
In addition to that you will be able to filter issues or merge requests by group milestones in all projects that belongs to the milestone group. In addition to that you will be able to filter issues or merge requests by group milestones in all projects that belongs to the milestone group.
## Milestone promotion
You will be able to promote a project milestone to a group milestone [in the future](https://gitlab.com/gitlab-org/gitlab-ce/issues/35833).
## Special milestone filters ## Special milestone filters
In addition to the milestones that exist in the project or group, there are some In addition to the milestones that exist in the project or group, there are some
...@@ -87,3 +91,7 @@ total merge requests and issues. ...@@ -87,3 +91,7 @@ total merge requests and issues.
are visual representations of the progress of completing a milestone. are visual representations of the progress of completing a milestone.
![burndown chart](img/burndown_chart.png) ![burndown chart](img/burndown_chart.png)
Burndown charts are only available for project milestones currently. They will be available for group milestones [in the future](https://gitlab.com/gitlab-org/gitlab-ee/issues/3064).
[Quick actions](../quick_actions.md) are available for assigning and removing project milestones only. [In the future](https://gitlab.com/gitlab-org/gitlab-ce/issues/34778), this will also apply to group milestones.
\ No newline at end of file
...@@ -100,7 +100,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps ...@@ -100,7 +100,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
group = owned_group group = owned_group
%w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path|
project = create(:empty_project, path: path, group: group) project = create(:project, path: path, group: group)
milestone = create :milestone, title: "Version 7.2", project: project milestone = create :milestone, title: "Version 7.2", project: project
create(:label, project: project, title: 'bug') create(:label, project: project, title: 'bug')
......
...@@ -14,12 +14,12 @@ class Spinach::Features::GroupHooks < Spinach::FeatureSteps ...@@ -14,12 +14,12 @@ class Spinach::Features::GroupHooks < Spinach::FeatureSteps
end end
step 'I own project "Shop" in group "Sourcing"' do step 'I own project "Shop" in group "Sourcing"' do
@project = create(:project, @project = create(:project, :repository,
name: 'Shop', group: @group) name: 'Shop', group: @group)
end end
step 'I own empty project "Empty Shop" in group "Sourcing"' do step 'I own empty project "Empty Shop" in group "Sourcing"' do
@project = create(:empty_project, @project = create(:project,
name: 'Shop', group: @group) name: 'Shop', group: @group)
end end
......
...@@ -15,7 +15,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -15,7 +15,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'Group "Owned" has a public project "Public-project"' do step 'Group "Owned" has a public project "Public-project"' do
group = owned_group group = owned_group
@project = create :empty_project, :public, @project = create :project, :public,
group: group, group: group,
name: "Public-project" name: "Public-project"
end end
...@@ -132,7 +132,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -132,7 +132,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'Group "Owned" has archived project' do step 'Group "Owned" has archived project' do
group = Group.find_by(name: 'Owned') group = Group.find_by(name: 'Owned')
@archived_project = create(:empty_project, :archived, namespace: group, path: "archived-project") @archived_project = create(:project, :archived, namespace: group, path: "archived-project")
end end
step 'I should see "archived" label' do step 'I should see "archived" label' do
......
...@@ -47,11 +47,11 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps ...@@ -47,11 +47,11 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end end
step 'other projects have deploy keys' do step 'other projects have deploy keys' do
@second_project = create(:empty_project, namespace: create(:group)) @second_project = create(:project, namespace: create(:group))
@second_project.team << [current_user, :master] @second_project.team << [current_user, :master]
create(:deploy_keys_project, project: @second_project) create(:deploy_keys_project, project: @second_project)
@third_project = create(:empty_project, namespace: create(:group)) @third_project = create(:project, namespace: create(:group))
@third_project.team << [current_user, :master] @third_project.team << [current_user, :master]
create(:deploy_keys_project, project: @third_project, deploy_key: @second_project.deploy_keys.first) create(:deploy_keys_project, project: @third_project, deploy_key: @second_project.deploy_keys.first)
end end
......
...@@ -16,7 +16,7 @@ class Spinach::Features::ProjectSearch < Spinach::FeatureSteps ...@@ -16,7 +16,7 @@ class Spinach::Features::ProjectSearch < Spinach::FeatureSteps
end end
step 'project has all data available for the search' do step 'project has all data available for the search' do
@project = create :project @project = create :project, :repository
@project.team << [current_user, :master] @project.team << [current_user, :master]
@issue = create :issue, title: 'bla-bla initial', project: @project @issue = create :issue, title: 'bla-bla initial', project: @project
......
...@@ -4,11 +4,11 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps ...@@ -4,11 +4,11 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
include SharedProject include SharedProject
step 'public project "Community"' do step 'public project "Community"' do
create(:empty_project, :public, name: 'Community') create(:project, :public, name: 'Community')
end end
step 'private project "Enterprise"' do step 'private project "Enterprise"' do
create(:empty_project, :private, name: 'Enterprise') create(:project, :private, name: 'Enterprise')
end end
step 'I visit project "Community" page' do step 'I visit project "Community" page' do
......
...@@ -34,7 +34,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps ...@@ -34,7 +34,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end end
step 'I own project "Website"' do step 'I own project "Website"' do
@project = create(:empty_project, name: "Website", namespace: @user.namespace) @project = create(:project, name: "Website", namespace: @user.namespace)
@project.team << [@user, :master] @project.team << [@user, :master]
end end
...@@ -68,7 +68,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps ...@@ -68,7 +68,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
step 'I share project with group "OpenSource"' do step 'I share project with group "OpenSource"' do
project = Project.find_by(name: 'Shop') project = Project.find_by(name: 'Shop')
os_group = create(:group, name: 'OpenSource') os_group = create(:group, name: 'OpenSource')
create(:empty_project, group: os_group) create(:project, group: os_group)
@os_user1 = create(:user) @os_user1 = create(:user)
@os_user2 = create(:user) @os_user2 = create(:user)
os_group.add_owner(@os_user1) os_group.add_owner(@os_user1)
......
...@@ -54,7 +54,7 @@ module SharedProject ...@@ -54,7 +54,7 @@ module SharedProject
# Create an empty project without caring about the name # Create an empty project without caring about the name
step 'I own an empty project' do step 'I own an empty project' do
@project = create(:empty_project, @project = create(:project,
name: 'Empty Project', namespace: @user.namespace) name: 'Empty Project', namespace: @user.namespace)
@project.team << [@user, :master] @project.team << [@user, :master]
end end
...@@ -276,7 +276,7 @@ module SharedProject ...@@ -276,7 +276,7 @@ module SharedProject
def user_owns_project(user_name:, project_name:, visibility: :private) def user_owns_project(user_name:, project_name:, visibility: :private)
user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
project = Project.find_by(name: project_name) project = Project.find_by(name: project_name)
project ||= create(:empty_project, visibility, name: project_name, namespace: user.namespace) project ||= create(:project, visibility, name: project_name, namespace: user.namespace)
project.team << [user, :master] project.team << [user, :master]
end end
end end
...@@ -55,6 +55,6 @@ class Spinach::Features::User < Spinach::FeatureSteps ...@@ -55,6 +55,6 @@ class Spinach::Features::User < Spinach::FeatureSteps
end end
def contributed_project def contributed_project
@contributed_project ||= create(:empty_project, :public) @contributed_project ||= create(:project, :public)
end end
end end
...@@ -45,7 +45,12 @@ module Gitlab ...@@ -45,7 +45,12 @@ module Gitlab
existing = ::Geo::ProjectRegistry.where(project_id: project_ids).pluck(:project_id) existing = ::Geo::ProjectRegistry.where(project_id: project_ids).pluck(:project_id)
missing_projects = project_ids - existing missing_projects = project_ids - existing
Rails.logger.debug("Missing projects: #{missing_projects}") Gitlab::Geo::Logger.debug(
class: self.class.name,
message: "Missing projects",
projects: missing_projects,
project_count: missing_projects.count)
missing_projects.each do |id| missing_projects.each do |id|
::Geo::ProjectRegistry.create(project_id: id) ::Geo::ProjectRegistry.create(project_id: id)
end end
...@@ -91,6 +96,15 @@ module Gitlab ...@@ -91,6 +96,15 @@ module Gitlab
registry.resync_wiki = true registry.resync_wiki = true
end end
Gitlab::Geo::Logger.info(
class: self.class.name,
message: "Repository update",
cursor_delay_s: (Time.now - updated_event.created_at).to_f.round(3),
project_id: updated_event.project_id,
source: updated_event.source,
resync_repository: registry.resync_repository,
resync_wiki: registry.resync_wiki)
registry.save! registry.save!
end end
......
...@@ -126,8 +126,16 @@ module Gitlab ...@@ -126,8 +126,16 @@ module Gitlab
end end
def send_artifacts_entry(build, entry) def send_artifacts_entry(build, entry)
file = build.artifacts_file
archive =
if file.file_storage?
file.path
else
file.url
end
params = { params = {
'Archive' => build.artifacts_file.path, 'Archive' => archive,
'Entry' => Base64.encode64(entry.path) 'Entry' => Base64.encode64(entry.path)
} }
......
...@@ -4,7 +4,7 @@ describe Admin::ApplicationSettingsController do ...@@ -4,7 +4,7 @@ describe Admin::ApplicationSettingsController do
include StubENV include StubENV
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) } let(:project) { create(:project, namespace: group) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:user) { create(:user)} let(:user) { create(:user)}
......
...@@ -29,8 +29,8 @@ describe Admin::DashboardController do ...@@ -29,8 +29,8 @@ describe Admin::DashboardController do
it 'does not retrieve projects that are pending deletion' do it 'does not retrieve projects that are pending deletion' do
sign_in(create(:admin)) sign_in(create(:admin))
project = create(:empty_project) project = create(:project)
pending_delete_project = create(:empty_project, pending_delete: true) pending_delete_project = create(:project, pending_delete: true)
get :index get :index
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Admin::GroupsController do describe Admin::GroupsController do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) } let(:project) { create(:project, namespace: group) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
before do before do
......
require 'spec_helper' require 'spec_helper'
describe Admin::ProjectsController do describe Admin::ProjectsController do
let!(:project) { create(:empty_project, :public) } let!(:project) { create(:project, :public) }
before do before do
sign_in(create(:admin)) sign_in(create(:admin))
......
...@@ -8,7 +8,7 @@ describe Admin::ServicesController do ...@@ -8,7 +8,7 @@ describe Admin::ServicesController do
end end
describe 'GET #edit' do describe 'GET #edit' do
let!(:project) { create(:empty_project) } let!(:project) { create(:project) }
Service.available_services_names.each do |service_name| Service.available_services_names.each do |service_name|
context "#{service_name}" do context "#{service_name}" do
...@@ -27,7 +27,7 @@ describe Admin::ServicesController do ...@@ -27,7 +27,7 @@ describe Admin::ServicesController do
end end
describe "#update" do describe "#update" do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let!(:service) do let!(:service) do
RedmineService.create( RedmineService.create(
project: project, project: project,
......
...@@ -9,7 +9,7 @@ describe Admin::UsersController do ...@@ -9,7 +9,7 @@ describe Admin::UsersController do
end end
describe 'DELETE #user with projects' do describe 'DELETE #user with projects' do
let(:project) { create(:empty_project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
let!(:issue) { create(:issue, author: user) } let!(:issue) { create(:issue, author: user) }
before do before do
......
require 'spec_helper' require 'spec_helper'
describe AutocompleteController do describe AutocompleteController do
let!(:project) { create(:empty_project) } let!(:project) { create(:project) }
let!(:user) { create(:user) } let!(:user) { create(:user) }
context 'GET users' do context 'GET users' do
...@@ -105,7 +105,7 @@ describe AutocompleteController do ...@@ -105,7 +105,7 @@ describe AutocompleteController do
end end
context 'non-member login for public project' do context 'non-member login for public project' do
let!(:project) { create(:empty_project, :public) } let!(:project) { create(:project, :public) }
before do before do
sign_in(non_member) sign_in(non_member)
...@@ -167,7 +167,7 @@ describe AutocompleteController do ...@@ -167,7 +167,7 @@ describe AutocompleteController do
end end
context 'unauthenticated user' do context 'unauthenticated user' do
let(:public_project) { create(:empty_project, :public) } let(:public_project) { create(:project, :public) }
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
describe 'GET #users with public project' do describe 'GET #users with public project' do
...@@ -271,8 +271,8 @@ describe AutocompleteController do ...@@ -271,8 +271,8 @@ describe AutocompleteController do
end end
context 'GET projects' do context 'GET projects' do
let(:authorized_project) { create(:empty_project) } let(:authorized_project) { create(:project) }
let(:authorized_search_project) { create(:empty_project, name: 'rugged') } let(:authorized_search_project) { create(:project, name: 'rugged') }
before do before do
sign_in(user) sign_in(user)
...@@ -329,8 +329,8 @@ describe AutocompleteController do ...@@ -329,8 +329,8 @@ describe AutocompleteController do
context 'authorized projects apply limit' do context 'authorized projects apply limit' do
before do before do
authorized_project2 = create(:empty_project) authorized_project2 = create(:project)
authorized_project3 = create(:empty_project) authorized_project3 = create(:project)
authorized_project.add_master(user) authorized_project.add_master(user)
authorized_project2.add_master(user) authorized_project2.add_master(user)
...@@ -355,8 +355,8 @@ describe AutocompleteController do ...@@ -355,8 +355,8 @@ describe AutocompleteController do
context 'authorized projects with offset' do context 'authorized projects with offset' do
before do before do
authorized_project2 = create(:empty_project) authorized_project2 = create(:project)
authorized_project3 = create(:empty_project) authorized_project3 = create(:project)
authorized_project.add_master(user) authorized_project.add_master(user)
authorized_project2.add_master(user) authorized_project2.add_master(user)
......
require 'spec_helper' require 'spec_helper'
describe Dashboard::LabelsController do describe Dashboard::LabelsController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
...@@ -11,7 +11,7 @@ describe Dashboard::LabelsController do ...@@ -11,7 +11,7 @@ describe Dashboard::LabelsController do
end end
describe "#index" do describe "#index" do
let!(:unrelated_label) { create(:label, project: create(:empty_project, :public)) } let!(:unrelated_label) { create(:label, project: create(:project, :public)) }
it 'returns global labels for projects the user has a relationship with' do it 'returns global labels for projects the user has a relationship with' do
get :index, format: :json get :index, format: :json
......
require 'spec_helper' require 'spec_helper'
describe Dashboard::MilestonesController do describe Dashboard::MilestonesController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) } let(:project_milestone) { create(:milestone, project: project) }
let(:milestone) do let(:milestone) do
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Dashboard::TodosController do describe Dashboard::TodosController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:author) { create(:user) } let(:author) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:todo_service) { TodoService.new } let(:todo_service) { TodoService.new }
before do before do
...@@ -14,7 +14,7 @@ describe Dashboard::TodosController do ...@@ -14,7 +14,7 @@ describe Dashboard::TodosController do
describe 'GET #index' do describe 'GET #index' do
context 'project authorization' do context 'project authorization' do
it 'renders 404 when user does not have read access on given project' do it 'renders 404 when user does not have read access on given project' do
unauthorized_project = create(:empty_project, :private) unauthorized_project = create(:project, :private)
get :index, project_id: unauthorized_project.id get :index, project_id: unauthorized_project.id
...@@ -34,7 +34,7 @@ describe Dashboard::TodosController do ...@@ -34,7 +34,7 @@ describe Dashboard::TodosController do
end end
it 'renders 200 when user has access on given project' do it 'renders 200 when user has access on given project' do
authorized_project = create(:empty_project, :public) authorized_project = create(:project, :public)
get :index, project_id: authorized_project.id get :index, project_id: authorized_project.id
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe DashboardController do describe DashboardController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:project) }
before do before do
project.team << [user, :master] project.team << [user, :master]
......
require 'spec_helper' require 'spec_helper'
describe Projects::BoardsController do # rubocop:disable RSpec/FilePath describe Projects::BoardsController do # rubocop:disable RSpec/FilePath
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
require('spec_helper') require('spec_helper')
describe ProjectsController do # rubocop:disable RSpec/FilePath describe ProjectsController do # rubocop:disable RSpec/FilePath
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -3,8 +3,8 @@ require 'spec_helper' ...@@ -3,8 +3,8 @@ require 'spec_helper'
describe Explore::ProjectsController do describe Explore::ProjectsController do
describe 'GET #trending' do describe 'GET #trending' do
context 'sorting by update date' do context 'sorting by update date' do
let(:project1) { create(:empty_project, :public, updated_at: 3.days.ago) } let(:project1) { create(:project, :public, updated_at: 3.days.ago) }
let(:project2) { create(:empty_project, :public, updated_at: 1.day.ago) } let(:project2) { create(:project, :public, updated_at: 1.day.ago) }
before do before do
create(:trending_project, project: project1) create(:trending_project, project: project1)
......
...@@ -2,8 +2,8 @@ require 'spec_helper' ...@@ -2,8 +2,8 @@ require 'spec_helper'
describe Groups::MilestonesController do describe Groups::MilestonesController do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:project) { create(:empty_project, group: group) } let!(:project) { create(:project, group: group) }
let!(:project2) { create(:empty_project, group: group) } let!(:project2) { create(:project, group: group) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:title) { '肯定不是中文的问题' } let(:title) { '肯定不是中文的问题' }
let(:milestone) do let(:milestone) do
......
...@@ -3,7 +3,7 @@ require 'rails_helper' ...@@ -3,7 +3,7 @@ require 'rails_helper'
describe GroupsController do describe GroupsController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
let(:project) { create(:empty_project, namespace: group) } let(:project) { create(:project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) } let!(:group_member) { create(:group_member, group: group, user: user) }
describe 'GET #index' do describe 'GET #index' do
......
...@@ -52,7 +52,7 @@ describe Import::BitbucketController do ...@@ -52,7 +52,7 @@ describe Import::BitbucketController do
end end
it "assigns variables" do it "assigns variables" do
@project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id) @project = create(:project, import_type: 'bitbucket', creator_id: user.id)
allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo])
get :status get :status
...@@ -63,7 +63,7 @@ describe Import::BitbucketController do ...@@ -63,7 +63,7 @@ describe Import::BitbucketController do
end end
it "does not show already added project" do it "does not show already added project" do
@project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim')
allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo])
get :status get :status
......
...@@ -16,7 +16,7 @@ describe Import::FogbugzController do ...@@ -16,7 +16,7 @@ describe Import::FogbugzController do
end end
it 'assigns variables' do it 'assigns variables' do
@project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id) @project = create(:project, import_type: 'fogbugz', creator_id: user.id)
stub_client(repos: [@repo]) stub_client(repos: [@repo])
get :status get :status
...@@ -26,7 +26,7 @@ describe Import::FogbugzController do ...@@ -26,7 +26,7 @@ describe Import::FogbugzController do
end end
it 'does not show already added project' do it 'does not show already added project' do
@project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim') @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim')
stub_client(repos: [@repo]) stub_client(repos: [@repo])
get :status get :status
......
...@@ -36,7 +36,7 @@ describe Import::GitlabController do ...@@ -36,7 +36,7 @@ describe Import::GitlabController do
end end
it "assigns variables" do it "assigns variables" do
@project = create(:empty_project, import_type: 'gitlab', creator_id: user.id) @project = create(:project, import_type: 'gitlab', creator_id: user.id)
stub_client(projects: [@repo]) stub_client(projects: [@repo])
get :status get :status
...@@ -46,7 +46,7 @@ describe Import::GitlabController do ...@@ -46,7 +46,7 @@ describe Import::GitlabController do
end end
it "does not show already added project" do it "does not show already added project" do
@project = create(:empty_project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') @project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim')
stub_client(projects: [@repo]) stub_client(projects: [@repo])
get :status get :status
......
...@@ -27,7 +27,7 @@ describe Import::GoogleCodeController do ...@@ -27,7 +27,7 @@ describe Import::GoogleCodeController do
end end
it "assigns variables" do it "assigns variables" do
@project = create(:empty_project, import_type: 'google_code', creator_id: user.id) @project = create(:project, import_type: 'google_code', creator_id: user.id)
stub_client(repos: [@repo], incompatible_repos: []) stub_client(repos: [@repo], incompatible_repos: [])
get :status get :status
...@@ -38,7 +38,7 @@ describe Import::GoogleCodeController do ...@@ -38,7 +38,7 @@ describe Import::GoogleCodeController do
end end
it "does not show already added project" do it "does not show already added project" do
@project = create(:empty_project, import_type: 'google_code', creator_id: user.id, import_source: 'vim') @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
stub_client(repos: [@repo], incompatible_repos: []) stub_client(repos: [@repo], incompatible_repos: [])
get :status get :status
......
require 'spec_helper' require 'spec_helper'
describe NotificationSettingsController do describe NotificationSettingsController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:group) { create(:group, :internal) } let(:group) { create(:group, :internal) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -99,7 +99,7 @@ describe NotificationSettingsController do ...@@ -99,7 +99,7 @@ describe NotificationSettingsController do
end end
context 'not authorized' do context 'not authorized' do
let(:private_project) { create(:empty_project, :private) } let(:private_project) { create(:project, :private) }
before do before do
sign_in(user) sign_in(user)
......
...@@ -3,7 +3,7 @@ require 'rails_helper' ...@@ -3,7 +3,7 @@ require 'rails_helper'
describe Projects::ApproverGroupsController do describe Projects::ApproverGroupsController do
describe '#destroy' do describe '#destroy' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
before do before do
......
...@@ -3,7 +3,7 @@ require 'rails_helper' ...@@ -3,7 +3,7 @@ require 'rails_helper'
describe Projects::ApproversController do describe Projects::ApproversController do
describe '#destroy' do describe '#destroy' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
before do before do
......
...@@ -65,26 +65,61 @@ describe Projects::ArtifactsController do ...@@ -65,26 +65,61 @@ describe Projects::ArtifactsController do
end end
describe 'GET raw' do describe 'GET raw' do
subject { get(:raw, namespace_id: project.namespace, project_id: project, job_id: job, path: path) }
context 'when the file exists' do context 'when the file exists' do
it 'serves the file using workhorse' do let(:path) { 'ci_artifacts.txt' }
get :raw, namespace_id: project.namespace, project_id: project, job_id: job, path: 'ci_artifacts.txt' let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline, artifacts_file_store: store, artifacts_metadata_store: store) }
shared_examples 'a valid file' do
it 'serves the file using workhorse' do
subject
send_data = response.headers[Gitlab::Workhorse::SEND_DATA_HEADER] expect(send_data).to start_with('artifacts-entry:')
expect(params.keys).to eq(%w(Archive Entry))
expect(params['Archive']).to start_with(archive_path)
# On object storage, the URL can end with a query string
expect(params['Archive']).to match(/build_artifacts.zip(\?[^?]+)?$/)
expect(params['Entry']).to eq(Base64.encode64('ci_artifacts.txt'))
end
expect(send_data).to start_with('artifacts-entry:') def send_data
response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]
end
base64_params = send_data.sub(/\Aartifacts\-entry:/, '') def params
params = JSON.parse(Base64.urlsafe_decode64(base64_params)) @params ||= begin
base64_params = send_data.sub(/\Aartifacts\-entry:/, '')
JSON.parse(Base64.urlsafe_decode64(base64_params))
end
end
end
expect(params.keys).to eq(%w(Archive Entry)) context 'when using local file storage' do
expect(params['Archive']).to end_with('build_artifacts.zip') it_behaves_like 'a valid file' do
expect(params['Entry']).to eq(Base64.encode64('ci_artifacts.txt')) let(:store) { ObjectStoreUploader::LOCAL_STORE }
let(:archive_path) { ArtifactUploader.local_artifacts_store }
end
end
context 'when using remote file storage' do
before do
stub_artifacts_object_storage
end
it_behaves_like 'a valid file' do
let(:store) { ObjectStoreUploader::REMOTE_STORE }
let(:archive_path) { 'https://' }
end
end end
end end
context 'when the file does not exist' do context 'when the file does not exist' do
let(:path) { 'unknown' }
it 'responds Not Found' do it 'responds Not Found' do
get :raw, namespace_id: project.namespace, project_id: project, job_id: job, path: 'unknown' subject
expect(response).to be_not_found expect(response).to be_not_found
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::AvatarsController do describe Projects::AvatarsController do
let(:project) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -193,7 +193,7 @@ describe Projects::BlobController do ...@@ -193,7 +193,7 @@ describe Projects::BlobController do
context "when user doesn't have access" do context "when user doesn't have access" do
before do before do
other_project = create(:empty_project) other_project = create(:project, :repository)
merge_request.update!(source_project: other_project, target_project: other_project) merge_request.update!(source_project: other_project, target_project: other_project)
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::Boards::IssuesController do describe Projects::Boards::IssuesController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:board) { create(:board, project: project) } let(:board) { create(:board, project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
......
require 'spec_helper' require 'spec_helper'
describe Projects::Boards::ListsController do describe Projects::Boards::ListsController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:board) { create(:board, project: project) } let(:board) { create(:board, project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
......
require 'spec_helper' require 'spec_helper'
describe Projects::BoardsController do describe Projects::BoardsController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -96,7 +96,7 @@ describe Projects::BranchesController do ...@@ -96,7 +96,7 @@ describe Projects::BranchesController do
end end
context 'repository-less project' do context 'repository-less project' do
let(:project) { create :empty_project } let(:project) { create :project }
it 'redirects to newly created branch' do it 'redirects to newly created branch' do
result = { status: :success, branch: double(name: branch) } result = { status: :success, branch: double(name: branch) }
......
...@@ -24,8 +24,8 @@ describe Projects::DeployKeysController do ...@@ -24,8 +24,8 @@ describe Projects::DeployKeysController do
end end
context 'when json requested' do context 'when json requested' do
let(:project2) { create(:empty_project, :internal)} let(:project2) { create(:project, :internal)}
let(:project_private) { create(:empty_project, :private)} let(:project_private) { create(:project, :private)}
let(:deploy_key_internal) do let(:deploy_key_internal) do
create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com') create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com')
......
...@@ -4,7 +4,7 @@ describe Projects::DeploymentsController do ...@@ -4,7 +4,7 @@ describe Projects::DeploymentsController do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:environment) { create(:environment, name: 'production', project: project) } let(:environment) { create(:environment, name: 'production', project: project) }
before do before do
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::EnvironmentsController do describe Projects::EnvironmentsController do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:project) { create(:empty_project) } set(:project) { create(:project) }
set(:environment) do set(:environment) do
create(:environment, name: 'production', project: project) create(:environment, name: 'production', project: project)
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::GroupLinksController do describe Projects::GroupLinksController do
let(:group) { create(:group, :private) } let(:group) { create(:group, :private) }
let(:group2) { create(:group, :private) } let(:group2) { create(:group, :private) }
let(:project) { create(:empty_project, :private, group: group2) } let(:project) { create(:project, :private, group: group2) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
require 'spec_helper' require 'spec_helper'
describe Projects::HooksController do describe Projects::HooksController do
let(:project) { create(:empty_project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment