Commit 5fcbe127 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into go-go-gadget-webpack

* master: (83 commits)
  Fix small typo on new branch button spec
  Abillity to promote project labels to group labels
  Update and pin the `jwt` gem to ~> 1.5.6
  refactor merge request build service
  Update index.md
  Clarify that Auto Deploy requires a public project.
  cop for gem fetched from a git source
  Add CHANGELOG entry
  Remove new branch button for confidential issues
  Remove flash warning from login page
  add complete changelog
  Add project ID index to `project_authorizations` table to optimize queries
  Fix disable storing of sensitive information when importing a new repo
  Fix notification when global=disabled, group=watch
  Ensure rake is called within the correct bundle context
  Fix 8.16.0 release date.
  Improve search within group logic
  Add changelog item about new attributes in group api
  Update group api doc with full_name and full_path attributes
  Add tests for nested groups in search service and search helper
  ...
parents ecdcd1be 2c358b4b
...@@ -273,7 +273,7 @@ rake db:migrate:reset: ...@@ -273,7 +273,7 @@ rake db:migrate:reset:
<<: *use-db <<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
script: script:
- rake db:migrate:reset - bundle exec rake db:migrate:reset
rake db:seed_fu: rake db:seed_fu:
stage: test stage: test
...@@ -303,7 +303,7 @@ karma: ...@@ -303,7 +303,7 @@ karma:
<<: *dedicated-runner <<: *dedicated-runner
script: script:
- npm link istanbul - npm link istanbul
- rake karma - bundle exec rake karma
artifacts: artifacts:
name: coverage-javascript name: coverage-javascript
expire_in: 31d expire_in: 31d
...@@ -354,10 +354,10 @@ migration paths: ...@@ -354,10 +354,10 @@ migration paths:
- cp config/resque.yml.example config/resque.yml - cp config/resque.yml.example config/resque.yml
- sed -i 's/localhost/redis/g' config/resque.yml - sed -i 's/localhost/redis/g' config/resque.yml
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3 - bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- rake db:drop db:create db:schema:load db:seed_fu - bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_BUILD_REF - git checkout $CI_BUILD_REF
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
- rake db:migrate - bundle exec rake db:migrate
coverage: coverage:
stage: post-test stage: post-test
......
...@@ -18,7 +18,7 @@ entry. ...@@ -18,7 +18,7 @@ entry.
- Prevent users from deleting system deploy keys via the project deploy key API. - Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2. - Upgrade omniauth gem to 1.3.2.
## 8.16.0 (2017-02-22) ## 8.16.0 (2017-01-22)
- Add LDAP Rake task to rename a provider. !2181 - Add LDAP Rake task to rename a provider. !2181
- Validate label's title length. !5767 (Tomáš Kukrál) - Validate label's title length. !5767 (Tomáš Kukrál)
......
...@@ -35,7 +35,7 @@ gem 'omniauth-twitter', '~> 1.2.0' ...@@ -35,7 +35,7 @@ gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.2.0' gem 'omniauth-authentiq', '~> 0.2.0'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt' gem 'jwt', '~> 1.5.6'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails' gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
......
...@@ -375,7 +375,7 @@ GEM ...@@ -375,7 +375,7 @@ GEM
json (1.8.3) json (1.8.3)
json-schema (2.6.2) json-schema (2.6.2)
addressable (~> 2.3.8) addressable (~> 2.3.8)
jwt (1.5.4) jwt (1.5.6)
kaminari (0.17.0) kaminari (0.17.0)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -900,7 +900,7 @@ DEPENDENCIES ...@@ -900,7 +900,7 @@ DEPENDENCIES
jquery-rails (~> 4.1.0) jquery-rails (~> 4.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
json-schema (~> 2.6.2) json-schema (~> 2.6.2)
jwt jwt (~> 1.5.6)
kaminari (~> 0.17.0) kaminari (~> 0.17.0)
knapsack (~> 1.11.0) knapsack (~> 1.11.0)
kubeclient (~> 2.2.0) kubeclient (~> 2.2.0)
...@@ -1007,4 +1007,4 @@ DEPENDENCIES ...@@ -1007,4 +1007,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.13.7 1.14.2
...@@ -12,106 +12,54 @@ etc.). ...@@ -12,106 +12,54 @@ etc.).
## Common actions ## Common actions
### Issue team ### Issue triaging
- Looks for issues without [workflow labels](#how-we-handle-issues) and triages Our issue triage policies are [described in our handbook]. You are very welcome
issue to help the GitLab team triage issues. We also organize [issue bash events] once
- Closes invalid issues with a comment (duplicates, every quarter.
[fixed in newer version](#issue-fixed-in-newer-version),
[issue report for old version](#issue-report-for-old-version), not a problem
in GitLab, etc.)
- Asks for feedback from issue reporter
([invalid issue reports](#improperly-formatted-issue),
[format code](#code-format), etc.)
- Monitors all issues for feedback (but especially ones commented on since
automatically watching them)
- Closes issues with no feedback from the reporter for two weeks
### Merge marshall & merge request coach
- Responds to merge requests the issue team mentions them in and monitors for
new merge requests
- Provides feedback to the merge request submitter to improve the merge request
(style, tests, etc.)
- Mark merge requests `Ready for Merge` when they meet the
[contribution acceptance criteria]
- Mention developer(s) based on the
[list of members and their specialities][team]
- Closes merge requests with no feedback from the reporter for two weeks
## Priorities of the issue team
1. Mentioning people (critical)
1. Workflow labels (normal)
1. Functional labels (minor)
1. Assigning issues (avoid if possible)
## Mentioning people
The most important thing is making sure valid issues receive feedback from the The most important thing is making sure valid issues receive feedback from the
development team. Therefore the priority is mentioning developers that can help development team. Therefore the priority is mentioning developers that can help
on those issues. Please select someone with relevant experience from on those issues. Please select someone with relevant experience from
[GitLab core team][core-team]. If there is nobody mentioned with that expertise [GitLab team][team]. If there is nobody mentioned with that expertise
look in the commit history for the affected files to find someone. Avoid look in the commit history for the affected files to find someone. Avoid
mentioning the lead developer, this is the person that is least likely to give a mentioning the lead developer, this is the person that is least likely to give a
timely response. If the involvement of the lead developer is needed the other timely response. If the involvement of the lead developer is needed the other
core team members will mention this person. core team members will mention this person.
## Workflow labels [described in our handbook]: https://about.gitlab.com/handbook/engineering/issues/issue-triage-policies/
[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
Workflow labels are purposely not very detailed since that would be hard to keep ### Merge request coaching
updated as you would need to re-evaluate them after every comment. We optionally
use functional labels on demand when we want to group related issues to get an
overview (for example all issues related to RVM, to tackle them in one go) and
to add details to the issue.
- ~"Awaiting Feedback" Feedback pending from the reporter Several people from the [GitLab team][team] are helping community members to get
- ~UX needs help from a UX designer their contributions accepted by meeting our [Definition of done][CONTRIBUTING.md#definition-of-done].
- ~Frontend needs help from a Front-end engineer. Please follow the
["Implement design & UI elements" guidelines].
- ~"Accepting Merge Requests" is a low priority, well-defined issue that we
encourage people to contribute to. Not exclusive with other labels.
- ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote
in support or comment for further detail. Do not use `feature request`.
- ~bug is an issue reporting undesirable or incorrect behavior.
- ~customer is an issue reported by enterprise subscribers. This label should
be accompanied by *bug* or *feature proposal* labels.
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. What you can expect from them is described at https://about.gitlab.com/jobs/merge-request-coach/.
## Workflow labels
## Functional labels Labelling issues is described in the [GitLab Inc engineering workflow].
These labels describe what development specialities are involved such as: `CI`, [GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
`Core`, `Documentation`, `Frontend`, `Issues`, `Merge Requests`, `Omnibus`,
`Release`, `Repository`, `UX`.
## Assigning issues ## Assigning issues
If an issue is complex and needs the attention of a specific person, assignment is a good option but assigning issues might discourage other people from contributing to that issue. We need all the contributions we can get so this should never be discouraged. Also, an assigned person might not have time for a few weeks, so others should feel free to takeover. If an issue is complex and needs the attention of a specific person, assignment is a good option but assigning issues might discourage other people from contributing to that issue. We need all the contributions we can get so this should never be discouraged. Also, an assigned person might not have time for a few weeks, so others should feel free to takeover.
## Label colors
- Light orange `#fef2c0`: workflow labels for issue team members (awaiting
feedback, awaiting confirmation of fix)
- Bright orange `#eb6420`: workflow labels for core team members (attached MR,
awaiting developer action/feedback)
- Light blue `#82C5FF`: functional labels
- Green labels `#009800`: issues that can generally be ignored. For example,
issues given the following labels normally can be closed immediately:
- Support (see copy & paste response:
[Support requests and configuration questions](#support-requests-and-configuration-questions)
## Be kind ## Be kind
Be kind to people trying to contribute. Be aware that people may be a non-native Be kind to people trying to contribute. Be aware that people may be a non-native
English speaker, they might not understand things or they might be very English speaker, they might not understand things or they might be very
sensitive as to how you word things. Use Emoji to express your feelings (heart, sensitive as to how you word things. Use Emoji to express your feelings (heart,
star, smile, etc.). Some good tips about giving feedback to merge requests is in star, smile, etc.). Some good tips about code reviews can be found in our
the [Thoughtbot code review guide]. [Code Review Guidelines].
[Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html
## Feature Freeze ## Feature Freeze
5 working days before the 22nd the stable branches for the upcoming release will On the 7th of each month, the stable branches for the upcoming release will
be frozen for major changes. Merge requests may still be merged into master be frozen for major changes. Merge requests may still be merged into master
during this period. By freezing the stable branches prior to a release there's during this period. By freezing the stable branches prior to a release there's
no need to worry about last minute merge requests potentially breaking a lot of no need to worry about last minute merge requests potentially breaking a lot of
...@@ -120,10 +68,9 @@ things. ...@@ -120,10 +68,9 @@ things.
What is considered to be a major change is determined on a case by case basis as What is considered to be a major change is determined on a case by case basis as
this definition depends very much on the context of changes. For example, a 5 this definition depends very much on the context of changes. For example, a 5
line change might have a big impact on the entire application. Ultimately the line change might have a big impact on the entire application. Ultimately the
decision will be made by those reviewing a merge request and the release decision will be made by the maintainers and the release managers.
manager.
During the feature freeze all merge requests that are meant to go into the next During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set. Merge requests without a milestone and this label will ~"Pick into Stable" set. Merge requests without a milestone and this label will
not be merged into any stable branches. not be merged into any stable branches.
...@@ -189,7 +136,6 @@ prevent duplication with the GitLab.com issue tracker. ...@@ -189,7 +136,6 @@ prevent duplication with the GitLab.com issue tracker.
Since this is an older issue I'll be closing this for now. If you think this is Since this is an older issue I'll be closing this for now. If you think this is
still an issue I encourage you to open it on the \[GitLab.com issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues). still an issue I encourage you to open it on the \[GitLab.com issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues).
[core-team]: https://about.gitlab.com/core-team/
[team]: https://about.gitlab.com/team/ [team]: https://about.gitlab.com/team/
[contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria [contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria
["Implement design & UI elements" guidelines]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#implement-design-ui-elements ["Implement design & UI elements" guidelines]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#implement-design-ui-elements
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
/* global autosize */ /* global autosize */
var autosize = require('vendor/autosize'); var autosize = require('vendor/autosize');
require('vendor/jquery.ba-resize');
(function() { (function() {
$(function() { $(function() {
......
...@@ -29,6 +29,12 @@ ...@@ -29,6 +29,12 @@
watch: { watch: {
detail: { detail: {
handler () { handler () {
if (this.issue.id !== this.detail.issue.id) {
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
$(el).data('glDropdown').clearMenu();
});
}
this.issue = this.detail.issue; this.issue = this.detail.issue;
}, },
deep: true deep: true
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
/* global ShortcutsIssuable */ /* global ShortcutsIssuable */
/* global ZenMode */ /* global ZenMode */
/* global Milestone */ /* global Milestone */
/* global GLForm */
/* global IssuableForm */ /* global IssuableForm */
/* global LabelsSelect */ /* global LabelsSelect */
/* global MilestoneSelect */ /* global MilestoneSelect */
...@@ -64,17 +63,6 @@ ...@@ -64,17 +63,6 @@
new UsernameValidator(); new UsernameValidator();
new ActiveTabMemoizer(); new ActiveTabMemoizer();
break; break;
case 'sessions:create':
if (!gon.u2f) break;
window.gl.u2fAuthenticate = new gl.U2FAuthenticate(
$("#js-authenticate-u2f"),
'#js-login-u2f-form',
gon.u2f,
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
window.gl.u2fAuthenticate.start();
break;
case 'projects:boards:show': case 'projects:boards:show':
case 'projects:boards:index': case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
...@@ -110,7 +98,7 @@ ...@@ -110,7 +98,7 @@
case 'projects:milestones:edit': case 'projects:milestones:edit':
new ZenMode(); new ZenMode();
new gl.DueDateSelectors(); new gl.DueDateSelectors();
new GLForm($('.milestone-form')); new gl.GLForm($('.milestone-form'));
break; break;
case 'groups:milestones:new': case 'groups:milestones:new':
new ZenMode(); new ZenMode();
...@@ -121,7 +109,7 @@ ...@@ -121,7 +109,7 @@
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 GLForm($('.issue-form')); new gl.GLForm($('.issue-form'));
new IssuableForm($('.issue-form')); new IssuableForm($('.issue-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -131,7 +119,7 @@ ...@@ -131,7 +119,7 @@
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 GLForm($('.merge-request-form')); new gl.GLForm($('.merge-request-form'));
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -139,11 +127,11 @@ ...@@ -139,11 +127,11 @@
break; break;
case 'projects:tags:new': case 'projects:tags:new':
new ZenMode(); new ZenMode();
new GLForm($('.tag-form')); new gl.GLForm($('.tag-form'));
break; break;
case 'projects:releases:edit': case 'projects:releases:edit':
new ZenMode(); new ZenMode();
new GLForm($('.release-form')); new gl.GLForm($('.release-form'));
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new gl.Diff(); new gl.Diff();
...@@ -280,6 +268,17 @@ ...@@ -280,6 +268,17 @@
break; break;
} }
switch (path.first()) { switch (path.first()) {
case 'sessions':
case 'omniauth_callbacks':
if (!gon.u2f) break;
gl.u2fAuthenticate = new gl.U2FAuthenticate(
$('#js-authenticate-u2f'),
'#js-login-u2f-form',
gon.u2f,
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
gl.u2fAuthenticate.start();
case 'admin': case 'admin':
new Admin(); new Admin();
switch (path[1]) { switch (path[1]) {
...@@ -332,7 +331,7 @@ ...@@ -332,7 +331,7 @@
new gl.Wikis(); new gl.Wikis();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new ZenMode(); new ZenMode();
new GLForm($('.wiki-form')); new gl.GLForm($('.wiki-form'));
break; break;
case 'snippets': case 'snippets':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
...@@ -357,7 +356,7 @@ ...@@ -357,7 +356,7 @@
} }
// If we haven't installed a custom shortcut handler, install the default one // If we haven't installed a custom shortcut handler, install the default one
if (!shortcut_handler) { if (!shortcut_handler) {
return new Shortcuts(); new Shortcuts();
} }
}; };
......
...@@ -512,12 +512,17 @@ ...@@ -512,12 +512,17 @@
// Append the menu into the dropdown // Append the menu into the dropdown
GitLabDropdown.prototype.appendMenu = function(html) { GitLabDropdown.prototype.appendMenu = function(html) {
return this.clearMenu().append(html);
};
GitLabDropdown.prototype.clearMenu = function() {
var selector; var selector;
selector = '.dropdown-content'; selector = '.dropdown-content';
if (this.dropdown.find(".dropdown-toggle-page").length) { if (this.dropdown.find(".dropdown-toggle-page").length) {
selector = ".dropdown-page-one .dropdown-content"; selector = ".dropdown-page-one .dropdown-content";
} }
return $(selector, this.dropdown).empty().append(html);
return $(selector, this.dropdown).empty();
}; };
GitLabDropdown.prototype.renderItem = function(data, group, index) { GitLabDropdown.prototype.renderItem = function(data, group, index) {
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */
/* global GitLab */
/* global DropzoneInput */
/* global autosize */
var autosize = require('vendor/autosize');
(function() {
this.GLForm = (function() {
function GLForm(form) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
// Before we start, we should clean up any previous data for this form
this.destroy();
// Setup the form
this.setupForm();
this.form.data('gl-form', this);
}
GLForm.prototype.destroy = function() {
// Clean form listeners
this.clearEventListeners();
return this.form.data('gl-form', null);
};
GLForm.prototype.setupForm = function() {
var isNewForm;
isNewForm = this.form.is(':not(.gfm-form)');
this.form.removeClass('js-new-note-form');
if (isNewForm) {
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes
gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form);
autosize(this.textarea);
// form and textarea event listeners
this.addEventListeners();
}
gl.text.init(this.form);
// hide discard button
this.form.find('.js-note-discard').hide();
return this.form.show();
};
GLForm.prototype.clearEventListeners = function() {
this.textarea.off('focus');
this.textarea.off('blur');
return gl.text.removeListeners(this.form);
};
GLForm.prototype.addEventListeners = function() {
this.textarea.on('focus', function() {
return $(this).closest('.md-area').addClass('is-focused');
});
return this.textarea.on('blur', function() {
return $(this).closest('.md-area').removeClass('is-focused');
});
};
return GLForm;
})();
}).call(this);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */
/* global GitLab */
/* global DropzoneInput */
/* global autosize */
(() => {
const global = window.gl || (window.gl = {});
function GLForm(form) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
// Before we start, we should clean up any previous data for this form
this.destroy();
// Setup the form
this.setupForm();
this.form.data('gl-form', this);
}
GLForm.prototype.destroy = function() {
// Clean form listeners
this.clearEventListeners();
return this.form.data('gl-form', null);
};
GLForm.prototype.setupForm = function() {
var isNewForm;
isNewForm = this.form.is(':not(.gfm-form)');
this.form.removeClass('js-new-note-form');
if (isNewForm) {
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes
gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form);
autosize(this.textarea);
// form and textarea event listeners
this.addEventListeners();
}
gl.text.init(this.form);
// hide discard button
this.form.find('.js-note-discard').hide();
this.form.show();
if (this.isAutosizeable) this.setupAutosize();
};
GLForm.prototype.setupAutosize = function () {
this.textarea.off('autosize:resized')
.on('autosize:resized', this.setHeightData.bind(this));
this.textarea.off('mouseup.autosize')
.on('mouseup.autosize', this.destroyAutosize.bind(this));
setTimeout(() => {
autosize(this.textarea);
this.textarea.css('resize', 'vertical');
}, 0);
};
GLForm.prototype.setHeightData = function () {
this.textarea.data('height', this.textarea.outerHeight());
};
GLForm.prototype.destroyAutosize = function () {
const outerHeight = this.textarea.outerHeight();
if (this.textarea.data('height') === outerHeight) return;
autosize.destroy(this.textarea);
this.textarea.data('height', outerHeight);
this.textarea.outerHeight(outerHeight);
this.textarea.css('max-height', window.outerHeight);
};
GLForm.prototype.clearEventListeners = function() {
this.textarea.off('focus');
this.textarea.off('blur');
return gl.text.removeListeners(this.form);
};
GLForm.prototype.addEventListeners = function() {
this.textarea.on('focus', function() {
return $(this).closest('.md-area').addClass('is-focused');
});
return this.textarea.on('blur', function() {
return $(this).closest('.md-area').removeClass('is-focused');
});
};
global.GLForm = GLForm;
})();
...@@ -59,11 +59,11 @@ ...@@ -59,11 +59,11 @@
} else { } else {
avatar = gon.default_avatar_url; avatar = gon.default_avatar_url;
} }
return "<div class='group-result'> <div class='group-name'>" + group.name + "</div> <div class='group-path'>" + group.path + "</div> </div>"; return "<div class='group-result'> <div class='group-name'>" + group.full_name + "</div> <div class='group-path'>" + group.full_path + "</div> </div>";
}; };
GroupsSelect.prototype.formatSelection = function(group) { GroupsSelect.prototype.formatSelection = function(group) {
return group.name; return group.full_name;
}; };
return GroupsSelect; return GroupsSelect;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels'); this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels');
this.otherLabels = otherLabels || $('.js-other-labels'); this.otherLabels = otherLabels || $('.js-other-labels');
this.errorMessage = 'Unable to update label prioritization at this time'; this.errorMessage = 'Unable to update label prioritization at this time';
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
this.prioritizedLabels.sortable({ this.prioritizedLabels.sortable({
items: 'li', items: 'li',
placeholder: 'list-placeholder', placeholder: 'list-placeholder',
...@@ -29,7 +30,12 @@ ...@@ -29,7 +30,12 @@
const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add'; const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`); const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`);
$tooltip.tooltip('destroy'); $tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action); _this.toggleLabelPriority($label, action);
_this.toggleEmptyState($label, $btn, action);
}
toggleEmptyState($label, $btn, action) {
this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li'));
} }
toggleLabelPriority($label, action, persistState) { toggleLabelPriority($label, action, persistState) {
......
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */ /* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */
/* global Flash */ /* global Flash */
/* global GLForm */
/* global Autosave */ /* global Autosave */
/* global ResolveService */ /* global ResolveService */
/* global mrRefreshWidgetUrl */ /* global mrRefreshWidgetUrl */
...@@ -421,7 +420,7 @@ require('vendor/task_list'); ...@@ -421,7 +420,7 @@ require('vendor/task_list');
Notes.prototype.setupNoteForm = function(form) { Notes.prototype.setupNoteForm = function(form) {
var textarea; var textarea;
new GLForm(form); new gl.GLForm(form);
textarea = form.find(".js-note-text"); textarea = form.find(".js-note-text");
return new Autosave(textarea, ["Note", form.find("#note_noteable_type").val(), form.find("#note_noteable_id").val(), form.find("#note_commit_id").val(), form.find("#note_type").val(), form.find("#note_line_code").val(), form.find("#note_position").val()]); return new Autosave(textarea, ["Note", form.find("#note_noteable_type").val(), form.find("#note_noteable_id").val(), form.find("#note_commit_id").val(), form.find("#note_type").val(), form.find("#note_line_code").val(), form.find("#note_position").val()]);
}; };
...@@ -885,7 +884,7 @@ require('vendor/task_list'); ...@@ -885,7 +884,7 @@ require('vendor/task_list');
var targetId = $originalContentEl.data('target-id'); var targetId = $originalContentEl.data('target-id');
var targetType = $originalContentEl.data('target-type'); var targetType = $originalContentEl.data('target-type');
new GLForm($editForm.find('form')); new gl.GLForm($editForm.find('form'));
$editForm.find('form') $editForm.find('form')
.attr('action', postUrl) .attr('action', postUrl)
......
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
filterable: true, filterable: true,
fieldName: 'group_id', fieldName: 'group_id',
search: { search: {
fields: ['name'] fields: ['full_name']
}, },
data: function(term, callback) { data: function(term, callback) {
return Api.groups(term, {}, function(data) { return Api.groups(term, {}, function(data) {
data.unshift({ data.unshift({
name: 'Any' full_name: 'Any'
}); });
data.splice(1, 0, 'divider'); data.splice(1, 0, 'divider');
return callback(data); return callback(data);
...@@ -28,10 +28,10 @@ ...@@ -28,10 +28,10 @@
return obj.id; return obj.id;
}, },
text: function(obj) { text: function(obj) {
return obj.name; return obj.full_name;
}, },
toggleLabel: function(obj) { toggleLabel: function(obj) {
return ($groupDropdown.data('default-label')) + " " + obj.name; return ($groupDropdown.data('default-label')) + " " + obj.full_name;
}, },
clicked: (function(_this) { clicked: (function(_this) {
return function() { return function() {
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
}, },
success: (data) => { success: (data) => {
$target.remove(); $target.remove();
$('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>'); $('.js-todos-all').html('<div class="nothing-here-block">You\'re all done!</div>');
return this.updateBadges(data); return this.updateBadges(data);
} }
}); });
......
...@@ -278,6 +278,10 @@ ...@@ -278,6 +278,10 @@
display: inline-block; display: inline-block;
} }
.btn {
margin: $btn-side-margin $btn-side-margin 0 0;
}
@media(max-width: $screen-xs-max) { @media(max-width: $screen-xs-max) {
margin-top: 50px; margin-top: 50px;
text-align: center; text-align: center;
...@@ -286,6 +290,12 @@ ...@@ -286,6 +290,12 @@
width: 100%; width: 100%;
} }
} }
@media(min-width: $screen-xs-max) {
&.labels .text-content {
margin-top: 70px;
}
}
} }
.flex-container-block { .flex-container-block {
......
...@@ -162,6 +162,10 @@ ...@@ -162,6 +162,10 @@
} }
} }
} }
&.panel-without-border {
border: 0;
}
} }
.panel-succes .panel-heading, .panel-succes .panel-heading,
......
...@@ -494,31 +494,27 @@ ...@@ -494,31 +494,27 @@
// Action Icons in big pipeline-graph nodes // Action Icons in big pipeline-graph nodes
> .ci-action-icon-container .ci-action-icon-wrapper { > .ci-action-icon-container .ci-action-icon-wrapper {
i {
color: $border-color;
border-radius: 100%;
border: 1px solid $border-color;
padding: 5px 6px;
font-size: 13px;
background: $white-light;
height: 30px; height: 30px;
width: 30px; width: 30px;
background: $white-light;
&::before { border: 1px solid $border-color;
position: relative; border-radius: 100%;
top: 3px; display: block;
left: 3px;
}
&:hover { &:hover {
color: $gl-text-color;
background-color: $stage-hover-bg; background-color: $stage-hover-bg;
border: 1px solid $stage-hover-bg; border: 1px solid $stage-hover-bg;
} }
svg {
fill: $border-color;
position: relative;
left: -1px;
top: -1px;
} }
.ci-play-icon { &:hover svg {
padding: 5px 5px 5px 7px; fill: $gl-text-color;
} }
} }
...@@ -657,7 +653,7 @@ ...@@ -657,7 +653,7 @@
font-weight: 100; font-weight: 100;
font-size: 15px; font-size: 15px;
position: absolute; position: absolute;
right: 5px; right: 13px;
top: 8px; top: 8px;
} }
...@@ -825,11 +821,23 @@ ...@@ -825,11 +821,23 @@
&:hover, &:hover,
&:focus { &:focus {
text-decoration: none;
color: $gl-text-color;
background-color: $stage-hover-bg; background-color: $stage-hover-bg;
border: 1px solid transparent; border: 1px solid transparent;
} }
svg {
width: 22px;
height: 22px;
left: -6px;
position: relative;
top: -3px;
fill: $action-icon-color;
}
&:hover svg,
&:focus svg {
fill: $gl-text-color;
}
} }
// link to the build // link to the build
......
...@@ -76,6 +76,10 @@ ...@@ -76,6 +76,10 @@
font-size: 14px; font-size: 14px;
} }
.action-name {
font-weight: normal;
}
.todo-body { .todo-body {
.todo-note { .todo-note {
word-wrap: break-word; word-wrap: break-word;
......
...@@ -4,6 +4,7 @@ class DashboardController < Dashboard::ApplicationController ...@@ -4,6 +4,7 @@ class DashboardController < Dashboard::ApplicationController
before_action :event_filter, only: :activity before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests] before_action :projects, only: [:issues, :merge_requests]
before_action :set_show_full_reference, only: [:issues, :merge_requests]
respond_to :html respond_to :html
...@@ -34,4 +35,8 @@ class DashboardController < Dashboard::ApplicationController ...@@ -34,4 +35,8 @@ class DashboardController < Dashboard::ApplicationController
@events = @event_filter.apply_filter(@events).with_associations @events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0) @events = @events.limit(20).offset(params[:offset] || 0)
end end
def set_show_full_reference
@show_full_reference = true
end
end end
...@@ -30,6 +30,17 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -30,6 +30,17 @@ class Projects::CommitController < Projects::ApplicationController
end end
def pipelines def pipelines
@pipelines = @commit.pipelines.order(id: :desc)
respond_to do |format|
format.html
format.json do
render json: PipelineSerializer
.new(project: @project, user: @current_user)
.with_pagination(request, response)
.represent(@pipelines)
end
end
end end
def branches def branches
......
...@@ -2,12 +2,13 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -2,12 +2,13 @@ class Projects::LabelsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
before_action :module_enabled before_action :module_enabled
before_action :label, only: [:edit, :update, :destroy] before_action :label, only: [:edit, :update, :destroy, :promote]
before_action :find_labels, only: [:index, :set_priorities, :remove_priority, :toggle_subscription] before_action :find_labels, only: [:index, :set_priorities, :remove_priority, :toggle_subscription]
before_action :authorize_read_label! before_action :authorize_read_label!
before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update,
:generate, :destroy, :remove_priority, :generate, :destroy, :remove_priority,
:set_priorities] :set_priorities]
before_action :authorize_admin_group!, only: [:promote]
respond_to :js, :html respond_to :js, :html
...@@ -71,13 +72,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -71,13 +72,7 @@ class Projects::LabelsController < Projects::ApplicationController
@label.destroy @label.destroy
@labels = find_labels @labels = find_labels
respond_to do |format| redirect_to(namespace_project_labels_path(@project.namespace, @project), notice: 'Label was removed')
format.html do
redirect_to(namespace_project_labels_path(@project.namespace, @project),
notice: 'Label was removed')
end
format.js
end
end end
def remove_priority def remove_priority
...@@ -108,6 +103,32 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -108,6 +103,32 @@ class Projects::LabelsController < Projects::ApplicationController
end end
end end
def promote
promote_service = Labels::PromoteService.new(@project, @current_user)
begin
return render_404 unless promote_service.execute(@label)
respond_to do |format|
format.html do
redirect_to(namespace_project_labels_path(@project.namespace, @project),
notice: 'Label was promoted to a Group Label')
end
format.js
end
rescue ActiveRecord::RecordInvalid => e
Gitlab::AppLogger.error "Failed to promote label \"#{@label.title}\" to group label"
Gitlab::AppLogger.error e
respond_to do |format|
format.html do
redirect_to(namespace_project_labels_path(@project.namespace, @project),
notice: 'Failed to promote label due to internal error. Please contact administrators.')
end
format.js
end
end
end
protected protected
def module_enabled def module_enabled
...@@ -135,4 +156,8 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -135,4 +156,8 @@ class Projects::LabelsController < Projects::ApplicationController
def authorize_admin_labels! def authorize_admin_labels!
return render_404 unless can?(current_user, :admin_label, @project) return render_404 unless can?(current_user, :admin_label, @project)
end end
def authorize_admin_group!
return render_404 unless can?(current_user, :admin_group, @project.group)
end
end end
...@@ -34,7 +34,7 @@ class Projects::MattermostsController < Projects::ApplicationController ...@@ -34,7 +34,7 @@ class Projects::MattermostsController < Projects::ApplicationController
end end
def teams def teams
@teams ||= @service.list_teams(current_user) @teams, @teams_error_message = @service.list_teams(current_user)
end end
def service def service
......
...@@ -214,7 +214,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -214,7 +214,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render 'show' render 'show'
end end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_pipelines') } }
format.json do
render json: {
html: view_to_html_string('projects/merge_requests/show/_pipelines'),
pipelines: PipelineSerializer
.new(project: @project, user: @current_user)
.with_pagination(request, response)
.represent(@pipelines)
}
end
end end
end end
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
# For users who haven't customized the setting, we simply delegate to # For users who haven't customized the setting, we simply delegate to
# `DashboardController#show`, which is the default. # `DashboardController#show`, which is the default.
class RootController < Dashboard::ProjectsController class RootController < Dashboard::ProjectsController
skip_before_action :authenticate_user!, only: [:index]
before_action :redirect_to_custom_dashboard, only: [:index] before_action :redirect_to_custom_dashboard, only: [:index]
def index def index
...@@ -16,7 +17,7 @@ class RootController < Dashboard::ProjectsController ...@@ -16,7 +17,7 @@ class RootController < Dashboard::ProjectsController
private private
def redirect_to_custom_dashboard def redirect_to_custom_dashboard
return unless current_user return redirect_to new_user_session_path unless current_user
case current_user.dashboard case current_user.dashboard
when 'stars' when 'stars'
......
...@@ -162,6 +162,10 @@ module IssuablesHelper ...@@ -162,6 +162,10 @@ module IssuablesHelper
] ]
end end
def issuable_reference(issuable)
@show_full_reference ? issuable.to_reference(full: true) : issuable.to_reference(@group || @project)
end
def issuable_filter_present? def issuable_filter_present?
issuable_filter_params.any? { |k| params.key?(k) } issuable_filter_params.any? { |k| params.key?(k) }
end end
......
...@@ -89,7 +89,7 @@ module SearchHelper ...@@ -89,7 +89,7 @@ module SearchHelper
{ {
category: "Groups", category: "Groups",
id: group.id, id: group.id,
label: "#{search_result_sanitize(group.name)}", label: "#{search_result_sanitize(group.full_name)}",
url: group_path(group) url: group_path(group)
} }
end end
......
...@@ -100,8 +100,8 @@ class Commit ...@@ -100,8 +100,8 @@ class Commit
commit_reference(from_project, id, full: full) commit_reference(from_project, id, full: full)
end end
def reference_link_text(from_project = nil) def reference_link_text(from_project = nil, full: false)
commit_reference(from_project, short_id) commit_reference(from_project, short_id, full: full)
end end
def diff_line_count def diff_line_count
......
class Environment < ActiveRecord::Base class Environment < ActiveRecord::Base
# Used to generate random suffixes for the slug # Used to generate random suffixes for the slug
LETTERS = 'a'..'z'
NUMBERS = '0'..'9' NUMBERS = '0'..'9'
SUFFIX_CHARS = ('a'..'z').to_a + NUMBERS.to_a SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a
belongs_to :project, required: true, validate: true belongs_to :project, required: true, validate: true
...@@ -148,17 +149,24 @@ class Environment < ActiveRecord::Base ...@@ -148,17 +149,24 @@ class Environment < ActiveRecord::Base
slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-') slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
# Must start with a letter # Must start with a letter
slugified = "env-" + slugified if NUMBERS.cover?(slugified[0]) slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0])
# Repeated dashes are invalid (OpenShift limitation)
slugified.gsub!(/\-+/, '-')
# Maximum length: 24 characters (OpenShift limitation) # Maximum length: 24 characters (OpenShift limitation)
slugified = slugified[0..23] slugified = slugified[0..23]
# Cannot end with a "-" character (Kubernetes label limitation) # Cannot end with a dash (Kubernetes label limitation)
slugified = slugified[0..-2] if slugified[-1] == "-" slugified.chop! if slugified.end_with?('-')
# Add a random suffix, shortening the current string if necessary, if it # Add a random suffix, shortening the current string if necessary, if it
# has been slugified. This ensures uniqueness. # has been slugified. This ensures uniqueness.
slugified = slugified[0..16] + "-" + random_suffix if slugified != name if slugified != name
slugified = slugified[0..16]
slugified << '-' unless slugified.end_with?('-')
slugified << random_suffix
end
self.slug = slugified self.slug = slugified
end end
......
...@@ -97,10 +97,11 @@ class Issue < ActiveRecord::Base ...@@ -97,10 +97,11 @@ class Issue < ActiveRecord::Base
end end
end end
def to_reference(from_project = nil, full: false) # `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from_project, full: full)}#{reference}" "#{project.to_reference(from, full: full)}#{reference}"
end end
def referenced_merge_requests(current_user = nil) def referenced_merge_requests(current_user = nil)
......
...@@ -179,10 +179,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -179,10 +179,11 @@ class MergeRequest < ActiveRecord::Base
work_in_progress?(title) ? title : "WIP: #{title}" work_in_progress?(title) ? title : "WIP: #{title}"
end end
def to_reference(from_project = nil, full: false) # `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from_project, full: full)}#{reference}" "#{project.to_reference(from, full: full)}#{reference}"
end end
def first_commit def first_commit
......
...@@ -225,6 +225,7 @@ class Project < ActiveRecord::Base ...@@ -225,6 +225,7 @@ class Project < ActiveRecord::Base
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') } scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
scope :with_statistics, -> { includes(:statistics) } scope :with_statistics, -> { includes(:statistics) }
scope :with_shared_runners, -> { where(shared_runners_enabled: true) } scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
scope :inside_path, ->(path) { joins(:route).where('routes.path LIKE ?', "#{path}/%") }
# "enabled" here means "not disabled". It includes private features! # "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) { scope :with_feature_enabled, ->(feature) {
...@@ -591,10 +592,11 @@ class Project < ActiveRecord::Base ...@@ -591,10 +592,11 @@ class Project < ActiveRecord::Base
end end
end end
def to_reference(from_project = nil, full: false) # `from` argument can be a Namespace or Project.
if full || cross_namespace_reference?(from_project) def to_reference(from = nil, full: false)
if full || cross_namespace_reference?(from)
path_with_namespace path_with_namespace
elsif cross_project_reference?(from_project) elsif cross_project_reference?(from)
path path
end end
end end
...@@ -1291,21 +1293,26 @@ class Project < ActiveRecord::Base ...@@ -1291,21 +1293,26 @@ class Project < ActiveRecord::Base
private private
def cross_namespace_reference?(from)
case from
when Project
namespace != from.namespace
when Namespace
namespace != from
end
end
# Check if a reference is being done cross-project # Check if a reference is being done cross-project
# def cross_project_reference?(from)
# from_project - Refering Project object return true if from.is_a?(Namespace)
def cross_project_reference?(from_project)
from_project && self != from_project from && self != from
end end
def pushes_since_gc_redis_key def pushes_since_gc_redis_key
"projects/#{id}/pushes_since_gc" "projects/#{id}/pushes_since_gc"
end end
def cross_namespace_reference?(from_project)
from_project && namespace != from_project.namespace
end
def default_branch_protected? def default_branch_protected?
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
......
...@@ -60,9 +60,9 @@ class JiraService < IssueTrackerService ...@@ -60,9 +60,9 @@ class JiraService < IssueTrackerService
end end
def help def help
'You need to configure JIRA before enabling this service. For more details "You need to configure JIRA before enabling this service. For more details
read the read the
[JIRA service documentation](https://docs.gitlab.com/ce/project_services/jira.html).' [JIRA service documentation](#{help_page_url('project_services/jira')})."
end end
def title def title
......
...@@ -28,8 +28,8 @@ class MattermostSlashCommandsService < ChatSlashCommandsService ...@@ -28,8 +28,8 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
[false, e.message] [false, e.message]
end end
def list_teams(user) def list_teams(current_user)
Mattermost::Team.new(user).all [Mattermost::Team.new(current_user).all, nil]
rescue Mattermost::Error => e rescue Mattermost::Error => e
[[], e.message] [[], e.message]
end end
......
...@@ -103,9 +103,9 @@ class Todo < ActiveRecord::Base ...@@ -103,9 +103,9 @@ class Todo < ActiveRecord::Base
def target_reference def target_reference
if for_commit? if for_commit?
target.short_id target.reference_link_text(full: true)
else else
target.to_reference target.to_reference(full: true)
end end
end end
......
module Ci module Ci
class BuildPolicy < CommitStatusPolicy class BuildPolicy < CommitStatusPolicy
def rules def rules
can! :read_build if @subject.project.public_builds?
super super
# If we can't read build we should also not have that # If we can't read build we should also not have that
......
...@@ -6,6 +6,7 @@ class BaseSerializer ...@@ -6,6 +6,7 @@ class BaseSerializer
def represent(resource, opts = {}) def represent(resource, opts = {})
self.class.entity_class self.class.entity_class
.represent(resource, opts.merge(request: @request)) .represent(resource, opts.merge(request: @request))
.as_json
end end
def self.entity(entity_class) def self.entity(entity_class)
......
class PipelineSerializer < BaseSerializer class PipelineSerializer < BaseSerializer
entity PipelineEntity
class InvalidResourceError < StandardError; end class InvalidResourceError < StandardError; end
include API::Helpers::Pagination include API::Helpers::Pagination
Struct.new('Pagination', :request, :response) Struct.new('Pagination', :request, :response)
entity PipelineEntity
def represent(resource, opts = {}) def represent(resource, opts = {})
if paginated? if paginated?
raise InvalidResourceError unless resource.respond_to?(:page) raise InvalidResourceError unless resource.respond_to?(:page)
......
module Labels
class PromoteService < BaseService
BATCH_SIZE = 1000
def execute(label)
return unless project.group &&
label.is_a?(ProjectLabel)
Label.transaction do
new_label = clone_label_to_group_label(label)
label_ids_for_merge(new_label).find_in_batches(batch_size: BATCH_SIZE) do |batched_ids|
update_issuables(new_label, batched_ids)
update_issue_board_lists(new_label, batched_ids)
update_priorities(new_label, batched_ids)
# Order is important, project labels need to be last
update_project_labels(batched_ids)
end
# We skipped validations during creation. Let's run them now, after deleting conflicting labels
raise ActiveRecord::RecordInvalid.new(new_label) unless new_label.valid?
new_label
end
end
private
def label_ids_for_merge(new_label)
LabelsFinder.
new(current_user, title: new_label.title, group_id: project.group.id).
execute(skip_authorization: true).
where.not(id: new_label).
select(:id) # Can't use pluck() to avoid object-creation because of the batching
end
def update_issuables(new_label, label_ids)
LabelLink.
where(label: label_ids).
update_all(label_id: new_label)
end
def update_issue_board_lists(new_label, label_ids)
List.
where(label: label_ids).
update_all(label_id: new_label)
end
def update_priorities(new_label, label_ids)
LabelPriority.
where(label: label_ids).
update_all(label_id: new_label)
end
def update_project_labels(label_ids)
Label.where(id: label_ids).delete_all
end
def clone_label_to_group_label(label)
params = label.attributes.slice('title', 'description', 'color')
# Since the title of the new label has to be the same as the previous labels
# and we're merging old labels in batches we'll skip validation to omit 2-step
# merge process and do it in one batch
# We'll be forcing validation at the end of the transaction to ensure everything
# was merged correctly
new_label = GroupLabel.new(params.merge(group: project.group))
new_label.save(validate: false)
new_label
end
end
end
module MergeRequests module MergeRequests
class BuildService < MergeRequests::BaseService class BuildService < MergeRequests::BaseService
def execute def execute
merge_request = MergeRequest.new(params) self.merge_request = MergeRequest.new(params)
# Set MR attributes
merge_request.can_be_created = true merge_request.can_be_created = true
merge_request.compare_commits = [] merge_request.compare_commits = []
merge_request.source_project = project unless merge_request.source_project merge_request.source_project = find_source_project
merge_request.target_project = find_target_project
merge_request.target_branch = find_target_branch
merge_request.target_project = nil unless can?(current_user, :read_project, merge_request.target_project) if branches_specified? && branches_valid?
compare_branches
assign_title_and_description
else
merge_request.can_be_created = false
end
merge_request.target_project ||= (project.forked_from_project || project) merge_request
merge_request.target_branch ||= merge_request.target_project.default_branch end
messages = validate_branches(merge_request) private
return build_failed(merge_request, messages) unless messages.empty?
compare = CompareService.new.execute( attr_accessor :merge_request
merge_request.source_project,
merge_request.source_branch,
merge_request.target_project,
merge_request.target_branch,
)
merge_request.compare_commits = compare.commits delegate :target_branch, :source_branch, :source_project, :target_project, :compare_commits, :wip_title, :description, :errors, to: :merge_request
merge_request.compare = compare
set_title_and_description(merge_request) def find_source_project
source_project || project
end end
private def find_target_project
return target_project if target_project.present? && can?(current_user, :read_project, target_project)
project.forked_from_project || project
end
def validate_branches(merge_request) def find_target_branch
messages = [] target_branch || target_project.default_branch
end
if merge_request.target_branch.blank? || merge_request.source_branch.blank? def branches_specified?
messages << params[:source_branch] && params[:target_branch]
if params[:source_branch] || params[:target_branch]
"You must select source and target branch"
end end
def branches_valid?
validate_branches
errors.blank?
end end
if merge_request.source_project == merge_request.target_project && def compare_branches
merge_request.target_branch == merge_request.source_branch compare = CompareService.new.execute(
source_project,
source_branch,
target_project,
target_branch
)
messages << 'You must select different branches' merge_request.compare_commits = compare.commits
merge_request.compare = compare
end
def validate_branches
add_error('You must select source and target branch') unless branches_present?
add_error('You must select different branches') if same_source_and_target?
add_error("Source branch \"#{source_branch}\" does not exist") unless source_branch_exists?
add_error("Target branch \"#{target_branch}\" does not exist") unless target_branch_exists?
end
def add_error(message)
errors.add(:base, message)
end
def branches_present?
target_branch.present? && source_branch.present?
end end
# See if source and target branches exist def same_source_and_target?
if merge_request.source_branch.present? && !merge_request.source_project.commit(merge_request.source_branch) source_project == target_project && target_branch == source_branch
messages << "Source branch \"#{merge_request.source_branch}\" does not exist"
end end
if merge_request.target_branch.present? && !merge_request.target_project.commit(merge_request.target_branch) def source_branch_exists?
messages << "Target branch \"#{merge_request.target_branch}\" does not exist" source_branch.blank? || source_project.commit(source_branch)
end end
messages def target_branch_exists?
target_branch.blank? || target_project.commit(target_branch)
end end
# When your branch name starts with an iid followed by a dash this pattern will be # When your branch name starts with an iid followed by a dash this pattern will be
...@@ -71,17 +97,17 @@ module MergeRequests ...@@ -71,17 +97,17 @@ module MergeRequests
# - Setting the title as 'Resolves "Emoji don't show up in commit title"' if there is # - Setting the title as 'Resolves "Emoji don't show up in commit title"' if there is
# more than one commit in the MR # more than one commit in the MR
# #
def set_title_and_description(merge_request) def assign_title_and_description
if match = merge_request.source_branch.match(/\A(\d+)-/) if match = source_branch.match(/\A(\d+)-/)
iid = match[1] iid = match[1]
end end
commits = merge_request.compare_commits commits = compare_commits
if commits && commits.count == 1 if commits && commits.count == 1
commit = commits.first commit = commits.first
merge_request.title = commit.title merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip) merge_request.description ||= commit.description.try(:strip)
elsif iid && issue = merge_request.target_project.get_issue(iid, current_user) elsif iid && issue = target_project.get_issue(iid, current_user)
case issue case issue
when Issue when Issue
merge_request.title = "Resolve \"#{issue.title}\"" merge_request.title = "Resolve \"#{issue.title}\""
...@@ -89,31 +115,20 @@ module MergeRequests ...@@ -89,31 +115,20 @@ module MergeRequests
merge_request.title = "Resolve #{issue.title}" merge_request.title = "Resolve #{issue.title}"
end end
else else
merge_request.title = merge_request.source_branch.titleize.humanize merge_request.title = source_branch.titleize.humanize
end end
if iid if iid
closes_issue = "Closes ##{iid}" closes_issue = "Closes ##{iid}"
if merge_request.description.present? if description.present?
merge_request.description += closes_issue.prepend("\n\n") merge_request.description += closes_issue.prepend("\n\n")
else else
merge_request.description = closes_issue merge_request.description = closes_issue
end end
end end
merge_request.title = merge_request.wip_title if commits.empty? merge_request.title = wip_title if commits.empty?
merge_request
end
def build_failed(merge_request, messages)
messages.compact.each do |message|
merge_request.errors.add(:base, message)
end
merge_request.compare_commits = []
merge_request.can_be_created = false
merge_request
end end
end end
end end
...@@ -365,7 +365,7 @@ class NotificationService ...@@ -365,7 +365,7 @@ class NotificationService
users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq) users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq)
users_with_project_setting = select_project_member_setting(project, users_with_project_level_global, users) users_with_project_setting = select_project_member_setting(project, users_with_project_level_global, users)
users_with_group_setting = select_group_member_setting(project, project_members, users_with_group_level_global, users) users_with_group_setting = select_group_member_setting(project.group, project_members, users_with_group_level_global, users)
User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a
end end
...@@ -415,8 +415,8 @@ class NotificationService ...@@ -415,8 +415,8 @@ class NotificationService
end end
# Build a list of users based on group notification settings # Build a list of users based on group notification settings
def select_group_member_setting(project, project_members, global_setting, users_global_level_watch) def select_group_member_setting(group, project_members, global_setting, users_global_level_watch)
uids = notification_settings_for(project, :watch) uids = notification_settings_for(group, :watch)
# Group setting is watch, add to users list if user is not project member # Group setting is watch, add to users list if user is not project member
users = [] users = []
...@@ -473,7 +473,7 @@ class NotificationService ...@@ -473,7 +473,7 @@ class NotificationService
setting = user.notification_settings_for(project) setting = user.notification_settings_for(project)
if !setting && project.group if project.group && (setting.nil? || setting.global?)
setting = user.notification_settings_for(project.group) setting = user.notification_settings_for(project.group)
end end
......
...@@ -9,7 +9,10 @@ module Search ...@@ -9,7 +9,10 @@ module Search
def execute def execute
group = Group.find_by(id: params[:group_id]) if params[:group_id].present? group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
projects = ProjectsFinder.new.execute(current_user) projects = ProjectsFinder.new.execute(current_user)
projects = projects.in_namespace(group.id) if group
if group
projects = projects.inside_path(group.full_path)
end
Gitlab::SearchResults.new(current_user, projects, params[:search]) Gitlab::SearchResults.new(current_user, projects, params[:search])
end end
......
...@@ -16,4 +16,4 @@ ...@@ -16,4 +16,4 @@
- if status.has_action? - if status.has_action?
= link_to status.action_path, class: 'ci-action-icon-wrapper js-ci-action-icon', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do = link_to status.action_path, class: 'ci-action-icon-wrapper js-ci-action-icon', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do
= icon(status.action_icon, class: status.action_class) = custom_icon(status.action_icon)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- subject = local_assigns.fetch(:subject) - subject = local_assigns.fetch(:subject)
- status = subject.detailed_status(current_user) - status = subject.detailed_status(current_user)
- klass = "ci-status-icon ci-status-icon-#{status.group}" - klass = "ci-status-icon ci-status-icon-#{status.group} js-ci-status-icon-#{status.group}"
- tooltip = "#{subject.name} - #{status.label}" - tooltip = "#{subject.name} - #{status.label}"
- if status.has_details? - if status.has_details?
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
- if status.has_action? - if status.has_action?
= link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do = link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do
%i.ci-action-icon-wrapper %i.ci-action-icon-wrapper{ class: "js-#{status.action_icon.dasherize}" }
= icon(status.action_icon, class: status.action_class) = custom_icon(status.action_icon)
...@@ -15,6 +15,4 @@ ...@@ -15,6 +15,4 @@
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue" = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
= render 'shared/issuable/filter', type: :issues = render 'shared/issuable/filter', type: :issues
= render 'shared/issues'
.prepend-top-default
= render 'shared/issues'
...@@ -7,6 +7,4 @@ ...@@ -7,6 +7,4 @@
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request" = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
= render 'shared/merge_requests'
.prepend-top-default
= render 'shared/merge_requests'
...@@ -11,8 +11,11 @@ ...@@ -11,8 +11,11 @@
= link_to_author(todo) = link_to_author(todo)
- else - else
(removed) (removed)
%span.todo-label
%span.action-name
= todo_action_name(todo) = todo_action_name(todo)
%span.todo-label
- if todo.target - if todo.target
= todo_target_link(todo) = todo_target_link(todo)
- else - else
......
...@@ -67,21 +67,17 @@ ...@@ -67,21 +67,17 @@
= sort_title_oldest_created = sort_title_oldest_created
.prepend-top-default .js-todos-all
- if @todos.any? - if @todos.any?
.js-todos-options{ data: {per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages} } .js-todos-options{ data: {per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages} }
- @todos.group_by(&:project).each do |group| .panel.panel-default.panel-small.panel-without-border
.panel.panel-default.panel-small
- project = group[0]
.panel-heading
= link_to project.name_with_namespace, namespace_project_path(project.namespace, project)
%ul.content-list.todos-list %ul.content-list.todos-list
= render group[1] = render @todos
= paginate @todos, theme: "gitlab" = paginate @todos, theme: "gitlab"
- elsif current_user.todos.any? - elsif current_user.todos.any?
.todos-all-done .todos-all-done
= render "shared/empty_states/todos_all_done.svg" = render "shared/empty_states/icons/todos_all_done.svg"
- if todos_filter_empty? - if todos_filter_empty?
%h4.text-center %h4.text-center
= Gitlab.config.gitlab.no_todos_messages.sample = Gitlab.config.gitlab.no_todos_messages.sample
...@@ -98,7 +94,7 @@ ...@@ -98,7 +94,7 @@
- else - else
.todos-empty .todos-empty
.todos-empty-hero .todos-empty-hero
= render "shared/empty_states/todos_empty.svg" = render "shared/empty_states/icons/todos_empty.svg"
.todos-empty-content .todos-empty-content
%h4 %h4
Todos let you see what you should do next. Todos let you see what you should do next.
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
- if current_user - if current_user
To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page. To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
.prepend-top-default
= render 'shared/issues' = render 'shared/issues'
- else - else
= render 'shared/empty_states/issues', project_select_button: true = render 'shared/empty_states/issues', project_select_button: true
...@@ -15,5 +15,4 @@ ...@@ -15,5 +15,4 @@
- if current_user - if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
.prepend-top-default = render 'shared/merge_requests'
= render 'shared/merge_requests'
- page_title "Pipelines", "#{@commit.title} (#{@commit.short_id})", "Commits" - page_title 'Pipelines', "#{@commit.title} (#{@commit.short_id})", 'Commits'
= render "commit_box" = render 'commit_box'
= render 'ci_menu'
= render "ci_menu" = render 'pipelines_list', pipelines: @pipelines
= render "pipelines_list", pipelines: @commit.pipelines.order(id: :desc)
...@@ -183,6 +183,8 @@ ...@@ -183,6 +183,8 @@
%li Build traces and artifacts %li Build traces and artifacts
%li LFS objects %li LFS objects
%li Container registry images %li Container registry images
%li CI variables
%li Any encrypted tokens
%hr %hr
- if can? current_user, :archive_project, @project - if can? current_user, :archive_project, @project
.row.prepend-top-default .row.prepend-top-default
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
= note_count = note_count
.issue-info .issue-info
#{issue.to_reference} &middot; #{issuable_reference(issue)} &middot;
opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
by #{link_to_member(@project, issue.author, avatar: false)} by #{link_to_member(@project, issue.author, avatar: false)}
- if issue.milestone - if issue.milestone
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
// This element is filled in using JavaScript. // This element is filled in using JavaScript.
.content-block.content-block-small .content-block.content-block-small
= render 'new_branch' = render 'new_branch' unless @issue.confidential?
= render 'award_emoji/awards_block', awardable: @issue, inline: true = render 'award_emoji/awards_block', awardable: @issue, inline: true
%section.issuable-discussion %section.issuable-discussion
......
- if @labels.empty?
$('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
- hide_class = '' - hide_class = ''
= render "projects/issues/head" = render "projects/issues/head"
%div{ class: container_class } - if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class }
.top-area.adjust .top-area.adjust
.nav-text .nav-text
Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.
...@@ -20,20 +21,17 @@ ...@@ -20,20 +21,17 @@
.prioritized-labels{ class: ('hide' if hide) } .prioritized-labels{ class: ('hide' if hide) }
%h5 Prioritized Labels %h5 Prioritized Labels
%ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) } %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
%p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" }
= render 'shared/empty_states/priority_labels'
- if @prioritized_labels.present? - if @prioritized_labels.present?
= render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label
- if @labels.present?
.other-labels .other-labels
- if can?(current_user, :admin_label, @project) - if can?(current_user, :admin_label, @project)
%h5{ class: ('hide' if hide) } Other Labels %h5{ class: ('hide' if hide) } Other Labels
%ul.content-list.manage-labels-list.js-other-labels %ul.content-list.manage-labels-list.js-other-labels
- if @labels.present?
= render partial: 'shared/label', subject: @project, collection: @labels, as: :label = render partial: 'shared/label', subject: @project, collection: @labels, as: :label
= paginate @labels, theme: 'gitlab' = paginate @labels, theme: 'gitlab'
- if @labels.blank? - else
.nothing-here-block = render 'shared/empty_states/labels'
- if can?(current_user, :admin_label, @project)
Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}.
- else
No labels created
- if @teams_error_message
= content_for :flash_message do
.alert.alert-danger= @teams_error_message
%p %p
You aren’t a member of any team on the Mattermost instance at You aren’t a member of any team on the Mattermost instance at
%strong= Gitlab.config.mattermost.host %strong= Gitlab.config.mattermost.host
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
= note_count = note_count
.merge-request-info .merge-request-info
#{merge_request.to_reference} &middot; #{issuable_reference(merge_request)} &middot;
opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
by #{link_to_member(@project, merge_request.author, avatar: false)} by #{link_to_member(@project, merge_request.author, avatar: false)}
- if merge_request.target_project.default_branch != merge_request.target_branch - if merge_request.target_project.default_branch != merge_request.target_branch
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= f.label :import_url, class: 'control-label' do = f.label :import_url, class: 'control-label' do
%span Git repository URL %span Git repository URL
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true = f.text_field :import_url, autocomplete: 'off', class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true
.well.prepend-top-20 .well.prepend-top-20
%ul %ul
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
%li %li
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination. The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination.
%li %li
To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}. To migrate an SVN repository, check out #{link_to "this document", help_page_path('workflow/importing/migrating_from_svn')}.
- if @issues.to_a.any? - if @issues.to_a.any?
- @issues.group_by(&:project).each do |group| .panel.panel-default.panel-small.panel-without-border
.panel.panel-default.panel-small
- project = group[0]
.panel-heading
= link_to project.name_with_namespace, namespace_project_issues_path(project.namespace, project)
- if can?(current_user, :create_issue, project)
.pull-right
= link_to 'New issue', new_namespace_project_issue_path(project.namespace, project)
%ul.content-list.issues-list %ul.content-list.issues-list
- group[1].each do |issue| = render partial: 'projects/issues/issue', collection: @issues
= render 'projects/issues/issue', issue: issue
= paginate @issues, theme: "gitlab" = paginate @issues, theme: "gitlab"
- else - else
= render 'shared/empty_states/issues' = render 'shared/empty_states/issues'
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
%li %li
= link_to 'Edit', edit_label_path(label) = link_to 'Edit', edit_label_path(label)
%li %li
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, remote: true, data: {confirm: 'Remove this label? Are you sure?'} = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'}
.pull-right.hidden-xs.hidden-sm.hidden-md .pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
...@@ -66,11 +66,15 @@ ...@@ -66,11 +66,15 @@
%a.js-subscribe-button{ data: { url: toggle_subscription_group_label_path(label.group, label) } } %a.js-subscribe-button{ data: { url: toggle_subscription_group_label_path(label.group, label) } }
Group level Group level
- if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_group, label.project.group)
= link_to promote_namespace_project_label_path(label.project.namespace, label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do
%span.sr-only Promote to Group
= icon('level-up')
- if can?(current_user, :admin_label, label) - if can?(current_user, :admin_label, label)
= link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do = link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do
%span.sr-only Edit %span.sr-only Edit
= icon('pencil-square-o') = icon('pencil-square-o')
= link_to destroy_label_path(label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, remote: true, data: {confirm: label_deletion_confirm_text(label), toggle: "tooltip"} do = link_to destroy_label_path(label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, data: {confirm: label_deletion_confirm_text(label), toggle: "tooltip"} do
%span.sr-only Delete %span.sr-only Delete
= icon('trash-o') = icon('trash-o')
......
- if @merge_requests.to_a.any? - if @merge_requests.to_a.any?
- @merge_requests.group_by(&:target_project).each do |group| .panel.panel-default.panel-small.panel-without-border
.panel.panel-default.panel-small
- project = group[0]
.panel-heading
= link_to project.name_with_namespace, namespace_project_merge_requests_path(project.namespace, project)
- if can?(current_user, :create_merge_request, project)
.pull-right
= link_to 'New merge request', new_namespace_project_merge_request_path(project.namespace, project)
%ul.content-list.mr-list %ul.content-list.mr-list
- group[1].each do |merge_request| = render partial: 'projects/merge_requests/merge_request', collection: @merge_requests
= render 'projects/merge_requests/merge_request', merge_request: merge_request
= paginate @merge_requests, theme: "gitlab" = paginate @merge_requests, theme: "gitlab"
- else - else
......
- if outdated_browser? - if outdated_browser?
- link = "https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/requirements.md#supported-web-browsers"
.browser-alert .browser-alert
GitLab may not work properly because you are using an outdated web browser. GitLab may not work properly because you are using an outdated web browser.
%br %br
Please install a Please install a
= link_to 'supported web browser', link = link_to 'supported web browser', help_page_url('install/requirements', anchor: 'supported-web-browsers')
for a better experience. for a better experience.
.row.empty-state.labels
.pull-right.col-xs-12.col-sm-6
.svg-content
= render 'shared/empty_states/icons/labels.svg'
.col-xs-12.col-sm-6
.text-content
%h4 Labels can be applied to issues and merge requests to categorize them.
%p You can also star label to make it a priority label.
- if can?(current_user, :admin_label, @project)
= link_to 'New label', new_namespace_project_label_path(@project.namespace, @project), class: 'btn btn-new', title: 'New label', id: 'new_label_link'
= link_to 'Generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link'
.text-center
= render 'shared/empty_states/icons/priority_labels.svg'
%p Star labels to start sorting by priority
<svg xmlns="http://www.w3.org/2000/svg" viewBox="787 240 386 274" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><circle id="a" cx="37" cy="107" r="8"/><mask id="e" width="16" height="16" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><circle id="b" cx="37" cy="75" r="8"/><mask id="f" width="16" height="16" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><circle id="c" cx="42" cy="93" r="8"/><mask id="g" width="16" height="16" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask><circle id="d" cx="43" cy="75" r="8"/><mask id="h" width="16" height="16" x="0" y="0" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(791 244)"><g transform="rotate(30 49.554 229.722)"><rect width="74" height="124" x="8.6" y="95.9" fill="#FAFAFA" rx="8"/><rect width="74" height="124" y="87" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="8"/><circle cx="26.5" cy="178.5" r="3.5" fill="#FC8A51"/><circle cx="47.5" cy="178.5" r="3.5" fill="#FC8A51"/><rect width="50" height="4" x="12" y="127" fill="#E5E5E5" rx="2"/><rect width="38" height="4" x="18" y="139" fill="#E5E5E5" rx="2"/><use stroke="#E5E5E5" stroke-width="8" mask="url(#e)" stroke-linecap="round" xlink:href="#a"/><path stroke="#EEE" stroke-width="4" d="M37.3 107S10.5 18.3 81 .6" stroke-linecap="round"/><path fill="#FDE5D8" d="M31 189c0 3.3 2.7 6 6 6s6-2.7 6-6"/></g><g transform="translate(105 47)"><rect width="74" height="124" y="64" fill="#FAFAFA" rx="8"/><rect width="74" height="124" y="55" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="8"/><rect width="50" height="4" x="12" y="95" fill="#E5E5E5" rx="2"/><rect width="38" height="4" x="18" y="107" fill="#E5E5E5" rx="2"/><use stroke="#E5E5E5" stroke-width="8" mask="url(#f)" stroke-linecap="round" xlink:href="#b"/><path fill="#B5A7DD" d="M56 149.7c-.6-1-.2-2 .7-2.7l1.8-1c1-.6 2-.2 2.7.7.5 1 .2 2.2-.7 2.8l-1.8 1c-1 .5-2 .2-2.7-.8zm-37.8 0c.5-1 .2-2-.7-2.7l-1.8-1c-1-.6-2-.2-2.7.7-.6 1-.2 2.2.7 2.8l1.8 1c1 .5 2 .2 2.7-.8zM33 151h9v4h-9v-4z"/><path fill="#6B4FBB" d="M59 153c0-5.5-4.6-10-10-10-5.7 0-10 4.5-10 10s4.3 10 10 10c5.4 0 10-4.5 10-10zm-16 0c0-3.3 2.6-6 6-6 3.2 0 6 2.7 6 6s-2.8 6-6 6c-3.4 0-6-2.7-6-6zM35 153c0-5.5-4.6-10-10-10-5.7 0-10 4.5-10 10s4.3 10 10 10c5.4 0 10-4.5 10-10zm-16 0c0-3.3 2.6-6 6-6 3.2 0 6 2.7 6 6s-2.8 6-6 6c-3.4 0-6-2.7-6-6z"/><path stroke="#EEE" stroke-width="4" d="M37 75S30 0 80 0" stroke-linecap="round"/></g><g transform="rotate(15 -82.507 752.644)"><rect width="74" height="124" x="14.6" y="81.8" fill="#FAFAFA" rx="8"/><rect width="74" height="124" x="5" y="73" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="8"/><path fill="#FDE5D8" d="M41 147c0-1 1-2 2-2s2 1 2 2v3c0 1-1 2-2 2s-2-1-2-2v-3zm16.8 6.2c.8-.7 2-.6 2.8.3.7.8.5 2-.3 2.8L58 158c-1 .8-2.2.7-3 0-.6-1-.4-2.3.4-3l2.4-1.8zm-32 3c-1-.6-1-2-.4-2.7.7-1 2-1 2.8-.3l2.4 1.8c.8.7 1 2 .3 3-.8.7-2 1-3 0l-2.3-1.7z"/><rect width="2" height="7" x="39" y="168" fill="#FC8A51" rx="1"/><rect width="2" height="7" x="45" y="168" fill="#FC8A51" rx="1"/><circle cx="40" cy="169" r="2" fill="#FC8A51"/><circle cx="46" cy="169" r="2" fill="#FC8A51"/><rect width="22" height="18" x="32" y="158" stroke="#FC8A51" stroke-width="4" rx="8"/><rect width="34" height="5" x="26" y="174" fill="#FC8A51" rx="2.5"/><rect width="50" height="4" x="17" y="113" fill="#E5E5E5" rx="2"/><rect width="38" height="4" x="23" y="125" fill="#E5E5E5" rx="2"/><use stroke="#E5E5E5" stroke-width="8" mask="url(#g)" stroke-linecap="round" xlink:href="#c"/><path stroke="#EEE" stroke-width="4" d="M42 93S50 0 0 0" stroke-linecap="round"/></g><g transform="rotate(-15 276.18 -697.744)"><rect width="74" height="124" x="18.7" y="65.6" fill="#FAFAFA" rx="8"/><rect width="74" height="124" x="6" y="55" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="8"/><g transform="translate(25 129)"><path stroke="#B5A7DD" stroke-width="4" d="M32 14c0-7.7-6.3-14-14-14S4 6.3 4 14" stroke-linecap="round"/><path stroke="#B5A7DD" stroke-width="2" d="M33 15v13c0 4.4-3.6 8-8 8" stroke-linecap="round"/><rect width="7" height="4" x="20" y="34" fill="#6B4FBB" rx="2"/><rect width="7" height="13" y="15" fill="#FFF" stroke="#6B4FBB" stroke-width="3" stroke-linejoin="round" rx="3.5"/><rect width="7" height="13" x="29" y="15" fill="#FFF" stroke="#6B4FBB" stroke-width="3" stroke-linejoin="round" transform="matrix(-1 0 0 1 65 0)" rx="3.5"/></g><rect width="50" height="4" x="18" y="95" fill="#E5E5E5" rx="2"/><rect width="38" height="4" x="24" y="107" fill="#E5E5E5" rx="2"/><use stroke="#E5E5E5" stroke-width="8" mask="url(#h)" stroke-linecap="round" xlink:href="#d"/><path stroke="#EEE" stroke-width="4" d="M43 75S50 0 0 0" stroke-linecap="round"/></g><circle cx="193" cy="47" r="12" fill="#FFF" stroke="#FDE5D8" stroke-width="4"/><circle cx="193" cy="47" r="5" fill="#FFF" stroke="#FDE5D8" stroke-width="4"/><g opacity=".2"><path fill="#FC8A51" d="M30.7 254.8l-2.6 1c-1 .5-1.7 0-1.7-1v-3l-1-2.7c-.4-1 .2-1.7 1.2-1.7h3l2.6-1c1.2-.4 2 .2 2 1.2l-.2 3 1 2.6c.5 1.2 0 2-1 2l-3-.2zM374.7 133.8l-2.6 1c-1 .5-1.7 0-1.7-1v-3l-1-2.7c-.4-1 .2-1.7 1.2-1.7h3l2.6-1c1.2-.4 2 .2 2 1.2l-.2 3 1 2.6c.5 1.2 0 2-1 2l-3-.2zM5.6 95H1.8c-1.3.2-2-.8-1.4-2l1.4-3.4-.2-3.8c0-1.3 1-2 2-1.4l3.6 1.4 3.7-.2c1.2 0 2 1 1.4 2L11 91.3V95c.2 1.2-.8 2-2 1.4L5.6 95z"/><path fill="#6B4FBB" d="M308.8 62l-2-2.3c-.7-.8-.5-1.7.6-2l2.8-1 2-2c1-.6 1.8-.4 2.2.7l.8 2.8 2 2c.8 1 .5 1.8-.5 2.2l-2.8.8-2.3 2c-.8.8-1.7.5-2-.5l-1-2.8zM318 226.6h-3c-1-.2-1.4-1-1-2l1.4-2.5v-3c.2-1 1-1.4 2-1l2.6 1.4h3c1 .2 1.5 1 1 2l-1.4 2.6v3c-.2 1-1 1.5-2 1l-2.5-1.4zM121.8 8l-2-2.3c-.7-.8-.5-1.7.6-2l2.8-1 2-2c1-.6 1.8-.4 2.2.7l.8 2.8 2 2c.8 1 .5 1.8-.5 2.2l-2.8.8-2.3 2c-.8.8-1.7.5-2-.5l-1-2.8z"/></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="68" viewBox="181 0 116 68"><g fill="none" fill-rule="evenodd" transform="translate(182)"><rect width="78" height="34" x="37" y="34" fill="#FAFAFA" rx="3"/><rect width="78" height="34" x="31" y="28" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="3"/><path fill="#FFF" stroke="#FC6D26" stroke-width="3" d="M34 35.8c-.6 0-1.4 0-1.8.4L29 38.8c-1 .7-1.7.4-2-.7l-.6-4c0-.5-.5-1.2-1-1.5L22 30.2c-1-.6-1-1.5 0-2l3.7-2c.5-.2 1-.8 1.2-1.3l1-4.2c.3-1 1-1.3 2-.5l3 3c.3.3 1 .6 1.6.6l4.2-.3c1 0 1.5.7 1 1.7L38 29c-.3.6-.3 1.4 0 2l1.3 3.8c.4 1 0 1.8-1.2 1.6l-4-.6z" stroke-linecap="round"/><path fill="#FDE5D8" d="M51.6 14.3c-.2-.2-.8-.4-1-.3l-2.8.5c-.7 0-1-.4-.8-1l1-2.8V9.5L46.6 7c-.3-.7 0-1.2.8-1h2.7c.3 0 .8-.2 1-.5l2-2c.6-.5 1-.4 1.3.3l.7 2.8c0 .3.4.8.7 1l2.3 1.2c.7.3.7 1 0 1.3l-2.2 1.7-.6 1-.4 3c-.2.6-.7.8-1.3.4l-2-1.7zM5.4 43.2c-.2-.2-.5-.2-.7-.2l-1.8.3c-.6 0-1-.2-.7-.7l.7-1.8V40l-1-1.7c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L6.5 36c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2zM10.4 9.2C10.2 9 10 9 9.7 9L8 9.3c-.6 0-1-.2-.7-.7L8 6.8V6L7 4.3c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L11.5 2c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2z"/><rect width="52" height="4" x="43" y="38" fill="#EEE" rx="2"/><rect width="36" height="4" x="43" y="48" fill="#EEE" rx="2"/></g></svg>
<svg width="30px" height="30px" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"><path d="M19.25,14.9765625 C19.25,14.1380166 19.0234398,13.3697952 18.5703125,12.671875 L12.6796875,18.5546875 C13.3932327,19.0182315 14.1666625,19.25 15,19.25 C15.5781279,19.25 16.1289036,19.1367199 16.6523438,18.9101562 C17.1757839,18.6835926 17.6276023,18.3802102 18.0078125,18 C18.3880227,17.6197898 18.690103,17.1653672 18.9140625,16.6367188 C19.138022,16.1080703 19.25,15.5546904 19.25,14.9765625 Z M11.4453125,17.3125 L17.34375,11.421875 C16.6406215,10.9479143 15.8593793,10.7109375 15,10.7109375 C14.2291628,10.7109375 13.5182324,10.9010398 12.8671875,11.28125 C12.2161426,11.6614602 11.7005227,12.1796842 11.3203125,12.8359375 C10.9401023,13.4921908 10.75,14.2057253 10.75,14.9765625 C10.75,15.8203167 10.9817685,16.5989548 11.4453125,17.3125 Z M21,14.9765625 C21,15.7942749 20.8411474,16.5755171 20.5234375,17.3203125 C20.2057276,18.0651079 19.7799506,18.7057265 19.2460938,19.2421875 C18.7122369,19.7786485 18.0742225,20.2057276 17.3320312,20.5234375 C16.58984,20.8411474 15.8125041,21 15,21 C14.1874959,21 13.41016,20.8411474 12.6679688,20.5234375 C11.9257775,20.2057276 11.2877631,19.7786485 10.7539062,19.2421875 C10.2200494,18.7057265 9.79427242,18.0651079 9.4765625,17.3203125 C9.15885258,16.5755171 9,15.7942749 9,14.9765625 C9,14.1588501 9.15885258,13.37891 9.4765625,12.6367188 C9.79427242,11.8945275 10.2200494,11.255211 10.7539062,10.71875 C11.2877631,10.182289 11.9257775,9.75520992 12.6679688,9.4375 C13.41016,9.11979008 14.1874959,8.9609375 15,8.9609375 C15.8125041,8.9609375 16.58984,9.11979008 17.3320312,9.4375 C18.0742225,9.75520992 18.7122369,10.182289 19.2460938,10.71875 C19.7799506,11.255211 20.2057276,11.8945275 20.5234375,12.6367188 C20.8411474,13.37891 21,14.1588501 21,14.9765625 Z"></path></svg>
<svg width="30px" height="30px" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"><path d="M21.5401786,15.2320328 L11.90625,20.5858274 C11.7950143,20.6486998 11.6994982,20.6559541 11.6196987,20.6075908 C11.5398992,20.5592275 11.5,20.4721748 11.5,20.3464301 L11.5,9.66785867 C11.5,9.54211399 11.5398992,9.45506129 11.6196987,9.40669795 C11.6994982,9.35833462 11.7950143,9.36558901 11.90625,9.42846135 L21.5401786,14.782256 C21.6514142,14.8451283 21.7070312,14.9200904 21.7070312,15.0071444 C21.7070312,15.0941984 21.6514142,15.1691604 21.5401786,15.2320328 Z"></path></svg>
<svg width="30px" height="30px" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"><path d="M20.6114971,16.0821413 C20.6114971,16.106323 20.6090789,16.1232499 20.6042426,16.1329226 C20.2947172,17.42906 19.6466582,18.4797378 18.6600462,19.2849873 C17.6734341,20.0902369 16.5175677,20.4928556 15.1924122,20.4928556 C14.4863075,20.4928556 13.8031856,20.3598584 13.1430261,20.0938601 C12.4828665,19.8278617 11.8940517,19.4482152 11.376564,18.9549092 L10.4407381,19.8907351 C10.3488478,19.9826254 10.2400319,20.0285699 10.1142872,20.0285699 C9.98854256,20.0285699 9.87972669,19.9826254 9.78783635,19.8907351 C9.69594601,19.7988447 9.65000153,19.6900289 9.65000153,19.5642842 L9.65000153,16.3142842 C9.65000153,16.1885395 9.69594601,16.0797236 9.78783635,15.9878333 C9.87972669,15.895943 9.98854256,15.8499985 10.1142872,15.8499985 L13.3642872,15.8499985 C13.4900319,15.8499985 13.5988478,15.895943 13.6907381,15.9878333 C13.7826285,16.0797236 13.828573,16.1885395 13.828573,16.3142842 C13.828573,16.4400289 13.7826285,16.5488447 13.6907381,16.6407351 L12.6968765,17.6345967 C13.0402562,17.9537947 13.4295752,18.200444 13.8648453,18.374552 C14.3001153,18.5486601 14.7523057,18.6357128 15.2214301,18.6357128 C15.8694988,18.6357128 16.4740315,18.4785343 17.0350462,18.1641726 C17.5960609,17.8498109 18.0458332,17.4169655 18.3843765,16.8656235 C18.4375762,16.7834058 18.5657371,16.5004845 18.7688631,16.0168512 C18.8075538,15.9056155 18.8800977,15.8499985 18.9864971,15.8499985 L20.3793542,15.8499985 C20.4422265,15.8499985 20.4966345,15.8729707 20.5425797,15.9189159 C20.5885248,15.9648611 20.6114971,16.019269 20.6114971,16.0821413 Z M20.7928587,10.2785699 L20.7928587,13.5285699 C20.7928587,13.6543146 20.7469142,13.7631305 20.6550238,13.8550208 C20.5631335,13.9469111 20.4543176,13.9928556 20.328573,13.9928556 L17.078573,13.9928556 C16.9528283,13.9928556 16.8440124,13.9469111 16.7521221,13.8550208 C16.6602317,13.7631305 16.6142872,13.6543146 16.6142872,13.5285699 C16.6142872,13.4028252 16.6602317,13.2940094 16.7521221,13.202119 L17.7532381,12.2010029 C17.0374607,11.5384252 16.1935332,11.2071413 15.2214301,11.2071413 C14.5733614,11.2071413 13.9688287,11.3643198 13.407814,11.6786815 C12.8467993,11.9930432 12.397027,12.4258886 12.0584837,12.9772306 C12.005284,13.0594483 11.8771231,13.3423696 11.6739971,13.8260029 C11.6353064,13.9372386 11.5627625,13.9928556 11.4563631,13.9928556 L10.0127247,13.9928556 C9.9498524,13.9928556 9.89544446,13.9698834 9.84949929,13.9239382 C9.80355412,13.877993 9.78058188,13.8235851 9.78058188,13.7607128 L9.78058188,13.7099315 C10.0949436,12.4137941 10.7478388,11.3631163 11.7392872,10.5578668 C12.7307356,9.75261722 13.8914383,9.34999847 15.2214301,9.34999847 C15.9275348,9.34999847 16.6142839,9.48420472 17.281698,9.75262124 C17.949112,10.0210378 18.541554,10.3994752 19.0590417,10.8879449 L20.0021221,9.95211901 C20.0940124,9.86022867 20.2028283,9.81428419 20.328573,9.81428419 C20.4543176,9.81428419 20.5631335,9.86022867 20.6550238,9.95211901 C20.7469142,10.0440094 20.7928587,10.1528252 20.7928587,10.2785699 Z"></path></svg>
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M20.1357204,10.2985704 L20.1357204,19.7271418 C20.1357204,19.8432138 20.0933101,19.9436592 20.0084882,20.0284811 C19.9236664,20.1133029 19.823221,20.1557132 19.707149,20.1557132 L10.2785775,20.1557132 C10.1625055,20.1557132 10.0620601,20.1133029 9.97723825,20.0284811 C9.89241639,19.9436592 9.8500061,19.8432138 9.8500061,19.7271418 L9.8500061,10.2985704 C9.8500061,10.1824984 9.89241639,10.0820529 9.97723825,9.99723107 C10.0620601,9.91240922 10.1625055,9.86999893 10.2785775,9.86999893 L19.707149,9.86999893 C19.823221,9.86999893 19.9236664,9.91240922 20.0084882,9.99723107 C20.0933101,10.0820529 20.1357204,10.1824984 20.1357204,10.2985704 Z"></path></svg>
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
%p %p
Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out
= succeed "." do = succeed "." do
%a{ href: "http://docs.gitlab.com/ce/workflow/notifications.html", target: "_blank" } notification emails %a{ href: help_page_path('workflow/notifications'), target: "_blank" } notification emails
.col-lg-8 .col-lg-8
- NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index| - NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index|
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]" - field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
......
---
title: Added labels empty state
merge_request: 7443
author:
---
title: Don't group issues by project on group-level and dashboard issue indexes.
merge_request: 8111
author: Bernardo Castro
---
title: Fix disable storing of sensitive information when importing a new repo
merge_request: 8885
author: Bernard Pietraga
---
title: Refactor MergeRequests::BuildService
merge_request: 8462
author: Rydkin Maxim
---
title: Remove flash warning from login page
merge_request: 8864
author: Gerald J. Padilla
---
title: Convert pipeline action icons to svg to have them propperly positioned
merge_request:
author:
---
title: Avoid repeated dashes in $CI_ENVIRONMENT_SLUG
merge_request: 8638
author:
---
title: Update and pin the `jwt` gem to ~> 1.5.6
merge_request:
author:
---
title: Fix notifications when set at group level
merge_request: 6813
author: Alexandre Maia
---
title: Cop for gem fetched from a git source
merge_request: 8856
author: Adam Pahlevi
---
title: Adds documentation for how to use Vue.js
merge_request: 8866
author:
---
title: Add read-only full_path and full_name attributes to Group API
merge_request: 8827
author:
---
title: Remove new branch button for confidential issues
merge_request:
author:
---
title: Improve build policy and access abilities
merge_request: 8711
author:
---
title: Ignore encrypted attributes in Import/Export
merge_request:
author:
---
title: Ensure autogenerated title does not cause failing spec
merge_request: 8963
author: brian m. carlson
---
title: Changed composer installer script in the CI PHP example doc
merge_request: 4342
author: Jeffrey Cafferata
---
title: Resets assignee dropdown when sidebar is open
merge_request:
author:
---
title: "Project labels can now be promoted to group labels"
merge_request: 7242
author: Olaf Tomalka
---
title: Add project ID index to `project_authorizations` table to optimize queries
merge_request:
author:
---
title: Improve performance of slash commands
merge_request: 8876
author:
...@@ -220,6 +220,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -220,6 +220,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
member do member do
post :promote
post :toggle_subscription post :toggle_subscription
delete :remove_priority delete :remove_priority
end end
......
...@@ -8,8 +8,9 @@ class AddEnvironmentSlug < ActiveRecord::Migration ...@@ -8,8 +8,9 @@ class AddEnvironmentSlug < ActiveRecord::Migration
DOWNTIME_REASON = 'Adding NOT NULL column environments.slug with dependent data' DOWNTIME_REASON = 'Adding NOT NULL column environments.slug with dependent data'
# Used to generate random suffixes for the slug # Used to generate random suffixes for the slug
LETTERS = 'a'..'z'
NUMBERS = '0'..'9' NUMBERS = '0'..'9'
SUFFIX_CHARS = ('a'..'z').to_a + NUMBERS.to_a SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a
def up def up
environments = Arel::Table.new(:environments) environments = Arel::Table.new(:environments)
...@@ -39,17 +40,24 @@ class AddEnvironmentSlug < ActiveRecord::Migration ...@@ -39,17 +40,24 @@ class AddEnvironmentSlug < ActiveRecord::Migration
slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-') slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
# Must start with a letter # Must start with a letter
slugified = "env-" + slugified if NUMBERS.cover?(slugified[0]) slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0])
# Repeated dashes are invalid (OpenShift limitation)
slugified.gsub!(/\-+/, '-')
# Maximum length: 24 characters (OpenShift limitation) # Maximum length: 24 characters (OpenShift limitation)
slugified = slugified[0..23] slugified = slugified[0..23]
# Cannot end with a "-" character (Kubernetes label limitation) # Cannot end with a dash (Kubernetes label limitation)
slugified = slugified[0..-2] if slugified[-1] == "-" slugified.chop! if slugified.end_with?('-')
# Add a random suffix, shortening the current string if necessary, if it # Add a random suffix, shortening the current string if necessary, if it
# has been slugified. This ensures uniqueness. # has been slugified. This ensures uniqueness.
slugified = slugified[0..16] + "-" + random_suffix if slugified != name if slugified != name
slugified = slugified[0..16]
slugified << '-' unless slugified.end_with?('-')
slugified << random_suffix
end
slugified slugified
end end
......
class AddIndexToProjectAuthorizations < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index(:project_authorizations, :project_id)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170121130655) do ActiveRecord::Schema.define(version: 20170130204620) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -874,6 +874,7 @@ ActiveRecord::Schema.define(version: 20170121130655) do ...@@ -874,6 +874,7 @@ ActiveRecord::Schema.define(version: 20170121130655) do
t.integer "access_level" t.integer "access_level"
end end
add_index "project_authorizations", ["project_id"], name: "index_project_authorizations_on_project_id", using: :btree
add_index "project_authorizations", ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree add_index "project_authorizations", ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree
create_table "project_features", force: :cascade do |t| create_table "project_features", force: :cascade do |t|
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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