Merge branch '35616-move-k8-to-cluster-page' into cluster-page-with-list-clusters

This commit is contained in:
Kamil Trzcinski 2017-12-05 13:29:41 +01:00
commit 9429514e30
164 changed files with 4319 additions and 1000 deletions

View File

@ -1 +1 @@
0.55.0
0.56.0

View File

@ -1 +1 @@
5.9.4
5.10.0

View File

@ -1,6 +1,6 @@
source 'https://rubygems.org'
gem 'rails', '4.2.8'
gem 'rails', '4.2.10'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
@ -400,7 +400,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.58.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false

View File

@ -4,38 +4,38 @@ GEM
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.2)
actionmailer (4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
actionmailer (4.2.10)
actionpack (= 4.2.10)
actionview (= 4.2.10)
activejob (= 4.2.10)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.8)
actionview (= 4.2.8)
activesupport (= 4.2.8)
actionpack (4.2.10)
actionview (= 4.2.10)
activesupport (= 4.2.10)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.8)
activesupport (= 4.2.8)
actionview (4.2.10)
activesupport (= 4.2.10)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.8)
activesupport (= 4.2.8)
activejob (4.2.10)
activesupport (= 4.2.10)
globalid (>= 0.3.0)
activemodel (4.2.8)
activesupport (= 4.2.8)
activemodel (4.2.10)
activesupport (= 4.2.10)
builder (~> 3.1)
activerecord (4.2.8)
activemodel (= 4.2.8)
activesupport (= 4.2.8)
activerecord (4.2.10)
activemodel (= 4.2.10)
activesupport (= 4.2.10)
arel (~> 6.0)
activerecord_sane_schema_dumper (0.2)
rails (>= 4, < 5)
activesupport (4.2.8)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
@ -276,7 +276,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly-proto (0.54.0)
gitaly-proto (0.58.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@ -300,8 +300,8 @@ GEM
omniauth (~> 1.3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
globalid (0.3.7)
activesupport (>= 4.1.0)
globalid (0.4.1)
activesupport (>= 4.2.0)
gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.2.7)
@ -400,7 +400,8 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.2)
i18n (0.8.6)
i18n (0.9.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb (0.2.3)
cause
@ -474,8 +475,8 @@ GEM
railties (>= 4, < 5.2)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.6)
mime-types (>= 1.16, < 4)
mail (2.7.0)
mini_mime (>= 0.1.1)
mail_room (0.9.1)
memoist (0.16.0)
memoizable (0.4.2)
@ -573,8 +574,8 @@ GEM
parallel (1.12.0)
paranoia (2.3.1)
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
ast (~> 2.2)
parser (2.4.0.2)
ast (~> 2.3)
parslet (1.5.0)
blankslate (~> 2.0)
path_expander (1.0.1)
@ -656,16 +657,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.8)
actionmailer (= 4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
activemodel (= 4.2.8)
activerecord (= 4.2.8)
activesupport (= 4.2.8)
rails (4.2.10)
actionmailer (= 4.2.10)
actionpack (= 4.2.10)
actionview (= 4.2.10)
activejob (= 4.2.10)
activemodel (= 4.2.10)
activerecord (= 4.2.10)
activesupport (= 4.2.10)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.8)
railties (= 4.2.10)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
@ -678,15 +679,15 @@ GEM
rails-i18n (4.0.9)
i18n (~> 0.7)
railties (~> 4.0)
railties (4.2.8)
actionpack (= 4.2.8)
activesupport (= 4.2.8)
railties (4.2.10)
actionpack (= 4.2.10)
activesupport (= 4.2.10)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
raindrops (0.18.0)
rake (12.1.0)
rake (12.3.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rbnacl (4.0.2)
@ -873,7 +874,7 @@ GEM
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.0)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
@ -911,7 +912,7 @@ GEM
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
tzinfo (1.2.3)
tzinfo (1.2.4)
thread_safe (~> 0.1)
u2f (0.2.1)
uber (0.1.0)
@ -1036,7 +1037,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.54.0)
gitaly-proto (~> 0.58.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2)
@ -1118,7 +1119,7 @@ DEPENDENCIES
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
rails (= 4.2.8)
rails (= 4.2.10)
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 4.0.9)
rainbow (~> 2.2)

View File

