Commit 1688d7d0 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream

parents cf3f89fd bf57a7e8
...@@ -427,19 +427,23 @@ gitlab:assets:compile: ...@@ -427,19 +427,23 @@ gitlab:assets:compile:
- webpack-report/ - webpack-report/
karma: karma:
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test stage: test
<<: *use-pg <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
CHROME_LOG_FILE: "chrome_debug.log"
script: script:
- bundle exec rake karma - bundle exec rake karma
coverage: '/^Statements *: (\d+\.\d+%)/' coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts: artifacts:
name: coverage-javascript name: coverage-javascript
expire_in: 31d expire_in: 31d
when: always
paths: paths:
- chrome_debug.log
- coverage-javascript/ - coverage-javascript/
codeclimate: codeclimate:
......
...@@ -88,7 +88,7 @@ gem 'kaminari', '~> 0.17.0' ...@@ -88,7 +88,7 @@ gem 'kaminari', '~> 0.17.0'
gem 'hamlit', '~> 2.6.1' gem 'hamlit', '~> 2.6.1'
# Files attachments # Files attachments
gem 'carrierwave', '~> 1.0' gem 'carrierwave', '~> 1.1'
# Drag and Drop UI # Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1' gem 'dropzonejs-rails', '~> 0.7.1'
...@@ -167,7 +167,7 @@ gem 'rufus-scheduler', '~> 3.4' ...@@ -167,7 +167,7 @@ gem 'rufus-scheduler', '~> 3.4'
gem 'httparty', '~> 0.13.3' gem 'httparty', '~> 0.13.3'
# Colored output to console # Colored output to console
gem 'rainbow', '~> 2.1.0' gem 'rainbow', '~> 2.2'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
...@@ -383,7 +383,7 @@ gem 'ruby-prof', '~> 0.16.2' ...@@ -383,7 +383,7 @@ gem 'ruby-prof', '~> 0.16.2'
gem 'oauth2', '~> 1.4' gem 'oauth2', '~> 1.4'
# Soft deletion # Soft deletion
gem 'paranoia', '~> 2.2' gem 'paranoia', '~> 2.3.1'
# Health check # Health check
gem 'health_check', '~> 2.6.0' gem 'health_check', '~> 2.6.0'
......
...@@ -116,7 +116,7 @@ GEM ...@@ -116,7 +116,7 @@ GEM
capybara-screenshot (1.0.14) capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3) capybara (>= 1.0, < 3)
launchy launchy
carrierwave (1.0.0) carrierwave (1.1.0)
activemodel (>= 4.0.0) activemodel (>= 4.0.0)
activesupport (>= 4.0.0) activesupport (>= 4.0.0)
mime-types (>= 1.16) mime-types (>= 1.16)
...@@ -574,8 +574,8 @@ GEM ...@@ -574,8 +574,8 @@ GEM
rubypants (~> 0.2) rubypants (~> 0.2)
orm_adapter (0.5.0) orm_adapter (0.5.0)
os (0.9.6) os (0.9.6)
paranoia (2.2.0) paranoia (2.3.1)
activerecord (>= 4.0, < 5.1) activerecord (>= 4.0, < 5.2)
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
path_expander (1.0.1) path_expander (1.0.1)
...@@ -679,7 +679,8 @@ GEM ...@@ -679,7 +679,8 @@ GEM
activesupport (= 4.2.8) activesupport (= 4.2.8)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.2.2)
rake
raindrops (0.17.0) raindrops (0.17.0)
rake (10.5.0) rake (10.5.0)
rblineprof (0.3.6) rblineprof (0.3.6)
...@@ -961,7 +962,7 @@ DEPENDENCIES ...@@ -961,7 +962,7 @@ DEPENDENCIES
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 1.0) carrierwave (~> 1.1)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
chronic (~> 0.10.2) chronic (~> 0.10.2)
chronic_duration (~> 0.10.6) chronic_duration (~> 0.10.6)
...@@ -1067,7 +1068,7 @@ DEPENDENCIES ...@@ -1067,7 +1068,7 @@ DEPENDENCIES
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12) org-ruby (~> 0.9.12)
paranoia (~> 2.2) paranoia (~> 2.3.1)
peek (~> 1.0.1) peek (~> 1.0.1)
peek-gc (~> 0.0.2) peek-gc (~> 0.0.2)
peek-host (~> 1.0.0) peek-host (~> 1.0.0)
...@@ -1089,7 +1090,7 @@ DEPENDENCIES ...@@ -1089,7 +1090,7 @@ DEPENDENCIES
rack-proxy (~> 0.6.0) rack-proxy (~> 0.6.0)
rails (= 4.2.8) rails (= 4.2.8)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.2)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rdoc (~> 4.2) rdoc (~> 4.2)
recaptcha (~> 3.0) recaptcha (~> 3.0)
...@@ -1155,4 +1156,4 @@ DEPENDENCIES ...@@ -1155,4 +1156,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.15.0 1.15.1
...@@ -77,7 +77,7 @@ const Api = { ...@@ -77,7 +77,7 @@ const Api = {
dataType: 'json', dataType: 'json',
}) })
.done(label => callback(label)) .done(label => callback(label))
.error(message => callback(message.responseJSON)); .fail(message => callback(message.responseJSON));
}, },
// Return group projects list. Filtered by query // Return group projects list. Filtered by query
...@@ -134,7 +134,7 @@ const Api = { ...@@ -134,7 +134,7 @@ const Api = {
dataType: 'json', dataType: 'json',
}) })
.done(file => callback(null, file)) .done(file => callback(null, file))
.error(callback); .fail(callback);
}, },
users(query, options) { users(query, options) {
......
...@@ -194,7 +194,7 @@ import AuditLogs from './audit_logs'; ...@@ -194,7 +194,7 @@ import AuditLogs from './audit_logs';
case 'groups:milestones:update': case 'groups:milestones:update':
new ZenMode(); new ZenMode();
new gl.DueDateSelectors(); new gl.DueDateSelectors();
new gl.GLForm($('.milestone-form')); new gl.GLForm($('.milestone-form'), true);
break; break;
case 'projects:compare:show': case 'projects:compare:show':
new gl.Diff(); new gl.Diff();
...@@ -206,7 +206,7 @@ import AuditLogs from './audit_logs'; ...@@ -206,7 +206,7 @@ import AuditLogs from './audit_logs';
case 'projects:issues:new': case 'projects:issues:new':
case 'projects:issues:edit': case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.issue-form')); new gl.GLForm($('.issue-form'), true);
new IssuableForm($('.issue-form')); new IssuableForm($('.issue-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -218,7 +218,7 @@ import AuditLogs from './audit_logs'; ...@@ -218,7 +218,7 @@ import AuditLogs from './audit_logs';
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
new gl.Diff(); new gl.Diff();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.merge-request-form')); new gl.GLForm($('.merge-request-form'), true);
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -227,7 +227,7 @@ import AuditLogs from './audit_logs'; ...@@ -227,7 +227,7 @@ import AuditLogs from './audit_logs';
break; break;
case 'projects:tags:new': case 'projects:tags:new':
new ZenMode(); new ZenMode();
new gl.GLForm($('.tag-form')); new gl.GLForm($('.tag-form'), true);
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs); new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
break; break;
case 'projects:snippets:new': case 'projects:snippets:new':
...@@ -240,11 +240,11 @@ import AuditLogs from './audit_logs'; ...@@ -240,11 +240,11 @@ import AuditLogs from './audit_logs';
case 'snippets:edit': case 'snippets:edit':
case 'snippets:create': case 'snippets:create':
case 'snippets:update': case 'snippets:update':
new gl.GLForm($('.snippet-form')); new gl.GLForm($('.snippet-form'), false);
break; break;
case 'projects:releases:edit': case 'projects:releases:edit':
new ZenMode(); new ZenMode();
new gl.GLForm($('.release-form')); new gl.GLForm($('.release-form'), true);
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new gl.Diff(); new gl.Diff();
...@@ -510,7 +510,7 @@ import AuditLogs from './audit_logs'; ...@@ -510,7 +510,7 @@ import AuditLogs from './audit_logs';
new gl.Wikis(); new gl.Wikis();
shortcut_handler = new ShortcutsWiki(); shortcut_handler = new ShortcutsWiki();
new ZenMode(); new ZenMode();
new gl.GLForm($('.wiki-form')); new gl.GLForm($('.wiki-form'), true);
break; break;
case 'snippets': case 'snippets':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
......
...@@ -287,6 +287,10 @@ window.DropzoneInput = (function() { ...@@ -287,6 +287,10 @@ window.DropzoneInput = (function() {
$uploadingErrorMessage.html(message); $uploadingErrorMessage.html(message);
}; };
closeAlertMessage = function() {
return form.find('.div-dropzone-alert').alert('close');
};
form.find('.markdown-selector').click(function(e) { form.find('.markdown-selector').click(function(e) {
e.preventDefault(); e.preventDefault();
$(this).closest('.gfm-form').find('.div-dropzone').click(); $(this).closest('.gfm-form').find('.div-dropzone').click();
......
...@@ -551,10 +551,10 @@ export default { ...@@ -551,10 +551,10 @@ export default {
</span> </span>
</div> </div>
<div class="table-section section-30 environments-actions table-button-footer" role="gridcell"> <div class="table-section section-30 table-button-footer" role="gridcell">
<div <div
v-if="!model.isFolder" v-if="!model.isFolder"
class="btn-group environment-action-buttons" class="btn-group table-action-buttons"
role="group"> role="group">
<actions-component <actions-component
......
...@@ -27,7 +27,7 @@ export default { ...@@ -27,7 +27,7 @@ export default {
if (this.group.hasSubgroups) { if (this.group.hasSubgroups) {
eventHub.$emit('toggleSubGroups', this.group); eventHub.$emit('toggleSubGroups', this.group);
} else { } else {
window.location.href = this.group.webUrl; window.location.href = this.group.groupPath;
} }
} }
}, },
...@@ -192,7 +192,7 @@ export default { ...@@ -192,7 +192,7 @@ export default {
<div <div
class="avatar-container s40 hidden-xs"> class="avatar-container s40 hidden-xs">
<a <a
:href="group.webUrl"> :href="group.groupPath">
<img <img
class="avatar s40" class="avatar s40"
:src="group.avatarUrl" :src="group.avatarUrl"
...@@ -202,7 +202,7 @@ export default { ...@@ -202,7 +202,7 @@ export default {
<div <div
class="title"> class="title">
<a <a
:href="group.webUrl">{{fullPath}}</a> :href="group.groupPath">{{fullPath}}</a>
<template v-if="group.permissions.humanGroupAccess"> <template v-if="group.permissions.humanGroupAccess">
as as
<span class="access-type">{{group.permissions.humanGroupAccess}}</span> <span class="access-type">{{group.permissions.humanGroupAccess}}</span>
......
...@@ -122,6 +122,7 @@ export default class GroupsStore { ...@@ -122,6 +122,7 @@ export default class GroupsStore {
canEdit: rawGroup.can_edit, canEdit: rawGroup.can_edit,
description: rawGroup.description, description: rawGroup.description,
webUrl: rawGroup.web_url, webUrl: rawGroup.web_url,
groupPath: rawGroup.group_path,
parentId: rawGroup.parent_id, parentId: rawGroup.parent_id,
visibility: rawGroup.visibility, visibility: rawGroup.visibility,
leavePath: rawGroup.leave_path, leavePath: rawGroup.leave_path,
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
}, },
methods: { methods: {
renderGFM() { renderGFM() {
$(this.$refs['gfm-entry-content']).renderGFM(); $(this.$refs['gfm-content']).renderGFM();
if (this.canUpdate) { if (this.canUpdate) {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
......
...@@ -34,7 +34,7 @@ window.dateFormat = dateFormat; ...@@ -34,7 +34,7 @@ window.dateFormat = dateFormat;
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) { w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
$timeagoEls.each((i, el) => { $timeagoEls.each((i, el) => {
el.setAttribute('title', gl.utils.formatDate(el.getAttribute('datetime'))); el.setAttribute('title', el.getAttribute('title'));
if (setTimeago) { if (setTimeago) {
// Recreate with custom template // Recreate with custom template
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -4,87 +4,7 @@ ...@@ -4,87 +4,7 @@
(function() { (function() {
this.Milestone = (function() { this.Milestone = (function() {
Milestone.updateIssue = function(li, issue_url, data) {
return $.ajax({
type: "PUT",
url: issue_url,
data: data,
success: function(_data) {
return Milestone.successCallback(_data, li);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
dataType: "json"
});
};
Milestone.sortIssues = function(url, data) {
return $.ajax({
type: "PUT",
url,
data: data,
success: function(_data) {
return Milestone.successCallback(_data);
},
error: function() {
return new Flash("Issues update failed", 'alert');
},
dataType: "json"
});
};
Milestone.sortMergeRequests = function(url, data) {
return $.ajax({
type: "PUT",
url,
data: data,
success: function(_data) {
return Milestone.successCallback(_data);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
dataType: "json"
});
};
Milestone.updateMergeRequest = function(li, merge_request_url, data) {
return $.ajax({
type: "PUT",
url: merge_request_url,
data: data,
success: function(_data) {
return Milestone.successCallback(_data, li);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
dataType: "json"
});
};
Milestone.successCallback = function(data, element) {
const $avatarContainer = $(element).find('.assignee-icon');
$avatarContainer.empty();
if (data.assignees && data.assignees.length > 0) {
const $avatars = data.assignees.map((assignee) => {
const img_tag = $('<img/>');
img_tag.attr('src', assignee.avatar_url);
img_tag.addClass('avatar s16');
return img_tag;
});
$avatarContainer.append($avatars);
}
};
function Milestone() { function Milestone() {
this.issuesSortEndpoint = $('#tab-issues').data('sort-endpoint');
this.mergeRequestsSortEndpoint = $('#tab-merge-requests').data('sort-endpoint');
this.bindIssuesSorting();
this.bindTabsSwitching(); this.bindTabsSwitching();
// Load merge request tab if it is active // Load merge request tab if it is active
...@@ -94,22 +14,6 @@ ...@@ -94,22 +14,6 @@
this.loadInitialTab(); this.loadInitialTab();
} }
Milestone.prototype.bindIssuesSorting = function() {
if (!this.issuesSortEndpoint) return;
$('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
this.createSortable(el, {
group: 'issue-list',
listEls: $('.issues-sortable-list'),
fieldName: 'issue',
sortCallback: (data) => {
Milestone.sortIssues(this.issuesSortEndpoint, data);
},
updateCallback: Milestone.updateIssue,
});
}.bind(this));
};
Milestone.prototype.bindTabsSwitching = function() { Milestone.prototype.bindTabsSwitching = function() {
return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => { return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
const $target = $(e.target); const $target = $(e.target);
...@@ -119,69 +23,6 @@ ...@@ -119,69 +23,6 @@
}); });
}; };
Milestone.prototype.bindMergeRequestSorting = function() {
if (!this.mergeRequestsSortEndpoint) return;
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
this.createSortable(el, {
group: 'merge-request-list',
listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
fieldName: 'merge_request',
sortCallback: (data) => {
Milestone.sortMergeRequests(this.mergeRequestsSortEndpoint, data);
},
updateCallback: Milestone.updateMergeRequest,
});
}.bind(this));
};
Milestone.prototype.createSortable = function(el, opts) {
return Sortable.create(el, {
group: opts.group,
filter: '.is-disabled',
forceFallback: true,
onStart: function(e) {
opts.listEls.css('min-height', e.item.offsetHeight);
},
onEnd: function () {
opts.listEls.css("min-height", "0px");
},
onUpdate: function(e) {
var ids = this.toArray(),
data;
if (ids.length) {
data = ids.map(function(id) {
return 'sortable_' + opts.fieldName + '[]=' + id;
}).join('&');
opts.sortCallback(data);
}
},
onAdd: function (e) {
var data, issuableId, issuableUrl, newState;
newState = e.to.dataset.state;
issuableUrl = e.item.dataset.url;
data = (function() {
switch (newState) {
case 'ongoing':
return `${opts.fieldName}[assignee_ids][]=${gon.current_user_id}`;
case 'unassigned':
return `${opts.fieldName}[assignee_ids][]=0`;
case 'closed':
return opts.fieldName + '[state_event]=close';
}
})();
if (e.from.dataset.state === 'closed') {
data += '&' + opts.fieldName + '[state_event]=reopen';
}
opts.updateCallback(e.item, issuableUrl, data);
this.options.onUpdate.call(this, e);
}
});
};
Milestone.prototype.loadInitialTab = function() { Milestone.prototype.loadInitialTab = function() {
const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`); const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
...@@ -203,10 +44,6 @@ ...@@ -203,10 +44,6 @@
.done((data) => { .done((data) => {
$(tabElId).html(data.html); $(tabElId).html(data.html);
$target.addClass('is-loaded'); $target.addClass('is-loaded');
if (tabElId === '#tab-merge-requests') {
this.bindMergeRequestSorting();
}
}); });
} }
}; };
......
...@@ -322,7 +322,9 @@ const normalizeNewlines = function(str) { ...@@ -322,7 +322,9 @@ const normalizeNewlines = function(str) {
Notes.updateNoteTargetSelector = function($note) { Notes.updateNoteTargetSelector = function($note) {
const hash = gl.utils.getLocationHash(); const hash = gl.utils.getLocationHash();
$note.toggleClass('target', hash && $note.filter(`#${hash}`).length > 0); // Needs to be an explicit true/false for the jQuery `toggleClass(force)`
const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
$note.toggleClass('target', addTargetClass);
}; };
/* /*
......
import Vue from 'vue'; import Vue from 'vue';
import Translate from '../../vue_shared/translate';
Vue.use(Translate);
const inputNameAttribute = 'schedule[cron]'; const inputNameAttribute = 'schedule[cron]';
...@@ -72,11 +75,11 @@ export default { ...@@ -72,11 +75,11 @@ export default {
/> />
<label for="custom"> <label for="custom">
Custom {{ s__('PipelineSheduleIntervalPattern|Custom') }}
</label> </label>
<span class="cron-syntax-link-wrap"> <span class="cron-syntax-link-wrap">
(<a :href="cronSyntaxUrl" target="_blank">Cron syntax</a>) (<a :href="cronSyntaxUrl" target="_blank">{{ __('Cron syntax') }}</a>)
</span> </span>
</div> </div>
...@@ -92,7 +95,7 @@ export default { ...@@ -92,7 +95,7 @@ export default {
/> />
<label class="label-light" for="every-day"> <label class="label-light" for="every-day">
Every day (at 4:00am) {{ __('Every day (at 4:00am)') }}
</label> </label>
</div> </div>
...@@ -108,7 +111,7 @@ export default { ...@@ -108,7 +111,7 @@ export default {
/> />
<label class="label-light" for="every-week"> <label class="label-light" for="every-week">
Every week (Sundays at 4:00am) {{ __('Every week (Sundays at 4:00am)') }}
</label> </label>
</div> </div>
...@@ -124,7 +127,7 @@ export default { ...@@ -124,7 +127,7 @@ export default {
/> />
<label class="label-light" for="every-month"> <label class="label-light" for="every-month">
Every month (on the 1st at 4:00am) {{ __('Every month (on the 1st at 4:00am)') }}
</label> </label>
</div> </div>
...@@ -133,7 +136,7 @@ export default { ...@@ -133,7 +136,7 @@ export default {
id="schedule_cron" id="schedule_cron"
class="form-control inline cron-interval-input" class="form-control inline cron-interval-input"
type="text" type="text"
placeholder="Define a custom pattern with cron syntax" :placeholder="__('Define a custom pattern with cron syntax')"
required="true" required="true"
v-model="cronInterval" v-model="cronInterval"
:name="inputNameAttribute" :name="inputNameAttribute"
......
import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Translate from '../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg'; import illustrationSvg from '../icons/intro_illustration.svg';
Vue.use(Translate);
const cookieKey = 'pipeline_schedules_callout_dismissed'; const cookieKey = 'pipeline_schedules_callout_dismissed';
export default { export default {
...@@ -29,20 +33,18 @@ export default { ...@@ -29,20 +33,18 @@ export default {
</button> </button>
<div class="svg-container" v-html="illustrationSvg"></div> <div class="svg-container" v-html="illustrationSvg"></div>
<div class="user-callout-copy"> <div class="user-callout-copy">
<h4>Scheduling Pipelines</h4> <h4>{{ __('Scheduling Pipelines') }}</h4>
<p> <p>
The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }}
Those scheduled pipelines will inherit limited project access based on their associated user.
</p> </p>
<p> Learn more in the <p> {{ __('Learn more in the') }}
<a <a
:href="docsUrl" :href="docsUrl"
target="_blank" target="_blank"
rel="nofollow">pipeline schedules documentation</a>. <!-- oneline to prevent extra space before period --> rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period -->
</p> </p>
</div> </div>
</div> </div>
</div> </div>
`, `,
}; };
...@@ -23,7 +23,7 @@ export default { ...@@ -23,7 +23,7 @@ export default {
}; };
</script> </script>
<template> <template>
<td> <div class="table-section section-15 hidden-xs hidden-sm">
<a <a
:href="pipeline.path" :href="pipeline.path"
class="js-pipeline-url-link"> class="js-pipeline-url-link">
...@@ -42,24 +42,26 @@ export default { ...@@ -42,24 +42,26 @@ export default {
class="js-pipeline-url-api api"> class="js-pipeline-url-api api">
API API
</span> </span>
<span <div class="label-container">
v-if="pipeline.flags.latest" <span
class="js-pipeline-url-lastest label label-success" v-if="pipeline.flags.latest"
title="Latest pipeline for this branch" class="js-pipeline-url-latest label label-success"
ref="tooltip"> title="Latest pipeline for this branch"
latest ref="tooltip">
</span> latest
<span </span>
v-if="pipeline.flags.yaml_errors" <span
class="js-pipeline-url-yaml label label-danger" v-if="pipeline.flags.yaml_errors"
:title="pipeline.yaml_errors" class="js-pipeline-url-yaml label label-danger"
ref="tooltip"> :title="pipeline.yaml_errors"
yaml invalid ref="tooltip">
</span> yaml invalid
<span </span>
v-if="pipeline.flags.stuck" <span
class="js-pipeline-url-stuck label label-warning"> v-if="pipeline.flags.stuck"
stuck class="js-pipeline-url-stuck label label-warning">
</span> stuck
</td> </span>
</div>
</div>
</template> </template>
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
<div class="btn-group"> <div class="btn-group">
<button <button
type="button" type="button"
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions" class="dropdown-new btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
title="Manual job" title="Manual job"
data-toggle="dropdown" data-toggle="dropdown"
data-placement="top" data-placement="top"
......
...@@ -55,31 +55,39 @@ ...@@ -55,31 +55,39 @@
}; };
</script> </script>
<template> <template>
<td class="pipelines-time-ago"> <div class="table-section section-15 pipelines-time-ago">
<p <div
class="duration" class="table-mobile-header"
v-if="hasDuration"> role="rowheader">
<span v-html="iconTimerSvg"> Duration
</span> </div>
{{durationFormated}} <div class="table-mobile-content">
</p> <p
class="duration"
v-if="hasDuration">
<span
v-html="iconTimerSvg">
</span>
{{durationFormated}}
</p>
<p <p
class="finished-at" class="finished-at hidden-xs hidden-sm"
v-if="hasFinishedTime"> v-if="hasFinishedTime">
<i <i
class="fa fa-calendar" class="fa fa-calendar"
aria-hidden="true"> aria-hidden="true">
</i> </i>
<time <time
ref="tooltip" ref="tooltip"
data-placement="top" data-placement="top"
data-container="body" data-container="body"
:title="tooltipTitle(finishedTime)"> :title="tooltipTitle(finishedTime)">
{{timeFormated(finishedTime)}} {{timeFormated(finishedTime)}}
</time> </time>
</p> </p>
</td> </div>
</div>
</script> </script>
import statusCodes from '~/lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '~/lib/utils/number_utils'; import { bytesToMiB } from '../../lib/utils/number_utils';
import MemoryGraph from '../../vue_shared/components/memory_graph'; import MemoryGraph from '../../vue_shared/components/memory_graph';
import MRWidgetService from '../services/mr_widget_service'; import MRWidgetService from '../services/mr_widget_service';
......
export default { export default {
name: 'MRWidgetRelatedLinks', name: 'MRWidgetRelatedLinks',
props: { props: {
isMerged: { type: Boolean, required: true },
relatedLinks: { type: Object, required: true }, relatedLinks: { type: Object, required: true },
}, },
computed: { computed: {
// TODO: the following should be handled by i18n
closingText() {
if (this.isMerged) {
return `Closed ${this.issueLabel('closing')}`;
}
return `Closes ${this.issueLabel('closing')}`;
},
hasLinks() { hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks; const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe; return closing || mentioned || assignToMe;
}, },
// TODO: the following should be handled by i18n
mentionedText() {
if (this.isMerged) {
if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
return 'are mentioned but were not closed';
}
return 'is mentioned but was not closed';
}
if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
return 'are mentioned but will not be closed';
}
return 'is mentioned but will not be closed';
},
}, },
methods: { methods: {
hasMultipleIssues(text) { hasMultipleIssues(text) {
return !text ? false : text.match(/<\/a> and <a/); return /<\/a>,? and <a/.test(text);
}, },
// TODO: the following should be handled by i18n
issueLabel(field) { issueLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue'; return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue';
}, },
verbLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
},
}, },
template: ` template: `
<section <div v-if="hasLinks">
v-if="hasLinks"
class="mr-info-list mr-links">
<div class="legend"></div> <div class="legend"></div>
<p v-if="relatedLinks.closing"> <p v-if="relatedLinks.closing">
Closes {{issueLabel('closing')}} {{closingText}}
<span v-html="relatedLinks.closing"></span>. <span v-html="relatedLinks.closing"></span>.
</p> </p>
<p v-if="relatedLinks.mentioned"> <p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span> <span class="capitalize">{{issueLabel('mentioned')}}</span>
<span v-html="relatedLinks.mentioned"></span> <span v-html="relatedLinks.mentioned"></span>
{{verbLabel('mentioned')}} mentioned but will not be closed. {{mentionedText}}
</p> </p>
<p v-if="relatedLinks.assignToMe"> <p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span> <span v-html="relatedLinks.assignToMe"></span>
</p> </p>
</section> </div>
`, `,
}; };
/* global Flash */ /* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import mrWidgetRelatedLinks from '../../components/mr_widget_related_links';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import '../../../flash';
export default { export default {
name: 'MRWidgetMerged', name: 'MRWidgetMerged',
...@@ -11,6 +13,7 @@ export default { ...@@ -11,6 +13,7 @@ export default {
}, },
components: { components: {
'mr-widget-author-and-time': mrWidgetAuthorTime, 'mr-widget-author-and-time': mrWidgetAuthorTime,
'mr-widget-related-links': mrWidgetRelatedLinks,
}, },
data() { data() {
return { return {
...@@ -18,6 +21,9 @@ export default { ...@@ -18,6 +21,9 @@ export default {
}; };
}, },
computed: { computed: {
shouldRenderRelatedLinks() {
return this.mr.relatedLinks && this.mr.isMerged;
},
shouldShowRemoveSourceBranch() { shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr; const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
...@@ -86,6 +92,10 @@ export default { ...@@ -86,6 +92,10 @@ export default {
aria-hidden="true" /> aria-hidden="true" />
The source branch is being removed. The source branch is being removed.
</p> </p>
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:is-merged="mr.isMerged()"
:related-links="mr.relatedLinks" />
</section> </section>
<div <div
v-if="shouldShowMergedButtons" v-if="shouldShowMergedButtons"
......
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
return stateMaps.stateToComponentMap[this.mr.state]; return stateMaps.stateToComponentMap[this.mr.state];
}, },
shouldRenderMergeHelp() { shouldRenderMergeHelp() {
return stateMaps.statesToShowHelpWidget.indexOf(this.mr.state) > -1; return !this.mr.isMerged;
}, },
shouldRenderPipelines() { shouldRenderPipelines() {
return Object.keys(this.mr.pipeline).length || this.mr.hasCI; return Object.keys(this.mr.pipeline).length || this.mr.hasCI;
...@@ -236,9 +236,14 @@ export default { ...@@ -236,9 +236,14 @@ export default {
:is="componentName" :is="componentName"
:mr="mr" :mr="mr"
:service="service" /> :service="service" />
<mr-widget-related-links <section
v-if="shouldRenderRelatedLinks" v-if="shouldRenderRelatedLinks"
:related-links="mr.relatedLinks" /> class="mr-info-list mr-links">
<div class="legend"></div>
<mr-widget-related-links
:is-merged="mr.isMerged"
:related-links="mr.relatedLinks" />
</section>
<mr-widget-merge-help v-if="shouldRenderMergeHelp" /> <mr-widget-merge-help v-if="shouldRenderMergeHelp" />
</div> </div>
`, `,
......
import Timeago from 'timeago.js'; import Timeago from 'timeago.js';
import { getStateKey } from '../dependencies'; import { getStateKey } from '../dependencies';
const unmergedStates = [
'locked',
'conflicts',
'workInProgress',
'readyToMerge',
'checking',
'unresolvedDiscussions',
'pipelineFailed',
'pipelineBlocked',
'autoMergeFailed',
];
export default class MergeRequestStore { export default class MergeRequestStore {
constructor(data) { constructor(data) {
this.sha = data.diff_head_sha; this.sha = data.diff_head_sha;
...@@ -67,6 +79,7 @@ export default class MergeRequestStore { ...@@ -67,6 +79,7 @@ export default class MergeRequestStore {
this.mergeActionsContentPath = data.commit_change_content_path; this.mergeActionsContentPath = data.commit_change_content_path;
this.isRemovingSourceBranch = this.isRemovingSourceBranch || false; this.isRemovingSourceBranch = this.isRemovingSourceBranch || false;
this.isOpen = data.state === 'opened' || data.state === 'reopened' || false; this.isOpen = data.state === 'opened' || data.state === 'reopened' || false;
this.isMerged = unmergedStates.indexOf(data.state) === -1;
this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false; this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false;
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false; this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canMerge = !!data.merge_path; this.canMerge = !!data.merge_path;
......
...@@ -19,19 +19,6 @@ const stateToComponentMap = { ...@@ -19,19 +19,6 @@ const stateToComponentMap = {
shaMismatch: 'mr-widget-sha-mismatch', shaMismatch: 'mr-widget-sha-mismatch',
}; };
const statesToShowHelpWidget = [
'locked',
'conflicts',
'workInProgress',
'readyToMerge',
'checking',
'unresolvedDiscussions',
'pipelineFailed',
'pipelineBlocked',
'autoMergeFailed',
];
export default { export default {
stateToComponentMap, stateToComponentMap,
statesToShowHelpWidget,
}; };
...@@ -110,7 +110,7 @@ ...@@ -110,7 +110,7 @@
</script> </script>
<template> <template>
<div class="branch-commit"> <div class="branch-commit">
<div v-if="hasCommitRef" class="icon-container"> <div v-if="hasCommitRef" class="icon-container hidden-xs">
<i <i
v-if="tag" v-if="tag"
class="fa fa-tag" class="fa fa-tag"
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
<a <a
v-if="hasCommitRef" v-if="hasCommitRef"
class="ref-name" class="ref-name hidden-xs"
:href="commitRef.ref_url"> :href="commitRef.ref_url">
{{commitRef.name}} {{commitRef.name}}
</a> </a>
......
...@@ -28,28 +28,37 @@ ...@@ -28,28 +28,37 @@
}; };
</script> </script>
<template> <template>
<table class="table ci-table"> <div class="ci-table">
<thead> <div
<tr> class="gl-responsive-table-row table-row-header"
<th class="js-pipeline-status pipeline-status">Status</th> role="row">
<th class="js-pipeline-info pipeline-info">Pipeline</th> <div
<th class="js-pipeline-commit pipeline-commit">Commit</th> class="table-section section-10 js-pipeline-status pipeline-status"
<th class="js-pipeline-stages pipeline-stages">Stages</th> role="rowheader">
<th class="js-pipeline-date pipeline-date"></th> Status
<th class="js-pipeline-actions pipeline-actions"></th> </div>
</tr> <div
</thead> class="table-section section-15 js-pipeline-info pipeline-info"
<tbody> role="rowheader">
<template Pipeline
v-for="model in pipelines" </div>
:model="model"> <div
<tr class="table-section section-25 js-pipeline-commit pipeline-commit"
is="pipelines-table-row-component" role="rowheader">
:pipeline="model" Commit
:service="service" </div>
:update-graph-dropdown="updateGraphDropdown" <div
/> class="table-section section-15 js-pipeline-stages pipeline-stages"
</template> role="rowheader">
</tbody> Stages
</table> </div>
</div>
<pipelines-table-row-component
v-for="model in pipelines"
:key="model.id"
:pipeline="model"
:service="service"
:update-graph-dropdown="updateGraphDropdown"
/>
</div>
</template> </template>
...@@ -200,47 +200,74 @@ export default { ...@@ -200,47 +200,74 @@ export default {
} }
return {}; return {};
}, },
displayPipelineActions() {
return this.pipeline.flags.retryable ||
this.pipeline.flags.cancelable ||
this.pipeline.details.manual_actions.length ||
this.pipeline.details.artifacts.length;
},
}, },
}; };
</script> </script>
<template> <template>
<tr class="commit"> <div class="commit gl-responsive-table-row">
<td class="commit-link"> <div class="table-section section-10 commit-link">
<ci-badge :status="pipelineStatus" /> <div class="table-mobile-header"
</td> role="rowheader">
Status
</div>
<div class="table-mobile-content">
<ci-badge :status="pipelineStatus"/>
</div>
</div>
<pipeline-url :pipeline="pipeline" /> <pipeline-url :pipeline="pipeline" />
<td> <div class="table-section section-25">
<commit-component <div
:tag="commitTag" class="table-mobile-header"
:commit-ref="commitRef" role="rowheader">
:commit-url="commitUrl" Commit
:short-sha="commitShortSha" </div>
:title="commitTitle" <div class="table-mobile-content">
:author="commitAuthor" <commit-component
/> :tag="commitTag"
</td> :commit-ref="commitRef"
:commit-url="commitUrl"
<td class="stage-cell"> :short-sha="commitShortSha"
<div class="stage-container dropdown js-mini-pipeline-graph" :title="commitTitle"
v-if="pipeline.details.stages.length > 0" :author="commitAuthor"/>
v-for="stage in pipeline.details.stages"> </div>
</div>
<pipeline-stage <div class="table-section section-wrap section-15 stage-cell">
:stage="stage" <div
:update-dropdown="updateGraphDropdown" class="table-mobile-header"
/> role="rowheader">
Stages
</div>
<div class="table-mobile-content">
<div class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.details.stages.length > 0"
v-for="stage in pipeline.details.stages">
<pipeline-stage
:stage="stage"
:update-dropdown="updateGraphDropdown"
/>
</div>
</div> </div>
</td> </div>
<pipelines-timeago <pipelines-timeago
:duration="pipelineDuration" :duration="pipelineDuration"
:finished-time="pipelineFinishedAt" :finished-time="pipelineFinishedAt"
/> />
<td class="pipeline-actions"> <div
<div class="pull-right btn-group"> v-if="displayPipelineActions"
class="table-section section-20 table-button-footer pipeline-actions">
<div class="btn-group table-action-buttons">
<pipelines-actions-component <pipelines-actions-component
v-if="pipeline.details.manual_actions.length" v-if="pipeline.details.manual_actions.length"
:actions="pipeline.details.manual_actions" :actions="pipeline.details.manual_actions"
...@@ -249,6 +276,7 @@ export default { ...@@ -249,6 +276,7 @@ export default {
<pipelines-artifacts-component <pipelines-artifacts-component
v-if="pipeline.details.artifacts.length" v-if="pipeline.details.artifacts.length"
class="hidden-xs hidden-sm"
:artifacts="pipeline.details.artifacts" :artifacts="pipeline.details.artifacts"
/> />
...@@ -271,6 +299,6 @@ export default { ...@@ -271,6 +299,6 @@ export default {
confirm-action-message="Are you sure you want to cancel this pipeline?" confirm-action-message="Are you sure you want to cancel this pipeline?"
/> />
</div> </div>
</td> </div>
</tr> </div>
</template> </template>
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
background-color: $gray-light; background-color: $gray-light;
text-align: right; text-align: right;
padding: 8px $gl-padding; padding: 8px $gl-padding;
border-bottom: 1px solid $border-color;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
text-align: left; text-align: left;
......
...@@ -152,7 +152,7 @@ ...@@ -152,7 +152,7 @@
} }
.value-container { .value-container {
background-color: $filter-value-selected-color; box-shadow: inset 0 0 0 100px $filtered-search-term-shadow-color;
} }
} }
......
...@@ -125,10 +125,11 @@ label { ...@@ -125,10 +125,11 @@ label {
.select-wrapper { .select-wrapper {
position: relative; position: relative;
.fa-caret-down { .fa-chevron-down {
position: absolute; position: absolute;
font-size: 10px;
right: 10px; right: 10px;
top: 10px; top: 12px;
color: $gray-darkest; color: $gray-darkest;
pointer-events: none; pointer-events: none;
} }
...@@ -138,6 +139,12 @@ label { ...@@ -138,6 +139,12 @@ label {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none;
appearance: none;
&::-ms-expand {
display: none;
}
} }
.form-control-inline { .form-control-inline {
......
...@@ -174,3 +174,14 @@ ...@@ -174,3 +174,14 @@
white-space: nowrap; white-space: nowrap;
} }
} }
@media(max-width: $screen-xs-max) {
.atwho-view-ul {
width: 350px;
}
.atwho-view ul li {
overflow: hidden;
text-overflow: ellipsis;
}
}
...@@ -59,4 +59,8 @@ ...@@ -59,4 +59,8 @@
margin: 0 2px 0 3px; margin: 0 2px 0 3px;
} }
} }
.ci-status {
margin-right: 10px;
}
} }
.panel { .panel {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
} }
<<<<<<< HEAD
.panel-slim { .panel-slim {
@extend .panel; @extend .panel;
...@@ -29,16 +30,45 @@ ...@@ -29,16 +30,45 @@
&.split { &.split {
display: flex; display: flex;
align-items: center; align-items: center;
=======
.panel-slim {
@extend .panel;
margin-bottom: $gl-vert-padding;
}
.panel-heading {
padding: $gl-vert-padding $gl-padding;
line-height: 36px;
.controls {
margin-top: -2px;
float: right;
}
.dropdown-menu-toggle {
line-height: 20px;
} }
.panel-empty-heading { .badge {
border-bottom: 0; margin-top: -2px;
margin-left: 5px;
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
} }
&.split {
display: flex;
align-items: center;
}
<<<<<<< HEAD
.panel-body { .panel-body {
padding: $gl-padding; padding: $gl-padding;
} }
=======
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
.left { .left {
flex: 1 1 auto; flex: 1 1 auto;
} }
...@@ -52,10 +82,17 @@ ...@@ -52,10 +82,17 @@
.panel-empty-heading { .panel-empty-heading {
border-bottom: 0; border-bottom: 0;
} }
<<<<<<< HEAD
.panel-body {
padding: $gl-padding;
=======
.panel-body { .panel-body {
padding: $gl-padding; padding: $gl-padding;
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
.form-actions { .form-actions {
margin: -$gl-padding; margin: -$gl-padding;
margin-top: $gl-padding; margin-top: $gl-padding;
......
...@@ -36,13 +36,58 @@ ...@@ -36,13 +36,58 @@
align-self: stretch; align-self: stretch;
padding: 10px; padding: 10px;
align-items: center; align-items: center;
height: 62px; min-height: 62px;
&:not(:first-of-type) { &:not(:first-of-type) {
border-top: 1px solid $white-normal; border-top: 1px solid $white-normal;
} }
} }
} }
&.section-wrap {
white-space: normal;
@media (max-width: $screen-sm-max) {
flex-wrap: wrap;
}
}
}
}
.table-button-footer {
@media (min-width: $screen-md-min) {
text-align: right;
}
@media (max-width: $screen-sm-max) {
background-color: $gray-normal;
align-self: stretch;
border-top: 1px solid $border-color;
.table-action-buttons {
padding: 10px 5px;
display: flex;
.btn {
border-radius: 3px;
}
> .btn-group,
> .external-url,
> .btn {
flex: 1 1 28px;
margin: 0 5px;
}
.dropdown-new {
width: 100%;
}
.dropdown-menu {
min-width: initial;
}
}
} }
} }
...@@ -56,6 +101,7 @@ ...@@ -56,6 +101,7 @@
.table-mobile-header { .table-mobile-header {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
text-align: left;
@include flex-max-width(40); @include flex-max-width(40);
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
......
...@@ -18,19 +18,28 @@ ...@@ -18,19 +18,28 @@
background-image: none; background-image: none;
background-color: transparent; background-color: transparent;
border: none; border: none;
padding-top: 6px; padding-top: 12px;
padding-right: 10px; padding-right: 20px;
font-size: 10px;
b { b {
display: inline-block; display: none;
width: 0; }
height: 0;
margin-left: 2px; &::after {
vertical-align: middle; content: "\f078";
border-top: 5px dashed; position: absolute;
border-right: 5px solid transparent; z-index: 1;
border-left: 5px solid transparent; text-align: center;
pointer-events: none;
box-sizing: border-box;
color: $gray-darkest; color: $gray-darkest;
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} }
} }
......
...@@ -287,6 +287,7 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%); ...@@ -287,6 +287,7 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%);
/* /*
* Filtered Search * Filtered Search
*/ */
$filtered-search-term-shadow-color: rgba(0, 0, 0, 0.09);
$dropdown-hover-color: $blue-400; $dropdown-hover-color: $blue-400;
/* /*
......
@import "framework/variables";
// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout
// used for Devise email templates, and _should not_ be included in any
// application stylesheets.
//
// Styles defined here are embedded directly into the resulting email HTML via
// the `premailer` gem.
$body-background-color: #363636;
$message-background-color: #fafafa;
$header-color: #6b4fbb;
$body-color: #444;
$cta-color: #e14329;
$footer-link-color: #7e7e7e;
$font-family: Helvetica, Arial, sans-serif;
body {
background-color: $body-background-color;
font-family: $font-family;
margin: 0;
padding: 0;
}
table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
border: 0;
border-collapse: separate;
&#wrapper {
background-color: $body-background-color;
width: 100%;
}
&#header {
margin: 0 auto;
text-align: left;
width: 600px;
& > td {
text-align: center;
}
}
&#body {
background-color: $message-background-color;
border: 1px solid $black;
border-radius: 4px;
margin: 0 auto;
width: 600px;
}
&#footer {
color: $footer-link-color;
font-size: 14px;
text-align: center;
width: 100%;
}
td {
&#body-container {
padding: 20px 40px;
}
}
}
.center {
text-align: center;
}
#logo {
border: none;
outline: none;
min-height: 88px;
width: 134px;
}
#content {
h2 {
color: $header-color;
font-size: 30px;
font-weight: 400;
line-height: 34px;
margin-top: 0;
}
p {
color: $body-color;
font-size: 17px;
line-height: 24px;
margin-bottom: 0;
}
}
#cta {
border: 1px solid $cta-color;
border-radius: 3px;
display: inline-block;
margin: 20px 0;
padding: 12px 24px;
a {
background-color: $message-background-color;
color: $cta-color;
display: inline-block;
text-decoration: none;
}
}
#tanuki {
padding: 40px 0 0;
img {
border: none;
outline: none;
width: 37px;
min-height: 36px;
}
}
#tagline {
font-size: 22px;
font-weight: 100;
padding: 4px 0 40px;
}
#social {
padding: 0 10px 20px;
width: 600px;
word-spacing: 20px;
a {
color: $footer-link-color;
text-decoration: none;
}
}
...@@ -274,43 +274,6 @@ ...@@ -274,43 +274,6 @@
} }
.gl-responsive-table-row { .gl-responsive-table-row {
.environments-actions {
@media (min-width: $screen-md-min) {
text-align: right;
}
@media (max-width: $screen-sm-max) {
background-color: $gray-normal;
align-self: stretch;
border-top: 1px solid $border-color;
.environment-action-buttons {
padding: 10px 5px;
display: flex;
.btn {
border-radius: 3px;
}
> .btn-group,
> .external-url,
> .btn {
flex: 1;
flex-basis: 28px;
margin: 0 5px;
}
.dropdown-new {
width: 100%;
}
.dropdown-menu {
min-width: initial;
}
}
}
}
.branch-commit { .branch-commit {
max-width: 100%; max-width: 100%;
} }
......
...@@ -372,6 +372,10 @@ ...@@ -372,6 +372,10 @@
margin-left: 12px; margin-left: 12px;
} }
&.mr-state-locked + .mr-info-list.mr-links {
margin-top: -16px;
}
&.empty-state { &.empty-state {
.artwork { .artwork {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
......
...@@ -111,8 +111,8 @@ ...@@ -111,8 +111,8 @@
} }
} }
.issues-sortable-list, .milestone-issues-list,
.merge_requests-sortable-list { .milestone-merge_requests-list {
.issuable-detail { .issuable-detail {
display: block; display: block;
margin-top: 7px; margin-top: 7px;
...@@ -197,8 +197,6 @@ ...@@ -197,8 +197,6 @@
.issuable-row { .issuable-row {
background-color: $white-light; background-color: $white-light;
cursor: -webkit-grab;
cursor: grab;
} }
// EE-only // EE-only
......
...@@ -509,11 +509,6 @@ ul.notes { ...@@ -509,11 +509,6 @@ ul.notes {
display: inline; display: inline;
line-height: 20px; line-height: 20px;
@include notes-media('min', $screen-sm-min) {
margin-left: 10px;
line-height: 24px;
}
.fa { .fa {
color: $gray-darkest; color: $gray-darkest;
position: relative; position: relative;
......
...@@ -37,17 +37,13 @@ ...@@ -37,17 +37,13 @@
.table-holder { .table-holder {
width: 100%; width: 100%;
@media (max-width: $screen-sm-max) {
overflow: auto;
}
} }
.commit-title { .commit-title {
margin: 0; margin: 0;
} }
.table.ci-table { .ci-table {
.label { .label {
margin-bottom: 3px; margin-bottom: 3px;
...@@ -57,11 +53,6 @@ ...@@ -57,11 +53,6 @@
color: $black; color: $black;
} }
.stage-cell {
min-width: 130px; // Guarantees we show at least 4 stages in line
width: 20%;
}
.pipelines-time-ago { .pipelines-time-ago {
text-align: right; text-align: right;
} }
...@@ -135,6 +126,7 @@ ...@@ -135,6 +126,7 @@
} }
} }
<<<<<<< HEAD
.table.ci-table { .table.ci-table {
&.builds-page tbody tr { &.builds-page tbody tr {
...@@ -172,6 +164,9 @@ ...@@ -172,6 +164,9 @@
border-top-width: 1px; border-top-width: 1px;
} }
=======
.ci-table {
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
.build.retried { .build.retried {
background-color: $gray-lightest; background-color: $gray-lightest;
} }
...@@ -225,13 +220,6 @@ ...@@ -225,13 +220,6 @@
color: $gl-link-color; color: $gl-link-color;
} }
.commit-title {
max-width: 225px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.label { .label {
margin-right: 4px; margin-right: 4px;
} }
...@@ -284,11 +272,7 @@ ...@@ -284,11 +272,7 @@
} }
.stage-cell { .stage-cell {
font-size: 0; .mini-pipeline-graph-dropdown-toggle svg {
padding: 0 4px;
> .stage-container > div > button > span > svg,
> .stage-container > button > svg {
height: 22px; height: 22px;
width: 22px; width: 22px;
position: absolute; position: absolute;
...@@ -656,6 +640,23 @@ ...@@ -656,6 +640,23 @@
font-weight: normal; font-weight: normal;
} }
@mixin mini-pipeline-graph-color($color-light, $color-main, $color-dark) {
border-color: $color-main;
color: $color-main;
&:hover,
&:focus,
&:active {
background-color: $color-light;
border-color: $color-dark;
color: $color-dark;
svg {
fill: $color-dark;
}
}
}
// Dropdown button in mini pipeline graph // Dropdown button in mini pipeline graph
.mini-pipeline-graph-dropdown-toggle, .mini-pipeline-graph-dropdown-toggle,
.linked-pipeline-mini-item { .linked-pipeline-mini-item {
...@@ -696,100 +697,32 @@ ...@@ -696,100 +697,32 @@
// Dropdown button animation in mini pipeline graph // Dropdown button animation in mini pipeline graph
&.ci-status-icon-success { &.ci-status-icon-success {
border-color: $green-500; @include mini-pipeline-graph-color($green-50, $green-500, $green-600);
color: $green-500;
&:hover,
&:focus,
&:active {
background-color: $green-50;
border-color: $green-600;
color: $green-600;
svg {
fill: $green-600;
}
}
} }
&.ci-status-icon-failed { &.ci-status-icon-failed {
border-color: $red-500; @include mini-pipeline-graph-color($red-50, $red-500, $red-600);
color: $red-500;
&:hover,
&:focus,
&:active {
background-color: $red-50;
border-color: $red-600;
color: $red-600;
svg {
fill: $red-600;
}
}
} }
&.ci-status-icon-pending, &.ci-status-icon-pending,
&.ci-status-icon-success_with_warnings { &.ci-status-icon-success_with_warnings {
border-color: $orange-500; @include mini-pipeline-graph-color($orange-50, $orange-500, $orange-600);
color: $orange-500;
&:hover,
&:focus,
&:active {
background-color: $orange-50;
border-color: $orange-600;
color: $orange-600;
svg {
fill: $orange-600;
}
}
} }
&.ci-status-icon-running { &.ci-status-icon-running {
border-color: $blue-400; @include mini-pipeline-graph-color($blue-50, $blue-400, $blue-600);
color: $blue-400;
&:hover,
&:focus,
&:active {
background-color: $blue-50;
border-color: $blue-600;
color: $blue-600;
svg {
fill: $blue-600;
}
}
} }
&.ci-status-icon-canceled, &.ci-status-icon-canceled,
&.ci-status-icon-disabled, &.ci-status-icon-disabled,
&.ci-status-icon-not-found, &.ci-status-icon-not-found,
&.ci-status-icon-manual { &.ci-status-icon-manual {
border-color: $gl-text-color; @include mini-pipeline-graph-color(rgba($gl-text-color, 0.1), $gl-text-color, $gl-text-color);
color: $gl-text-color;
&:hover,
&:focus,
&:active {
background-color: rgba($gl-text-color, 0.1);
border-color: $gl-text-color;
}
} }
&.ci-status-icon-created, &.ci-status-icon-created,
&.ci-status-icon-skipped { &.ci-status-icon-skipped {
border-color: $gray-darkest; @include mini-pipeline-graph-color(rgba($gray-darkest, 0.1), $gray-darkest, $gray-darkest);
color: $gray-darkest;
&:hover,
&:focus,
&:active {
background-color: rgba($gray-darkest, 0.1);
border-color: $gray-darkest;
}
} }
} }
...@@ -868,6 +801,10 @@ ...@@ -868,6 +801,10 @@
top: 1px; top: 1px;
vertical-align: text-bottom; vertical-align: text-bottom;
position: relative; position: relative;
@media (max-width: $screen-xs-max) {
max-width: 60%;
}
} }
// status icon on the left // status icon on the left
...@@ -958,6 +895,11 @@ ...@@ -958,6 +895,11 @@
left: 50%; left: 50%;
transform: translate(-50%, 0); transform: translate(-50%, 0);
border-width: 0 5px 6px; border-width: 0 5px 6px;
@media (max-width: $screen-sm-max) {
left: 100%;
margin-left: -12px;
}
} }
&::before { &::before {
...@@ -975,9 +917,15 @@ ...@@ -975,9 +917,15 @@
* Center dropdown menu in mini graph * Center dropdown menu in mini graph
*/ */
.mini-pipeline-graph-dropdown-menu.dropdown-menu { .mini-pipeline-graph-dropdown-menu.dropdown-menu {
right: auto; transform: translate(-80%, 0);
left: 50%; min-width: 150px;
transform: translate(-50%, 0);
@media(min-width: $screen-md-min) {
transform: translate(-50%, 0);
right: auto;
left: 50%;
min-width: 240px;
}
} }
/** /**
* Terminal * Terminal
......
.container-fluid { @mixin status-color($color-light, $color-main, $color-dark) {
.ci-status { color: $color-main;
padding: 2px 7px 4px; border-color: $color-main;
margin-right: 10px;
border: 1px solid $gray-darker;
white-space: nowrap;
border-radius: 4px;
&:hover,
&:focus {
text-decoration: none;
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 2px;
overflow: visible;
}
&.ci-failed { &:not(span):hover {
color: $red-500; background-color: $color-light;
border-color: $red-500; color: $color-dark;
border-color: $color-dark;
&:not(span):hover { svg {
background-color: $red-50; fill: $color-dark;
color: $red-600;
border-color: $red-600;
svg {
fill: $red-600;
}
}
svg {
fill: $red-500;
}
} }
}
&.ci-success { svg {
color: $green-600; fill: $color-main;
border-color: $green-500; }
}
&:not(span):hover { .ci-status {
background-color: $green-50; padding: 2px 7px 4px;
color: $green-700; border: 1px solid $gray-darker;
border-color: $green-600; white-space: nowrap;
border-radius: 4px;
svg { &:hover,
fill: $green-600; &:focus {
} text-decoration: none;
} }
svg { svg {
fill: $green-500; height: 13px;
} width: 13px;
} position: relative;
top: 2px;
overflow: visible;
}
&.ci-canceled, &.ci-failed {
&.ci-disabled { @include status-color($red-50, $red-500, $red-600);
color: $gl-text-color; }
border-color: $gl-text-color;
&:not(span):hover { &.ci-success {
background-color: rgba($gl-text-color, .07); @include status-color($green-50, $green-500, $green-700);
} }
svg { &.ci-canceled,
fill: $gl-text-color; &.ci-disabled,
} &.ci-manual {
} color: $gl-text-color;
border-color: $gl-text-color;
&.ci-pending, &:not(span):hover {
&.ci-success_with_warnings, background-color: rgba($gl-text-color, .07);
&.ci-failed_with_warnings {
color: $orange-600;
border-color: $orange-500;
&:not(span):hover {
background-color: $orange-50;
color: $orange-700;
border-color: $orange-600;
svg {
fill: $orange-600;
}
}
svg {
fill: $orange-500;
}
} }
}
&.ci-info, &.ci-pending,
&.ci-running { &.ci-failed_with_warnings,
color: $blue-500; &.ci-success_with_warnings {
border-color: $blue-500; @include status-color($orange-50, $orange-500, $orange-700);
}
&:not(span):hover {
background-color: $blue-50;
color: $blue-600;
border-color: $blue-600;
svg {
fill: $blue-600;
}
}
svg {
fill: $blue-500;
}
}
&.ci-created, &.ci-info,
&.ci-skipped { &.ci-running {
color: $gl-text-color-secondary; @include status-color($blue-50, $blue-500, $blue-600);
border-color: $gl-text-color-secondary; }
&:not(span):hover { &.ci-created,
background-color: rgba($gl-text-color-secondary, .07); &.ci-skipped {
} color: $gl-text-color-secondary;
border-color: $gl-text-color-secondary;
svg { &:not(span):hover {
fill: $gl-text-color-secondary; background-color: rgba($gl-text-color-secondary, .07);
}
} }
&.ci-manual { svg {
color: $gl-text-color; fill: $gl-text-color-secondary;
border-color: $gl-text-color;
&:not(span):hover {
background-color: rgba($gl-text-color, .07);
}
svg {
fill: $gl-text-color;
}
} }
} }
} }
......
...@@ -6,7 +6,7 @@ module MilestoneActions ...@@ -6,7 +6,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path } format.html { redirect_to milestone_redirect_path }
format.json do format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", { render json: tabs_json("shared/milestones/_merge_requests_tab", {
merge_requests: @milestone.merge_requests, merge_requests: @milestone.sorted_merge_requests,
show_project_name: true show_project_name: true
}) })
end end
......
...@@ -2,7 +2,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -2,7 +2,7 @@ class Projects::MilestonesController < Projects::ApplicationController
include MilestoneActions include MilestoneActions
before_action :module_enabled before_action :module_enabled
before_action :milestone, only: [:edit, :update, :destroy, :show, :sort_issues, :sort_merge_requests, :merge_requests, :participants, :labels] before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels]
# Allow read any milestone # Allow read any milestone
before_action :authorize_read_milestone! before_action :authorize_read_milestone!
...@@ -86,22 +86,6 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -86,22 +86,6 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
end end
def sort_issues
@milestone.sort_issues(params['sortable_issue'].map(&:to_i))
render json: { saved: true }
end
def sort_merge_requests
@merge_requests = @milestone.merge_requests.where(id: params['sortable_merge_request'])
@merge_requests.each do |merge_request|
merge_request.position = params['sortable_merge_request'].index(merge_request.id.to_s) + 1
merge_request.save
end
render json: { saved: true }
end
protected protected
def milestone def milestone
......
...@@ -5,8 +5,10 @@ class GroupsFinder < UnionFinder ...@@ -5,8 +5,10 @@ class GroupsFinder < UnionFinder
end end
def execute def execute
groups = find_union(all_groups, Group).with_route.order_id_desc items = all_groups.map do |item|
by_parent(groups) by_parent(item)
end
find_union(items, Group).with_route.order_id_desc
end end
private private
...@@ -16,12 +18,22 @@ class GroupsFinder < UnionFinder ...@@ -16,12 +18,22 @@ class GroupsFinder < UnionFinder
def all_groups def all_groups
groups = [] groups = []
groups << current_user.authorized_groups if current_user if current_user
groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups
end
groups << Group.unscoped.public_to_user(current_user) groups << Group.unscoped.public_to_user(current_user)
groups groups
end end
def groups_for_ancestors
current_user.authorized_groups
end
def groups_for_descendants
current_user.groups
end
def by_parent(groups) def by_parent(groups)
return groups unless params[:parent] return groups unless params[:parent]
......
...@@ -46,6 +46,7 @@ class IssuableFinder ...@@ -46,6 +46,7 @@ class IssuableFinder
items = by_iids(items) items = by_iids(items)
items = by_milestone(items) items = by_milestone(items)
items = by_label(items) items = by_label(items)
items = by_created_at(items)
# Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
items = by_project(items) items = by_project(items)
...@@ -432,6 +433,18 @@ class IssuableFinder ...@@ -432,6 +433,18 @@ class IssuableFinder
params[:non_archived].present? ? items.non_archived : items params[:non_archived].present? ? items.non_archived : items
end end
def by_created_at(items)
if params[:created_after].present?
items = items.where(items.klass.arel_table[:created_at].gteq(params[:created_after]))
end
if params[:created_before].present?
items = items.where(items.klass.arel_table[:created_at].lteq(params[:created_before]))
end
items
end
def current_user_related? def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
......
...@@ -170,9 +170,9 @@ module ApplicationHelper ...@@ -170,9 +170,9 @@ module ApplicationHelper
css_classes = short_format ? 'js-short-timeago' : 'js-timeago' css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
css_classes << " #{html_class}" unless html_class.blank? css_classes << " #{html_class}" unless html_class.blank?
element = content_tag :time, time.strftime("%b %d, %Y"), element = content_tag :time, l(time, format: "%b %d, %Y"),
class: css_classes, class: css_classes,
title: time.to_time.in_time_zone.to_s(:medium), title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
datetime: time.to_time.getutc.iso8601, datetime: time.to_time.getutc.iso8601,
data: { data: {
toggle: 'tooltip', toggle: 'tooltip',
......
...@@ -66,12 +66,12 @@ module DiffHelper ...@@ -66,12 +66,12 @@ module DiffHelper
discussions_left = discussions_right = nil discussions_left = discussions_right = nil
if left && (left.unchanged? || left.discussable?) if left && left.discussable? && (left.unchanged? || left.removed?)
line_code = diff_file.line_code(left) line_code = diff_file.line_code(left)
discussions_left = @grouped_diff_discussions[line_code] discussions_left = @grouped_diff_discussions[line_code]
end end
if right&.discussable? if right && right.discussable? && right.added?
line_code = diff_file.line_code(right) line_code = diff_file.line_code(right)
discussions_right = @grouped_diff_discussions[line_code] discussions_right = @grouped_diff_discussions[line_code]
end end
......
...@@ -66,4 +66,17 @@ module EmailsHelper ...@@ -66,4 +66,17 @@ module EmailsHelper
) )
end end
end end
def email_default_heading(text)
content_tag :h1, text, style: [
"font-family:'Helvetica Neue',Helvetica,Arial,sans-serif",
'color:#333333',
'font-size:18px',
'font-weight:400',
'line-height:1.4',
'padding:0',
'margin:0',
'text-align:center'
].join(';')
end
end end
...@@ -80,7 +80,7 @@ module ProjectsHelper ...@@ -80,7 +80,7 @@ module ProjectsHelper
end end
def remove_fork_project_message(project) def remove_fork_project_message(project)
_("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") % _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
{ forked_from_project: @project.forked_from_project.name_with_namespace } { forked_from_project: @project.forked_from_project.name_with_namespace }
end end
...@@ -151,14 +151,21 @@ module ProjectsHelper ...@@ -151,14 +151,21 @@ module ProjectsHelper
disabled: disabled_option disabled: disabled_option
) )
content_tag( content_tag :div, class: "select-wrapper" do
:select, concat(
options, content_tag(
name: "project[project_feature_attributes][#{field}]", :select,
id: "project_project_feature_attributes_#{field}", options,
class: "pull-right form-control #{repo_children_classes(field)}", name: "project[project_feature_attributes][#{field}]",
data: { field: field } id: "project_project_feature_attributes_#{field}",
).html_safe class: "pull-right form-control select-control #{repo_children_classes(field)} ",
data: { field: field }
)
)
concat(
icon('chevron-down')
)
end.html_safe
end end
def link_to_autodeploy_doc def link_to_autodeploy_doc
......
...@@ -2,7 +2,9 @@ class DeviseMailer < Devise::Mailer ...@@ -2,7 +2,9 @@ class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>" default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to default reply_to: Gitlab.config.gitlab.email_reply_to
layout 'devise_mailer' layout 'mailer/devise'
helper EmailsHelper
protected protected
......
...@@ -67,7 +67,6 @@ module Issuable ...@@ -67,7 +67,6 @@ module Issuable
scope :authored, ->(user) { where(author_id: user) } scope :authored, ->(user) { where(author_id: user) }
scope :recent, -> { reorder(id: :desc) } scope :recent, -> { reorder(id: :desc) }
scope :order_position_asc, -> { reorder(position: :asc) }
scope :of_projects, ->(ids) { where(project_id: ids) } scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) } scope :of_milestones, ->(ids) { where(milestone_id: ids) }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) } scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
...@@ -142,7 +141,6 @@ module Issuable ...@@ -142,7 +141,6 @@ module Issuable
when 'upvotes_desc' then order_upvotes_desc when 'upvotes_desc' then order_upvotes_desc
when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels) when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels) when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
when 'position_asc' then order_position_asc
else else
order_by(method) order_by(method)
end end
......
...@@ -40,10 +40,18 @@ module Milestoneish ...@@ -40,10 +40,18 @@ module Milestoneish
def issues_visible_to_user(user) def issues_visible_to_user(user)
memoize_per_user(user, :issues_visible_to_user) do memoize_per_user(user, :issues_visible_to_user) do
IssuesFinder.new(user, issues_finder_params) IssuesFinder.new(user, issues_finder_params)
.execute.includes(:assignees).where(milestone_id: milestoneish_ids) .execute.preload(:assignees).where(milestone_id: milestoneish_ids)
end end
end end
def sorted_issues(user)
issues_visible_to_user(user).preload_associations.sort('label_priority')
end
def sorted_merge_requests
merge_requests.sort('label_priority')
end
def upcoming? def upcoming?
start_date && start_date.future? start_date && start_date.future?
end end
......
...@@ -12,6 +12,9 @@ class Issue < ActiveRecord::Base ...@@ -12,6 +12,9 @@ class Issue < ActiveRecord::Base
include Elastic::IssuesSearch include Elastic::IssuesSearch
include FasterCacheKeys include FasterCacheKeys
include RelativePositioning include RelativePositioning
include IgnorableColumn
ignore_column :position
WEIGHT_RANGE = 1..9 WEIGHT_RANGE = 1..9
WEIGHT_ALL = 'Everything'.freeze WEIGHT_ALL = 'Everything'.freeze
...@@ -54,7 +57,7 @@ class Issue < ActiveRecord::Base ...@@ -54,7 +57,7 @@ class Issue < ActiveRecord::Base
scope :created_after, -> (datetime) { where("created_at >= ?", datetime) } scope :created_after, -> (datetime) { where("created_at >= ?", datetime) }
scope :include_associations, -> { includes(:labels, project: :namespace) } scope :preload_associations, -> { preload(:labels, project: :namespace) }
after_save :expire_etag_cache after_save :expire_etag_cache
......
...@@ -47,7 +47,7 @@ class LegacyDiffNote < Note ...@@ -47,7 +47,7 @@ class LegacyDiffNote < Note
end end
def for_line?(line) def for_line?(line)
!line.meta? && diff_file.line_code(line) == self.line_code line.discussable? && diff_file.line_code(line) == self.line_code
end end
def original_line_code def original_line_code
......
...@@ -4,8 +4,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -4,8 +4,14 @@ class MergeRequest < ActiveRecord::Base
include Noteable include Noteable
include Referable include Referable
include Sortable include Sortable
<<<<<<< HEAD
include Elastic::MergeRequestsSearch include Elastic::MergeRequestsSearch
include Approvable include Approvable
=======
include IgnorableColumn
ignore_column :position
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
belongs_to :target_project, class_name: "Project" belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project" belongs_to :source_project, class_name: "Project"
......
...@@ -10,6 +10,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -10,6 +10,7 @@ class MergeRequestDiff < ActiveRecord::Base
VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze
belongs_to :merge_request belongs_to :merge_request
has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) }
serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize
serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize
...@@ -91,7 +92,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -91,7 +92,7 @@ class MergeRequestDiff < ActiveRecord::Base
head_commit_sha).diffs(options) head_commit_sha).diffs(options)
else else
@raw_diffs ||= {} @raw_diffs ||= {}
@raw_diffs[options] ||= load_diffs(st_diffs, options) @raw_diffs[options] ||= load_diffs(options)
end end
end end
...@@ -253,24 +254,44 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -253,24 +254,44 @@ class MergeRequestDiff < ActiveRecord::Base
update_columns_serialized(new_attributes) update_columns_serialized(new_attributes)
end end
def dump_diffs(diffs) def create_merge_request_diff_files(diffs)
if diffs.respond_to?(:map) rows = diffs.map.with_index do |diff, index|
diffs.map(&:to_hash) diff.to_hash.merge(
merge_request_diff_id: self.id,
relative_order: index
)
end end
Gitlab::Database.bulk_insert('merge_request_diff_files', rows)
end end
def load_diffs(raw, options) def load_diffs(options)
if valid_raw_diff?(raw) return Gitlab::Git::DiffCollection.new([]) unless diffs_from_database
if paths = options[:paths]
raw = raw.select do |diff|
paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
end
end
Gitlab::Git::DiffCollection.new(raw, options) raw = diffs_from_database
else
Gitlab::Git::DiffCollection.new([]) if paths = options[:paths]
raw = raw.select do |diff|
paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
end
end end
Gitlab::Git::DiffCollection.new(raw, options)
end
def diffs_from_database
return @diffs_from_database if defined?(@diffs_from_database)
@diffs_from_database =
if st_diffs.present?
if valid_raw_diff?(st_diffs)
st_diffs
end
elsif merge_request_diff_files.present?
merge_request_diff_files
.as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS)
.map(&:with_indifferent_access)
end
end end
# Load diffs between branches related to current merge request diff from repo # Load diffs between branches related to current merge request diff from repo
...@@ -285,11 +306,10 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -285,11 +306,10 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:real_size] = diff_collection.real_size new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any? if diff_collection.any?
new_diffs = dump_diffs(diff_collection)
new_attributes[:state] = :collected new_attributes[:state] = :collected
end
new_attributes[:st_diffs] = new_diffs || [] create_merge_request_diff_files(diff_collection)
end
# Set our state to 'overflow' to make the #empty? and #collected? # Set our state to 'overflow' to make the #empty? and #collected?
# methods (generated by StateMachine) return false. # methods (generated by StateMachine) return false.
......
class MergeRequestDiffFile < ActiveRecord::Base
include Gitlab::EncodingHelper
belongs_to :merge_request_diff
def utf8_diff
return '' if diff.blank?
encode_utf8(diff) if diff.respond_to?(:encoding)
end
end
...@@ -166,38 +166,6 @@ class Milestone < ActiveRecord::Base ...@@ -166,38 +166,6 @@ class Milestone < ActiveRecord::Base
write_attribute(:title, sanitize_title(value)) if value.present? write_attribute(:title, sanitize_title(value)) if value.present?
end end
# Sorts the issues for the given IDs.
#
# This method runs a single SQL query using a CASE statement to update the
# position of all issues in the current milestone (scoped to the list of IDs).
#
# Given the ids [10, 20, 30] this method produces a SQL query something like
# the following:
#
# UPDATE issues
# SET position = CASE
# WHEN id = 10 THEN 1
# WHEN id = 20 THEN 2
# WHEN id = 30 THEN 3
# ELSE position
# END
# WHERE id IN (10, 20, 30);
#
# This method expects that the IDs given in `ids` are already Fixnums.
def sort_issues(ids)
pairs = []
ids.each_with_index do |id, index|
pairs << id
pairs << index + 1
end
conditions = 'WHEN id = ? THEN ? ' * ids.length
issues.where(id: ids).
update_all(["position = CASE #{conditions} ELSE position END", *pairs])
end
private private
def milestone_format_reference(format = :iid) def milestone_format_reference(format = :iid)
......
...@@ -41,10 +41,8 @@ class NotificationSetting < ActiveRecord::Base ...@@ -41,10 +41,8 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline :success_pipeline
].freeze ].freeze
store :events, accessors: EMAIL_EVENTS, coder: JSON store :events, coder: JSON
before_save :convert_events
before_create :set_events
before_save :events_to_boolean
def self.find_or_create_for(source) def self.find_or_create_for(source)
setting = find_or_initialize_by(source: source) setting = find_or_initialize_by(source: source)
...@@ -56,21 +54,18 @@ class NotificationSetting < ActiveRecord::Base ...@@ -56,21 +54,18 @@ class NotificationSetting < ActiveRecord::Base
setting setting
end end
# Set all event attributes to false when level is not custom or being initialized for UX reasons # 1. Check if this event has a value stored in its database column.
def set_events # 2. If it does, return that value.
return if custom? # 3. If it doesn't (the value is nil), return the value from the serialized
# JSON hash in `events`.
self.events = {} (EMAIL_EVENTS - [:failed_pipeline]).each do |event|
end define_method(event) do
bool = super()
# Validates store accessors values as boolean bool.nil? ? !!events[event] : bool
# It is a text field so it does not cast correct boolean values in JSON
def events_to_boolean
EMAIL_EVENTS.each do |event|
bool = ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(public_send(event))
events[event] = bool
end end
alias_method :"#{event}?", event
end end
# Allow people to receive failed pipeline notifications if they already have # Allow people to receive failed pipeline notifications if they already have
...@@ -78,7 +73,23 @@ class NotificationSetting < ActiveRecord::Base ...@@ -78,7 +73,23 @@ class NotificationSetting < ActiveRecord::Base
# custom settings. # custom settings.
def failed_pipeline def failed_pipeline
bool = super bool = super
bool = events[:failed_pipeline] if bool.nil?
bool.nil? || bool bool.nil? || bool
end end
alias_method :failed_pipeline?, :failed_pipeline
def event_enabled?(event)
respond_to?(event) && public_send(event)
end
def convert_events
return if events_before_type_cast.nil?
EMAIL_EVENTS.each do |event|
write_attribute(event, public_send(event))
end
write_attribute(:events, nil)
end
end end
...@@ -150,21 +150,21 @@ class User < ActiveRecord::Base ...@@ -150,21 +150,21 @@ class User < ActiveRecord::Base
presence: true, presence: true,
uniqueness: { case_sensitive: false } uniqueness: { case_sensitive: false }
validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :namespace_uniq, if: :username_changed?
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? } validate :unique_email, if: :email_changed?
validate :owns_notification_email, if: ->(user) { user.notification_email_changed? } validate :owns_notification_email, if: :notification_email_changed?
validate :owns_public_email, if: ->(user) { user.public_email_changed? } validate :owns_public_email, if: :public_email_changed?
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id } validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :sanitize_attrs before_validation :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? } before_validation :set_notification_email, if: :email_changed?
before_validation :set_public_email, if: ->(user) { user.public_email_changed? } before_validation :set_public_email, if: :public_email_changed?
after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? } after_update :update_emails_with_primary_email, if: :email_changed?
before_save :ensure_authentication_token, :ensure_incoming_email_token before_save :ensure_authentication_token, :ensure_incoming_email_token
before_save :ensure_external_user_rights before_save :ensure_user_rights_and_limits, if: :external_changed?
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_initialize :set_projects_limit after_initialize :set_projects_limit
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
...@@ -1067,11 +1067,14 @@ class User < ActiveRecord::Base ...@@ -1067,11 +1067,14 @@ class User < ActiveRecord::Base
super super
end end
def ensure_external_user_rights def ensure_user_rights_and_limits
return unless external? if external?
self.can_create_group = false
self.can_create_group = false self.projects_limit = 0
self.projects_limit = 0 else
self.can_create_group = gitlab_config.default_can_create_group
self.projects_limit = current_application_settings.default_projects_limit
end
end end
def signup_domain_valid? def signup_domain_valid?
......
...@@ -6,10 +6,15 @@ class GroupEntity < Grape::Entity ...@@ -6,10 +6,15 @@ class GroupEntity < Grape::Entity
expose :id, :name, :path, :description, :visibility expose :id, :name, :path, :description, :visibility
expose :full_name, :full_path expose :full_name, :full_path
expose :web_url
expose :parent_id expose :parent_id
expose :created_at, :updated_at expose :created_at, :updated_at
<<<<<<< HEAD
expose :web_url do |group| expose :web_url do |group|
=======
expose :group_path do |group|
>>>>>>> bf57a7e80c44080dc7ec0fd774148afdae29cc31
group_path(group) group_path(group)
end end
......
...@@ -5,7 +5,6 @@ class IssuableEntity < Grape::Entity ...@@ -5,7 +5,6 @@ class IssuableEntity < Grape::Entity
expose :description expose :description
expose :lock_version expose :lock_version
expose :milestone_id expose :milestone_id
expose :position
expose :state expose :state
expose :title expose :title
expose :updated_by_id expose :updated_by_id
......
...@@ -8,7 +8,7 @@ class NotificationRecipientService ...@@ -8,7 +8,7 @@ class NotificationRecipientService
@project = project @project = project
end end
def build_recipients(target, current_user, action: nil, previous_assignee: nil, skip_current_user: true) def build_recipients(target, current_user, action:, previous_assignee: nil, skip_current_user: true)
custom_action = build_custom_key(action, target) custom_action = build_custom_key(action, target)
recipients = target.participants(current_user) recipients = target.participants(current_user)
...@@ -59,7 +59,7 @@ class NotificationRecipientService ...@@ -59,7 +59,7 @@ class NotificationRecipientService
return [] if notification_setting.mention? || notification_setting.disabled? return [] if notification_setting.mention? || notification_setting.disabled?
return [] if notification_setting.custom? && !notification_setting.public_send(custom_action) return [] if notification_setting.custom? && !notification_setting.event_enabled?(custom_action)
return [] if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action) return [] if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
...@@ -176,7 +176,7 @@ class NotificationRecipientService ...@@ -176,7 +176,7 @@ class NotificationRecipientService
if notification_level if notification_level
settings = resource.notification_settings.where(level: NotificationSetting.levels[notification_level]) settings = resource.notification_settings.where(level: NotificationSetting.levels[notification_level])
settings = settings.select { |setting| setting.events[action] } if action.present? settings = settings.select { |setting| setting.event_enabled?(action) } if action.present?
settings.map(&:user_id) settings.map(&:user_id)
else else
resource.notification_settings.pluck(:user_id) resource.notification_settings.pluck(:user_id)
...@@ -225,7 +225,7 @@ class NotificationRecipientService ...@@ -225,7 +225,7 @@ class NotificationRecipientService
def user_ids_with_global_level_custom(ids, action) def user_ids_with_global_level_custom(ids, action)
settings = settings_with_global_level_of(:custom, ids) settings = settings_with_global_level_of(:custom, ids)
settings = settings.select { |setting| setting.events[action] } settings = settings.select { |setting| setting.event_enabled?(action) }
settings.map(&:user_id) settings.map(&:user_id)
end end
......
...@@ -296,7 +296,7 @@ class NotificationService ...@@ -296,7 +296,7 @@ class NotificationService
end end
def issue_moved(issue, new_issue, current_user) def issue_moved(issue, new_issue, current_user)
recipients = NotificationRecipientService.new(issue.project).build_recipients(issue, current_user) recipients = NotificationRecipientService.new(issue.project).build_recipients(issue, current_user, action: 'moved')
recipients.map do |recipient| recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user) email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
......
...@@ -34,7 +34,7 @@ module Users ...@@ -34,7 +34,7 @@ module Users
# Keep trying until we obtain the lease. If we don't do so we may end up # Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent # not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries. # hammering Redis too much we'll wait for a bit between retries.
sleep(1) sleep(0.1)
end end
begin begin
......
...@@ -341,8 +341,9 @@ ...@@ -341,8 +341,9 @@
%fieldset %fieldset
%legend Metrics - Prometheus %legend Metrics - Prometheus
%p %p
Setup Prometheus to measure a variety of statistics that partially overlap and complement Influx based metrics. Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
This setting requires a = link_to 'here', admin_health_check_path
\. This setting requires a
= link_to 'restart', help_page_path('administration/restart_gitlab') = link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect. to take effect.
= link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction') = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction')
......
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
.form-group.js-toggle-colors-container.hide .form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label' = f.label :color, "Background Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :color, class: "form-control" = f.color_field :color, class: "form-control"
.form-group.js-toggle-colors-container.hide .form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label' = f.label :font, "Font Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :font, class: "form-control" = f.color_field :font, class: "form-control"
.form-group .form-group
= f.label :starts_at, class: 'control-label' = f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls .col-sm-10.datetime-controls
......
.center - if @resource.unconfirmed_email.present?
- if @resource.unconfirmed_email.present? #content
#content = email_default_heading(@resource.unconfirmed_email)
%h2= @resource.unconfirmed_email %p Click the link below to confirm your email address.
%p Click the link below to confirm your email address. #cta
#cta = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token)
= link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) - else
- else #content
#content - if Gitlab.com?
- if Gitlab.com? = email_default_heading('Thanks for signing up to GitLab!')
%h2 Thanks for signing up to GitLab! - else
- else = email_default_heading("Welcome, #{@resource.name}!")
%h2 Welcome, #{@resource.name}! %p To get started, click the link below to confirm your account.
%p To get started, click the link below to confirm your account. #cta
#cta = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token)
= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token)
.center = email_default_heading("Hello, #{@resource.name}!")
#content %p
%h2 Hello, #{@resource.name}! The password for your GitLab account on
%p #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}
The password for your GitLab account on has successfully been changed.
#{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)} %p
has successfully been changed. If you did not initiate this change, please contact your administrator
%p immediately.
If you did not initiate this change, please contact your administrator
immediately.
.center = email_default_heading("Hello, #{@resource.name}!")
#content %p
%h2 Hello, #{@resource.name}! Someone, hopefully you, has requested to reset the password for your
%p GitLab account on #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}.
Someone, hopefully you, has requested to reset the password for your %p
GitLab account on #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}. If you did not perform this request, you can safely ignore this email.
%p %p
If you did not perform this request, you can safely ignore this email. Otherwise, click the link below to complete the process.
%p #cta
Otherwise, click the link below to complete the process. = link_to('Reset password', edit_password_url(@resource, reset_password_token: @token))
#cta
= link_to('Reset password', edit_password_url(@resource, reset_password_token: @token))
.center #content
#content = email_default_heading("Hello, #{@resource.name}!")
%h2 Hello, #{@resource.name}! %p
%p Your GitLab account has been locked due to an excessive amount of unsuccessful
Your GitLab account has been locked due to an excessive amount of unsuccessful sign in attempts. Your account will automatically unlock in #{time_ago_in_words(Devise.unlock_in.from_now)}
sign in attempts. Your account will automatically unlock in #{time_ago_in_words(Devise.unlock_in.from_now)} or you may click the link below to unlock now.
or you may click the link below to unlock now. #cta
#cta = link_to('Unlock account', unlock_url(@resource, unlock_token: @token))
= link_to('Unlock account', unlock_url(@resource, unlock_token: @token))
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: "en" }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tbody
%tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
= yield
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
%a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
&middot;
%a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
%div
You're receiving this email because of your account on
= succeed "." do
%a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
= yield :additional_footer
!!! 5
%html
%head
%meta{ content: 'text/html; charset=UTF-8', 'http-equiv'=> 'Content-Type' }
= stylesheet_link_tag 'mailers/devise'
%body
%table#wrapper
%tr
%td
%table#header
%td{ valign: "top" }
= image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark')
%table#body
%tr
%td#body-container
= yield
- if Gitlab.com?
%table#footer
%tr
%td#tanuki
= image_tag('mailers/gitlab_tanuki_2x.png', alt: 'GitLab Logo')
%tr
%td#tagline
Everyone can contribute
%tr
%td#social
= link_to 'Blog', 'https://about.gitlab.com/blog/'
= link_to 'Twitter', 'https://twitter.com/gitlab'
= link_to 'Facebook', 'https://www.facebook.com/gitlab/'
= link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg'
= link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com'
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> = render 'layouts/mailer'
%html{ lang: "en" }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tbody
%tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
= yield
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
%a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
&middot;
%a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
%div
You're receiving this email because of your account on
= succeed "." do
%a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
- if Gitlab.com?
- content_for :additional_footer do
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%div
Everyone can contribute
%div
= link_to 'Blog', 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
&middot;
= link_to 'Twitter', 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
&middot;
= link_to 'Facebook', 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
&middot;
= link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
&middot;
= link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
= render layout: 'layouts/mailer' do
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
= yield
...@@ -42,10 +42,17 @@ ...@@ -42,10 +42,17 @@
- if current_user.ldap_user? - if current_user.ldap_user?
Some options are unavailable for LDAP accounts Some options are unavailable for LDAP accounts
.col-lg-9 .col-lg-9
.form-group .row
= f.label :name, class: "label-light" .form-group.col-md-9
= f.text_field :name, class: "form-control", required: true = f.label :name, class: "label-light"
%span.help-block Enter your name, so people you know can recognize you. = f.text_field :name, class: "form-control", required: true
%span.help-block Enter your name, so people you know can recognize you.
.form-group.col-md-3
= f.label :id, class: 'label-light' do
User ID
= f.text_field :id, class: 'form-control', readonly: true
.form-group .form-group
= f.label :email, class: "label-light" = f.label :email, class: "label-light"
......
- if can_change_visibility_level?(@project, current_user) - if can_change_visibility_level?(@project, current_user)
= form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select') .select-wrapper
= form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select select-control')
= icon('chevron-down')
- else - else
.info.js-locked{ data: { help_block: visibility_level_description(@project.visibility_level, @project) } } .info.js-locked{ data: { help_block: visibility_level_description(@project.visibility_level, @project) } }
= visibility_level_icon(@project.visibility_level) = visibility_level_icon(@project.visibility_level)
......
...@@ -9,8 +9,10 @@ ...@@ -9,8 +9,10 @@
.dropzone .dropzone
.dropzone-previews.blob-upload-dropzone-previews .dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light %p.dz-message.light
Attach a file by drag &amp; drop or - upload_link = link_to n_('UploadLink|click to upload'), '#', class: "markdown-selector"
= link_to 'click to upload', '#', class: "markdown-selector" - dropzone_text = _('Attach a file by drag &amp; drop or %{upload_link}') % { upload_link: upload_link }
#{ dropzone_text.html_safe }
%br %br
.dropzone-alerts.alert.alert-danger.data{ style: "display:none" } .dropzone-alerts.alert.alert-danger.data{ style: "display:none" }
...@@ -18,7 +20,7 @@ ...@@ -18,7 +20,7 @@
.form-actions .form-actions
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all' = button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project) - unless can?(current_user, :push_code, @project)
.inline.prepend-left-10 .inline.prepend-left-10
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project) - if !project.empty_repo? && can?(current_user, :download_code, project)
.project-action-button.dropdown.inline> .project-action-button.dropdown.inline>
%button.btn.has-tooltip{ title: 'Download', 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') } %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
= icon('download') = icon('download')
= icon("caret-down") = icon("caret-down")
%span.sr-only= _('Select Archive Format') %span.sr-only= _('Select Archive Format')
......
- if current_user - if current_user
.project-action-button.dropdown.inline .project-action-button.dropdown.inline
%a.btn.dropdown-toggle.has-tooltip{ href: '#', title: 'Create new...', 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => 'Create new...' } %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') }
= icon('plus') = icon('plus')
= icon("caret-down") = icon("caret-down")
%ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
......
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
%span= s_('GoToYourFork|Fork') %span= s_('GoToYourFork|Fork')
- elsif !current_user.can_create_project?
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do
= custom_icon('icon_fork')
%span= s_('CreateNewFork|Fork')
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do = link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
%span= s_('CreateNewFork|Fork') %span= s_('CreateNewFork|Fork')
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
= link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Forks', @project.forks_count), class: 'count' do = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do
= @project.forks_count = @project.forks_count
- case type.to_s - case type.to_s
- when 'revert' - when 'revert'
- label = 'Revert' - label = s_('ChangeTypeAction|Revert')
- branch_label = 'Revert in branch' - branch_label = s_('ChangeTypeActionLabel|Revert in branch')
- revert_merge_request = _('Revert this merge request')
- revert_commit = _('Revert this commit')
- title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit
- when 'cherry-pick' - when 'cherry-pick'
- label = 'Cherry-pick' - label = s_('ChangeTypeAction|Cherry-pick')
- branch_label = 'Pick into branch' - branch_label = s_('ChangeTypeActionLabel|Pick into branch')
- title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
.modal{ id: "modal-#{type}-commit" } .modal{ id: "modal-#{type}-commit" }
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } × %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title== #{label} this #{commit.change_type_title(current_user)} %h3.page-title= title
.modal-body .modal-body
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do = form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch .form-group.branch
= label_tag 'start_branch', branch_label, class: 'control-label' = label_tag 'start_branch', branch_label, class: 'control-label'
.col-sm-10 .col-sm-10
= hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch' = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
= dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } }) = dropdown_tag(@project.default_branch, options: { title: n_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: n_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.checkbox = render 'shared/new_merge_request_checkbox'
= label_tag do
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: nil
Start a <strong>new merge request</strong> with these changes
- else - else
= hidden_field_tag 'create_merge_request', 1, id: nil = hidden_field_tag 'create_merge_request', 1, id: nil
.form-actions .form-actions
= submit_tag label, class: 'btn btn-create' = submit_tag label, class: 'btn btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project) - unless can?(current_user, :push_code, @project)
.inline.prepend-left-10 .inline.prepend-left-10
......
.page-content-header .page-content-header
.header-main-content .header-main-content
%strong %strong
Commit #{ s_('CommitBoxTitle|Commit') }
%span.commit-sha= @commit.short_id %span.commit-sha= @commit.short_id
= clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(text: @commit.id, title: _("Copy commit SHA to clipboard"))
%span.hidden-xs authored %span.hidden-xs authored
#{time_ago_with_tooltip(@commit.authored_date)} #{time_ago_with_tooltip(@commit.authored_date)}
%span by %span= s_('ByAuthor|by')
= author_avatar(@commit, size: 24) = author_avatar(@commit, size: 24)
%strong %strong
= commit_author_link(@commit, avatar: true, size: 24) = commit_author_link(@commit, avatar: true, size: 24)
- if @commit.different_committer? - if @commit.different_committer?
%span.light Committed by %span.light= _('Committed by')
%strong %strong
= commit_committer_link(@commit, avatar: true, size: 24) = commit_committer_link(@commit, avatar: true, size: 24)
#{time_ago_with_tooltip(@commit.committed_date)} #{time_ago_with_tooltip(@commit.committed_date)}
...@@ -22,15 +22,15 @@ ...@@ -22,15 +22,15 @@
= icon('comment') = icon('comment')
= @notes_count = @notes_count
= link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do
Browse files #{ _('Browse files') }
.dropdown.inline .dropdown.inline
%a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } } %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
%span Options %span= _('Options')
= icon('caret-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li.visible-xs-block.visible-sm-block %li.visible-xs-block.visible-sm-block
= link_to namespace_project_tree_path(@project.namespace, @project, @commit) do = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do
Browse Files _('Browse Files')
- unless @commit.has_been_reverted?(current_user) - unless @commit.has_been_reverted?(current_user)
%li.clearfix %li.clearfix
= revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
...@@ -38,13 +38,13 @@ ...@@ -38,13 +38,13 @@
= cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
- if can_collaborate_with_project? - if can_collaborate_with_project?
%li.clearfix %li.clearfix
= link_to "Tag", new_namespace_project_tag_path(@project.namespace, @project, ref: @commit) = link_to s_("CreateTag|Tag"), new_namespace_project_tag_path(@project.namespace, @project, ref: @commit)
%li.divider %li.divider
%li.dropdown-header %li.dropdown-header
Download #{ _('Download') }
- unless @commit.parents.length > 1 - unless @commit.parents.length > 1
%li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) %li= link_to s_("DownloadCommit|Email Patches"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
%li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) %li= link_to s_("DownloadCommit|Plain Diff"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
.commit-box .commit-box
%h3.commit-title %h3.commit-title
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
.well-segment.branch-info .well-segment.branch-info
.icon-container.commit-icon .icon-container.commit-icon
= custom_icon("icon_commit") = custom_icon("icon_commit")
%span.cgray= pluralize(@commit.parents.count, "parent") %span.cgray= n_('parent', 'parents', @commit.parents.count)
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "commit-sha" = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "commit-sha"
%span.commit-info.branches %span.commit-info.branches
...@@ -69,11 +69,11 @@ ...@@ -69,11 +69,11 @@
.status-icon-container{ class: "ci-status-icon-#{@commit.status}" } .status-icon-container{ class: "ci-status-icon-#{@commit.status}" }
= link_to namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) do = link_to namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) do
= ci_icon_for_status(last_pipeline.status) = ci_icon_for_status(last_pipeline.status)
Pipeline #{ _('Pipeline') }
= link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) = link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id)
= ci_label_for_status(last_pipeline.status) = ci_label_for_status(last_pipeline.status)
- if last_pipeline.stages_count.nonzero? - if last_pipeline.stages_count.nonzero?
with #{"stage".pluralize(last_pipeline.stages_count)} #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), last_pipeline.stages_count) }
.mr-widget-pipeline-graph .mr-widget-pipeline-graph
= render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph' = render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph'
in in
......
...@@ -30,9 +30,11 @@ ...@@ -30,9 +30,11 @@
%pre.commit-row-description.js-toggle-content %pre.commit-row-description.js-toggle-content
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
.commiter .commiter
= commit_author_link(commit, avatar: false, size: 24) - commit_author_link = commit_author_link(commit, avatar: false, size: 24)
#{ _('committed') } - commit_timeago = time_ago_with_tooltip(commit.committed_date)
#{time_ago_with_tooltip(commit.committed_date)} - commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
#{ commit_text.html_safe }
.commit-actions.flex-row.hidden-xs .commit-actions.flex-row.hidden-xs
- if commit.status(ref) - if commit.status(ref)
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
.table-mobile-header{ role: 'rowheader' } Created .table-mobile-header{ role: 'rowheader' } Created
%span.table-mobile-content= time_ago_with_tooltip(deployment.created_at) %span.table-mobile-content= time_ago_with_tooltip(deployment.created_at)
.table-section.section-20.environments-actions.table-button-footer{ role: 'gridcell' } .table-section.section-20.table-button-footer{ role: 'gridcell' }
.btn-group.environment-action-buttons .btn-group.table-action-button
= render 'projects/deployments/actions', deployment: deployment = render 'projects/deployments/actions', deployment: deployment
= render 'projects/deployments/rollback', deployment: deployment = render 'projects/deployments/rollback', deployment: deployment
...@@ -113,9 +113,9 @@ ...@@ -113,9 +113,9 @@
Git Large File Storage Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
.col-md-3 .col-md-3
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select', data: { field: 'lfs_enabled' } .select-wrapper
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
= icon('chevron-down')
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) } .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox .checkbox
......
.form-horizontal.resolve-conflicts-form .form-horizontal.resolve-conflicts-form
.form-group .form-group
%label.col-sm-2.control-label{ "for" => "commit-message" } %label.col-sm-2.control-label{ "for" => "commit-message" }
Commit message #{ _('Commit message') }
.col-sm-10 .col-sm-10
.commit-message-container .commit-message-container
.max-width-marker .max-width-marker
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.form-group .form-group
.col-md-9 .col-md-9
= f.label :description, _('Description'), class: 'label-light' = f.label :description, _('Description'), class: 'label-light'
= f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: _('PipelineSchedules|Provide a short description for this pipeline') = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: s_('PipelineSchedules|Provide a short description for this pipeline')
.form-group .form-group
.col-md-9 .col-md-9
= f.label :cron, _('Interval Pattern'), class: 'label-light' = f.label :cron, _('Interval Pattern'), class: 'label-light'
...@@ -15,19 +15,19 @@ ...@@ -15,19 +15,19 @@
.form-group .form-group
.col-md-9 .col-md-9
= f.label :cron_timezone, _('Cron Timezone'), class: 'label-light' = f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("Filter"), data: { data: timezone_data } } ) = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
= f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true = f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
.form-group .form-group
.col-md-9 .col-md-9
= f.label :ref, _('Target Branch'), class: 'label-light' = f.label :ref, _('Target Branch'), class: 'label-light'
= dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } ) = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
.form-group .form-group
.col-md-9 .col-md-9
= f.label :active, _('PipelineSchedules|Activated'), class: 'label-light' = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light'
%div %div
= f.check_box :active, required: false, value: @schedule.active? = f.check_box :active, required: false, value: @schedule.active?
Active = _('Active')
.footer-block.row-content-block .footer-block.row-content-block
= f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3 = f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3
= link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel' = link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel'
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
= ci_icon_for_status(pipeline_schedule.last_pipeline.status) = ci_icon_for_status(pipeline_schedule.last_pipeline.status)
%span ##{pipeline_schedule.last_pipeline.id} %span ##{pipeline_schedule.last_pipeline.id}
- else - else
= _("PipelineSchedules|None") = s_("PipelineSchedules|None")
%td.next-run-cell %td.next-run-cell
- if pipeline_schedule.active? - if pipeline_schedule.active?
= time_ago_with_tooltip(pipeline_schedule.real_next_run) = time_ago_with_tooltip(pipeline_schedule.real_next_run)
- else - else
= _("PipelineSchedules|Inactive") = s_("PipelineSchedules|Inactive")
%td %td
- if pipeline_schedule.owner - if pipeline_schedule.owner
= image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20" = image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20"
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.nav-controls .nav-controls
= link_to new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' do = link_to new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' do
%span New schedule %span= _('New schedule')
- if @schedules.present? - if @schedules.present?
%ul.content-list %ul.content-list
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
= users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite") = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
.form-group .form-group
= label_tag :access_level, "Choose a role permission", class: "label-light" = label_tag :access_level, "Choose a role permission", class: "label-light"
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select" .select-wrapper
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control"
= icon('chevron-down')
.help-block.append-bottom-10 .help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink" = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions about role permissions
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= label_tag :link_group_access, "Max access level", class: "label-light" = label_tag :link_group_access, "Max access level", class: "label-light"
.select-wrapper .select-wrapper
= select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control" = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
= icon('caret-down') = icon('chevron-down')
.help-block.append-bottom-10 .help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink" = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions about role permissions
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- nonce = SecureRandom.hex - nonce = SecureRandom.hex
- descriptions = local_assigns.slice(:message_with_description, :message_without_description) - descriptions = local_assigns.slice(:message_with_description, :message_without_description)
= label_tag "commit_message-#{nonce}", class: 'control-label' do = label_tag "commit_message-#{nonce}", class: 'control-label' do
Commit message #{ _('Commit message') }
.col-sm-10 .col-sm-10
.commit-message-container .commit-message-container
.max-width-marker .max-width-marker
......
...@@ -5,16 +5,12 @@ ...@@ -5,16 +5,12 @@
- else - else
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.form-group.branch .form-group.branch
= label_tag 'branch_name', 'Target branch', class: 'control-label' = label_tag 'branch_name', _('Target Branch'), class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag 'branch_name', @branch_name || tree_edit_branch, required: true, class: "form-control js-branch-name ref-name" = text_field_tag 'branch_name', @branch_name || tree_edit_branch, required: true, class: "form-control js-branch-name ref-name"
.js-create-merge-request-container .js-create-merge-request-container
.checkbox = render 'shared/new_merge_request_checkbox'
- nonce = SecureRandom.hex
= label_tag "create_merge_request-#{nonce}" do
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
Start a <strong>new merge request</strong> with these changes
- else - else
= hidden_field_tag 'branch_name', @branch_name || tree_edit_branch = hidden_field_tag 'branch_name', @branch_name || tree_edit_branch
= hidden_field_tag 'create_merge_request', 1 = hidden_field_tag 'create_merge_request', 1
......
.checkbox
- nonce = SecureRandom.hex
= label_tag "create_merge_request-#{nonce}" do
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
- translation_variables = { new_merge_request: "<strong>#{_('new merge request')}</strong>" }
- translation = _('Start a %{new_merge_request} with these changes') % translation_variables
#{ translation.html_safe }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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