Commit 216173d1 authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into 35616-move-k8-to-cluster-page

parents 346dcb06 8f9622bb
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '4.2.8' gem 'rails', '4.2.10'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
......
...@@ -4,38 +4,38 @@ GEM ...@@ -4,38 +4,38 @@ GEM
RedCloth (4.3.2) RedCloth (4.3.2)
abstract_type (0.0.7) abstract_type (0.0.7)
ace-rails-ap (4.1.2) ace-rails-ap (4.1.2)
actionmailer (4.2.8) actionmailer (4.2.10)
actionpack (= 4.2.8) actionpack (= 4.2.10)
actionview (= 4.2.8) actionview (= 4.2.10)
activejob (= 4.2.8) activejob (= 4.2.10)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.8) actionpack (4.2.10)
actionview (= 4.2.8) actionview (= 4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.8) actionview (4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.8) activejob (4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.8) activemodel (4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.8) activerecord (4.2.10)
activemodel (= 4.2.8) activemodel (= 4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
arel (~> 6.0) arel (~> 6.0)
activerecord_sane_schema_dumper (0.2) activerecord_sane_schema_dumper (0.2)
rails (>= 4, < 5) rails (>= 4, < 5)
activesupport (4.2.8) activesupport (4.2.10)
i18n (~> 0.7) i18n (~> 0.7)
minitest (~> 5.1) minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4) thread_safe (~> 0.3, >= 0.3.4)
...@@ -300,8 +300,8 @@ GEM ...@@ -300,8 +300,8 @@ GEM
omniauth (~> 1.3) omniauth (~> 1.3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1) pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5) rubyntlm (~> 0.5)
globalid (0.3.7) globalid (0.4.1)
activesupport (>= 4.1.0) activesupport (>= 4.2.0)
gollum-grit_adapter (1.0.1) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.2.7) gollum-lib (4.2.7)
...@@ -400,7 +400,8 @@ GEM ...@@ -400,7 +400,8 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpclient (2.8.2) httpclient (2.8.2)
i18n (0.8.6) i18n (0.9.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2) ice_nine (0.11.2)
influxdb (0.2.3) influxdb (0.2.3)
cause cause
...@@ -474,8 +475,8 @@ GEM ...@@ -474,8 +475,8 @@ GEM
railties (>= 4, < 5.2) railties (>= 4, < 5.2)
loofah (2.0.3) loofah (2.0.3)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.6.6) mail (2.7.0)
mime-types (>= 1.16, < 4) mini_mime (>= 0.1.1)
mail_room (0.9.1) mail_room (0.9.1)
memoist (0.16.0) memoist (0.16.0)
memoizable (0.4.2) memoizable (0.4.2)
...@@ -573,8 +574,8 @@ GEM ...@@ -573,8 +574,8 @@ GEM
parallel (1.12.0) parallel (1.12.0)
paranoia (2.3.1) paranoia (2.3.1)
activerecord (>= 4.0, < 5.2) activerecord (>= 4.0, < 5.2)
parser (2.4.0.0) parser (2.4.0.2)
ast (~> 2.2) ast (~> 2.3)
parslet (1.5.0) parslet (1.5.0)
blankslate (~> 2.0) blankslate (~> 2.0)
path_expander (1.0.1) path_expander (1.0.1)
...@@ -656,16 +657,16 @@ GEM ...@@ -656,16 +657,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.8) rails (4.2.10)
actionmailer (= 4.2.8) actionmailer (= 4.2.10)
actionpack (= 4.2.8) actionpack (= 4.2.10)
actionview (= 4.2.8) actionview (= 4.2.10)
activejob (= 4.2.8) activejob (= 4.2.10)
activemodel (= 4.2.8) activemodel (= 4.2.10)
activerecord (= 4.2.8) activerecord (= 4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.8) railties (= 4.2.10)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
...@@ -678,15 +679,15 @@ GEM ...@@ -678,15 +679,15 @@ GEM
rails-i18n (4.0.9) rails-i18n (4.0.9)
i18n (~> 0.7) i18n (~> 0.7)
railties (~> 4.0) railties (~> 4.0)
railties (4.2.8) railties (4.2.10)
actionpack (= 4.2.8) actionpack (= 4.2.10)
activesupport (= 4.2.8) activesupport (= 4.2.10)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.2.2) rainbow (2.2.2)
rake rake
raindrops (0.18.0) raindrops (0.18.0)
rake (12.1.0) rake (12.3.0)
rblineprof (0.3.6) rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rbnacl (4.0.2) rbnacl (4.0.2)
...@@ -873,7 +874,7 @@ GEM ...@@ -873,7 +874,7 @@ GEM
sprockets (3.7.1) sprockets (3.7.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (> 1, < 3)
sprockets-rails (3.2.0) sprockets-rails (3.2.1)
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
...@@ -911,7 +912,7 @@ GEM ...@@ -911,7 +912,7 @@ GEM
truncato (0.7.10) truncato (0.7.10)
htmlentities (~> 4.3.1) htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0) nokogiri (~> 1.8.0, >= 1.7.0)
tzinfo (1.2.3) tzinfo (1.2.4)
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1) u2f (0.2.1)
uber (0.1.0) uber (0.1.0)
...@@ -1118,7 +1119,7 @@ DEPENDENCIES ...@@ -1118,7 +1119,7 @@ DEPENDENCIES
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0) rack-proxy (~> 0.6.0)
rails (= 4.2.8) rails (= 4.2.10)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 4.0.9) rails-i18n (~> 4.0.9)
rainbow (~> 2.2) rainbow (~> 2.2)
......
<script> <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 { export default {
component: { mixins: [
Issuable,
],
components: {
Icon, Icon,
}, },
}; };
...@@ -16,7 +20,7 @@ ...@@ -16,7 +20,7 @@
:size="16" :size="16"
class="icon"> class="icon">
</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> </span>
</div> </div>
</template> </template>
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
import * as constants from '../constants'; import * as constants from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue'; import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue'; import noteSignedOutWidget from './note_signed_out_widget.vue';
import issueDiscussionLockedWidget from './issue_discussion_locked_widget.vue'; import discussionLockedWidget from './discussion_locked_widget.vue';
import markdownField from '../../vue_shared/components/markdown/field.vue'; import markdownField from '../../vue_shared/components/markdown/field.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issuableStateMixin from '../mixins/issuable_state'; import issuableStateMixin from '../mixins/issuable_state';
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
}, },
components: { components: {
issueWarning, issueWarning,
issueNoteSignedOutWidget, noteSignedOutWidget,
issueDiscussionLockedWidget, discussionLockedWidget,
markdownField, markdownField,
userAvatarLink, userAvatarLink,
}, },
...@@ -240,8 +240,11 @@ ...@@ -240,8 +240,11 @@
<template> <template>
<div> <div>
<issue-note-signed-out-widget v-if="!isLoggedIn" /> <note-signed-out-widget v-if="!isLoggedIn" />
<issue-discussion-locked-widget v-else-if="!canCreateNote" /> <discussion-locked-widget
issuable-type="issue"
v-else-if="!canCreateNote"
/>
<ul <ul
v-else v-else
class="notes notes-form timeline"> class="notes notes-form timeline">
......
...@@ -4,10 +4,9 @@ ...@@ -4,10 +4,9 @@
import { SYSTEM_NOTE } from '../constants'; import { SYSTEM_NOTE } from '../constants';
import issueNote from './issue_note.vue'; import issueNote from './issue_note.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issueNoteHeader from './issue_note_header.vue'; import noteHeader from './note_header.vue';
import issueNoteActions from './issue_note_actions.vue'; import noteSignedOutWidget from './note_signed_out_widget.vue';
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue'; import noteEditedText from './note_edited_text.vue';
import issueNoteEditedText from './issue_note_edited_text.vue';
import issueNoteForm from './issue_note_form.vue'; import issueNoteForm from './issue_note_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
...@@ -28,10 +27,9 @@ ...@@ -28,10 +27,9 @@
components: { components: {
issueNote, issueNote,
userAvatarLink, userAvatarLink,
issueNoteHeader, noteHeader,
issueNoteActions, noteSignedOutWidget,
issueNoteSignedOutWidget, noteEditedText,
issueNoteEditedText,
issueNoteForm, issueNoteForm,
placeholderNote, placeholderNote,
placeholderSystemNote, placeholderSystemNote,
...@@ -171,7 +169,7 @@ ...@@ -171,7 +169,7 @@
<div class="timeline-content"> <div class="timeline-content">
<div class="discussion"> <div class="discussion">
<div class="discussion-header"> <div class="discussion-header">
<issue-note-header <note-header
:author="author" :author="author"
:created-at="discussion.created_at" :created-at="discussion.created_at"
:note-id="discussion.id" :note-id="discussion.id"
...@@ -180,7 +178,7 @@ ...@@ -180,7 +178,7 @@
action-text="started a discussion" action-text="started a discussion"
class="discussion" class="discussion"
/> />
<issue-note-edited-text <note-edited-text
v-if="lastUpdatedAt" v-if="lastUpdatedAt"
:edited-at="lastUpdatedAt" :edited-at="lastUpdatedAt"
:edited-by="lastUpdatedBy" :edited-by="lastUpdatedBy"
...@@ -220,7 +218,7 @@ ...@@ -220,7 +218,7 @@
@cancelFormEdition="cancelReplyForm" @cancelFormEdition="cancelReplyForm"
ref="noteForm" ref="noteForm"
/> />
<issue-note-signed-out-widget v-if="!canReply" /> <note-signed-out-widget v-if="!canReply" />
</div> </div>
</div> </div>
</div> </div>
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash'; import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import issueNoteHeader from './issue_note_header.vue'; import noteHeader from './note_header.vue';
import issueNoteActions from './issue_note_actions.vue'; import noteActions from './note_actions.vue';
import issueNoteBody from './issue_note_body.vue'; import issueNoteBody from './issue_note_body.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
}, },
components: { components: {
userAvatarLink, userAvatarLink,
issueNoteHeader, noteHeader,
issueNoteActions, noteActions,
issueNoteBody, issueNoteBody,
}, },
computed: { computed: {
...@@ -155,13 +155,13 @@ ...@@ -155,13 +155,13 @@
</div> </div>
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"> <div class="note-header">
<issue-note-header <note-header
:author="author" :author="author"
:created-at="note.created_at" :created-at="note.created_at"
:note-id="note.id" :note-id="note.id"
action-text="commented" action-text="commented"
/> />
<issue-note-actions <note-actions
:author-id="author.id" :author-id="author.id"
:note-id="note.id" :note-id="note.id"
:access-level="note.human_access" :access-level="note.human_access"
......
<script> <script>
import issueNoteEditedText from './issue_note_edited_text.vue'; import noteEditedText from './note_edited_text.vue';
import issueNoteAwardsList from './issue_note_awards_list.vue'; import noteAwardsList from './note_awards_list.vue';
import issueNoteAttachment from './issue_note_attachment.vue'; import noteAttachment from './note_attachment.vue';
import issueNoteForm from './issue_note_form.vue'; import issueNoteForm from './issue_note_form.vue';
import TaskList from '../../task_list'; import TaskList from '../../task_list';
import autosave from '../mixins/autosave'; import autosave from '../mixins/autosave';
...@@ -26,9 +26,9 @@ ...@@ -26,9 +26,9 @@
autosave, autosave,
], ],
components: { components: {
issueNoteEditedText, noteEditedText,
issueNoteAwardsList, noteAwardsList,
issueNoteAttachment, noteAttachment,
issueNoteForm, issueNoteForm,
}, },
computed: { computed: {
...@@ -101,20 +101,20 @@ ...@@ -101,20 +101,20 @@
v-model="note.note" v-model="note.note"
:data-update-url="note.path" :data-update-url="note.path"
class="hidden js-task-list-field"></textarea> class="hidden js-task-list-field"></textarea>
<issue-note-edited-text <note-edited-text
v-if="note.last_edited_at" v-if="note.last_edited_at"
:edited-at="note.last_edited_at" :edited-at="note.last_edited_at"
:edited-by="note.last_edited_by" :edited-by="note.last_edited_by"
action-text="Edited" action-text="Edited"
/> />
<issue-note-awards-list <note-awards-list
v-if="note.award_emoji.length" v-if="note.award_emoji.length"
:note-id="note.id" :note-id="note.id"
:note-author-id="note.author.id" :note-author-id="note.author.id"
:awards="note.award_emoji" :awards="note.award_emoji"
:toggle-award-path="note.toggle_award_path" :toggle-award-path="note.toggle_award_path"
/> />
<issue-note-attachment <note-attachment
v-if="note.attachment" v-if="note.attachment"
:attachment="note.attachment" :attachment="note.attachment"
/> />
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
import emojiSmiley from 'icons/_emoji_smiley.svg'; import emojiSmiley from 'icons/_emoji_smiley.svg';
import editSvg from 'icons/_icon_pencil.svg'; import editSvg from 'icons/_icon_pencil.svg';
import ellipsisSvg from 'icons/_ellipsis_v.svg'; import ellipsisSvg from 'icons/_ellipsis_v.svg';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
export default { export default {
name: 'issueNoteActions', name: 'noteActions',
props: { props: {
authorId: { authorId: {
type: Number, type: Number,
......
<script> <script>
export default { export default {
name: 'issueNoteAttachment', name: 'noteAttachment',
props: { props: {
attachment: { attachment: {
type: Object, type: Object,
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
export default { export default {
name: 'singInLinksNotes',
computed: { computed: {
...mapGetters([ ...mapGetters([
'getNotesDataByProp', 'getNotesDataByProp',
......
...@@ -18,11 +18,6 @@ export default { ...@@ -18,11 +18,6 @@ export default {
required: true, required: true,
type: Function, type: Function,
}, },
issuableType: {
required: true,
type: String,
},
}, },
mixins: [ mixins: [
...@@ -39,13 +34,13 @@ export default { ...@@ -39,13 +34,13 @@ export default {
<div class="dropdown open"> <div class="dropdown open">
<div class="dropdown-menu sidebar-item-warning-message"> <div class="dropdown-menu sidebar-item-warning-message">
<p class="text" v-if="isLocked"> <p class="text" v-if="isLocked">
Unlock this {{ issuableDisplayName(issuableType) }}? Unlock this {{ issuableDisplayName }}?
<strong>Everyone</strong> <strong>Everyone</strong>
will be able to comment. will be able to comment.
</p> </p>
<p class="text" v-else> <p class="text" v-else>
Lock this {{ issuableDisplayName(issuableType) }}? Lock this {{ issuableDisplayName }}?
Only Only
<strong>project members</strong> <strong>project members</strong>
will be able to comment. will be able to comment.
......
...@@ -23,11 +23,6 @@ export default { ...@@ -23,11 +23,6 @@ export default {
return mediatorObject.service && mediatorObject.service.update && mediatorObject.store; return mediatorObject.service && mediatorObject.service.update && mediatorObject.store;
}, },
}, },
issuableType: {
required: true,
type: String,
},
}, },
mixins: [ mixins: [
...@@ -59,7 +54,7 @@ export default { ...@@ -59,7 +54,7 @@ export default {
discussion_locked: locked, discussion_locked: locked,
}) })
.then(() => location.reload()) .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 { ...@@ -77,7 +72,7 @@ export default {
</div> </div>
<div class="title hide-collapsed"> <div class="title hide-collapsed">
Lock {{issuableDisplayName(issuableType) }} Lock {{ issuableDisplayName }}
<button <button
v-if="isEditable" v-if="isEditable"
class="pull-right lock-edit btn btn-blank" class="pull-right lock-edit btn btn-blank"
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* /> * />
*/ */
import { mapGetters } from 'vuex'; 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'; import { spriteIcon } from '../../../lib/utils/common_utils';
export default { export default {
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
}, },
}, },
components: { components: {
issueNoteHeader, noteHeader,
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
</div> </div>
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"> <div class="note-header">
<issue-note-header <note-header
:author="note.author" :author="note.author"
:created-at="note.created_at" :created-at="note.created_at"
:note-id="note.id" :note-id="note.id"
......
export default { export default {
methods: { props: {
issuableDisplayName(issuableType) { issuableType: {
const displayName = issuableType.replace(/_/, ' '); required: true,
type: String,
},
},
return this.__ ? this.__(displayName) : displayName; computed: {
issuableDisplayName() {
return this.issuableType.replace(/_/g, ' ');
}, },
}, },
}; };
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
color: $brand-info; color: $brand-info;
} }
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: $hint-color; } .hint { font-style: italic; color: $hint-color; }
.light { color: $common-gray; } .light { color: $common-gray; }
......
...@@ -14,6 +14,5 @@ ...@@ -14,6 +14,5 @@
&:hover { &:hover {
background-color: $user-mention-bg-hover; background-color: $user-mention-bg-hover;
text-decoration: none;
} }
} }
...@@ -20,6 +20,11 @@ ...@@ -20,6 +20,11 @@
.ref-name { .ref-name {
font-size: 12px; font-size: 12px;
&:hover {
text-decoration: underline;
color: $gl-text-color;
}
} }
} }
......
...@@ -110,6 +110,10 @@ ...@@ -110,6 +110,10 @@
padding: 6px 10px; padding: 6px 10px;
border-radius: $label-border-radius; border-radius: $label-border-radius;
} }
&:hover .color-label {
text-decoration: underline;
}
} }
&.has-labels { &.has-labels {
...@@ -174,6 +178,14 @@ ...@@ -174,6 +178,14 @@
color: $gray-darkest; color: $gray-darkest;
} }
} }
&.assignee {
.author_link:hover {
.author {
text-decoration: underline;
}
}
}
} }
.block-first { .block-first {
...@@ -468,7 +480,6 @@ ...@@ -468,7 +480,6 @@
a:not(.btn-retry) { a:not(.btn-retry) {
&:hover { &:hover {
color: $md-link-color; color: $md-link-color;
text-decoration: none;
.avatar { .avatar {
border-color: rgba($avatar-border, .2); border-color: rgba($avatar-border, .2);
......
...@@ -208,7 +208,6 @@ ul.notes { ...@@ -208,7 +208,6 @@ ul.notes {
a { a {
color: $gl-link-color; color: $gl-link-color;
text-decoration: none;
} }
p { p {
...@@ -395,6 +394,10 @@ ul.notes { ...@@ -395,6 +394,10 @@ ul.notes {
&:focus, &:focus,
&:hover { &:hover {
text-decoration: none; text-decoration: none;
.note-header-author-name {
text-decoration: underline;
}
} }
} }
...@@ -461,6 +464,10 @@ ul.notes { ...@@ -461,6 +464,10 @@ ul.notes {
.system-note-message { .system-note-message {
white-space: normal; white-space: normal;
} }
a:hover {
text-decoration: underline;
}
} }
/** /**
......
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
.profile-link-holder { .profile-link-holder {
display: inline; display: inline;
a { a:not(.text-link) {
text-decoration: none; text-decoration: none;
} }
} }
......
...@@ -811,6 +811,7 @@ a.deploy-project-label { ...@@ -811,6 +811,7 @@ a.deploy-project-label {
&:hover, &:hover,
&:focus { &:focus {
color: $gl-text-color; color: $gl-text-color;
text-decoration: underline;
} }
} }
} }
......
...@@ -124,7 +124,11 @@ ...@@ -124,7 +124,11 @@
&:hover, &:hover,
&.active { &.active {
color: $black; text-decoration: none;
span {
text-decoration: underline;
}
} }
} }
......
...@@ -25,7 +25,7 @@ class UsersFinder ...@@ -25,7 +25,7 @@ class UsersFinder
end end
def execute def execute
users = User.all users = User.all.order_id_desc
users = by_username(users) users = by_username(users)
users = by_search(users) users = by_search(users)
users = by_blocked(users) users = by_blocked(users)
......
...@@ -63,7 +63,7 @@ module CommitsHelper ...@@ -63,7 +63,7 @@ module CommitsHelper
# Returns a link formatted as a commit branch link # Returns a link formatted as a commit branch link
def commit_branch_link(url, text) def commit_branch_link(url, text)
link_to(url, class: 'label label-gray ref-name branch-link') do 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
end end
...@@ -77,7 +77,7 @@ module CommitsHelper ...@@ -77,7 +77,7 @@ module CommitsHelper
# Returns a link formatted as a commit tag link # Returns a link formatted as a commit tag link
def commit_tag_link(url, text) def commit_tag_link(url, text)
link_to(url, class: 'label label-gray ref-name') do link_to(url, class: 'label label-gray ref-name') do
icon('tag') + " #{text}" icon('tag', class: 'append-right-5') + "#{text}"
end end
end end
......
...@@ -113,7 +113,13 @@ module MarkupHelper ...@@ -113,7 +113,13 @@ module MarkupHelper
text = wiki_page.content text = wiki_page.content
return '' unless text.present? 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 = html =
case wiki_page.format case wiki_page.format
......
...@@ -289,6 +289,14 @@ class Group < Namespace ...@@ -289,6 +289,14 @@ class Group < Namespace
"#{parent.full_path}/#{path_was}" "#{parent.full_path}/#{path_was}"
end 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 private
def update_two_factor_requirement def update_two_factor_requirement
......
...@@ -562,8 +562,7 @@ class Project < ActiveRecord::Base ...@@ -562,8 +562,7 @@ class Project < ActiveRecord::Base
if forked? if forked?
RepositoryForkWorker.perform_async(id, RepositoryForkWorker.perform_async(id,
forked_from_project.repository_storage_path, forked_from_project.repository_storage_path,
forked_from_project.full_path, forked_from_project.disk_path)
self.namespace.full_path)
else else
RepositoryImportWorker.perform_async(self.id) RepositoryImportWorker.perform_async(self.id)
end end
...@@ -1114,8 +1113,12 @@ class Project < ActiveRecord::Base ...@@ -1114,8 +1113,12 @@ class Project < ActiveRecord::Base
end end
def project_member(user) def project_member(user)
if project_members.loaded?
project_members.find { |member| member.user_id == user.id }
else
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
end
def default_branch def default_branch
@default_branch ||= repository.root_ref if repository.exists? @default_branch ||= repository.root_ref if repository.exists?
......
...@@ -256,7 +256,7 @@ class Repository ...@@ -256,7 +256,7 @@ class Repository
end end
def diverging_commit_counts(branch) 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 cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather # Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes # than SHA-1 hashes
......
...@@ -487,8 +487,12 @@ class User < ActiveRecord::Base ...@@ -487,8 +487,12 @@ class User < ActiveRecord::Base
end end
def two_factor_u2f_enabled? def two_factor_u2f_enabled?
if u2f_registrations.loaded?
u2f_registrations.any?
else
u2f_registrations.exists? u2f_registrations.exists?
end end
end
def namespace_uniq def namespace_uniq
# Return early if username already failed the first uniqueness validation # Return early if username already failed the first uniqueness validation
...@@ -998,8 +1002,12 @@ class User < ActiveRecord::Base ...@@ -998,8 +1002,12 @@ class User < ActiveRecord::Base
end end
def notification_settings_for(source) def notification_settings_for(source)
if notification_settings.loaded?
notification_settings.find { |notification| notification.source == source }
else
notification_settings.find_or_initialize_by(source: source) notification_settings.find_or_initialize_by(source: source)
end end
end
# Lazy load global notification setting # Lazy load global notification setting
# Initializes User setting with Participating level if setting not persisted # Initializes User setting with Participating level if setting not persisted
......
...@@ -12,8 +12,12 @@ class BaseCountService ...@@ -12,8 +12,12 @@ class BaseCountService
Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i
end end
def refresh_cache def count_stored?
Rails.cache.write(cache_key, uncached_count, raw: raw?) Rails.cache.read(cache_key).present?
end
def refresh_cache(&block)
Rails.cache.write(cache_key, block_given? ? yield : uncached_count, raw: raw?)
end end
def uncached_count def uncached_count
......
...@@ -98,6 +98,12 @@ module NotificationRecipientService ...@@ -98,6 +98,12 @@ module NotificationRecipientService
self << [target.participants(user), :participating] self << [target.participants(user), :participating]
end 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 # Get project/group users with CUSTOM notification level
def add_custom_notifications def add_custom_notifications
user_ids = [] user_ids = []
...@@ -227,6 +233,11 @@ module NotificationRecipientService ...@@ -227,6 +233,11 @@ module NotificationRecipientService
add_subscribed_users add_subscribed_users
if [:new_issue, :new_merge_request].include?(custom_action) 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 add_labels_subscribers
end end
end end
...@@ -263,7 +274,7 @@ module NotificationRecipientService ...@@ -263,7 +274,7 @@ module NotificationRecipientService
def build! def build!
# Add all users participating in the thread (author, assignee, comment authors) # Add all users participating in the thread (author, assignee, comment authors)
add_participants(note.author) add_participants(note.author)
self << [note.mentioned_users, :mention] add_mentions(note.author, target: note)
unless note.for_personal_snippet? unless note.for_personal_snippet?
# Merge project watchers # Merge project watchers
......
# 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
# 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
# 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
...@@ -11,6 +11,10 @@ module Projects ...@@ -11,6 +11,10 @@ module Projects
@project = project @project = project
end end
def relation_for_count
self.class.query(@project.id)
end
def cache_key_name def cache_key_name
raise( raise(
NotImplementedError, NotImplementedError,
...@@ -21,5 +25,12 @@ module Projects ...@@ -21,5 +25,12 @@ module Projects
def cache_key def cache_key
['projects', 'count_service', VERSION, @project.id, cache_key_name] ['projects', 'count_service', VERSION, @project.id, cache_key_name]
end end
def self.query(project_ids)
raise(
NotImplementedError,
'"query" must be implemented and return an ActiveRecord::Relation'
)
end
end end
end end
module Projects module Projects
# Service class for getting and caching the number of forks of a project. # Service class for getting and caching the number of forks of a project.
class ForksCountService < Projects::CountService class ForksCountService < Projects::CountService
def relation_for_count
@project.forks
end
def cache_key_name def cache_key_name
'forks_count' 'forks_count'
end 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
end end
...@@ -2,14 +2,14 @@ module Projects ...@@ -2,14 +2,14 @@ module Projects
# Service class for counting and caching the number of open issues of a # Service class for counting and caching the number of open issues of a
# project. # project.
class OpenIssuesCountService < Projects::CountService 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 def cache_key_name
'open_issues_count' 'open_issues_count'
end 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
end end
...@@ -19,5 +19,5 @@ ...@@ -19,5 +19,5 @@
distributed with computer software, forming part of its documentation. distributed with computer software, forming part of its documentation.
%p %p
We recommend you to 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. file to the repository and GitLab will render it here instead of this message.
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
%tr.tree-item{ 'data-link' => path_to_directory } %tr.tree-item{ 'data-link' => path_to_directory }
%td.tree-item-file-name %td.tree-item-file-name
= tree_icon('folder', '755', directory.name) = tree_icon('folder', '755', directory.name)
= link_to path_to_directory do = link_to path_to_directory, class: 'str-truncated' do
%span.str-truncated= directory.name %span= directory.name
%td %td
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
%td.tree-item-file-name %td.tree-item-file-name
= tree_icon('file', blob.mode, blob.name) = tree_icon('file', blob.mode, blob.name)
- if external_link - 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 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') = icon('external-link', class: 'js-artifact-tree-external-icon')
- else - else
= link_to path_to_file do = link_to path_to_file, class: 'str-truncated' do
%span.str-truncated= blob.name %span= blob.name
%td %td
= number_to_human_size(blob.size, precision: 2) = number_to_human_size(blob.size, precision: 2)
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
%li{ class: "js-branch-#{branch.name}" } %li{ class: "js-branch-#{branch.name}" }
%div %div
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do
= icon('code-fork') = icon('code-fork', class: 'append-right-5') + "#{branch.name}"
= branch.name
&nbsp; &nbsp;
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-primary default %span.label.label-primary default
......
...@@ -41,6 +41,6 @@ ...@@ -41,6 +41,6 @@
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: 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")) = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
...@@ -14,12 +14,12 @@ ...@@ -14,12 +14,12 @@
%p %p
Otherwise you can start with adding a Otherwise you can start with adding a
= succeed ',' do = 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 a
= succeed ',' do = 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 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. to this project.
%p %p
You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%li prevent pushes from everybody except Masters %li prevent pushes from everybody except Masters
%li prevent <strong>anyone</strong> from force pushing to the branch %li prevent <strong>anyone</strong> from force pushing to the branch
%li prevent <strong>anyone</strong> from deleting 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 - if can? current_user, :admin_project, @project
= content_for :create_protected_branch = content_for :create_protected_branch
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%li Prevent <strong>anyone</strong> from updating the tag %li Prevent <strong>anyone</strong> from updating the tag
%li Prevent <strong>anyone</strong> from deleting 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 - if can? current_user, :admin_project, @project
= yield :create_protected_tag = yield :create_protected_tag
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
%td.tree-item-file-name %td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name) = tree_icon(type, blob_item.mode, blob_item.name)
- file_name = 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 = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span.str-truncated= file_name %span= file_name
%td.hidden-xs.tree-commit %td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right %td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner' = render 'projects/tree/spinner'
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
%td.tree-item-file-name %td.tree-item-file-name
= tree_icon(type, tree_item.mode, tree_item.name) = tree_icon(type, tree_item.mode, tree_item.name)
- path = flatten_tree(@path, tree_item) - path = flatten_tree(@path, tree_item)
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do = link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
%span.str-truncated= path %span= path
%td.hidden-xs.tree-commit %td.hidden-xs.tree-commit
%td.tree-time-ago.text-right %td.tree-time-ago.text-right
= render 'projects/tree/spinner' = render 'projects/tree/spinner'
...@@ -6,9 +6,8 @@ ...@@ -6,9 +6,8 @@
- git_access_url = project_wikis_git_access_path(@project) - git_access_url = project_wikis_git_access_path(@project)
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do
= succeed '&nbsp;' do = icon('cloud-download', class: 'append-right-5')
= icon('cloud-download') %span= _("Clone repository")
= _("Clone repository")
.blocks-container .blocks-container
.block.block-first .block.block-first
......
...@@ -38,9 +38,9 @@ ...@@ -38,9 +38,9 @@
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'} = 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 .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 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 view open issues
- if current_user - if current_user
......
...@@ -31,8 +31,7 @@ ...@@ -31,8 +31,7 @@
.note-header .note-header
.note-header-info .note-header-info
%a{ href: user_path(note.author) } %a{ href: user_path(note.author) }
%span.note-header-author-name %span.note-header-author-name= sanitize(note.author.name)
= sanitize(note.author.name)
%span.note-headline-light %span.note-headline-light
= note.author.to_reference = note.author.to_reference
%span.note-headline-light %span.note-headline-light
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
.cover-desc .cover-desc
- unless @user.public_email.blank? - unless @user.public_email.blank?
.profile-link-holder.middle-dot-divider .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? - unless @user.skype.blank?
.profile-link-holder.middle-dot-divider .profile-link-holder.middle-dot-divider
= link_to "skype:#{@user.skype}", title: "Skype" do = link_to "skype:#{@user.skype}", title: "Skype" do
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
= icon('twitter-square') = icon('twitter-square')
- unless @user.website_url.blank? - unless @user.website_url.blank?
.profile-link-holder.middle-dot-divider .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? - unless @user.location.blank?
.profile-link-holder.middle-dot-divider .profile-link-holder.middle-dot-divider
= icon('map-marker') = icon('map-marker')
......
...@@ -32,17 +32,15 @@ module RepositoryCheck ...@@ -32,17 +32,15 @@ module RepositoryCheck
end end
def git_fsck(repository) def git_fsck(repository)
path = repository.path_to_repo return false unless repository.exists?
cmd = %W(nice git --git-dir=#{path} fsck)
output, status = Gitlab::Popen.popen(cmd) repository.raw_repository.fsck
if status.zero?
true true
else rescue Gitlab::Git::Repository::GitError => e
Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") Gitlab::RepositoryCheckLogger.error(e.message)
false false
end end
end
def has_pushes?(project) def has_pushes?(project)
Project.with_push.exists?(project.id) Project.with_push.exists?(project.id)
......
...@@ -8,18 +8,18 @@ class RepositoryForkWorker ...@@ -8,18 +8,18 @@ class RepositoryForkWorker
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION 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) project = Project.find(project_id)
return unless start_fork(project) return unless start_fork(project)
Gitlab::Metrics.add_event(:fork_repository, Gitlab::Metrics.add_event(:fork_repository,
source_path: source_path, source_path: source_disk_path,
target_path: target_path) target_path: project.disk_path)
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path, result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_disk_path,
project.repository_storage_path, target_path) project.repository_storage_path, project.disk_path)
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result raise ForkError, "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result
project.repository.after_import project.repository.after_import
raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo? raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo?
......
---
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
---
title: show status of gitlab reference links in wiki
merge_request: 15694
author: haseebeqx
type: added
---
title: Fix the fork project functionality for projects with hashed storage
merge_request: 15671
author:
type: fixed
---
title: Fix typo in docs about Elasticsearch
merge_request: 15699
author: Takuya Noguchi
type: other
---
title: Keep track of all circuitbreaker keys in a set
merge_request: 15613
author:
type: performance
---
title: Added default order to UsersFinder
merge_request: 15679
author:
type: fixed
---
title: Optimize API /groups/:id/projects by preloading associations
merge_request:
author:
type: performance
# 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 module Gollum
GIT_ADAPTER = "rugged".freeze GIT_ADAPTER = "rugged".freeze
end end
......
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
...@@ -80,7 +80,7 @@ changes. ...@@ -80,7 +80,7 @@ changes.
The first example focuses on _how_ we fixed something, not on _what_ it fixes. 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 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 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 reading the compiled changelog. Does this entry add value? Does it offer context
......
...@@ -170,12 +170,6 @@ You can combine one or more of the following: ...@@ -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' = 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.** 1. **Using links inline of some text.**
```haml ```haml
......
...@@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of ...@@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of
### EC2 Instance ### 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. 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.
......
...@@ -370,6 +370,9 @@ This also works for the asciidoctor `:stem: latexmath`. For details see the [asc ...@@ -370,6 +370,9 @@ This also works for the asciidoctor `:stem: latexmath`. For details see the [asc
### Mermaid ### Mermaid
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in
GitLab 10.3.
> If this is not rendered correctly, see > If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
......
...@@ -88,13 +88,29 @@ module API ...@@ -88,13 +88,29 @@ module API
end end
class BasicProjectDetails < ProjectIdentity class BasicProjectDetails < ProjectIdentity
expose :default_branch, :tag_list include ::API::ProjectsRelationBuilder
expose :default_branch
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
expose :tag_list do |project|
# project.tags.order(:name).pluck(:name) is the most suitable option
# to avoid loading all the ActiveRecord objects but, if we use it here
# it override the preloaded associations and makes a query
# (fixed in https://github.com/rails/rails/pull/25976).
project.tags.map(&:name).sort
end
expose :ssh_url_to_repo, :http_url_to_repo, :web_url expose :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :avatar_url do |project, options| expose :avatar_url do |project, options|
project.avatar_url(only_path: false) project.avatar_url(only_path: false)
end end
expose :star_count, :forks_count expose :star_count, :forks_count
expose :last_activity_at expose :last_activity_at
def self.preload_relation(projects_relation, options = {})
projects_relation.preload(:project_feature, :route)
.preload(namespace: [:route, :owner],
tags: :taggings)
end
end end
class Project < BasicProjectDetails class Project < BasicProjectDetails
...@@ -146,7 +162,7 @@ module API ...@@ -146,7 +162,7 @@ module API
expose :shared_runners_enabled expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id expose :creator_id
expose :namespace, using: 'API::Entities::Namespace' expose :namespace, using: 'API::Entities::NamespaceBasic'
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
expose :import_status expose :import_status
expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
...@@ -156,7 +172,7 @@ module API ...@@ -156,7 +172,7 @@ module API
expose :public_builds, as: :public_jobs expose :public_builds, as: :public_jobs
expose :ci_config_path expose :ci_config_path
expose :shared_with_groups do |project, options| expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options) SharedGroup.represent(project.project_group_links, options)
end end
expose :only_allow_merge_if_pipeline_succeeds expose :only_allow_merge_if_pipeline_succeeds
expose :request_access_enabled expose :request_access_enabled
...@@ -164,6 +180,18 @@ module API ...@@ -164,6 +180,18 @@ module API
expose :printing_merge_request_link_enabled expose :printing_merge_request_link_enabled
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
def self.preload_relation(projects_relation, options = {})
super(projects_relation).preload(:group)
.preload(project_group_links: :group,
fork_network: :root_project,
forked_project_link: :forked_from_project,
forked_from_project: [:route, :forks, namespace: :route, tags: :taggings])
end
def self.forks_counting_projects(projects_relation)
projects_relation + projects_relation.map(&:forked_from_project).compact
end
end end
class ProjectStatistics < Grape::Entity class ProjectStatistics < Grape::Entity
...@@ -618,9 +646,11 @@ module API ...@@ -618,9 +646,11 @@ module API
expose :created_at expose :created_at
end end
class Namespace < Grape::Entity class NamespaceBasic < Grape::Entity
expose :id, :name, :path, :kind, :full_path, :parent_id expose :id, :name, :path, :kind, :full_path, :parent_id
end
class Namespace < NamespaceBasic
expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _| expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
namespace.users_with_descendants.count namespace.users_with_descendants.count
end end
...@@ -680,7 +710,7 @@ module API ...@@ -680,7 +710,7 @@ module API
if options.key?(:project_members) if options.key?(:project_members)
(options[:project_members] || []).find { |member| member.source_id == project.id } (options[:project_members] || []).find { |member| member.source_id == project.id }
else else
project.project_members.find_by(user_id: options[:current_user].id) project.project_member(options[:current_user])
end end
end end
...@@ -689,10 +719,24 @@ module API ...@@ -689,10 +719,24 @@ module API
if options.key?(:group_members) if options.key?(:group_members)
(options[:group_members] || []).find { |member| member.source_id == project.namespace_id } (options[:group_members] || []).find { |member| member.source_id == project.namespace_id }
else else
project.group.group_members.find_by(user_id: options[:current_user].id) project.group.group_member(options[:current_user])
end
end
end
end end
def self.preload_relation(projects_relation, options = {})
relation = super(projects_relation, options)
unless options.key?(:group_members)
relation = relation.preload(group: [group_members: [:source, user: [notification_settings: :source]]])
end end
unless options.key?(:project_members)
relation = relation.preload(project_members: [:source, user: [notification_settings: :source]])
end end
relation
end end
end end
......
...@@ -52,6 +52,13 @@ module API ...@@ -52,6 +52,13 @@ module API
groups groups
end end
def find_group_projects(params)
group = find_group!(params[:id])
projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute
projects = reorder_projects(projects)
paginate(projects)
end
def present_groups(params, groups) def present_groups(params, groups)
options = { options = {
with: Entities::Group, with: Entities::Group,
...@@ -170,11 +177,10 @@ module API ...@@ -170,11 +177,10 @@ module API
use :pagination use :pagination
end end
get ":id/projects" do get ":id/projects" do
group = find_group!(params[:id]) projects = find_group_projects(params)
projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute
projects = reorder_projects(projects)
entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project
present paginate(projects), with: entity, current_user: current_user
present entity.prepare_relation(projects), with: entity, current_user: current_user
end end
desc 'Get a list of subgroups in this group.' do desc 'Get a list of subgroups in this group.' do
......
...@@ -2,6 +2,8 @@ module API ...@@ -2,6 +2,8 @@ module API
module Helpers module Helpers
module Pagination module Pagination
def paginate(relation) def paginate(relation)
relation = add_default_order(relation)
relation.page(params[:page]).per(params[:per_page]).tap do |data| relation.page(params[:page]).per(params[:per_page]).tap do |data|
add_pagination_headers(data) add_pagination_headers(data)
end end
...@@ -45,6 +47,14 @@ module API ...@@ -45,6 +47,14 @@ module API
# Ensure there is in total at least 1 page # Ensure there is in total at least 1 page
[paginated_data.total_pages, 1].max [paginated_data.total_pages, 1].max
end end
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
relation = relation.order(:id)
end
relation
end
end end
end end
end end
...@@ -79,11 +79,11 @@ module API ...@@ -79,11 +79,11 @@ module API
projects = projects.with_statistics if params[:statistics] projects = projects.with_statistics if params[:statistics]
projects = projects.with_issues_enabled if params[:with_issues_enabled] projects = projects.with_issues_enabled if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled] projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
projects = paginate(projects)
if current_user if current_user
projects = projects.includes(:route, :taggings, namespace: :route) project_members = current_user.project_members.preload(:source, user: [notification_settings: :source])
project_members = current_user.project_members group_members = current_user.group_members.preload(:source, user: [notification_settings: :source])
group_members = current_user.group_members
end end
options = options.reverse_merge( options = options.reverse_merge(
...@@ -95,7 +95,7 @@ module API ...@@ -95,7 +95,7 @@ module API
) )
options[:with] = Entities::BasicProjectDetails if params[:simple] options[:with] = Entities::BasicProjectDetails if params[:simple]
present paginate(projects), options present options[:with].prepare_relation(projects, options), options
end end
end end
......
module API
module ProjectsRelationBuilder
extend ActiveSupport::Concern
module ClassMethods
def prepare_relation(projects_relation, options = {})
projects_relation = preload_relation(projects_relation, options)
execute_batch_counting(projects_relation)
projects_relation
end
def preload_relation(projects_relation, options = {})
projects_relation
end
def forks_counting_projects(projects_relation)
projects_relation
end
def batch_forks_counting(projects_relation)
::Projects::BatchForksCountService.new(forks_counting_projects(projects_relation)).refresh_cache
end
def batch_open_issues_counting(projects_relation)
::Projects::BatchOpenIssuesCountService.new(projects_relation).refresh_cache
end
def execute_batch_counting(projects_relation)
batch_forks_counting(projects_relation)
batch_open_issues_counting(projects_relation)
end
end
end
end
...@@ -76,6 +76,8 @@ module API ...@@ -76,6 +76,8 @@ module API
forbidden!("Not authorized to access /api/v4/users") unless authorized forbidden!("Not authorized to access /api/v4/users") unless authorized
entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
present paginate(users), with: entity present paginate(users), with: entity
end end
......
...@@ -1118,9 +1118,11 @@ module Gitlab ...@@ -1118,9 +1118,11 @@ module Gitlab
end end
# Refactoring aid; allows us to copy code from app/models/repository.rb # Refactoring aid; allows us to copy code from app/models/repository.rb
def run_git(args, env: {}) def run_git(args, env: {}, nice: false)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
circuit_breaker.perform do circuit_breaker.perform do
popen([Gitlab.config.git.bin_path, *args], path, env) popen(cmd, path, env)
end end
end end
...@@ -1187,6 +1189,12 @@ module Gitlab ...@@ -1187,6 +1189,12 @@ module Gitlab
end end
end end
def fsck
output, status = run_git(%W[--git-dir=#{path} fsck], nice: true)
raise GitError.new("Could not fsck repository:\n#{output}") unless status.zero?
end
def gitaly_repository def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end end
......
...@@ -15,6 +15,7 @@ module Gitlab ...@@ -15,6 +15,7 @@ module Gitlab
Failing = Class.new(Inaccessible) Failing = Class.new(Inaccessible)
REDIS_KEY_PREFIX = 'storage_accessible:'.freeze REDIS_KEY_PREFIX = 'storage_accessible:'.freeze
REDIS_KNOWN_KEYS = "#{REDIS_KEY_PREFIX}known_keys_set".freeze
def self.redis def self.redis
Gitlab::Redis::SharedState Gitlab::Redis::SharedState
......
...@@ -13,10 +13,8 @@ module Gitlab ...@@ -13,10 +13,8 @@ module Gitlab
delegate :last_failure, :failure_count, to: :failure_info delegate :last_failure, :failure_count, to: :failure_info
def self.reset_all! def self.reset_all!
pattern = "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}*"
Gitlab::Git::Storage.redis.with do |redis| Gitlab::Git::Storage.redis.with do |redis|
all_storage_keys = redis.scan_each(match: pattern).to_a all_storage_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
redis.del(*all_storage_keys) unless all_storage_keys.empty? redis.del(*all_storage_keys) unless all_storage_keys.empty?
end end
...@@ -135,23 +133,29 @@ module Gitlab ...@@ -135,23 +133,29 @@ module Gitlab
redis.hset(cache_key, :last_failure, last_failure.to_i) redis.hset(cache_key, :last_failure, last_failure.to_i)
redis.hincrby(cache_key, :failure_count, 1) redis.hincrby(cache_key, :failure_count, 1)
redis.expire(cache_key, failure_reset_time) redis.expire(cache_key, failure_reset_time)
maintain_known_keys(redis)
end end
end end
end end
def track_storage_accessible def track_storage_accessible
return if no_failures?
@failure_info = FailureInfo.new(nil, 0) @failure_info = FailureInfo.new(nil, 0)
Gitlab::Git::Storage.redis.with do |redis| Gitlab::Git::Storage.redis.with do |redis|
redis.pipelined do redis.pipelined do
redis.hset(cache_key, :last_failure, nil) redis.hset(cache_key, :last_failure, nil)
redis.hset(cache_key, :failure_count, 0) redis.hset(cache_key, :failure_count, 0)
maintain_known_keys(redis)
end end
end end
end end
def maintain_known_keys(redis)
expire_time = Time.now.to_i + failure_reset_time
redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, expire_time, cache_key)
redis.zremrangebyscore(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, '-inf', Time.now.to_i)
end
def get_failure_info def get_failure_info
last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis| last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis|
redis.hmget(cache_key, :last_failure, :failure_count) redis.hmget(cache_key, :last_failure, :failure_count)
......
...@@ -4,8 +4,8 @@ module Gitlab ...@@ -4,8 +4,8 @@ module Gitlab
class Health class Health
attr_reader :storage_name, :info attr_reader :storage_name, :info
def self.pattern_for_storage(storage_name) def self.prefix_for_storage(storage_name)
"#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:*" "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:"
end end
def self.for_all_storages def self.for_all_storages
...@@ -25,26 +25,15 @@ module Gitlab ...@@ -25,26 +25,15 @@ module Gitlab
private_class_method def self.all_keys_for_storages(storage_names, redis) private_class_method def self.all_keys_for_storages(storage_names, redis)
keys_per_storage = {} keys_per_storage = {}
all_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
redis.pipelined do
storage_names.each do |storage_name| storage_names.each do |storage_name|
pattern = pattern_for_storage(storage_name) prefix = prefix_for_storage(storage_name)
matched_keys = redis.scan_each(match: pattern)
keys_per_storage[storage_name] = matched_keys keys_per_storage[storage_name] = all_keys.select { |key| key.starts_with?(prefix) }
end
end end
# We need to make sure each lazy-loaded `Enumerator` for matched keys keys_per_storage
# is loaded into an array.
#
# Otherwise it would be loaded in the second `Redis#pipelined` block
# within `.load_for_keys`. In this pipelined call, the active
# Redis-client changes again, so the values would not be available
# until the end of that pipelined-block.
keys_per_storage.each do |storage_name, key_future|
keys_per_storage[storage_name] = key_future.to_a
end
end end
private_class_method def self.load_for_keys(keys_per_storage, redis) private_class_method def self.load_for_keys(keys_per_storage, redis)
......
...@@ -8,7 +8,8 @@ end ...@@ -8,7 +8,8 @@ end
module Gitlab module Gitlab
class Seeder class Seeder
def self.quiet def self.quiet
mute_mailer mute_mailer unless Rails.env.test?
SeedFu.quiet = true SeedFu.quiet = true
yield yield
......
...@@ -143,20 +143,27 @@ module Gitlab ...@@ -143,20 +143,27 @@ module Gitlab
storage, "#{path}.git", "#{new_path}.git"]) storage, "#{path}.git", "#{new_path}.git"])
end end
# Fork repository to new namespace # Fork repository to new path
# forked_from_storage - forked-from project's storage path # forked_from_storage - forked-from project's storage path
# path - project path with namespace # forked_from_disk_path - project disk path
# forked_to_storage - forked-to project's storage path # forked_to_storage - forked-to project's storage path
# fork_namespace - namespace for forked project # forked_to_disk_path - forked project disk path
# #
# Ex. # Ex.
# fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "randx") # fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "new-namespace/gitlab-ci")
# #
# Gitaly note: JV: not easy to migrate because this involves two Gitaly servers, not one. # Gitaly note: JV: not easy to migrate because this involves two Gitaly servers, not one.
def fork_repository(forked_from_storage, path, forked_to_storage, fork_namespace) def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
gitlab_shell_fast_execute([gitlab_shell_projects_path, 'fork-project', gitlab_shell_fast_execute(
forked_from_storage, "#{path}.git", forked_to_storage, [
fork_namespace]) gitlab_shell_projects_path,
'fork-repository',
forked_from_storage,
"#{forked_from_disk_path}.git",
forked_to_storage,
"#{forked_to_disk_path}.git"
]
)
end end
# Remove repository from file system # Remove repository from file system
......
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-03 12:30-0400\n" "PO-Revision-Date: 2017-11-28 11:32-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: German\n" "Language-Team: German\n"
"Language: de_DE\n" "Language: de_DE\n"
...@@ -2070,7 +2070,7 @@ msgid "Time until first merge request" ...@@ -2070,7 +2070,7 @@ msgid "Time until first merge request"
msgstr "Zeit bis zum ersten Merge Request" msgstr "Zeit bis zum ersten Merge Request"
msgid "Timeago|%s days ago" msgid "Timeago|%s days ago"
msgstr "" msgstr "vor %s Tagen"
msgid "Timeago|%s days remaining" msgid "Timeago|%s days remaining"
msgstr "%s Tage verbleibend" msgstr "%s Tage verbleibend"
......
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-03 12:30-0400\n" "PO-Revision-Date: 2017-11-21 16:43-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: French\n" "Language-Team: French\n"
"Language: fr_FR\n" "Language: fr_FR\n"
...@@ -36,8 +36,8 @@ msgstr "%{commit_author_link} a validé %{commit_timeago}" ...@@ -36,8 +36,8 @@ msgstr "%{commit_author_link} a validé %{commit_timeago}"
msgid "%{count} participant" msgid "%{count} participant"
msgid_plural "%{count} participants" msgid_plural "%{count} participants"
msgstr[0] "" msgstr[0] "%{count} participant•e"
msgstr[1] "" msgstr[1] "%{count} participant•e•s"
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "%{number_commits_behind} validations de retard sur %{default_branch}, %{number_commits_ahead} validations d'avance" msgstr "%{number_commits_behind} validations de retard sur %{default_branch}, %{number_commits_ahead} validations d'avance"
...@@ -60,10 +60,10 @@ msgid "(checkout the %{link} for information on how to install it)." ...@@ -60,10 +60,10 @@ msgid "(checkout the %{link} for information on how to install it)."
msgstr "(Lisez %{link} pour savoir comment l'installer)." msgstr "(Lisez %{link} pour savoir comment l'installer)."
msgid "+ %{moreCount} more" msgid "+ %{moreCount} more"
msgstr "" msgstr "+ %{moreCount} de plus"
msgid "- show less" msgid "- show less"
msgstr "" msgstr "- en montrer moins"
msgid "1 pipeline" msgid "1 pipeline"
msgid_plural "%d pipelines" msgid_plural "%d pipelines"
...@@ -125,7 +125,7 @@ msgid "AdminHealthPageLink|health page" ...@@ -125,7 +125,7 @@ msgid "AdminHealthPageLink|health page"
msgstr "État des services" msgstr "État des services"
msgid "Advanced settings" msgid "Advanced settings"
msgstr "" msgstr "Paramètres avancés"
msgid "All" msgid "All"
msgstr "Tous" msgstr "Tous"
...@@ -625,8 +625,8 @@ msgstr[1] "Validations" ...@@ -625,8 +625,8 @@ msgstr[1] "Validations"
msgid "Commit %d file" msgid "Commit %d file"
msgid_plural "Commit %d files" msgid_plural "Commit %d files"
msgstr[0] "" msgstr[0] "Valider %d fichier"
msgstr[1] "" msgstr[1] "Valider %d fichiers"
msgid "Commit Message" msgid "Commit Message"
msgstr "Message de validation" msgstr "Message de validation"
...@@ -692,10 +692,10 @@ msgid "ContainerRegistry|Size" ...@@ -692,10 +692,10 @@ msgid "ContainerRegistry|Size"
msgstr "Taille" msgstr "Taille"
msgid "ContainerRegistry|Tag" msgid "ContainerRegistry|Tag"
msgstr "Étiquette" msgstr "Tag"
msgid "ContainerRegistry|Tag ID" msgid "ContainerRegistry|Tag ID"
msgstr "ID d‘étiquette" msgstr "ID du tag"
msgid "ContainerRegistry|Use different image names" msgid "ContainerRegistry|Use different image names"
msgstr "Utilisez des noms d’images différents" msgstr "Utilisez des noms d’images différents"
...@@ -704,7 +704,7 @@ msgid "ContainerRegistry|With the Docker Container Registry integrated into GitL ...@@ -704,7 +704,7 @@ msgid "ContainerRegistry|With the Docker Container Registry integrated into GitL
msgstr "Avec le registre de conteneur Docker intégré à GitLab, chaque projet peut avoir son propre espace pour stocker ses images Docker." msgstr "Avec le registre de conteneur Docker intégré à GitLab, chaque projet peut avoir son propre espace pour stocker ses images Docker."
msgid "Contribution guide" msgid "Contribution guide"
msgstr "Guilde de contribution" msgstr "Guide de contribution"
msgid "Contributors" msgid "Contributors"
msgstr "Contributeurs" msgstr "Contributeurs"
...@@ -737,7 +737,7 @@ msgid "Create empty bare repository" ...@@ -737,7 +737,7 @@ msgid "Create empty bare repository"
msgstr "Créer un dépôt vide" msgstr "Créer un dépôt vide"
msgid "Create file" msgid "Create file"
msgstr "" msgstr "Créer un fichier"
msgid "Create merge request" msgid "Create merge request"
msgstr "Créer une demande de fusion" msgstr "Créer une demande de fusion"
...@@ -746,10 +746,10 @@ msgid "Create new branch" ...@@ -746,10 +746,10 @@ msgid "Create new branch"
msgstr "Créer une nouvelle branche" msgstr "Créer une nouvelle branche"
msgid "Create new directory" msgid "Create new directory"
msgstr "" msgstr "Créer un nouveau dossier"
msgid "Create new file" msgid "Create new file"
msgstr "" msgstr "Créer un nouveau fichier"
msgid "Create new..." msgid "Create new..."
msgstr "Créer nouveau..." msgstr "Créer nouveau..."
...@@ -922,7 +922,7 @@ msgid "Failed to remove the pipeline schedule" ...@@ -922,7 +922,7 @@ msgid "Failed to remove the pipeline schedule"
msgstr "Échec de la suppression du pipeline programmé" msgstr "Échec de la suppression du pipeline programmé"
msgid "File name" msgid "File name"
msgstr "" msgstr "Nom du fichier"
msgid "Files" msgid "Files"
msgstr "Fichiers" msgstr "Fichiers"
...@@ -1364,10 +1364,10 @@ msgid "Notifications" ...@@ -1364,10 +1364,10 @@ msgid "Notifications"
msgstr "Notifications" msgstr "Notifications"
msgid "Number of access attempts" msgid "Number of access attempts"
msgstr "" msgstr "Nombre de tentatives d'accès"
msgid "Number of failures before backing off" msgid "Number of failures before backing off"
msgstr "" msgstr "Nombre d'échecs avant annulation"
msgid "OfSearchInADropdown|Filter" msgid "OfSearchInADropdown|Filter"
msgstr "Filtre" msgstr "Filtre"
...@@ -1625,7 +1625,7 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr ...@@ -1625,7 +1625,7 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr
msgstr "Ce paramètre s’appliquera à tous les projets à moins qu’un administrateur ne le modifie." msgstr "Ce paramètre s’appliquera à tous les projets à moins qu’un administrateur ne le modifie."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails." msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
msgstr "" msgstr "Les utilisateurs peuvent uniquement pousser sur ce dépôt des validations qui ont été validées avec une de leurs adresses courriels vérifiées."
msgid "Projects" msgid "Projects"
msgstr "Projets" msgstr "Projets"
...@@ -1664,7 +1664,7 @@ msgid "Push events" ...@@ -1664,7 +1664,7 @@ msgid "Push events"
msgstr "Évènements de poussée" msgstr "Évènements de poussée"
msgid "PushRule|Committer restriction" msgid "PushRule|Committer restriction"
msgstr "" msgstr "Restriction du validateur"
msgid "Read more" msgid "Read more"
msgstr "Lire plus" msgstr "Lire plus"
...@@ -1682,7 +1682,7 @@ msgid "Registry" ...@@ -1682,7 +1682,7 @@ msgid "Registry"
msgstr "Registre" msgstr "Registre"
msgid "Related Commits" msgid "Related Commits"
msgstr "Validations liés" msgstr "Validations liées"
msgid "Related Deployed Jobs" msgid "Related Deployed Jobs"
msgstr "Tâches de déploiement liées" msgstr "Tâches de déploiement liées"
...@@ -1730,7 +1730,7 @@ msgid "SSH Keys" ...@@ -1730,7 +1730,7 @@ msgid "SSH Keys"
msgstr "Clés SSH" msgstr "Clés SSH"
msgid "Save" msgid "Save"
msgstr "" msgstr "Enregistrer"
msgid "Save changes" msgid "Save changes"
msgstr "Enregistrer les modifications" msgstr "Enregistrer les modifications"
...@@ -1939,10 +1939,10 @@ msgid "Subgroups" ...@@ -1939,10 +1939,10 @@ msgid "Subgroups"
msgstr "Sous-groupes" msgstr "Sous-groupes"
msgid "Subscribe" msgid "Subscribe"
msgstr "" msgstr "S’abonner"
msgid "Switch branch/tag" msgid "Switch branch/tag"
msgstr "Changer de branche / d'étiquette" msgstr "Changer de branche / tag"
msgid "System Hooks" msgid "System Hooks"
msgstr "Crochets système" msgstr "Crochets système"
...@@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa ...@@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa
msgstr "La Recherche Globale Avancée de GitLab est un outils qui vous fait gagner du temps. Au lieu de créer du code similaire et perdre du temps, vous pouvez maintenant chercher dans le code d'autres équipes pour vous aider sur votre projet." msgstr "La Recherche Globale Avancée de GitLab est un outils qui vous fait gagner du temps. Au lieu de créer du code similaire et perdre du temps, vous pouvez maintenant chercher dans le code d'autres équipes pour vous aider sur votre projet."
msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
msgstr "" msgstr "Le seuil d’interruption du disjoncteur devrait être inférieur au seuil de nombre de défaillance"
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion." msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
...@@ -1983,10 +1983,10 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni ...@@ -1983,10 +1983,10 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni
msgstr "L'étape des tickets montre le temps nécessaire entre la création d'un ticket et son assignation à un jalon, ou son ajout à une liste d'un tableau de tickets. Commencez par créer des tickets pour voir des données pour cette étape." msgstr "L'étape des tickets montre le temps nécessaire entre la création d'un ticket et son assignation à un jalon, ou son ajout à une liste d'un tableau de tickets. Commencez par créer des tickets pour voir des données pour cette étape."
msgid "The number of attempts GitLab will make to access a storage." msgid "The number of attempts GitLab will make to access a storage."
msgstr "" msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder au stockage."
msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host" msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
msgstr "" msgstr "Le nombre d'échecs avant que GitLab ne commence à désactiver l'accès à la partition de stockage sur l'hôte"
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}." msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant le %{api_documentation_link}." msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant le %{api_documentation_link}."
...@@ -2224,7 +2224,7 @@ msgid "Unstar" ...@@ -2224,7 +2224,7 @@ msgid "Unstar"
msgstr "Supprimer des favoris" msgstr "Supprimer des favoris"
msgid "Unsubscribe" msgid "Unsubscribe"
msgstr "" msgstr "Se désabonner"
msgid "Upgrade your plan to activate Advanced Global Search." msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Mettez à jour votre abonnement pour activer la Recherche Globale Avancée." msgstr "Mettez à jour votre abonnement pour activer la Recherche Globale Avancée."
......
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-03 12:31-0400\n" "PO-Revision-Date: 2017-11-20 03:59-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Italian\n" "Language-Team: Italian\n"
"Language: it_IT\n" "Language: it_IT\n"
...@@ -2085,7 +2085,7 @@ msgid "Timeago|%s minutes remaining" ...@@ -2085,7 +2085,7 @@ msgid "Timeago|%s minutes remaining"
msgstr "%s minuti rimanenti" msgstr "%s minuti rimanenti"
msgid "Timeago|%s months ago" msgid "Timeago|%s months ago"
msgstr "%s minuti fa" msgstr "%s mesi fa"
msgid "Timeago|%s months remaining" msgid "Timeago|%s months remaining"
msgstr "%s mesi rimanenti" msgstr "%s mesi rimanenti"
......
This diff is collapsed.
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-04 13:27-0400\n" "PO-Revision-Date: 2017-11-18 12:51-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Portuguese, Brazilian\n" "Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n" "Language: pt_BR\n"
...@@ -361,10 +361,10 @@ msgid "Browse Directory" ...@@ -361,10 +361,10 @@ msgid "Browse Directory"
msgstr "Navegar no Diretório" msgstr "Navegar no Diretório"
msgid "Browse File" msgid "Browse File"
msgstr "Pesquisar Arquivo" msgstr "Acessar arquivo"
msgid "Browse Files" msgid "Browse Files"
msgstr "Pesquisar Arquivos" msgstr "Acessar arquivos"
msgid "Browse files" msgid "Browse files"
msgstr "Navegar pelos arquivos" msgstr "Navegar pelos arquivos"
...@@ -1367,7 +1367,7 @@ msgid "Number of access attempts" ...@@ -1367,7 +1367,7 @@ msgid "Number of access attempts"
msgstr "Número de tentativas de acesso" msgstr "Número de tentativas de acesso"
msgid "Number of failures before backing off" msgid "Number of failures before backing off"
msgstr "" msgstr "Número de falhas antes de reverter"
msgid "OfSearchInADropdown|Filter" msgid "OfSearchInADropdown|Filter"
msgstr "Filtrar" msgstr "Filtrar"
...@@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa ...@@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa
msgstr "A pesquisa global avançado no GitLab é um serviço poderoso de pesquisa que poupa seu tempo. Ao invés de criar código duplicado e perder seu tempo, você pode agora pesquisar por código de outros times que podem ajudar no seu próprio projeto." msgstr "A pesquisa global avançado no GitLab é um serviço poderoso de pesquisa que poupa seu tempo. Ao invés de criar código duplicado e perder seu tempo, você pode agora pesquisar por código de outros times que podem ajudar no seu próprio projeto."
msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
msgstr "" msgstr "O limite do recuso do circuitbreaker deve ser inferior ao limite de contagem de falhas"
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "A etapa de codificação mostra o tempo desde a entrega do primeiro commit até a criação do merge request. Os dados serão automaticamente adicionados aqui desde o momento de criação do merge request." msgstr "A etapa de codificação mostra o tempo desde a entrega do primeiro commit até a criação do merge request. Os dados serão automaticamente adicionados aqui desde o momento de criação do merge request."
......
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-05 14:39-0500\n" "PO-Revision-Date: 2017-11-17 07:55-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Russian\n" "Language-Team: Russian\n"
"Language: ru_RU\n" "Language: ru_RU\n"
...@@ -188,7 +188,7 @@ msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly ...@@ -188,7 +188,7 @@ msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly
msgstr "Приложения для автоматического ревью и автоматического развёртывания требуют указания %{kubernetes} для корректной работы." msgstr "Приложения для автоматического ревью и автоматического развёртывания требуют указания %{kubernetes} для корректной работы."
msgid "AutoDevOps|Auto DevOps (Beta)" msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "" msgstr "Auto DevOps (бета)"
msgid "AutoDevOps|Auto DevOps documentation" msgid "AutoDevOps|Auto DevOps documentation"
msgstr "" msgstr ""
......
This diff is collapsed.
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-06 01:25-0500\n" "PO-Revision-Date: 2017-11-23 02:44-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Simplified\n" "Language-Team: Chinese Simplified\n"
"Language: zh_CN\n" "Language: zh_CN\n"
...@@ -656,10 +656,10 @@ msgid "ContainerRegistry|Created" ...@@ -656,10 +656,10 @@ msgid "ContainerRegistry|Created"
msgstr "已创建" msgstr "已创建"
msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:" msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
msgstr "首先使用您的 GitLab 用户名和密码登录 GitLab 的容器注册表。如果您有%{link_2fa},您需要使用%{link_token}:" msgstr "首先使用您的 GitLab 用户名和密码登录 GitLab 的容器注册表。如果您已经%{link_2fa},则需要使用%{link_token}:"
msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:" msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
msgstr "GitLab 最多支持3个级别的镜像名称。以下镜像示例对您的项目有效:" msgstr "GitLab 最多支持3个级别的镜像命名。以下镜像名称示例对当前项目有效:"
msgid "ContainerRegistry|How to use the Container Registry" msgid "ContainerRegistry|How to use the Container Registry"
msgstr "如何使用容器注册表" msgstr "如何使用容器注册表"
...@@ -671,7 +671,7 @@ msgid "ContainerRegistry|No tags in Container Registry for this container image. ...@@ -671,7 +671,7 @@ msgid "ContainerRegistry|No tags in Container Registry for this container image.
msgstr "容器注册表中没有此容器镜像的标签。" msgstr "容器注册表中没有此容器镜像的标签。"
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands" msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
msgstr "登录后您可以使用通用的%{build}和%{push}命令自由创建和上传容器映像" msgstr "登录后您可以使用通用的%{build}和%{push}命令创建和上传容器镜像"
msgid "ContainerRegistry|Remove repository" msgid "ContainerRegistry|Remove repository"
msgstr "删除存储库" msgstr "删除存储库"
...@@ -692,7 +692,7 @@ msgid "ContainerRegistry|Use different image names" ...@@ -692,7 +692,7 @@ msgid "ContainerRegistry|Use different image names"
msgstr "使用不同的镜像名称" msgstr "使用不同的镜像名称"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images." msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "将 Docker 容器注册表集成到 GitLab 中,每个项目都可以有自己的空间来存储 Docker 的图像。" msgstr "将 Docker 容器注册表集成到 GitLab 中,每个项目都可以有各自的空间来存储 Docker 的镜像。"
msgid "Contribution guide" msgid "Contribution guide"
msgstr "贡献指南" msgstr "贡献指南"
...@@ -1155,7 +1155,7 @@ msgid "Last update" ...@@ -1155,7 +1155,7 @@ msgid "Last update"
msgstr "最后更新" msgstr "最后更新"
msgid "Last updated" msgid "Last updated"
msgstr "最更新" msgstr "最更新"
msgid "LastPushEvent|You pushed to" msgid "LastPushEvent|You pushed to"
msgstr "您推送了" msgstr "您推送了"
...@@ -1274,7 +1274,7 @@ msgid "New tag" ...@@ -1274,7 +1274,7 @@ msgid "New tag"
msgstr "新建标签" msgstr "新建标签"
msgid "No container images stored for this project. Add one by following the instructions above." msgid "No container images stored for this project. Add one by following the instructions above."
msgstr "没有为此项目存储容器镜像。请按照上述说明添加一个。" msgstr "此项目当前未存储容器镜像。如需使用,请参照上述说明新建容器镜像。"
msgid "No repository" msgid "No repository"
msgstr "没有存储库" msgstr "没有存储库"
...@@ -1361,7 +1361,7 @@ msgid "Only project members can comment." ...@@ -1361,7 +1361,7 @@ msgid "Only project members can comment."
msgstr "只有项目成员可以发表评论。" msgstr "只有项目成员可以发表评论。"
msgid "OpenedNDaysAgo|Opened" msgid "OpenedNDaysAgo|Opened"
msgstr "开始于" msgstr "创建于"
msgid "Opens in a new window" msgid "Opens in a new window"
msgstr "打开一个新窗口" msgstr "打开一个新窗口"
...@@ -1775,10 +1775,10 @@ msgid "Settings" ...@@ -1775,10 +1775,10 @@ msgid "Settings"
msgstr "设置" msgstr "设置"
msgid "Show parent pages" msgid "Show parent pages"
msgstr "查看页面" msgstr "查看上级页面"
msgid "Show parent subgroups" msgid "Show parent subgroups"
msgstr "查看群组中的子群组" msgstr "查看上级子群组"
msgid "Showing %d event" msgid "Showing %d event"
msgid_plural "Showing %d events" msgid_plural "Showing %d events"
...@@ -1830,13 +1830,13 @@ msgid "SortOptions|Largest repository" ...@@ -1830,13 +1830,13 @@ msgid "SortOptions|Largest repository"
msgstr "最大存储库" msgstr "最大存储库"
msgid "SortOptions|Last created" msgid "SortOptions|Last created"
msgstr "最创建" msgstr "最创建"
msgid "SortOptions|Last joined" msgid "SortOptions|Last joined"
msgstr "最新加入" msgstr "最新加入"
msgid "SortOptions|Last updated" msgid "SortOptions|Last updated"
msgstr "最更新" msgstr "最更新"
msgid "SortOptions|Least popular" msgid "SortOptions|Least popular"
msgstr "最不受欢迎" msgstr "最不受欢迎"
...@@ -1869,7 +1869,7 @@ msgid "SortOptions|Name, descending" ...@@ -1869,7 +1869,7 @@ msgid "SortOptions|Name, descending"
msgstr "名称,降序排列" msgstr "名称,降序排列"
msgid "SortOptions|Oldest created" msgid "SortOptions|Oldest created"
msgstr "最早创建" msgstr "最早创建"
msgid "SortOptions|Oldest joined" msgid "SortOptions|Oldest joined"
msgstr "最早的加入" msgstr "最早的加入"
...@@ -1878,7 +1878,7 @@ msgid "SortOptions|Oldest sign in" ...@@ -1878,7 +1878,7 @@ msgid "SortOptions|Oldest sign in"
msgstr "最早的登录" msgstr "最早的登录"
msgid "SortOptions|Oldest updated" msgid "SortOptions|Oldest updated"
msgstr "最早的提交" msgstr "最早更新"
msgid "SortOptions|Popularity" msgid "SortOptions|Popularity"
msgstr "人气" msgstr "人气"
...@@ -1945,7 +1945,7 @@ msgid "Team" ...@@ -1945,7 +1945,7 @@ msgid "Team"
msgstr "团队" msgstr "团队"
msgid "Thanks! Don't show me this again" msgid "Thanks! Don't show me this again"
msgstr "谢谢 ! 请不要再显示" msgstr "不再显示该提示"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project." msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "GitLab 中的高级全局搜索功能是非常强大的搜索服务。您可以搜索其他团队的代码以帮助您完善自己项目中的代码。从而避免创建重复的代码或浪费时间。" msgstr "GitLab 中的高级全局搜索功能是非常强大的搜索服务。您可以搜索其他团队的代码以帮助您完善自己项目中的代码。从而避免创建重复的代码或浪费时间。"
...@@ -2465,7 +2465,7 @@ msgstr "通知邮件" ...@@ -2465,7 +2465,7 @@ msgstr "通知邮件"
msgid "parent" msgid "parent"
msgid_plural "parents" msgid_plural "parents"
msgstr[0] "级" msgstr[0] "级"
msgid "password" msgid "password"
msgstr "密码" msgstr "密码"
......
...@@ -3,7 +3,7 @@ msgstr "" ...@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: gitlab-ee\n" "Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-02 14:42+0100\n" "POT-Creation-Date: 2017-11-02 14:42+0100\n"
"PO-Revision-Date: 2017-11-03 12:30-0400\n" "PO-Revision-Date: 2017-11-15 02:54-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Traditional, Hong Kong\n" "Language-Team: Chinese Traditional, Hong Kong\n"
"Language: zh_HK\n" "Language: zh_HK\n"
...@@ -609,7 +609,7 @@ msgid "ClusterIntegration|properly configured" ...@@ -609,7 +609,7 @@ msgid "ClusterIntegration|properly configured"
msgstr "" msgstr ""
msgid "Comments" msgid "Comments"
msgstr "評論 (Comment)" msgstr "評論"
msgid "Commit" msgid "Commit"
msgid_plural "Commits" msgid_plural "Commits"
...@@ -876,7 +876,7 @@ msgid "EventFilterBy|Filter by all" ...@@ -876,7 +876,7 @@ msgid "EventFilterBy|Filter by all"
msgstr "全部" msgstr "全部"
msgid "EventFilterBy|Filter by comments" msgid "EventFilterBy|Filter by comments"
msgstr "按評論 (comment) 過濾" msgstr "按評論過濾"
msgid "EventFilterBy|Filter by issue events" msgid "EventFilterBy|Filter by issue events"
msgstr "按議題事件 (issue event) 過濾" msgstr "按議題事件 (issue event) 過濾"
......
This diff is collapsed.
...@@ -205,7 +205,7 @@ describe MarkupHelper do ...@@ -205,7 +205,7 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown) allow(@wiki).to receive(:format).and_return(:markdown)
expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page") expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true)
helper.render_wiki_content(@wiki) helper.render_wiki_content(@wiki)
end end
......
import Vue from 'vue'; import Vue from 'vue';
import store from '~/notes/stores'; import store from '~/notes/stores';
import issueActions from '~/notes/components/issue_note_actions.vue'; import noteActions from '~/notes/components/note_actions.vue';
import { userDataMock } from '../mock_data'; import { userDataMock } from '../mock_data';
describe('issse_note_actions component', () => { describe('issse_note_actions component', () => {
...@@ -8,7 +8,7 @@ describe('issse_note_actions component', () => { ...@@ -8,7 +8,7 @@ describe('issse_note_actions component', () => {
let Component; let Component;
beforeEach(() => { beforeEach(() => {
Component = Vue.extend(issueActions); Component = Vue.extend(noteActions);
}); });
afterEach(() => { afterEach(() => {
......
import Vue from 'vue'; import Vue from 'vue';
import issueNoteAttachment from '~/notes/components/issue_note_attachment.vue'; import noteAttachment from '~/notes/components/note_attachment.vue';
describe('issue note attachment', () => { describe('issue note attachment', () => {
it('should render properly', () => { it('should render properly', () => {
...@@ -11,7 +11,7 @@ describe('issue note attachment', () => { ...@@ -11,7 +11,7 @@ describe('issue note attachment', () => {
}, },
}; };
const Component = Vue.extend(issueNoteAttachment); const Component = Vue.extend(noteAttachment);
const vm = new Component({ const vm = new Component({
propsData: props, propsData: props,
}).$mount(); }).$mount();
......
import Vue from 'vue'; import Vue from 'vue';
import store from '~/notes/stores'; import store from '~/notes/stores';
import awardsNote from '~/notes/components/issue_note_awards_list.vue'; import awardsNote from '~/notes/components/note_awards_list.vue';
import { noteableDataMock, notesDataMock } from '../mock_data'; import { noteableDataMock, notesDataMock } from '../mock_data';
describe('issue_note_awards_list component', () => { describe('note_awards_list component', () => {
let vm; let vm;
let awardsMock; let awardsMock;
......
import Vue from 'vue'; import Vue from 'vue';
import issueNoteEditedText from '~/notes/components/issue_note_edited_text.vue'; import noteEditedText from '~/notes/components/note_edited_text.vue';
describe('issue_note_edited_text', () => { describe('note_edited_text', () => {
let vm; let vm;
let props; let props;
beforeEach(() => { beforeEach(() => {
const Component = Vue.extend(issueNoteEditedText); const Component = Vue.extend(noteEditedText);
props = { props = {
actionText: 'Edited', actionText: 'Edited',
className: 'foo-bar', className: 'foo-bar',
......
import Vue from 'vue'; import Vue from 'vue';
import issueNoteHeader from '~/notes/components/issue_note_header.vue'; import noteHeader from '~/notes/components/note_header.vue';
import store from '~/notes/stores'; import store from '~/notes/stores';
describe('issue_note_header component', () => { describe('note_header component', () => {
let vm; let vm;
let Component; let Component;
beforeEach(() => { beforeEach(() => {
Component = Vue.extend(issueNoteHeader); Component = Vue.extend(noteHeader);
}); });
afterEach(() => { afterEach(() => {
......
import Vue from 'vue'; import Vue from 'vue';
import issueNoteSignedOut from '~/notes/components/issue_note_signed_out_widget.vue'; import noteSignedOut from '~/notes/components/note_signed_out_widget.vue';
import store from '~/notes/stores'; import store from '~/notes/stores';
import { notesDataMock } from '../mock_data'; import { notesDataMock } from '../mock_data';
describe('issue_note_signed_out_widget component', () => { describe('note_signed_out_widget component', () => {
let vm; let vm;
beforeEach(() => { beforeEach(() => {
const Component = Vue.extend(issueNoteSignedOut); const Component = Vue.extend(noteSignedOut);
store.dispatch('setNotesData', notesDataMock); store.dispatch('setNotesData', notesDataMock);
vm = new Component({ vm = new Component({
......
...@@ -92,6 +92,27 @@ describe API::Helpers::Pagination do ...@@ -92,6 +92,27 @@ describe API::Helpers::Pagination do
subject.paginate(resource) subject.paginate(resource)
end end
end end
context 'if order' do
it 'is not present it adds default order(:id) if no order is present' do
resource.order_values = []
paginated_relation = subject.paginate(resource)
expect(resource.order_values).to be_empty
expect(paginated_relation.order_values).to be_present
expect(paginated_relation.order_values.first).to be_ascending
expect(paginated_relation.order_values.first.expr.name).to eq :id
end
it 'is present it does not add anything' do
paginated_relation = subject.paginate(resource.order(created_at: :desc))
expect(paginated_relation.order_values).to be_present
expect(paginated_relation.order_values.first).to be_descending
expect(paginated_relation.order_values.first.expr.name).to eq :created_at
end
end
end end
context 'when resource empty' do context 'when resource empty' 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.
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