@ -20,6 +20,7 @@ class ListIssue {
this.isFetching = {
subscriptions: true,
};
this.isLoading = {};
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
@ -86,6 +87,10 @@ class ListIssue {
this.isFetching[key] = value;
}
setLoadingState(key, value) {
this.isLoading[key] = value;
}
update (url) {
const data = {
issue: {

View File

@ -3,3 +3,4 @@ import './polyfills';
import './jquery';
import './bootstrap';
import './vue';
import '../lib/utils/axios_utils';

View File

@ -514,10 +514,11 @@ GitLabDropdown = (function() {
const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle');
const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update');
const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open');
const hasMultiSelect = dropdownToggle.hasClass('js-multiselect');
// Makes indeterminate items effective
if (this.fullData && hasFilterBulkUpdate) {
if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) {
this.parseData(this.fullData);
}

View File

@ -1,8 +1,12 @@
<script>
import Icon from '../../vue_shared/components/icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import Issuable from '~/vue_shared/mixins/issuable';
export default {
component: {
mixins: [
Issuable,
],
components: {
Icon,
},
};
@ -16,7 +20,7 @@
:size="16"
class="icon">
</icon>
<span>This issue is locked. Only <b>project members</b> can comment.</span>
<span>This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment.</span>
</span>
</div>
</template>

View File

@ -8,8 +8,8 @@
import * as constants from '../constants';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
import issueDiscussionLockedWidget from './issue_discussion_locked_widget.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import markdownField from '../../vue_shared/components/markdown/field.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issuableStateMixin from '../mixins/issuable_state';
@ -29,8 +29,8 @@
},
components: {
issueWarning,
issueNoteSignedOutWidget,
issueDiscussionLockedWidget,
noteSignedOutWidget,
discussionLockedWidget,
markdownField,
userAvatarLink,
},
@ -240,8 +240,11 @@
<template>
<div>
<issue-note-signed-out-widget v-if="!isLoggedIn" />
<issue-discussion-locked-widget v-else-if="!canCreateNote" />
<note-signed-out-widget v-if="!isLoggedIn" />
<discussion-locked-widget
issuable-type="issue"
v-else-if="!canCreateNote"
/>
<ul
v-else
class="notes notes-form timeline">

View File

@ -4,10 +4,9 @@
import { SYSTEM_NOTE } from '../constants';
import issueNote from './issue_note.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issueNoteHeader from './issue_note_header.vue';
import issueNoteActions from './issue_note_actions.vue';
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
import issueNoteEditedText from './issue_note_edited_text.vue';
import noteHeader from './note_header.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import noteEditedText from './note_edited_text.vue';
import issueNoteForm from './issue_note_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
@ -28,10 +27,9 @@
components: {
issueNote,
userAvatarLink,
issueNoteHeader,
issueNoteActions,
issueNoteSignedOutWidget,
issueNoteEditedText,
noteHeader,
noteSignedOutWidget,
noteEditedText,
issueNoteForm,
placeholderNote,
placeholderSystemNote,
@ -171,7 +169,7 @@
<div class="timeline-content">
<div class="discussion">
<div class="discussion-header">
<issue-note-header
<note-header
:author="author"
:created-at="discussion.created_at"
:note-id="discussion.id"
@ -179,8 +177,8 @@
@toggleHandler="toggleDiscussionHandler"
action-text="started a discussion"
class="discussion"
/>
<issue-note-edited-text
/>
<note-edited-text
v-if="lastUpdatedAt"
:edited-at="lastUpdatedAt"
:edited-by="lastUpdatedBy"
@ -220,7 +218,7 @@
@cancelFormEdition="cancelReplyForm"
ref="noteForm"
/>
<issue-note-signed-out-widget v-if="!canReply" />
<note-signed-out-widget v-if="!canReply" />
</div>
</div>
</div>

View File

@ -2,8 +2,8 @@
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issueNoteHeader from './issue_note_header.vue';
import issueNoteActions from './issue_note_actions.vue';
import noteHeader from './note_header.vue';
import noteActions from './note_actions.vue';
import issueNoteBody from './issue_note_body.vue';
import eventHub from '../event_hub';
@ -23,8 +23,8 @@
},
components: {
userAvatarLink,
issueNoteHeader,
issueNoteActions,
noteHeader,
noteActions,
issueNoteBody,
},
computed: {
@ -155,13 +155,13 @@
</div>
<div class="timeline-content">
<div class="note-header">
<issue-note-header
<note-header
:author="author"
:created-at="note.created_at"
:note-id="note.id"
action-text="commented"
/>
<issue-note-actions
<note-actions
:author-id="author.id"
:note-id="note.id"
:access-level="note.human_access"

View File

@ -1,7 +1,7 @@
<script>
import issueNoteEditedText from './issue_note_edited_text.vue';
import issueNoteAwardsList from './issue_note_awards_list.vue';
import issueNoteAttachment from './issue_note_attachment.vue';
import noteEditedText from './note_edited_text.vue';
import noteAwardsList from './note_awards_list.vue';
import noteAttachment from './note_attachment.vue';
import issueNoteForm from './issue_note_form.vue';
import TaskList from '../../task_list';
import autosave from '../mixins/autosave';
@ -26,9 +26,9 @@
autosave,
],
components: {
issueNoteEditedText,
issueNoteAwardsList,
issueNoteAttachment,
noteEditedText,
noteAwardsList,
noteAttachment,
issueNoteForm,
},
computed: {
@ -101,20 +101,20 @@
v-model="note.note"
:data-update-url="note.path"
class="hidden js-task-list-field"></textarea>
<issue-note-edited-text
<note-edited-text
v-if="note.last_edited_at"
:edited-at="note.last_edited_at"
:edited-by="note.last_edited_by"
action-text="Edited"
/>
<issue-note-awards-list
<note-awards-list
v-if="note.award_emoji.length"
:note-id="note.id"
:note-author-id="note.author.id"
:awards="note.award_emoji"
:toggle-award-path="note.toggle_award_path"
/>
<issue-note-attachment
<note-attachment
v-if="note.attachment"
:attachment="note.attachment"
/>

View File

@ -5,11 +5,11 @@
import emojiSmiley from 'icons/_emoji_smiley.svg';
import editSvg from 'icons/_icon_pencil.svg';
import ellipsisSvg from 'icons/_ellipsis_v.svg';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
name: 'issueNoteActions',
name: 'noteActions',
props: {
authorId: {
type: Number,

View File

@ -1,6 +1,6 @@
<script>
export default {
name: 'issueNoteAttachment',
name: 'noteAttachment',
props: {
attachment: {
type: Object,

View File

@ -2,7 +2,6 @@
import { mapGetters } from 'vuex';
export default {
name: 'singInLinksNotes',
computed: {
...mapGetters([
'getNotesDataByProp',

View File

@ -78,11 +78,13 @@
<div class="ci-job-component">
<a
v-tooltip
v-if="job.status.details_path"
v-if="job.status.has_details"
:href="job.status.details_path"
:title="tooltipText"
:class="cssClassJobName"
data-container="body">
data-container="body"
class="js-pipeline-graph-job-link"
>
<job-name-component
:name="job.name"
@ -95,7 +97,8 @@
v-tooltip
:title="tooltipText"
:class="cssClassJobName"
data-container="body">
data-container="body"
>
<job-name-component
:name="job.name"

View File

@ -15,7 +15,7 @@ import Cookies from 'js-cookie';
Sidebar.prototype.removeListeners = function () {
this.sidebar.off('click', '.sidebar-collapsed-icon');
$('.dropdown').off('hidden.gl.dropdown');
this.sidebar.off('hidden.gl.dropdown');
$('.dropdown').off('loading.gl.dropdown');
$('.dropdown').off('loaded.gl.dropdown');
$(document).off('click', '.js-sidebar-toggle');
@ -25,7 +25,7 @@ import Cookies from 'js-cookie';
const $document = $(document);
this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
$('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
this.sidebar.on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
@ -180,7 +180,7 @@ import Cookies from 'js-cookie';
var $block, sidebar;
sidebar = e.data;
e.preventDefault();
$block = $(this).closest('.block');
$block = $(e.target).closest('.block');
return sidebar.sidebarDropdownHidden($block);
};

View File

@ -18,11 +18,6 @@ export default {
required: true,
type: Function,
},
issuableType: {
required: true,
type: String,
},
},
mixins: [
@ -39,13 +34,13 @@ export default {
<div class="dropdown open">
<div class="dropdown-menu sidebar-item-warning-message">
<p class="text" v-if="isLocked">
Unlock this {{ issuableDisplayName(issuableType) }}?
Unlock this {{ issuableDisplayName }}?
<strong>Everyone</strong>
will be able to comment.
</p>
<p class="text" v-else>
Lock this {{ issuableDisplayName(issuableType) }}?
Lock this {{ issuableDisplayName }}?
Only
<strong>project members</strong>
will be able to comment.

View File

@ -23,11 +23,6 @@ export default {
return mediatorObject.service && mediatorObject.service.update && mediatorObject.store;
},
},
issuableType: {
required: true,
type: String,
},
},
mixins: [
@ -59,7 +54,7 @@ export default {
discussion_locked: locked,
})
.then(() => location.reload())
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName(this.issuableType)}`)));
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`)));
},
},
};
@ -77,7 +72,7 @@ export default {
</div>
<div class="title hide-collapsed">
Lock {{issuableDisplayName(issuableType) }}
Lock {{ issuableDisplayName }}
<button
v-if="isEditable"
class="pull-right lock-edit btn btn-blank"

View File

@ -0,0 +1,104 @@
import Vue from 'vue';
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking';
import SidebarAssignees from './components/assignees/sidebar_assignees';
import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue';
import SidebarMoveIssue from './lib/sidebar_move_issue';
import LockIssueSidebar from './components/lock/lock_issue_sidebar.vue';
import sidebarParticipants from './components/participants/sidebar_participants.vue';
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
import Translate from '../vue_shared/translate';
Vue.use(Translate);
function mountConfidentialComponent(mediator) {
const el = document.getElementById('js-confidential-entry-point');
if (!el) return;
const dataNode = document.getElementById('js-confidential-issue-data');
const initialData = JSON.parse(dataNode.innerHTML);
const ConfidentialComp = Vue.extend(ConfidentialIssueSidebar);
new ConfidentialComp({
propsData: {
isConfidential: initialData.is_confidential,
isEditable: initialData.is_editable,
service: mediator.service,
},
}).$mount(el);
}
function mountLockComponent(mediator) {
const el = document.getElementById('js-lock-entry-point');
if (!el) return;
const dataNode = document.getElementById('js-lock-issue-data');
const initialData = JSON.parse(dataNode.innerHTML);
const LockComp = Vue.extend(LockIssueSidebar);
new LockComp({
propsData: {
isLocked: initialData.is_locked,
isEditable: initialData.is_editable,
mediator,
issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
},
}).$mount(el);
}
function mountParticipantsComponent() {
const el = document.querySelector('.js-sidebar-participants-entry-point');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
sidebarParticipants,
},
render: createElement => createElement('sidebar-participants', {}),
});
}
function mountSubscriptionsComponent() {
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
sidebarSubscriptions,
},
render: createElement => createElement('sidebar-subscriptions', {}),
});
}
function mount(mediator) {
const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
// Only create the sidebarAssignees vue app if it is found in the DOM
// We currently do not use sidebarAssignees for the MR page
if (sidebarAssigneesEl) {
new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
}
mountConfidentialComponent(mediator);
mountLockComponent(mediator);
mountParticipantsComponent();
mountSubscriptionsComponent();
new SidebarMoveIssue(
mediator,
$('.js-move-issue'),
$('.js-move-issue-confirmation-button'),
).init();
new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
}
export default mount;

View File

@ -1,110 +1,12 @@
import Vue from 'vue';
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking';
import SidebarAssignees from './components/assignees/sidebar_assignees';
import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue';
import SidebarMoveIssue from './lib/sidebar_move_issue';
import LockIssueSidebar from './components/lock/lock_issue_sidebar.vue';
import sidebarParticipants from './components/participants/sidebar_participants.vue';
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
import Translate from '../vue_shared/translate';
import Mediator from './sidebar_mediator';
Vue.use(Translate);
function mountConfidentialComponent(mediator) {
const el = document.getElementById('js-confidential-entry-point');
if (!el) return;
const dataNode = document.getElementById('js-confidential-issue-data');
const initialData = JSON.parse(dataNode.innerHTML);
const ConfidentialComp = Vue.extend(ConfidentialIssueSidebar);
new ConfidentialComp({
propsData: {
isConfidential: initialData.is_confidential,
isEditable: initialData.is_editable,
service: mediator.service,
},
}).$mount(el);
}
function mountLockComponent(mediator) {
const el = document.getElementById('js-lock-entry-point');
if (!el) return;
const dataNode = document.getElementById('js-lock-issue-data');
const initialData = JSON.parse(dataNode.innerHTML);
const LockComp = Vue.extend(LockIssueSidebar);
new LockComp({
propsData: {
isLocked: initialData.is_locked,
isEditable: initialData.is_editable,
mediator,
issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
},
}).$mount(el);
}
function mountParticipantsComponent() {
const el = document.querySelector('.js-sidebar-participants-entry-point');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
sidebarParticipants,
},
render: createElement => createElement('sidebar-participants', {}),
});
}
function mountSubscriptionsComponent() {
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
sidebarSubscriptions,
},
render: createElement => createElement('sidebar-subscriptions', {}),
});
}
import mountSidebar from './mount_sidebar';
function domContentLoaded() {
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
const mediator = new Mediator(sidebarOptions);
mediator.fetch();
const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
// Only create the sidebarAssignees vue app if it is found in the DOM
// We currently do not use sidebarAssignees for the MR page
if (sidebarAssigneesEl) {
new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
}
mountConfidentialComponent(mediator);
mountLockComponent(mediator);
mountParticipantsComponent();
mountSubscriptionsComponent();
new SidebarMoveIssue(
mediator,
$('.js-move-issue'),
$('.js-move-issue-confirmation-button'),
).init();
new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
mountSidebar(mediator);
}
document.addEventListener('DOMContentLoaded', domContentLoaded);

