Commit 3de86826 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream[ci skip]

parents ad8e7eb1 c00e5bfa
This diff is collapsed.
...@@ -50,6 +50,8 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._ ...@@ -50,6 +50,8 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._
Thank you for your interest in contributing to GitLab. This guide details how Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is efficient for everyone. to contribute to GitLab in a way that is efficient for everyone.
Looking for something to work on? Look for the label [Accepting Merge Requests](#i-want-to-contribute).
GitLab comes into two flavors, GitLab Community Edition (CE) our free and open GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
source edition, and GitLab Enterprise Edition (EE) which is our commercial source edition, and GitLab Enterprise Edition (EE) which is our commercial
edition. Throughout this guide you will see references to CE and EE for edition. Throughout this guide you will see references to CE and EE for
......
...@@ -2,6 +2,7 @@ source 'https://rubygems.org' ...@@ -2,6 +2,7 @@ source 'https://rubygems.org'
gem 'rails', '4.2.8' gem 'rails', '4.2.8'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
gem 'bootsnap', '~> 1.1'
# Responders respond_to and respond_with # Responders respond_to and respond_with
gem 'responders', '~> 2.0' gem 'responders', '~> 2.0'
...@@ -266,7 +267,7 @@ gem "gitlab-license", "~> 1.0" ...@@ -266,7 +267,7 @@ gem "gitlab-license", "~> 1.0"
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 2.4.0' gem 'sentry-raven', '~> 2.4.0'
gem 'premailer-rails', '~> 1.9.0' gem 'premailer-rails', '~> 1.9.7'
# I18n # I18n
gem 'ruby_parser', '~> 3.8', require: false gem 'ruby_parser', '~> 3.8', require: false
......
...@@ -91,6 +91,8 @@ GEM ...@@ -91,6 +91,8 @@ GEM
bindata (2.3.5) bindata (2.3.5)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.1.1)
msgpack (~> 1.0)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
...@@ -145,7 +147,7 @@ GEM ...@@ -145,7 +147,7 @@ GEM
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
creole (0.5.0) creole (0.5.0)
css_parser (1.4.1) css_parser (1.5.0)
addressable addressable
d3_rails (3.5.11) d3_rails (3.5.11)
railties (>= 3.1.0) railties (>= 3.1.0)
...@@ -377,7 +379,7 @@ GEM ...@@ -377,7 +379,7 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grpc (1.2.5) grpc (1.4.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
gssapi (1.2.0) gssapi (1.2.0)
...@@ -491,6 +493,7 @@ GEM ...@@ -491,6 +493,7 @@ GEM
minitest (5.7.0) minitest (5.7.0)
mmap2 (2.2.6) mmap2 (2.2.6)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
msgpack (1.1.0)
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
...@@ -617,10 +620,11 @@ GEM ...@@ -617,10 +620,11 @@ GEM
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
posix-spawn (0.3.11) posix-spawn (0.3.11)
powerpack (0.1.1) powerpack (0.1.1)
premailer (1.8.6) premailer (1.10.4)
css_parser (>= 1.3.6) addressable
css_parser (>= 1.4.10)
htmlentities (>= 4.0.0) htmlentities (>= 4.0.0)
premailer-rails (1.9.2) premailer-rails (1.9.7)
actionmailer (>= 3, < 6) actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
prometheus-client-mmap (0.7.0.beta5) prometheus-client-mmap (0.7.0.beta5)
...@@ -955,6 +959,7 @@ DEPENDENCIES ...@@ -955,6 +959,7 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0) better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootsnap (~> 1.1)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
bootstrap_form (~> 2.7.0) bootstrap_form (~> 2.7.0)
brakeman (~> 3.6.0) brakeman (~> 3.6.0)
...@@ -1081,7 +1086,7 @@ DEPENDENCIES ...@@ -1081,7 +1086,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3) peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta5) prometheus-client-mmap (~> 0.7.0.beta5)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
......
...@@ -58,6 +58,7 @@ import RefSelectDropdown from './ref_select_dropdown'; ...@@ -58,6 +58,7 @@ import RefSelectDropdown from './ref_select_dropdown';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import ShortcutsBlob from './shortcuts_blob'; import ShortcutsBlob from './shortcuts_blob';
import initSettingsPanels from './settings_panels'; import initSettingsPanels from './settings_panels';
import initExperimentalFlags from './experimental_flags';
// EE-only // EE-only
import ApproversSelect from './approvers_select'; import ApproversSelect from './approvers_select';
...@@ -127,6 +128,9 @@ import AuditLogs from './audit_logs'; ...@@ -127,6 +128,9 @@ import AuditLogs from './audit_logs';
} }
switch (page) { switch (page) {
case 'profiles:preferences:show':
initExperimentalFlags();
break;
case 'sessions:new': case 'sessions:new':
new UsernameValidator(); new UsernameValidator();
new ActiveTabMemoizer(); new ActiveTabMemoizer();
......
import Cookies from 'js-cookie';
export default () => {
$('.js-experiment-feature-toggle').on('change', (e) => {
const el = e.target;
Cookies.set(el.name, el.value, {
expires: 365 * 10,
});
});
};
...@@ -47,7 +47,8 @@ ...@@ -47,7 +47,8 @@
ref="textarea" ref="textarea"
slot="textarea" slot="textarea"
placeholder="Write a comment or drag your files here..." placeholder="Write a comment or drag your files here..."
@keydown.meta.enter="updateIssuable"> @keydown.meta.enter="updateIssuable"
@keydown.ctrl.enter="updateIssuable">
</textarea> </textarea>
</markdown-field> </markdown-field>
</div> </div>
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
placeholder="Issue title" placeholder="Issue title"
aria-label="Issue title" aria-label="Issue title"
v-model="formState.title" v-model="formState.title"
@keydown.meta.enter="updateIssuable" /> @keydown.meta.enter="updateIssuable"
@keydown.ctrl.enter="updateIssuable" />
</fieldset> </fieldset>
</template> </template>
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
} }
bindEvents() { bindEvents() {
this.prioritizedLabels.find('.btn-action').on('mousedown', this, this.onButtonActionClick);
return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick); return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
} }
...@@ -36,6 +37,11 @@ ...@@ -36,6 +37,11 @@
_this.toggleEmptyState($label, $btn, action); _this.toggleEmptyState($label, $btn, action);
} }
onButtonActionClick(e) {
e.stopPropagation();
$(e.currentTarget).tooltip('hide');
}
toggleEmptyState($label, $btn, action) { toggleEmptyState($label, $btn, action) {
this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li')); this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li'));
} }
......
This diff is collapsed.
...@@ -307,9 +307,10 @@ $(function () { ...@@ -307,9 +307,10 @@ $(function () {
// Commit show suppressed diff // Commit show suppressed diff
}); });
$('.navbar-toggle').on('click', function () { $('.navbar-toggle').on('click', function () {
$('.header-content .title').toggle(); $('.header-content .title, .header-content .navbar-sub-nav').toggle();
$('.header-content .header-logo').toggle(); $('.header-content .header-logo').toggle();
$('.header-content .navbar-collapse').toggle(); $('.header-content .navbar-collapse').toggle();
$('.js-navbar-toggle-left, .js-navbar-toggle-right, .title-container').toggle();
return $('.navbar-toggle').toggleClass('active'); return $('.navbar-toggle').toggleClass('active');
}); });
// Show/hide comments on diff // Show/hide comments on diff
......
...@@ -28,6 +28,7 @@ export default { ...@@ -28,6 +28,7 @@ export default {
required: false, required: false,
}, },
}, },
<<<<<<< HEAD
directives: { directives: {
tooltip, tooltip,
...@@ -37,6 +38,14 @@ export default { ...@@ -37,6 +38,14 @@ export default {
loadingIcon, loadingIcon,
}, },
=======
directives: {
tooltip,
},
components: {
loadingIcon,
},
>>>>>>> c00e5bfa065128c5212a991a5cfcb6f152981d51
data() { data() {
return { return {
isLoading: false, isLoading: false,
...@@ -77,7 +86,6 @@ export default { ...@@ -77,7 +86,6 @@ export default {
:aria-label="title" :aria-label="title"
data-container="body" data-container="body"
data-placement="top" data-placement="top"
ref="tooltip"
:disabled="isLoading"> :disabled="isLoading">
<i <i
:class="iconClass" :class="iconClass"
......
...@@ -40,7 +40,6 @@ export default { ...@@ -40,7 +40,6 @@ export default {
return { return {
isLoading: false, isLoading: false,
dropdownContent: '', dropdownContent: '',
endpoint: this.stage.dropdown_path,
}; };
}, },
...@@ -73,7 +72,7 @@ export default { ...@@ -73,7 +72,7 @@ export default {
}, },
fetchJobs() { fetchJobs() {
this.$http.get(this.endpoint) this.$http.get(this.stage.dropdown_path)
.then((response) => { .then((response) => {
this.dropdownContent = response.json().html; this.dropdownContent = response.json().html;
this.isLoading = false; this.isLoading = false;
......
...@@ -14,6 +14,11 @@ export default { ...@@ -14,6 +14,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
showToggle: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
assigneeTitle() { assigneeTitle() {
...@@ -36,6 +41,19 @@ export default { ...@@ -36,6 +41,19 @@ export default {
> >
Edit Edit
</a> </a>
<a
v-if="showToggle"
aria-label="Toggle sidebar"
class="gutter-toggle pull-right js-sidebar-toggle"
href="#"
role="button"
>
<i
aria-hidden="true"
data-hidden="true"
class="fa fa-angle-double-right"
/>
</a>
</div> </div>
`, `,
}; };
...@@ -62,6 +62,7 @@ export default { ...@@ -62,6 +62,7 @@ export default {
}, },
beforeMount() { beforeMount() {
this.field = this.$el.dataset.field; this.field = this.$el.dataset.field;
this.signedIn = typeof this.$el.dataset.signedIn !== 'undefined';
}, },
template: ` template: `
<div> <div>
...@@ -69,6 +70,7 @@ export default { ...@@ -69,6 +70,7 @@ export default {
:number-of-assignees="store.assignees.length" :number-of-assignees="store.assignees.length"
:loading="loading || store.isFetching.assignees" :loading="loading || store.isFetching.assignees"
:editable="store.editable" :editable="store.editable"
:show-toggle="!signedIn"
/> />
<assignees <assignees
v-if="!store.isFetching.assignees" v-if="!store.isFetching.assignees"
......
...@@ -19,6 +19,9 @@ export default { ...@@ -19,6 +19,9 @@ export default {
return hasCI && !ciStatus; return hasCI && !ciStatus;
}, },
hasPipeline() {
return Object.keys(this.mr.pipeline || {}).length > 0;
},
svg() { svg() {
return statusIconEntityMap.icon_status_failed; return statusIconEntityMap.icon_status_failed;
}, },
...@@ -45,7 +48,11 @@ export default { ...@@ -45,7 +48,11 @@ export default {
template: ` template: `
<div class="mr-widget-heading"> <div class="mr-widget-heading">
<div class="ci-widget"> <div class="ci-widget">
<template v-if="hasCIError"> <template v-if="!hasPipeline">
<i class="fa fa-spinner fa-spin append-right-10" aria-hidden="true"></i>
Waiting for pipeline...
</template>
<template v-else-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error"> <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error">
<span class="js-icon-link icon-link"> <span class="js-icon-link icon-link">
<span <span
......
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
} }
/** /**
* Annotate file * Blame file
*/ */
&.blame { &.blame {
table { table {
......
...@@ -34,6 +34,8 @@ header { ...@@ -34,6 +34,8 @@ header {
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
color: $gl-text-color-secondary;
border-radius: 0;
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
padding: 0 16px; padding: 0 16px;
...@@ -59,7 +61,7 @@ header { ...@@ -59,7 +61,7 @@ header {
padding: 0; padding: 0;
.nav > li > a { .nav > li > a {
color: $gl-text-color-secondary; color: currentColor;
font-size: 18px; font-size: 18px;
padding: 0; padding: 0;
margin: (($header-height - 28) / 2) 3px; margin: (($header-height - 28) / 2) 3px;
...@@ -84,7 +86,7 @@ header { ...@@ -84,7 +86,7 @@ header {
&:hover, &:hover,
&:focus, &:focus,
&:active { &:active {
background-color: $gray-light; background-color: transparent;
color: $gl-text-color; color: $gl-text-color;
svg { svg {
...@@ -96,13 +98,19 @@ header { ...@@ -96,13 +98,19 @@ header {
font-size: 14px; font-size: 14px;
} }
.fa-chevron-down {
position: relative;
top: -3px;
font-size: 10px;
}
svg { svg {
position: relative; position: relative;
top: 2px; top: 2px;
height: 17px; height: 17px;
// hack to get SVG to line up with FA icons // hack to get SVG to line up with FA icons
width: 23px; width: 23px;
fill: $gl-text-color-secondary; fill: currentColor;
} }
} }
...@@ -225,7 +233,7 @@ header { ...@@ -225,7 +233,7 @@ header {
} }
a { a {
color: $gl-text-color; color: currentColor;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
...@@ -346,6 +354,7 @@ header { ...@@ -346,6 +354,7 @@ header {
width: auto; width: auto;
min-width: 140px; min-width: 140px;
margin-top: -5px; margin-top: -5px;
color: $gl-text-color;
left: auto; left: auto;
.current-user { .current-user {
......
...@@ -74,6 +74,10 @@ $red-700: #a62d19; ...@@ -74,6 +74,10 @@ $red-700: #a62d19;
$red-800: #8b2615; $red-800: #8b2615;
$red-900: #711e11; $red-900: #711e11;
$purple-700: #4a2192;
$purple-800: #2c0a5c;
$purple-900: #380d75;
$black: #000; $black: #000;
$black-transparent: rgba(0, 0, 0, 0.3); $black-transparent: rgba(0, 0, 0, 0.3);
......
@import "framework/variables";
@import 'framework/tw_bootstrap_variables';
@import "bootstrap/variables";
header.navbar-gitlab-new {
color: $white-light;
background-color: $purple-900;
border-bottom: 0;
.header-content {
padding-left: 0;
.title-container {
padding-top: 0;
overflow: visible;
}
.title {
display: block;
height: 100%;
padding-right: 0;
color: currentColor;
> a {
display: flex;
align-items: center;
height: 100%;
padding-top: 3px;
padding-right: $gl-padding;
padding-left: $gl-padding;
margin-left: -$gl-padding;
border-bottom: 3px solid transparent;
@media (min-width: $screen-sm-min) {
padding-right: $gl-padding;
padding-left: $gl-padding;
}
svg {
margin-top: -3px;
@media (min-width: $screen-sm-min) {
margin-right: 10px;
}
}
&:hover,
&:focus {
color: currentColor;
text-decoration: none;
border-bottom-color: $white-light;
}
}
}
.dropdown.open {
> a {
border-bottom-color: $white-light;
}
}
.dropdown-menu {
margin-top: 4px;
min-width: 130px;
@media (max-width: $screen-xs-max) {
left: auto;
right: 0;
}
}
}
.navbar-collapse {
padding-left: 0;
color: $white-light;
box-shadow: 0;
@media (max-width: $screen-xs-max) {
margin-left: -$gl-padding;
margin-right: -10px;
}
.dropdown-bold-header {
color: initial;
}
.nav {
> li:not(.hidden-xs) a {
@media (max-width: $screen-xs-max) {
margin-left: 0;
min-width: 100%;
}
}
}
}
.container-fluid {
.navbar-toggle {
min-width: 45px;
padding: 6px $gl-padding;
margin-right: -7px;
font-size: 14px;
text-align: center;
color: currentColor;
border-left: 1px solid lighten($purple-700, 10%);
&:hover,
&:focus,
&.active {
color: currentColor;
background-color: transparent;
}
}
.navbar-nav {
@media (max-width: $screen-xs-max) {
display: flex;
padding-right: 10px;
}
li {
.badge {
box-shadow: none;
}
}
}
.nav > li {
&.header-user {
@media (max-width: $screen-xs-max) {
padding-left: 10px;
}
}
> a {
background: none;
opacity: .9;
will-change: opacity;
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $white-light;
}
}
&:hover,
&:focus {
color: $white-light;
opacity: 1;
> svg {
fill: $white-light;
}
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $white-light;
}
}
}
}
}
}
}
.navbar-sub-nav {
display: flex;
margin-bottom: 0;
color: $white-light;
> li {
&.active > a,
a:hover,
a:focus {
border-bottom-color: $white-light;
text-decoration: none;
outline: 0;
opacity: 1;
}
> a {
display: block;
padding: 16px 10px 13px;
font-size: 13px;
color: currentColor;
border-bottom: 3px solid transparent;
opacity: .9;
will-change: opacity;
@media (min-width: $screen-sm-min) {
padding: 15px $gl-padding 12px;
font-size: 14px;
}
}
}
.dropdown-chevron {
position: relative;
top: -1px;
font-size: 10px;
}
}
.header-user .dropdown-menu-nav,
.header-new .dropdown-menu-nav {
margin-top: 4px;
}
.search {
form {
border-color: $purple-800;
&:hover {
border-color: rgba($white-light, .6);
box-shadow: none;
}
}
&.search-active form {
border-color: $white-light;
}
form,
.search-input {
background-color: $purple-700;
}
.search-input {
color: $white-light;
}
.search-input::placeholder {
color: rgba($white-light, .6);
}
.location-badge {
font-size: 12px;
color: rgba($white-light, .6);
background-color: $purple-800;
transition: color 0.15s;
will-change: color;
}
.search-input-wrap {
.search-icon,
.clear-icon {
color: rgba($white-light, .6);
}
}
&.search-active {
.location-badge {
color: $white-light;
background-color: $purple-800;
}
.search-input-wrap {
.search-icon {
color: rgba($white-light, .6);
}
.clear-icon {
color: $white-light;
}
}
}
}
...@@ -228,6 +228,12 @@ ...@@ -228,6 +228,12 @@
padding-top: 10px; padding-top: 10px;
} }
&:not(.issue-boards-sidebar):not([data-signed-in]) {
.issuable-sidebar-header {
display: none;
}
}
.assign-yourself .btn-link { .assign-yourself .btn-link {
padding-left: 0; padding-left: 0;
} }
...@@ -249,6 +255,10 @@ ...@@ -249,6 +255,10 @@
border-left: 1px solid $border-gray-normal; border-left: 1px solid $border-gray-normal;
} }
.title .gutter-toggle {
margin-top: 0;
}
.assignee .avatar { .assignee .avatar {
float: left; float: left;
margin-right: 10px; margin-right: 10px;
......
...@@ -138,6 +138,7 @@ ...@@ -138,6 +138,7 @@
.fa { .fa {
font-size: 18px; font-size: 18px;
vertical-align: middle; vertical-align: middle;
pointer-events: none;
} }
&:hover { &:hover {
......
...@@ -210,6 +210,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -210,6 +210,7 @@ class Admin::UsersController < Admin::ApplicationController
] ]
end end
<<<<<<< HEAD
def user_params_ee def user_params_ee
[ [
:note, :note,
...@@ -217,6 +218,8 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -217,6 +218,8 @@ class Admin::UsersController < Admin::ApplicationController
] ]
end end
=======
>>>>>>> c00e5bfa065128c5212a991a5cfcb6f152981d51
def update_user(&block) def update_user(&block)
result = Users::UpdateService.new(user).execute(&block) result = Users::UpdateService.new(user).execute(&block)
......
...@@ -135,7 +135,12 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -135,7 +135,12 @@ class Projects::PipelinesController < Projects::ApplicationController
@charts[:week] = Ci::Charts::WeekChart.new(project) @charts[:week] = Ci::Charts::WeekChart.new(project)
@charts[:month] = Ci::Charts::MonthChart.new(project) @charts[:month] = Ci::Charts::MonthChart.new(project)
@charts[:year] = Ci::Charts::YearChart.new(project) @charts[:year] = Ci::Charts::YearChart.new(project)
@charts[:build_times] = Ci::Charts::BuildTime.new(project) @charts[:pipeline_times] = Ci::Charts::PipelineTime.new(project)
@counts = {}
@counts[:total] = @project.pipelines.count(:all)
@counts[:success] = @project.pipelines.success.count(:all)
@counts[:failed] = @project.pipelines.failed.count(:all)
end end
private private
......
...@@ -304,4 +304,12 @@ module ApplicationHelper ...@@ -304,4 +304,12 @@ module ApplicationHelper
"https://www.twitter.com/#{name}" "https://www.twitter.com/#{name}"
end end
end end
def can_toggle_new_nav?
Rails.env.development?
end
def show_new_nav?
cookies["new_nav"] == "true"
end
end end
...@@ -50,10 +50,17 @@ module ButtonHelper ...@@ -50,10 +50,17 @@ module ButtonHelper
def http_clone_button(project, placement = 'right', append_link: true) def http_clone_button(project, placement = 'right', append_link: true)
klass = 'http-selector' klass = 'http-selector'
klass << ' has-tooltip' if current_user.try(:require_password?) klass << ' has-tooltip' if current_user.try(:require_password?) || current_user.try(:require_personal_access_token?)
protocol = gitlab_config.protocol.upcase protocol = gitlab_config.protocol.upcase
tooltip_title =
if current_user.try(:require_password?)
_("Set a password on your account to pull or push via %{protocol}.") % { protocol: protocol }
else
_("Create a personal access token on your account to pull or push via %{protocol}.") % { protocol: protocol }
end
content_tag (append_link ? :a : :span), protocol, content_tag (append_link ? :a : :span), protocol,
class: klass, class: klass,
href: (project.http_url_to_repo if append_link), href: (project.http_url_to_repo if append_link),
...@@ -61,8 +68,12 @@ module ButtonHelper ...@@ -61,8 +68,12 @@ module ButtonHelper
html: true, html: true,
placement: placement, placement: placement,
container: 'body', container: 'body',
<<<<<<< HEAD
title: _("Set a password on your account to pull or push via %{protocol}") % { protocol: protocol }, title: _("Set a password on your account to pull or push via %{protocol}") % { protocol: protocol },
primary_url: (geo_primary_http_url_to_repo(project) if Gitlab::Geo.secondary?) primary_url: (geo_primary_http_url_to_repo(project) if Gitlab::Geo.secondary?)
=======
title: tooltip_title
>>>>>>> c00e5bfa065128c5212a991a5cfcb6f152981d51
} }
end end
......
...@@ -17,13 +17,10 @@ module GraphHelper ...@@ -17,13 +17,10 @@ module GraphHelper
ids.zip(parent_spaces) ids.zip(parent_spaces)
end end
def success_ratio(success_builds, failed_builds) def success_ratio(counts)
failed_builds = failed_builds.count(:all) return 100 if counts[:failed].zero?
success_builds = success_builds.count(:all)
return 100 if failed_builds.zero? ratio = (counts[:success].to_f / (counts[:success] + counts[:failed])) * 100
ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100
ratio.to_i ratio.to_i
end end
end end
...@@ -133,21 +133,28 @@ module LabelsHelper ...@@ -133,21 +133,28 @@ module LabelsHelper
end end
end end
def can_subscribe_to_label_in_different_levels?(label)
defined?(@project) && label.is_a?(GroupLabel)
end
def label_subscription_status(label, project) def label_subscription_status(label, project)
return 'project-level' if label.subscribed?(current_user, project)
return 'group-level' if label.subscribed?(current_user) return 'group-level' if label.subscribed?(current_user)
return 'project-level' if label.subscribed?(current_user, project)
'unsubscribed' 'unsubscribed'
end end
def group_label_unsubscribe_path(label, project) def toggle_subscription_label_path(label, project)
return toggle_subscription_group_label_path(label.group, label) unless project
case label_subscription_status(label, project) case label_subscription_status(label, project)
when 'project-level' then toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)
when 'group-level' then toggle_subscription_group_label_path(label.group, label) when 'group-level' then toggle_subscription_group_label_path(label.group, label)
when 'project-level' then toggle_subscription_namespace_project_label_path(project.namespace, project, label)
when 'unsubscribed' then toggle_subscription_namespace_project_label_path(project.namespace, project, label)
end end
end end
def label_subscription_toggle_button_text(label, project) def label_subscription_toggle_button_text(label, project = nil)
label.subscribed?(current_user, project) ? 'Unsubscribe' : 'Subscribe' label.subscribed?(current_user, project) ? 'Unsubscribe' : 'Subscribe'
end end
......
...@@ -198,6 +198,23 @@ module ProjectsHelper ...@@ -198,6 +198,23 @@ module ProjectsHelper
.load_in_batch_for_projects(projects) .load_in_batch_for_projects(projects)
end end
def show_no_ssh_key_message?
cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
end
def show_no_password_message?
cookies[:hide_no_password_message].blank? && !current_user.hide_no_password &&
( current_user.require_password? || current_user.require_personal_access_token? )
end
def link_to_set_password
if current_user.require_password?
link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
else
link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path
end
end
private private
def repo_children_classes(field) def repo_children_classes(field)
......
...@@ -11,18 +11,21 @@ module HasStatus ...@@ -11,18 +11,21 @@ module HasStatus
class_methods do class_methods do
def status_sql def status_sql
scope = respond_to?(:exclude_ignored) ? exclude_ignored : all scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
builds = scope.select('count(*)').to_sql
created = scope.created.select('count(*)').to_sql builds = scope_relevant.select('count(*)').to_sql
success = scope.success.select('count(*)').to_sql created = scope_relevant.created.select('count(*)').to_sql
manual = scope.manual.select('count(*)').to_sql success = scope_relevant.success.select('count(*)').to_sql
pending = scope.pending.select('count(*)').to_sql manual = scope_relevant.manual.select('count(*)').to_sql
running = scope.running.select('count(*)').to_sql pending = scope_relevant.pending.select('count(*)').to_sql
skipped = scope.skipped.select('count(*)').to_sql running = scope_relevant.running.select('count(*)').to_sql
canceled = scope.canceled.select('count(*)').to_sql skipped = scope_relevant.skipped.select('count(*)').to_sql
canceled = scope_relevant.canceled.select('count(*)').to_sql
warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
"(CASE "(CASE
WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success' WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created' WHEN (#{builds})=(#{created}) THEN 'created'
......
...@@ -45,6 +45,7 @@ class Environment < ActiveRecord::Base ...@@ -45,6 +45,7 @@ class Environment < ActiveRecord::Base
.to_sql .to_sql
order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC')) order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC'))
end end
scope :in_review_folder, -> { where(environment_type: "review") }
state_machine :state, initial: :available do state_machine :state, initial: :available do
event :start do event :start do
......
...@@ -799,6 +799,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -799,6 +799,7 @@ class MergeRequest < ActiveRecord::Base
"refs/heads/#{source_branch}", "refs/heads/#{source_branch}",
ref_path ref_path
) )
update_column(:ref_fetched, true)
end end
def ref_path def ref_path
...@@ -806,7 +807,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -806,7 +807,13 @@ class MergeRequest < ActiveRecord::Base
end end
def ref_fetched? def ref_fetched?
project.repository.ref_exists?(ref_path) super ||
begin
computed_value = project.repository.ref_exists?(ref_path)
update_column(:ref_fetched, true) if computed_value
computed_value
end
end end
def ensure_ref_fetched def ensure_ref_fetched
......
...@@ -351,7 +351,10 @@ class Project < ActiveRecord::Base ...@@ -351,7 +351,10 @@ class Project < ActiveRecord::Base
project.run_after_commit { add_import_job } project.run_after_commit { add_import_job }
end end
after_transition started: :finished, do: :reset_cache_and_import_attrs after_transition started: :finished do |project, _|
project.reset_cache_and_import_attrs
project.perform_housekeeping
end
end end
class << self class << self
...@@ -511,6 +514,18 @@ class Project < ActiveRecord::Base ...@@ -511,6 +514,18 @@ class Project < ActiveRecord::Base
remove_import_data remove_import_data
end end
def perform_housekeeping
return unless repo_exists?
run_after_commit do
begin
Projects::HousekeepingService.new(self).execute
rescue Projects::HousekeepingService::LeaseTaken => e
Rails.logger.info("Could not perform housekeeping for project #{self.path_with_namespace} (#{self.id}): #{e}")
end
end
end
def remove_import_data def remove_import_data
import_data&.destroy import_data&.destroy
end end
......
...@@ -60,7 +60,7 @@ class PrometheusService < MonitoringService ...@@ -60,7 +60,7 @@ class PrometheusService < MonitoringService
def deployment_metrics(deployment) def deployment_metrics(deployment)
metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.id, &method(:rename_data_to_metrics)) metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.id, &method(:rename_data_to_metrics))
metrics&.merge(deployment_time: created_at.to_i) || {} metrics&.merge(deployment_time: deployment.created_at.to_i) || {}
end end
def additional_environment_metrics(environment) def additional_environment_metrics(environment)
......
...@@ -597,7 +597,13 @@ class User < ActiveRecord::Base ...@@ -597,7 +597,13 @@ class User < ActiveRecord::Base
end end
def require_password? def require_password?
password_automatically_set? && !ldap_user? password_automatically_set? && !ldap_user? && current_application_settings.signin_enabled?
end
def require_personal_access_token?
return false if current_application_settings.signin_enabled? || ldap_user?
PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none?
end end
def can_change_username? def can_change_username?
......
...@@ -58,20 +58,23 @@ ...@@ -58,20 +58,23 @@
%br %br
.table-holder - if @runners.any?
%table.table .table-holder
%thead %table.table
%tr %thead
%th Type %tr
%th Runner token %th Type
%th Description %th Runner token
%th Version %th Description
%th Projects %th Version
%th Jobs %th Projects
%th Tags %th Jobs
%th Last contact %th Tags
%th %th Last contact
%th
- @runners.each do |runner| - @runners.each do |runner|
= render "admin/runners/runner", runner: runner = render "admin/runners/runner", runner: runner
= paginate @runners, theme: "gitlab" = paginate @runners, theme: "gitlab"
- else
.nothing-here-block No runners found
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.avatar-container.s70.group-avatar .avatar-container.s70.group-avatar
= image_tag group_icon(@group), class: "avatar s70 avatar-tile" = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
%h1.group-title %h1.group-title
@#{@group.path} = @group.name
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, fw: false) = visibility_level_icon(@group.visibility_level, fw: false)
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
= stylesheet_link_tag "test", media: "all" if Rails.env.test? = stylesheet_link_tag "test", media: "all" if Rails.env.test?
= stylesheet_link_tag 'peek' if peek_enabled? = stylesheet_link_tag 'peek' if peek_enabled?
- if show_new_nav?
= stylesheet_link_tag "new_nav", media: "all"
= Gon::Base.render_data = Gon::Base.render_data
= webpack_bundle_tag "runtime" = webpack_bundle_tag "runtime"
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
= render "layouts/init_auto_complete" if @gfm_form = render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar' = render 'peek/bar'
= render "layouts/header/default", title: header_title - if show_new_nav?
= render "layouts/header/new"
- else
= render "layouts/header/default", title: header_title
= render 'layouts/page', sidebar: sidebar, nav: nav = render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body = yield :scripts_body
...@@ -86,6 +86,9 @@ ...@@ -86,6 +86,9 @@
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username } = link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li %li
= link_to "Settings", profile_path = link_to "Settings", profile_path
- if can_toggle_new_nav?
%li
= link_to "Turn on new nav", profile_preferences_path(anchor: "new-navigation")
%li.divider %li.divider
%li %li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link" = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
......
%header.navbar.navbar-gitlab.navbar-gitlab-new{ class: nav_header_class }
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
.title-container
%h1.title
= link_to root_path, title: 'Dashboard' do
= brand_header_logo
%span.hidden-xs
GitLab
- if current_user
= render "layouts/nav/new_dashboard"
- else
= render "layouts/nav/new_explore"
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.hidden-sm.hidden-xs
= render 'layouts/search' unless current_controller?(:search)
%li.visible-sm-inline-block.visible-xs-inline-block
= link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search')
- if current_user
- if session[:impersonator_id]
%li.impersonation
= link_to admin_impersonation_path, method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret fw')
- if current_user.admin?
%li
= link_to admin_root_path, title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
= render 'layouts/header/new_dropdown'
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('tachometer fw')
%li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw')
- issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
%li
= link_to assigned_mrs_dashboard_path, title: 'Merge requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= custom_icon('mr_bold')
- merge_requests_count = assigned_issuables_count(:merge_requests)
%span.badge.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
= number_with_delimiter(merge_requests_count)
%li
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('check-circle fw')
%span.badge.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
= todos_count_format(todos_pending_count)
%li.header-user.dropdown
= link_to current_user, class: "header-user-dropdown-toggle", data: { toggle: "dropdown" } do
= image_tag avatar_icon(current_user, 26), width: 26, height: 26, class: "header-user-avatar"
= icon('chevron-down')
.dropdown-menu-nav.dropdown-menu-align-right
%ul
%li.current-user
.user-name.bold
= current_user.name
@#{current_user.username}
%li.divider
%li
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li
= link_to "Settings", profile_path
%li
= link_to "Turn off new nav", profile_preferences_path(anchor: "new-navigation")
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
- else
%li
%div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%button.navbar-toggle.hidden-sm.hidden-md.hidden-lg{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v', class: 'js-navbar-toggle-right')
= icon('times', class: 'js-navbar-toggle-left', style: 'display: none;')
= yield :header_content
= render 'shared/outdated_browser'
- if @project && !@project.empty_repo?
- if ref = @ref || @project.repository.root_ref
:javascript
var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, ref)}";
%li.header-new.dropdown %li.header-new.dropdown
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do
= icon('plus fw') - if show_new_nav?
= icon('caret-down') = icon('plus')
= icon('chevron-down')
- else
= icon('plus fw')
= icon('caret-down')
.dropdown-menu-nav.dropdown-menu-align-right .dropdown-menu-nav.dropdown-menu-align-right
%ul %ul
- if @group&.persisted? - if @group&.persisted?
......
%ul.list-unstyled.navbar-sub-nav
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
Groups
= nav_link(path: 'dashboard#activity', html_options: { class: "hidden-xs hidden-sm" }) do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
Activity
%li.dropdown
%a{ href: "#", data: { toggle: "dropdown" } }
More
= icon("chevron-down", class: "dropdown-chevron")
.dropdown-menu
%ul
= nav_link(path: 'dashboard#activity', html_options: { class: "visible-xs visible-sm" }) do
= link_to activity_dashboard_path, title: 'Activity' do
Activity
= nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
Milestones
= nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
Snippets
%li.divider
%li
= link_to "Help", help_path, title: 'About GitLab CE'
%ul.list-unstyled.navbar-sub-nav
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
Groups
%li.dropdown
%a{ href: "#", data: { toggle: "dropdown" } }
More
= icon("chevron-down", class: "dropdown-chevron")
.dropdown-menu
%ul
= nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
Snippets
%li.divider
%li
= link_to "Help", help_path, title: 'About GitLab CE'
...@@ -15,6 +15,25 @@ ...@@ -15,6 +15,25 @@
.preview= image_tag "#{scheme.css_class}-scheme-preview.png" .preview= image_tag "#{scheme.css_class}-scheme-preview.png"
= f.radio_button :color_scheme_id, scheme.id = f.radio_button :color_scheme_id, scheme.id
= scheme.name = scheme.name
- if can_toggle_new_nav?
.col-sm-12
%hr
.col-lg-3.profile-settings-sidebar#new-navigation
%h4.prepend-top-0
New Navigation
%p
This setting allows you to turn on or off the new upcoming navigation concept.
= succeed '.' do
= link_to 'Learn more', '', target: '_blank'
.col-lg-9.syntax-theme
= label_tag do
.preview= image_tag "old_nav.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_nav", checked: !show_new_nav? }
Old
= label_tag do
.preview= image_tag "new_nav.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "true", name: "new_nav", checked: show_new_nav? }
New
.col-sm-12 .col-sm-12
%hr %hr
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
- @no_container = true - @no_container = true
- project_duration = age_map_duration(@blame_groups, @project) - project_duration = age_map_duration(@blame_groups, @project)
- page_title "Annotate", @blob.path, @ref - page_title "Blame", @blob.path, @ref
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div{ class: container_class }
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
= link_to 'Normal view', namespace_project_blob_path(@project.namespace, @project, @id), = link_to 'Normal view', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn' class: 'btn'
- else - else
= link_to 'Annotate', namespace_project_blame_path(@project.namespace, @project, @id), = link_to 'Blame', namespace_project_blame_path(@project.namespace, @project, @id),
class: 'btn js-blob-blame-link' unless blob.empty? class: 'btn js-blob-blame-link' unless blob.empty?
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
......
...@@ -11,11 +11,14 @@ ...@@ -11,11 +11,14 @@
.table-mobile-header{ role: 'rowheader' } Job .table-mobile-header{ role: 'rowheader' } Job
- if deployment.deployable - if deployment.deployable
.table-mobile-content .table-mobile-content
= link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do .flex-truncate-parent
#{deployment.deployable.name} (##{deployment.deployable.id}) .flex-truncate-child
= link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do
#{deployment.deployable.name} (##{deployment.deployable.id})
- if deployment.user - if deployment.user
by %div
= user_avatar(user: deployment.user, size: 20) by
= user_avatar(user: deployment.user, size: 20)
.table-section.section-15{ role: 'gridcell' } .table-section.section-15{ role: 'gridcell' }
.table-mobile-header{ role: 'rowheader' } Created .table-mobile-header{ role: 'rowheader' } Created
......
...@@ -31,8 +31,8 @@ ...@@ -31,8 +31,8 @@
.ci-table.environments{ role: 'grid' } .ci-table.environments{ role: 'grid' }
.gl-responsive-table-row.table-row-header{ role: 'row' } .gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-10{ role: 'columnheader' } ID .table-section.section-10{ role: 'columnheader' } ID
.table-section.section-40{ role: 'columnheader' } Commit .table-section.section-30{ role: 'columnheader' } Commit
.table-section.section-15{ role: 'columnheader' } Job .table-section.section-25{ role: 'columnheader' } Job
.table-section.section-15{ role: 'columnheader' } Created .table-section.section-15{ role: 'columnheader' } Created
= render @deployments = render @deployments
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= merge_request.to_reference = merge_request.to_reference
%span.merge-request-info %span.merge-request-info
%strong %strong
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" = link_to merge_request.title, merge_request_path(merge_request), class: "row_title"
- unless @issue.project.id == merge_request.target_project.id - unless @issue.project.id == merge_request.target_project.id
in in
- project = merge_request.target_project - project = merge_request.target_project
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.col-md-6 .col-md-6
= render 'projects/pipelines/charts/overall' = render 'projects/pipelines/charts/overall'
.col-md-6 .col-md-6
= render 'projects/pipelines/charts/build_times' = render 'projects/pipelines/charts/pipeline_times'
%hr %hr
= render 'projects/pipelines/charts/builds' = render 'projects/pipelines/charts/pipelines'
...@@ -2,18 +2,14 @@ ...@@ -2,18 +2,14 @@
%ul %ul
%li %li
Total: Total:
%strong= pluralize @project.builds.count(:all), 'job' %strong= pluralize @counts[:total], 'pipeline'
%li %li
Successful: Successful:
%strong= pluralize @project.builds.success.count(:all), 'job' %strong= pluralize @counts[:success], 'pipeline'
%li %li
Failed: Failed:
%strong= pluralize @project.builds.failed.count(:all), 'job' %strong= pluralize @counts[:failed], 'pipeline'
%li %li
Success ratio: Success ratio:
%strong %strong
#{success_ratio(@project.builds.success, @project.builds.failed)}% #{success_ratio(@counts)}%
%li
Commits covered:
%strong
= @project.pipelines.count(:all)
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
:javascript :javascript
var data = { var data = {
labels : #{@charts[:build_times].labels.to_json}, labels : #{@charts[:pipeline_times].labels.to_json},
datasets : [ datasets : [
{ {
fillColor : "rgba(220,220,220,0.5)", fillColor : "rgba(220,220,220,0.5)",
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
barStrokeWidth: 1, barStrokeWidth: 1,
barValueSpacing: 1, barValueSpacing: 1,
barDatasetSpacing: 1, barDatasetSpacing: 1,
data : #{@charts[:build_times].build_times.to_json} data : #{@charts[:pipeline_times].pipeline_times.to_json}
} }
] ]
} }
......
- label_css_id = dom_id(label) - label_css_id = dom_id(label)
- status = label_subscription_status(label, @project).inquiry if current_user - status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject] - subject = local_assigns[:subject]
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
%li{ id: label_css_id, data: { id: label.id } } %li{ id: label_css_id, data: { id: label.id } }
= render "shared/label_row", label: label = render "shared/label_row", label: label
.visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown .visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown
%button.btn.btn-default.label-options-toggle{ data: { toggle: "dropdown" } } %button.btn.btn-default.label-options-toggle{ type: 'button', data: { toggle: "dropdown" } }
Options Options
= icon('caret-down') = icon('caret-down')
.dropdown-menu.dropdown-menu-align-right .dropdown-menu.dropdown-menu-align-right
...@@ -17,18 +18,18 @@ ...@@ -17,18 +18,18 @@
%li %li
= link_to_label(label, subject: subject) do = link_to_label(label, subject: subject) do
view open issues view open issues
- if current_user && defined?(@project) - if current_user
%li.label-subscription %li.label-subscription
- if label.is_a?(ProjectLabel) - if can_subscribe_to_label_in_different_levels?(label)
%a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', data: { status: status, url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } %a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } }
%span= label_subscription_toggle_button_text(label, @project)
- else
%a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: group_label_unsubscribe_path(label, @project) } }
%span Unsubscribe %span Unsubscribe
%a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
%span Subscribe at project level %span Subscribe at project level
%a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } } %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } }
%span Subscribe at group level %span Subscribe at group level
- else
%a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', data: { status: status, url: toggle_subscription_path } }
%span= label_subscription_toggle_button_text(label, @project)
- if can?(current_user, :admin_label, label) - if can?(current_user, :admin_label, label)
%li %li
...@@ -42,14 +43,10 @@ ...@@ -42,14 +43,10 @@
= 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') do
view open issues view open issues
- if current_user && defined?(@project) - if current_user
.label-subscription.inline .label-subscription.inline
- if label.is_a?(ProjectLabel) - if can_subscribe_to_label_in_different_levels?(label)
%button.js-subscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', data: { status: status, url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } }
%span= label_subscription_toggle_button_text(label, @project)
= icon('spinner spin', class: 'label-subscribe-button-loading')
- else
%button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', class: ('hidden' if status.unsubscribed?), data: { url: group_label_unsubscribe_path(label, @project) } }
%span Unsubscribe %span Unsubscribe
= icon('spinner spin', class: 'label-subscribe-button-loading') = icon('spinner spin', class: 'label-subscribe-button-loading')
...@@ -59,10 +56,14 @@ ...@@ -59,10 +56,14 @@
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu %ul.dropdown-menu
%li %li
%a.js-subscribe-button{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } } %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
Project level Project level
%a.js-subscribe-button{ data: { url: toggle_subscription_group_label_path(label.group, label) } } %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } }
Group level Group level
- else
%button.js-subscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', data: { status: status, url: toggle_subscription_path } }
%span= label_subscription_toggle_button_text(label, @project)
= icon('spinner spin', class: 'label-subscribe-button-loading')
- if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
= link_to promote_namespace_project_label_path(label.project.namespace, label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do = link_to promote_namespace_project_label_path(label.project.namespace, label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do
...@@ -76,10 +77,10 @@ ...@@ -76,10 +77,10 @@
%span.sr-only Delete %span.sr-only Delete
= icon('trash-o') = icon('trash-o')
- if current_user && defined?(@project) - if current_user
- if label.is_a?(ProjectLabel) - if can_subscribe_to_label_in_different_levels?(label)
:javascript :javascript
new gl.ProjectLabelSubscription('##{dom_id(label)} .label-subscription'); new gl.GroupLabelSubscription('##{dom_id(label)} .label-subscription');
- else - else
:javascript :javascript
new gl.GroupLabelSubscription('##{dom_id(label)} .label-subscription'); new gl.ProjectLabelSubscription('##{dom_id(label)} .label-subscription');
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
= icon('bars') = icon('bars')
.js-toggle-priority.toggle-priority{ data: { url: remove_priority_namespace_project_label_path(@project.namespace, @project, label), .js-toggle-priority.toggle-priority{ data: { url: remove_priority_namespace_project_label_path(@project.namespace, @project, label),
dom_id: dom_id(label), type: label.type } } dom_id: dom_id(label), type: label.type } }
%button.add-priority.btn.has-tooltip{ title: 'Prioritize', :'data-placement' => 'top' } %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' }
= icon('star-o') = icon('star-o')
%button.remove-priority.btn.has-tooltip{ title: 'Remove priority', :'data-placement' => 'top' } %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' }
= icon('star') = icon('star')
%span.label-name %span.label-name
= link_to_label(label, subject: @project, tooltip: false) = link_to_label(label, subject: @project, tooltip: false)
......
- if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password? - if show_no_password_message?
.no-password-message.alert.alert-warning .no-password-message.alert.alert-warning
- set_password_link = link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path - translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: link_to_set_password }
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: set_password_link }
- set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params - set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
= set_password_message.html_safe
.alert-link-group .alert-link-group
= link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put = link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put
| |
......
- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key? - if show_no_ssh_key_message?
.no-ssh-key-message.alert.alert-warning .no-ssh-key-message.alert.alert-warning
- add_ssh_key_link = link_to s_('MissingSSHKeyWarningLink|add an SSH key'), profile_keys_path, class: 'alert-link' - add_ssh_key_link = link_to s_('MissingSSHKeyWarningLink|add an SSH key'), profile_keys_path, class: 'alert-link'
- ssh_message = _("You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile") % { add_ssh_key_link: add_ssh_key_link } - ssh_message = _("You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile") % { add_ssh_key_link: add_ssh_key_link }
#{ ssh_message.html_safe } = ssh_message.html_safe
.alert-link-group .alert-link-group
= link_to _("Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link' = link_to _("Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
| |
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('sidebar') = page_specific_javascript_bundle_tag('sidebar')
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix", signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } } .issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } }
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.block.issuable-sidebar-header .block.issuable-sidebar-header
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
.block.todo.hide-expanded .block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable, is_collapsed: true = render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable, is_collapsed: true
.block.assignee .block.assignee
= render "shared/issuable/sidebar_assignees", issuable: issuable, can_edit_issuable: can_edit_issuable = render "shared/issuable/sidebar_assignees", issuable: issuable, can_edit_issuable: can_edit_issuable, signed_in: current_user.present?
.block.milestone .block.milestone
.sidebar-collapsed-icon .sidebar-collapsed-icon
= icon('clock-o', 'aria-hidden': 'true') = icon('clock-o', 'aria-hidden': 'true')
......
- if issuable.is_a?(Issue) - if issuable.is_a?(Issue)
#js-vue-sidebar-assignees{ data: { field: "#{issuable.to_ability_name}[assignee_ids]" } } #js-vue-sidebar-assignees{ data: { field: "#{issuable.to_ability_name}[assignee_ids]", signed_in: signed_in } }
.title.hide-collapsed .title.hide-collapsed
Assignee Assignee
= icon('spinner spin') = icon('spinner spin')
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
- if !signed_in
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon
.value.hide-collapsed .value.hide-collapsed
- if issuable.assignee - if issuable.assignee
= link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do = link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%strong #{project.name_with_namespace} &middot; %strong #{project.name_with_namespace} &middot;
- if issuable.is_a?(Issue) - if issuable.is_a?(Issue)
= confidential_icon(issuable) = confidential_icon(issuable)
= link_to_gfm issuable.title, issuable_url_args, title: issuable.title = link_to issuable.title, issuable_url_args, title: issuable.title
.issuable-detail .issuable-detail
= link_to [project.namespace.becomes(Namespace), project, issuable] do = link_to [project.namespace.becomes(Namespace), project, issuable] do
%span.issuable-number= issuable.to_reference %span.issuable-number= issuable.to_reference
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.row .row
.col-sm-6 .col-sm-6
%strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path %strong= link_to truncate(milestone.title, length: 100), milestone_path
.col-sm-6 .col-sm-6
.pull-right.light #{milestone.percent_complete(current_user)}% complete .pull-right.light #{milestone.percent_complete(current_user)}% complete
.row .row
......
require_relative "../lib/ci/upgrader"
Ci::Upgrader.new.execute
---
title: Changed Blame to Annotate in the UI to promote blameless culture
merge_request: 10378
author: Ilya Vassilevsky
---
title: Implement web hook logging
merge_request: 11027
author: Alexander Randa
---
title: Fix long urls in the title of commit
merge_request: 10938
author: Alexander Randa
---
title: Support descriptions for snippets
merge_request:
author:
---
title: Introduce an Events API
merge_request: 11755
author:
---
title: Hide clone panel and file list when user is only a guest
merge_request:
author: James Clark
---
title: Reorder Issue action buttons in order of usability
merge_request: 11642
author:
---
title: 'New issue'/'New merge request' dropdowns should show only projects with issues/merge requests feature enabled
merge_request: 19107
author: blackst0ne
---
title: Remove redirect for old issue url containing id instead of iid
merge_request: 11135
author: blackst0ne
---
title: Replace 'starred_projects.feature' spinach test with an rspec analog
merge_request: 11752
author: blackst0ne
---
title: Replace 'dashboard/merge_requests' spinach with rspec
merge_request: 12440
author: Alexander Randa (@randaalex)
---
title: Replace 'dashboard/todos' spinach with rspec
merge_request: 12453
author: Alexander Randa (@randaalex)
---
title: Add extra context-sensitive functionality for the top right menu button
merge_request: 11632
author:
---
title: Automatically adjust project settings to match changes in project visibility
merge_request: 11831
author:
---
title: Add protected variables which would only be passed to protected branches or
protected tags
merge_request: 11688
author:
---
title: 'Notes: Warning message should go away once resolved'
merge_request: 10823
author: Jacopo Beschi @jacopo-beschi
---
title: Don’t create comment on JIRA if it already exists for the entity
merge_request:
author:
---
title: Update Dashboard Groups UI with better support for subgroups
merge_request:
author:
---
title: Add $CI_ENVIRONMENT_URL to predefined variables for pipelines
merge_request: 11695
author:
---
title: 'Backported new SystemHook event: `repository_update`'
merge_request: 11140
author:
---
title: Limit non-administrators to adding 100 members at a time to groups and projects
merge_request: 11940
author:
---
title: Add performance deltas between app deployments on Merge Request widget
merge_request: 11730
author:
---
title: Improve user experience around slash commands in instant comments
merge_request: 11612
author:
---
title: Refactored gitlab:app:check into SystemCheck liberary and improve some checks
merge_request: 9173
author:
---
title: Confirm Project forking behaviour via the API
merge_request:
author:
---
title: Allow users to be hard-deleted from the admin panel
merge_request: 11874
author:
---
title: Allow users to be hard-deleted from the API
merge_request: 11853
author:
---
title: Add an optional performance bar to view performance metrics for the current page
merge_request: 11439
author:
---
title: Add prometheus based metrics collection to gitlab webapp
merge_request:
author:
---
title: Add a Rake task to aid in rotating otp_key_base
merge_request: 11881
author:
---
title: Fix LaTeX formatting for AsciiDoc wiki
merge_request: 11212
author:
---
title: Simplify project repository settings page
merge_request: 11698
author:
---
title: Revert the feature that would include the current user's username in the HTTP
clone URL
merge_request: 11792
author:
---
title: Add ConvDev Index page to admin area
merge_request: 11377
author:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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