View File

@ -5,19 +5,23 @@ import Store from './stores/sidebar_store';
export default class SidebarMediator {
constructor(options) {
if (!SidebarMediator.singleton) {
this.store = new Store(options);
this.service = new Service({
endpoint: options.endpoint,
toggleSubscriptionEndpoint: options.toggleSubscriptionEndpoint,
moveIssueEndpoint: options.moveIssueEndpoint,
projectsAutocompleteEndpoint: options.projectsAutocompleteEndpoint,
});
SidebarMediator.singleton = this;
this.initSingleton(options);
}
return SidebarMediator.singleton;
}
initSingleton(options) {
this.store = new Store(options);
this.service = new Service({
endpoint: options.endpoint,
toggleSubscriptionEndpoint: options.toggleSubscriptionEndpoint,
moveIssueEndpoint: options.moveIssueEndpoint,
projectsAutocompleteEndpoint: options.projectsAutocompleteEndpoint,
});
SidebarMediator.singleton = this;
}
assignYourself() {
this.store.addAssignee(this.store.currentUser);
}
@ -35,17 +39,21 @@ export default class SidebarMediator {
}
fetch() {
this.service.get()
return this.service.get()
.then(response => response.json())
.then((data) => {
this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data);
this.store.setParticipantsData(data);
this.store.setSubscriptionsData(data);
this.processFetchedData(data);
})
.catch(() => new Flash('Error occurred when fetching sidebar data'));
}
processFetchedData(data) {
this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data);
this.store.setParticipantsData(data);
this.store.setSubscriptionsData(data);
}
toggleSubscription() {
this.store.setFetchingState('subscriptions', true);
return this.service.toggleSubscription()

View File

@ -15,6 +15,7 @@ export default class SidebarStore {
participants: true,
subscriptions: true,
};
this.isLoading = {};
this.autocompleteProjects = [];
this.moveToProjectId = 0;
this.isLockDialogOpen = false;
@ -55,6 +56,10 @@ export default class SidebarStore {
this.isFetching[key] = value;
}
setLoadingState(key, value) {
this.isLoading[key] = value;
}
addAssignee(assignee) {
if (!this.findAssignee(assignee)) {
this.assignees.push(assignee);

View File

@ -17,7 +17,7 @@
* />
*/
import { mapGetters } from 'vuex';
import issueNoteHeader from '../../../notes/components/issue_note_header.vue';
import noteHeader from '~/notes/components/note_header.vue';
import { spriteIcon } from '../../../lib/utils/common_utils';
export default {
@ -29,7 +29,7 @@
},
},
components: {
issueNoteHeader,
noteHeader,
},
computed: {
...mapGetters([
@ -60,12 +60,12 @@
</div>
<div class="timeline-content">
<div class="note-header">
<issue-note-header
<note-header
:author="note.author"
:created-at="note.created_at"
:note-id="note.id"
:action-text-html="note.note_html"
/>
/>
</div>
</div>
</div>

View File

@ -1,9 +1,14 @@
export default {
methods: {
issuableDisplayName(issuableType) {
const displayName = issuableType.replace(/_/, ' ');
props: {
issuableType: {
required: true,
type: String,
},
},
return this.__ ? this.__(displayName) : displayName;
computed: {
issuableDisplayName() {
return this.issuableType.replace(/_/g, ' ');
},
},
};

View File

@ -39,7 +39,6 @@
color: $brand-info;
}
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: $hint-color; }
.light { color: $common-gray; }

View File

@ -14,6 +14,5 @@
&:hover {
background-color: $user-mention-bg-hover;
text-decoration: none;
}
}

View File

@ -20,6 +20,11 @@
.ref-name {
font-size: 12px;
&:hover {
text-decoration: underline;
color: $gl-text-color;
}
}
}

View File

@ -110,6 +110,10 @@
padding: 6px 10px;
border-radius: $label-border-radius;
}
&:hover .color-label {
text-decoration: underline;
}
}
&.has-labels {
@ -174,6 +178,14 @@
color: $gray-darkest;
}
}
&.assignee {
.author_link:hover {
.author {
text-decoration: underline;
}
}
}
}
.block-first {
@ -468,7 +480,6 @@
a:not(.btn-retry) {
&:hover {
color: $md-link-color;
text-decoration: none;
.avatar {
border-color: rgba($avatar-border, .2);

View File

@ -208,7 +208,6 @@ ul.notes {
a {
color: $gl-link-color;
text-decoration: none;
}
p {
@ -395,6 +394,10 @@ ul.notes {
&:focus,
&:hover {
text-decoration: none;
.note-header-author-name {
text-decoration: underline;
}
}
}
@ -461,6 +464,10 @@ ul.notes {
.system-note-message {
white-space: normal;
}
a:hover {
text-decoration: underline;
}
}
/**

View File

@ -73,7 +73,7 @@
.profile-link-holder {
display: inline;
a {
a:not(.text-link) {
text-decoration: none;
}
}

View File

@ -724,6 +724,7 @@ a.deploy-project-label {
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
}
}
}

View File

@ -124,7 +124,11 @@
&:hover,
&.active {
color: $black;
text-decoration: none;
span {
text-decoration: underline;
}
}
}

View File

@ -11,7 +11,7 @@ class Projects::Clusters::UserController < Projects::ApplicationController
def create
@cluster = ::Clusters::CreateService
.new(project, current_user, create_params)
.execute(nil)
.execute
if @cluster.persisted?
redirect_to project_cluster_path(project, @cluster)

View File

@ -22,7 +22,7 @@ class Projects::ClustersController < Projects::ApplicationController
def status
respond_to do |format|
format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000)
Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
render json: ClusterSerializer
.new(project: @project, current_user: @current_user)
@ -70,7 +70,8 @@ class Projects::ClustersController < Projects::ApplicationController
private
def cluster
@cluster ||= project.clusters.find_by(id: params[:id])&.present(current_user: current_user) || render_404
@cluster ||= project.clusters.find(params[:id])
.present(current_user: current_user)
end
def create_params

View File

@ -25,7 +25,7 @@ class UsersFinder
end
def execute
users = User.all
users = User.all.order_id_desc
users = by_username(users)
users = by_search(users)
users = by_blocked(users)

View File

@ -63,7 +63,7 @@ module CommitsHelper
# Returns a link formatted as a commit branch link
def commit_branch_link(url, text)
link_to(url, class: 'label label-gray ref-name branch-link') do
icon('code-fork') + " #{text}"
icon('code-fork', class: 'append-right-5') + "#{text}"
end
end
@ -77,7 +77,7 @@ module CommitsHelper
# Returns a link formatted as a commit tag link
def commit_tag_link(url, text)
link_to(url, class: 'label label-gray ref-name') do
icon('tag') + " #{text}"
icon('tag', class: 'append-right-5') + "#{text}"
end
end

View File

@ -113,7 +113,13 @@ module MarkupHelper
text = wiki_page.content
return '' unless text.present?
context = { pipeline: :wiki, project: @project, project_wiki: @project_wiki, page_slug: wiki_page.slug }
context = {
pipeline: :wiki,
project: @project,
project_wiki: @project_wiki,
page_slug: wiki_page.slug,
issuable_state_filter_enabled: true
}
html =
case wiki_page.format

View File

@ -97,11 +97,6 @@ module Clusters
return false
end
if managed? && name_changed?
errors.add(:base, "cannot modify cluster name")
return false
end
true
end
end

View File

@ -289,6 +289,14 @@ class Group < Namespace
"#{parent.full_path}/#{path_was}"
end
def group_member(user)
if group_members.loaded?
group_members.find { |gm| gm.user_id == user.id }
else
group_members.find_by(user_id: user)
end
end
private
def update_two_factor_requirement

View File

@ -139,7 +139,17 @@ class Namespace < ActiveRecord::Base
def find_fork_of(project)
return nil unless project.fork_network
project.fork_network.find_forks_in(projects).first
if RequestStore.active?
forks_in_namespace = RequestStore.fetch("namespaces:#{id}:forked_projects") do
Hash.new do |found_forks, project|
found_forks[project] = project.fork_network.find_forks_in(projects).first
end
end
forks_in_namespace[project]
else
project.fork_network.find_forks_in(projects).first
end
end
def lfs_enabled?

View File

@ -562,8 +562,7 @@ class Project < ActiveRecord::Base
if forked?
RepositoryForkWorker.perform_async(id,
forked_from_project.repository_storage_path,
forked_from_project.full_path,
self.namespace.full_path)
forked_from_project.disk_path)
else
RepositoryImportWorker.perform_async(self.id)
end
@ -1114,7 +1113,11 @@ class Project < ActiveRecord::Base
end
def project_member(user)
project_members.find_by(user_id: user)
if project_members.loaded?
project_members.find { |member| member.user_id == user.id }
else
project_members.find_by(user_id: user)
end
end
def default_branch

View File

@ -256,7 +256,7 @@ class Repository
end
def diverging_commit_counts(branch)
root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
root_ref_hash = raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes

View File

@ -487,7 +487,11 @@ class User < ActiveRecord::Base
end
def two_factor_u2f_enabled?
u2f_registrations.exists?
if u2f_registrations.loaded?
u2f_registrations.any?
else
u2f_registrations.exists?
end
end
def namespace_uniq
@ -998,7 +1002,11 @@ class User < ActiveRecord::Base
end
def notification_settings_for(source)
notification_settings.find_or_initialize_by(source: source)
if notification_settings.loaded?
notification_settings.find { |notification| notification.source == source }
else
notification_settings.find_or_initialize_by(source: source)
end
end
# Lazy load global notification setting

View File

@ -12,8 +12,12 @@ class BaseCountService
Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i
end
def refresh_cache
Rails.cache.write(cache_key, uncached_count, raw: raw?)
def count_stored?
Rails.cache.read(cache_key).present?
end
def refresh_cache(&block)
Rails.cache.write(cache_key, block_given? ? yield : uncached_count, raw: raw?)
end
def uncached_count

View File

@ -2,7 +2,7 @@ module Clusters
class CreateService < BaseService
attr_reader :access_token
def execute(access_token)
def execute(access_token = nil)
@access_token = access_token
raise Exception.new('Instance does not support multiple clusters') unless can_create_cluster?

View File

@ -98,6 +98,12 @@ module NotificationRecipientService
self << [target.participants(user), :participating]
end
def add_mentions(user, target:)
return unless target.respond_to?(:mentioned_users)
self << [target.mentioned_users(user), :mention]
end
# Get project/group users with CUSTOM notification level
def add_custom_notifications
user_ids = []
@ -227,6 +233,11 @@ module NotificationRecipientService
add_subscribed_users
if [:new_issue, :new_merge_request].include?(custom_action)
# These will all be participants as well, but adding with the :mention
# type ensures that users with the mention notification level will
# receive them, too.
add_mentions(current_user, target: target)
add_labels_subscribers
end
end
@ -263,7 +274,7 @@ module NotificationRecipientService
def build!
# Add all users participating in the thread (author, assignee, comment authors)
add_participants(note.author)
self << [note.mentioned_users, :mention]
add_mentions(note.author, target: note)
unless note.for_personal_snippet?
# Merge project watchers

View File

@ -0,0 +1,31 @@
# Service class for getting and caching the number of elements of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids.
module Projects
class BatchCountService
def initialize(projects)
@projects = projects
end
def refresh_cache
@projects.each do |project|
service = count_service.new(project)
unless service.count_stored?
service.refresh_cache { global_count[project.id].to_i }
end
end
end
def project_ids
@projects.map(&:id)
end
def global_count(project)
raise NotImplementedError, 'global_count must be implemented and return an hash indexed by the project id'
end
def count_service
raise NotImplementedError, 'count_service must be implemented and return a Projects::CountService object'
end
end
end

View File

@ -0,0 +1,18 @@
# Service class for getting and caching the number of forks of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
module Projects
class BatchForksCountService < Projects::BatchCountService
def global_count
@global_count ||= begin
count_service.query(project_ids)
.group(:forked_from_project_id)
.count
end
end
def count_service
::Projects::ForksCountService
end
end
end

View File

@ -0,0 +1,16 @@
# Service class for getting and caching the number of issues of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
module Projects
class BatchOpenIssuesCountService < Projects::BatchCountService
def global_count
@global_count ||= begin
count_service.query(project_ids).group(:project_id).count
end
end
def count_service
::Projects::OpenIssuesCountService
end
end
end

View File

@ -11,6 +11,10 @@ module Projects
@project = project
end
def relation_for_count
self.class.query(@project.id)
end
def cache_key_name
raise(
NotImplementedError,
@ -21,5 +25,12 @@ module Projects
def cache_key
['projects', 'count_service', VERSION, @project.id, cache_key_name]
end
def self.query(project_ids)
raise(
NotImplementedError,
'"query" must be implemented and return an ActiveRecord::Relation'
)
end
end
end

View File

@ -1,12 +1,15 @@
module Projects
# Service class for getting and caching the number of forks of a project.
class ForksCountService < Projects::CountService
def relation_for_count
@project.forks
end
def cache_key_name
'forks_count'
end
def self.query(project_ids)
# We can't directly change ForkedProjectLink to ForkNetworkMember here
# Nowadays, when a call using v3 to projects/:id/fork is made,
# the relationship to ForkNetworkMember is not updated
ForkedProjectLink.where(forked_from_project: project_ids)
end
end
end

View File

@ -2,14 +2,14 @@ module Projects
# Service class for counting and caching the number of open issues of a
# project.
class OpenIssuesCountService < Projects::CountService
def relation_for_count
# We don't include confidential issues in this number since this would
# expose the number of confidential issues to non project members.
@project.issues.opened.public_only
end
def cache_key_name
'open_issues_count'
end
def self.query(project_ids)
# We don't include confidential issues in this number since this would
# expose the number of confidential issues to non project members.
Issue.opened.public_only.where(project: project_ids)
end
end
end

View File

@ -3,11 +3,7 @@
# Custom validator for ClusterName.
class ClusterNameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if record.user?
unless value.present?
record.errors.add(attribute, " has to be present")
end
elsif record.gcp?
if record.managed?
if record.persisted? && record.name_changed?
record.errors.add(attribute, " can not be changed because it's synchronized with provider")
end
@ -19,6 +15,10 @@ class ClusterNameValidator < ActiveModel::EachValidator
unless value =~ Gitlab::Regex.kubernetes_namespace_regex
record.errors.add(attribute, Gitlab::Regex.kubernetes_namespace_regex_message)
end
else
unless value.present?
record.errors.add(attribute, " has to be present")
end
end
end
end

View File

@ -1,7 +1,7 @@
= render 'devise/shared/tab_single', tab_title:'Change your password'
.login-box
.login-body
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'gl-show-field-errors' }) do |f|
= form_for(resource, as: resource_name, url: password_path(:user), html: { method: :put, class: 'gl-show-field-errors' }) do |f|
.devise-errors
= devise_error_messages!
= f.hidden_field :reset_password_token
@ -17,5 +17,5 @@
.clearfix.prepend-top-20
%p
%span.light Didn't receive a confirmation email?
= link_to "Request a new one", new_confirmation_path(resource_name)
= link_to "Request a new one", new_confirmation_path(:user)
= render 'devise/shared/sign_in_link'

View File

@ -11,6 +11,6 @@
= f.check_box :remember_me, class: 'remember-me-checkbox'
%span Remember me
.pull-right.forgot-password
= link_to "Forgot your password?", new_password_path(resource_name)
= link_to "Forgot your password?", new_password_path(:user)
.submit-container.move-submit-down
= f.submit "Sign in", class: "btn btn-save"

View File

@ -1,19 +1,19 @@
<%- if controller_name != 'sessions' %>
<%= link_to "Sign in", new_session_path(resource_name), class: "btn" %><br />
<%= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: "btn" %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' && allow_signup? %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<%= link_to "Sign up", new_registration_path(:user) %><br />
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
<%= link_to "Forgot your password?", new_password_path(resource_name), class: "btn" %><br />
<%= link_to "Forgot your password?", new_password_path(:user), class: "btn" %><br />
<% end -%>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(:user) %><br />
<% end -%>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(:user) %><br />
<% end -%>

View File

@ -1,4 +1,4 @@
%p
%span.light
Already have login and password?
= link_to "Sign in", new_session_path(resource_name)
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')

View File

@ -31,4 +31,4 @@
%p
%span.light Didn't receive a confirmation email?
= succeed '.' do
= link_to "Request a new one", new_confirmation_path(resource_name)
= link_to "Request a new one", new_confirmation_path(:user)

View File

@ -9,7 +9,7 @@
%p Try logging in using your username or email. If you have forgotten your password, try recovering it
= link_to "Sign in", new_session_path(:user), class: 'btn primary'
= link_to "Recover password", new_password_path(resource_name), class: 'btn secondary'
= link_to "Recover password", new_password_path(:user), class: 'btn secondary'
%hr
%p.light If none of the options work, try contacting a GitLab administrator.

View File

@ -19,5 +19,5 @@
distributed with computer software, forming part of its documentation.
%p
We recommend you to
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md')
file to the repository and GitLab will render it here instead of this message.

View File

@ -3,6 +3,6 @@
%tr.tree-item{ 'data-link' => path_to_directory }
%td.tree-item-file-name
= tree_icon('folder', '755', directory.name)
= link_to path_to_directory do
%span.str-truncated= directory.name
= link_to path_to_directory, class: 'str-truncated' do
%span= directory.name
%td

View File

@ -6,12 +6,12 @@
%td.tree-item-file-name
= tree_icon('file', blob.mode, blob.name)
- if external_link
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip',
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip str-truncated',
target: '_blank', rel: 'noopener noreferrer', title: _('Opens in a new window') do
%span.str-truncated>= blob.name
%span>= blob.name
= icon('external-link', class: 'js-artifact-tree-external-icon')
- else
= link_to path_to_file do
%span.str-truncated= blob.name
= link_to path_to_file, class: 'str-truncated' do
%span= blob.name
%td
= number_to_human_size(blob.size, precision: 2)

View File

@ -8,8 +8,7 @@
%li{ class: "js-branch-#{branch.name}" }
%div
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do
= icon('code-fork')
= branch.name
= icon('code-fork', class: 'append-right-5') + "#{branch.name}"
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default

View File

@ -41,6 +41,6 @@
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent"
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent btn-link"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit)

View File

@ -14,12 +14,12 @@
%p
Otherwise you can start with adding a
= succeed ',' do
= link_to "README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
= link_to "README", add_special_file_path(@project, file_name: 'README.md')
a
= succeed ',' do
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
or a
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link'
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
to this project.
%p
You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.

View File

@ -16,7 +16,7 @@
%li prevent pushes from everybody except Masters
%li prevent <strong>anyone</strong> from force pushing to the branch
%li prevent <strong>anyone</strong> from deleting the branch
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches")} and #{link_to "project permissions", help_page_path("user/permissions")}.
- if can? current_user, :admin_project, @project
= content_for :create_protected_branch

View File

@ -16,7 +16,7 @@
%li Prevent <strong>anyone</strong> from updating the tag
%li Prevent <strong>anyone</strong> from deleting the tag
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}.
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags")}.
- if can? current_user, :admin_project, @project
= yield :create_protected_tag

View File

@ -2,8 +2,8 @@
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do
%span.str-truncated= file_name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'

View File

@ -2,8 +2,8 @@
%td.tree-item-file-name
= tree_icon(type, tree_item.mode, tree_item.name)
- path = flatten_tree(@path, tree_item)
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do
%span.str-truncated= path
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
%span= path
%td.hidden-xs.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'

View File

@ -6,9 +6,8 @@
- git_access_url = project_wikis_git_access_path(@project)
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do
= succeed '&nbsp;' do
= icon('cloud-download')
= _("Clone repository")
= icon('cloud-download', class: 'append-right-5')
%span= _("Clone repository")
.blocks-container
.block.block-first

View File

@ -38,9 +38,9 @@
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'}
.pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do
view merge requests
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do
view open issues
- if current_user

View File

@ -1,6 +1,5 @@
- type = local_assigns.fetch(:type, :issues)
- page_context_word = type.to_s.humanize(capitalize: false)
- issuables = @issues || @merge_requests
%ul.nav-links.issues-state-filters
%li{ class: active_when(params[:state] == 'opened') }>
@ -20,6 +19,4 @@
= link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by issues that are currently closed.', data: { state: 'closed' } do
#{issuables_state_counter_text(type, :closed)}
%li{ class: active_when(params[:state] == 'all') }>
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
#{issuables_state_counter_text(type, :all)}
= render 'shared/issuable/nav_links/all', page_context_word: page_context_word, counter: issuables_state_counter_text(type, :all)

View File

@ -0,0 +1,6 @@
- page_context_word = local_assigns.fetch(:page_context_word)
- counter = local_assigns.fetch(:counter)
%li{ class: active_when(params[:state] == 'all') }>
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
#{counter}

View File

@ -31,8 +31,7 @@
.note-header
.note-header-info
%a{ href: user_path(note.author) }
%span.note-header-author-name
= sanitize(note.author.name)
%span.note-header-author-name= sanitize(note.author.name)
%span.note-headline-light
= note.author.to_reference
%span.note-headline-light

View File

@ -51,7 +51,7 @@
.cover-desc
- unless @user.public_email.blank?
.profile-link-holder.middle-dot-divider
= link_to @user.public_email, "mailto:#{@user.public_email}"
= link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link'
- unless @user.skype.blank?
.profile-link-holder.middle-dot-divider
= link_to "skype:#{@user.skype}", title: "Skype" do
@ -66,7 +66,7 @@
= icon('twitter-square')
- unless @user.website_url.blank?
.profile-link-holder.middle-dot-divider
= link_to @user.short_website_url, @user.full_website_url
= link_to @user.short_website_url, @user.full_website_url, class: 'text-link'
- unless @user.location.blank?
.profile-link-holder.middle-dot-divider
= icon('map-marker')

View File

@ -32,16 +32,14 @@ module RepositoryCheck
end
def git_fsck(repository)
path = repository.path_to_repo
cmd = %W(nice git --git-dir=#{path} fsck)
output, status = Gitlab::Popen.popen(cmd)
return false unless repository.exists?
if status.zero?
true
else
Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}")
false
end
repository.raw_repository.fsck
true
rescue Gitlab::Git::Repository::GitError => e
Gitlab::RepositoryCheckLogger.error(e.message)
false
end
def has_pushes?(project)

View File

@ -8,18 +8,18 @@ class RepositoryForkWorker
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
def perform(project_id, forked_from_repository_storage_path, source_disk_path)
project = Project.find(project_id)
return unless start_fork(project)
Gitlab::Metrics.add_event(:fork_repository,
source_path: source_path,
target_path: target_path)
source_path: source_disk_path,
target_path: project.disk_path)
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
project.repository_storage_path, target_path)
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_disk_path,
project.repository_storage_path, project.disk_path)
raise ForkError, "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result
project.repository.after_import
raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo?

View File

@ -0,0 +1,5 @@
---
title: Create a new form to add Existing Kubernetes Cluster
merge_request: 14805
author:
type: added

View File

@ -0,0 +1,6 @@
---
title: Fix sending notification emails to users with the mention level set who were
mentioned in an issue or merge request description
merge_request:
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Confirming email with invalid token should no longer generate an error
merge_request: 15726
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add axios to common file
merge_request:
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: show status of gitlab reference links in wiki
merge_request: 15694
author: haseebeqx
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix the fork project functionality for projects with hashed storage
merge_request: 15671
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix typo in docs about Elasticsearch
merge_request: 15699
author: Takuya Noguchi
type: other

View File

@ -0,0 +1,5 @@
---
title: Keep track of all circuitbreaker keys in a set
merge_request: 15613
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Reduce requests for project forks on show page of projects that have forks
merge_request: 15663
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Added default order to UsersFinder
merge_request: 15679
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Optimize API /groups/:id/projects by preloading associations
merge_request:
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Use custom user agent header in all GCP API requests.
merge_request: 15705
author:
type: changed

View File

@ -1,3 +1,7 @@
# WARNING changes in this file must be manually propagated to gitaly-ruby.
#
# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb
module Gollum
GIT_ADAPTER = "rugged".freeze
end

View File

@ -1,15 +0,0 @@
require './spec/support/sidekiq'
Gitlab::Seeder.quiet do
User.seed do |s|
s.id = 1
s.name = 'Administrator'
s.email = 'admin@example.com'
s.notification_email = 'admin@example.com'
s.username = 'root'
s.password = '5iveL!fe'
s.admin = true
s.projects_limit = 100
s.confirmed_at = DateTime.now
end
end

View File

@ -80,7 +80,7 @@ changes.
The first example focuses on _how_ we fixed something, not on _what_ it fixes.
The rewritten version clearly describes the _end benefit_ to the user (fewer 500
errors), and _when_ (searching commits with ElasticSearch).
errors), and _when_ (searching commits with Elasticsearch).
Use your best judgement and try to put yourself in the mindset of someone
reading the compiled changelog. Does this entry add value? Does it offer context

View File

@ -170,12 +170,6 @@ You can combine one or more of the following:
= link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
```
1. **Underlining a link.**
```haml
= link_to 'Help page', help_page_path('user/permissions'), class: 'underlined-link'
```
1. **Using links inline of some text.**
```haml

View File

@ -152,12 +152,23 @@ CE and EE.
## Previewing the changes live
If you want to preview the doc changes of your merge request live, you can use
the manual `review-docs-deploy` job in your merge request.
the manual `review-docs-deploy` job in your merge request. You will need at
least Master permissions to be able to run it and is currently enabled for the
following projects:
- https://gitlab.com/gitlab-org/gitlab-ce
- https://gitlab.com/gitlab-org/gitlab-ee
NOTE: **Note:**
You will need to push a branch to those repositories, it doesn't work for forks.
TIP: **Tip:**
If your branch contains only documentation changes, you can use
[special branch names](#testing) to avoid long running pipelines.
In the mini pipeline graph, you should see an `>>` icon. Clicking on it will
reveal the `review-docs-deploy` job. Hit the play button for the job to start.
![Manual trigger a docs build](img/manual_build_docs.png)
This job will:

View File

@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of
### EC2 Instance
### ElasticSearch
### Elasticsearch
Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data.

Some files were not shown because too many files have changed in this diff Show More