Commit 1b3de2f5 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents 82d20621 9b137533
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-phantomjs-2.1-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-phantomjs-2.1-node-8.x-yarn-1.0-postgresql-9.6"
.default-cache: &default-cache .default-cache: &default-cache
key: "ruby-233-with-yarn" key: "ruby-233-with-yarn"
...@@ -198,6 +198,9 @@ review-docs-deploy: ...@@ -198,6 +198,9 @@ review-docs-deploy:
stage: build stage: build
environment: environment:
name: review-docs/$CI_COMMIT_REF_NAME name: review-docs/$CI_COMMIT_REF_NAME
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
url: http://$CI_COMMIT_REF_SLUG-built-from-ce-ee.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup on_stop: review-docs-cleanup
script: script:
- gem install gitlab --no-doc - gem install gitlab --no-doc
...@@ -530,7 +533,7 @@ karma: ...@@ -530,7 +533,7 @@ karma:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache <<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-60.0-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-61.0-node-8.x-yarn-1.0-postgresql-9.6"
stage: test stage: test
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
......
...@@ -287,7 +287,10 @@ might be edited to make them small and simple. ...@@ -287,7 +287,10 @@ might be edited to make them small and simple.
Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker. Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
For changes in the interface, it can be helpful to create a mockup first. For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
If you want to create something yourself, consider opening an issue first to If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab. discuss whether it is interesting to include this in GitLab.
......
...@@ -382,7 +382,7 @@ GEM ...@@ -382,7 +382,7 @@ GEM
rake rake
grape_logging (1.7.0) grape_logging (1.7.0)
grape grape
grpc (1.4.5) grpc (1.6.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
gssapi (1.2.0) gssapi (1.2.0)
......
...@@ -7,6 +7,7 @@ class DeleteModal { ...@@ -7,6 +7,7 @@ class DeleteModal {
this.$branchName = $('.js-branch-name', this.$modal); this.$branchName = $('.js-branch-name', this.$modal);
this.$confirmInput = $('.js-delete-branch-input', this.$modal); this.$confirmInput = $('.js-delete-branch-input', this.$modal);
this.$deleteBtn = $('.js-delete-branch', this.$modal); this.$deleteBtn = $('.js-delete-branch', this.$modal);
this.$notMerged = $('.js-not-merged', this.$modal);
this.bindEvents(); this.bindEvents();
} }
...@@ -16,8 +17,10 @@ class DeleteModal { ...@@ -16,8 +17,10 @@ class DeleteModal {
} }
setModalData(e) { setModalData(e) {
this.branchName = e.currentTarget.dataset.branchName || ''; const branchData = e.currentTarget.dataset;
this.deletePath = e.currentTarget.dataset.deletePath || ''; this.branchName = branchData.branchName || '';
this.deletePath = branchData.deletePath || '';
this.isMerged = !!branchData.isMerged;
this.updateModal(); this.updateModal();
} }
...@@ -30,6 +33,7 @@ class DeleteModal { ...@@ -30,6 +33,7 @@ class DeleteModal {
this.$confirmInput.val(''); this.$confirmInput.val('');
this.$deleteBtn.attr('href', this.deletePath); this.$deleteBtn.attr('href', this.deletePath);
this.$deleteBtn.attr('disabled', true); this.$deleteBtn.attr('disabled', true);
this.$notMerged.toggleClass('hidden', this.isMerged);
} }
} }
......
...@@ -15,6 +15,11 @@ class DropdownUser extends gl.FilteredSearchDropdown { ...@@ -15,6 +15,11 @@ class DropdownUser extends gl.FilteredSearchDropdown {
params: { params: {
per_page: 20, per_page: 20,
active: true, active: true,
<<<<<<< HEAD
=======
group_id: this.getGroupId(),
project_id: this.getProjectId(),
>>>>>>> ce-com/master
current_user: true, current_user: true,
...this.projectOrGroupId(), ...this.projectOrGroupId(),
}, },
......
...@@ -77,10 +77,11 @@ export const hideMenu = (el) => { ...@@ -77,10 +77,11 @@ export const hideMenu = (el) => {
export const moveSubItemsToPosition = (el, subItems) => { export const moveSubItemsToPosition = (el, subItems) => {
const boundingRect = el.getBoundingClientRect(); const boundingRect = el.getBoundingClientRect();
const top = calculateTop(boundingRect, subItems.offsetHeight); const top = calculateTop(boundingRect, subItems.offsetHeight);
const left = sidebar ? sidebar.offsetWidth : 50;
const isAbove = top < boundingRect.top; const isAbove = top < boundingRect.top;
subItems.classList.add('fly-out-list'); subItems.classList.add('fly-out-list');
subItems.style.transform = `translate3d(0, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign subItems.style.transform = `translate3d(${left}px, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign
const subItemsRect = subItems.getBoundingClientRect(); const subItemsRect = subItems.getBoundingClientRect();
......
...@@ -127,13 +127,6 @@ import DropdownUtils from './filtered_search/dropdown_utils'; ...@@ -127,13 +127,6 @@ import DropdownUtils from './filtered_search/dropdown_utils';
$('.has-tooltip', $value).tooltip({ $('.has-tooltip', $value).tooltip({
container: 'body' container: 'body'
}); });
return $value.find('a').each(function(i) {
return setTimeout((function(_this) {
return function() {
return gl.animate.animate($(_this), 'pulse');
};
})(this), 200 * i);
});
}); });
}; };
$dropdown.glDropdown({ $dropdown.glDropdown({
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, max-len */
(function() {
(function(w) {
if (w.gl == null) {
w.gl = {};
}
if (gl.animate == null) {
gl.animate = {};
}
gl.animate.animate = function($el, animation, options, done) {
if ((options != null ? options.cssStart : void 0) != null) {
$el.css(options.cssStart);
}
$el.removeClass(animation + ' animated').addClass(animation + ' animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
$(this).removeClass(animation + ' animated');
if (done != null) {
done();
}
if ((options != null ? options.cssEnd : void 0) != null) {
$el.css(options.cssEnd);
}
});
};
gl.animate.animateEach = function($els, animation, time, options, done) {
var dfd;
dfd = $.Deferred();
if (!$els.length) {
dfd.resolve();
}
$els.each(function(i) {
setTimeout((function(_this) {
return function() {
var $this;
$this = $(_this);
return gl.animate.animate($this, animation, options, function() {
if (i === $els.length - 1) {
dfd.resolve();
if (done != null) {
return done();
}
}
});
};
})(this), time * i);
});
return dfd.promise();
};
})(window);
}).call(window);
...@@ -39,7 +39,6 @@ import './commit/file'; ...@@ -39,7 +39,6 @@ import './commit/file';
import './commit/image_file'; import './commit/image_file';
// lib/utils // lib/utils
import './lib/utils/animate';
import './lib/utils/bootstrap_linked_tabs'; import './lib/utils/bootstrap_linked_tabs';
import './lib/utils/common_utils'; import './lib/utils/common_utils';
import './lib/utils/datetime_utility'; import './lib/utils/datetime_utility';
......
...@@ -45,7 +45,7 @@ import _ from 'underscore'; ...@@ -45,7 +45,7 @@ import _ from 'underscore';
if (issueUpdateURL) { if (issueUpdateURL) {
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>'); collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- name %><br /><%- remaining %>" data-placement="left" data-html="true"> <%- title %> </span>');
} }
return $dropdown.glDropdown({ return $dropdown.glDropdown({
showMenuAbove: showMenuAbove, showMenuAbove: showMenuAbove,
...@@ -231,6 +231,7 @@ import _ from 'underscore'; ...@@ -231,6 +231,7 @@ import _ from 'underscore';
if (data.milestone != null) { if (data.milestone != null) {
data.milestone.full_path = _this.currentProject.full_path; data.milestone.full_path = _this.currentProject.full_path;
data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
data.milestone.name = data.milestone.title;
$value.html(milestoneLinkTemplate(data.milestone)); $value.html(milestoneLinkTemplate(data.milestone));
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
} else { } else {
......
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
<div class="note-actions"> <div class="note-actions">
<span <span
v-if="accessLevel" v-if="accessLevel"
class="note-role">{{accessLevel}}</span> class="note-role note-role-access">{{accessLevel}}</span>
<div <div
v-if="canAddAwardEmoji" v-if="canAddAwardEmoji"
class="note-actions-item"> class="note-actions-item">
......
...@@ -158,11 +158,23 @@ ...@@ -158,11 +158,23 @@
box-shadow: inset 4px 0 0 $color-700; box-shadow: inset 4px 0 0 $color-700;
> a { > a {
color: $color-900; color: $color-800;
} }
svg { svg {
fill: $color-900; fill: $color-800;
}
}
.sidebar-top-level-items > li.active .badge {
color: $color-800;
}
.nav-links li.active a {
border-bottom-color: $color-500;
.badge {
font-weight: $gl-font-weight-bold;
} }
} }
} }
...@@ -261,5 +273,9 @@ body { ...@@ -261,5 +273,9 @@ body {
fill: $theme-gray-900; fill: $theme-gray-900;
} }
} }
.sidebar-top-level-items > li.active .badge {
color: $theme-gray-900;
}
} }
} }
...@@ -355,7 +355,7 @@ ...@@ -355,7 +355,7 @@
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
text-align: center; text-align: center;
margin-top: $header-height; margin-top: $new-navbar-height;
.container-fluid { .container-fluid {
position: relative; position: relative;
......
...@@ -78,16 +78,16 @@ ...@@ -78,16 +78,16 @@
.right-sidebar { .right-sidebar {
border-left: 1px solid $border-color; border-left: 1px solid $border-color;
height: calc(100% - #{$header-height}); height: calc(100% - #{$new-navbar-height});
&.affix { &.affix {
position: fixed; position: fixed;
top: $header-height; top: $new-navbar-height;
} }
} }
.with-performance-bar .right-sidebar.affix { .with-performance-bar .right-sidebar.affix {
top: $header-height + $performance-bar-height; top: $new-navbar-height + $performance-bar-height;
} }
@mixin maintain-sidebar-dimensions { @mixin maintain-sidebar-dimensions {
......
...@@ -13,6 +13,7 @@ $sidebar-breakpoint: 1024px; ...@@ -13,6 +13,7 @@ $sidebar-breakpoint: 1024px;
$darken-normal-factor: 7%; $darken-normal-factor: 7%;
$darken-dark-factor: 10%; $darken-dark-factor: 10%;
$darken-border-factor: 5%; $darken-border-factor: 5%;
$darken-border-dashed-factor: 25%;
$white-light: #fff; $white-light: #fff;
$white-normal: #f0f0f0; $white-normal: #f0f0f0;
...@@ -135,6 +136,7 @@ $border-white-normal: darken($white-normal, $darken-border-factor); ...@@ -135,6 +136,7 @@ $border-white-normal: darken($white-normal, $darken-border-factor);
$border-gray-light: darken($gray-light, $darken-border-factor); $border-gray-light: darken($gray-light, $darken-border-factor);
$border-gray-normal: darken($gray-normal, $darken-border-factor); $border-gray-normal: darken($gray-normal, $darken-border-factor);
$border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
$border-gray-dark: darken($white-normal, $darken-border-factor); $border-gray-dark: darken($white-normal, $darken-border-factor);
/* /*
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
@import "bootstrap/variables"; @import "bootstrap/variables";
$active-background: rgba(0, 0, 0, .04); $active-background: rgba(0, 0, 0, .04);
$active-border: $indigo-500;
$active-color: $indigo-700;
$active-hover-background: $active-background; $active-hover-background: $active-background;
$active-hover-color: $gl-text-color; $active-hover-color: $gl-text-color;
$inactive-badge-background: rgba(0, 0, 0, .08); $inactive-badge-background: rgba(0, 0, 0, .08);
...@@ -107,7 +105,8 @@ $new-sidebar-collapsed-width: 50px; ...@@ -107,7 +105,8 @@ $new-sidebar-collapsed-width: 50px;
} }
&.sidebar-icons-only { &.sidebar-icons-only {
width: $new-sidebar-collapsed-width; width: auto;
min-width: $new-sidebar-collapsed-width;
.nav-sidebar-inner-scroll { .nav-sidebar-inner-scroll {
overflow-x: hidden; overflow-x: hidden;
...@@ -126,6 +125,10 @@ $new-sidebar-collapsed-width: 50px; ...@@ -126,6 +125,10 @@ $new-sidebar-collapsed-width: 50px;
.fly-out-top-item { .fly-out-top-item {
display: block; display: block;
} }
.avatar-container {
margin-right: 0;
}
} }
&.nav-sidebar-expanded { &.nav-sidebar-expanded {
...@@ -189,7 +192,7 @@ $new-sidebar-collapsed-width: 50px; ...@@ -189,7 +192,7 @@ $new-sidebar-collapsed-width: 50px;
.nav-sidebar-inner-scroll { .nav-sidebar-inner-scroll {
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: auto; overflow: scroll;
} }
.with-performance-bar .nav-sidebar { .with-performance-bar .nav-sidebar {
...@@ -217,7 +220,6 @@ $new-sidebar-collapsed-width: 50px; ...@@ -217,7 +220,6 @@ $new-sidebar-collapsed-width: 50px;
&:hover, &:hover,
&:focus { &:focus {
background: $active-background; background: $active-background;
color: $active-color;
} }
} }
} }
...@@ -251,7 +253,7 @@ $new-sidebar-collapsed-width: 50px; ...@@ -251,7 +253,7 @@ $new-sidebar-collapsed-width: 50px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
position: fixed; position: fixed;
top: 0; top: 0;
left: $new-sidebar-width; left: 0;
min-width: 150px; min-width: 150px;
margin-top: -1px; margin-top: -1px;
padding: 4px 1px; padding: 4px 1px;
...@@ -317,7 +319,6 @@ $new-sidebar-collapsed-width: 50px; ...@@ -317,7 +319,6 @@ $new-sidebar-collapsed-width: 50px;
} }
.badge { .badge {
color: $active-color;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
...@@ -390,10 +391,6 @@ $new-sidebar-collapsed-width: 50px; ...@@ -390,10 +391,6 @@ $new-sidebar-collapsed-width: 50px;
} }
.sidebar-sub-level-items { .sidebar-sub-level-items {
@media (min-width: $screen-sm-min) {
left: $new-sidebar-collapsed-width;
}
&:not(.flyout-list) { &:not(.flyout-list) {
display: none; display: none;
} }
...@@ -494,13 +491,3 @@ $new-sidebar-collapsed-width: 50px; ...@@ -494,13 +491,3 @@ $new-sidebar-collapsed-width: 50px;
.with-performance-bar .boards-list { .with-performance-bar .boards-list {
height: calc(100vh - #{$new-navbar-height} - #{$performance-bar-height}); height: calc(100vh - #{$new-navbar-height} - #{$performance-bar-height});
} }
// Change color of all horizontal tabs to match the new indigo color
.nav-links li.active a {
border-bottom-color: $active-border;
.badge {
font-weight: $gl-font-weight-bold;
}
}
...@@ -64,10 +64,10 @@ ...@@ -64,10 +64,10 @@
color: $gl-text-color; color: $gl-text-color;
position: sticky; position: sticky;
position: -webkit-sticky; position: -webkit-sticky;
top: $header-height; top: $new-navbar-height;
&.affix { &.affix {
top: $header-height; top: $new-navbar-height;
} }
// with sidebar // with sidebar
...@@ -174,10 +174,10 @@ ...@@ -174,10 +174,10 @@
.with-performance-bar .build-page { .with-performance-bar .build-page {
.top-bar { .top-bar {
top: $header-height + $performance-bar-height; top: $new-navbar-height + $performance-bar-height;
&.affix { &.affix {
top: $header-height + $performance-bar-height; top: $new-navbar-height + $performance-bar-height;
} }
} }
} }
......
...@@ -634,8 +634,16 @@ ...@@ -634,8 +634,16 @@
padding-top: 8px; padding-top: 8px;
padding-bottom: 8px; padding-bottom: 8px;
} }
.diff-changed-file {
display: flex;
align-items: center;
}
} }
.diff-file-changes-path { .diff-file-changes-path {
@include str-truncated(78%); flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
...@@ -449,6 +449,12 @@ ...@@ -449,6 +449,12 @@
} }
} }
} }
.milestone-title span {
@include str-truncated(100%);
display: block;
margin: 0 4px;
}
} }
a { a {
......
...@@ -95,6 +95,8 @@ ...@@ -95,6 +95,8 @@
} }
.omniauth-container { .omniauth-container {
font-size: 13px;
p { p {
margin: 0; margin: 0;
} }
......
...@@ -236,6 +236,11 @@ ...@@ -236,6 +236,11 @@
} }
.stage-cell { .stage-cell {
@media (min-width: $screen-md-min) {
min-width: 148px;
margin-right: -4px;
}
.mini-pipeline-graph-dropdown-toggle svg { .mini-pipeline-graph-dropdown-toggle svg {
height: $ci-action-icon-size; height: $ci-action-icon-size;
width: $ci-action-icon-size; width: $ci-action-icon-size;
......
...@@ -760,7 +760,7 @@ a.deploy-project-label { ...@@ -760,7 +760,7 @@ a.deploy-project-label {
} }
li.missing { li.missing {
border: 1px dashed $border-gray-normal; border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default; border-radius: $border-radius-default;
a { a {
......
...@@ -71,6 +71,11 @@ ...@@ -71,6 +71,11 @@
height: 100%; height: 100%;
.monaco-editor.vs { .monaco-editor.vs {
.current-line {
border: none;
background: $well-light-border;
}
.line-numbers { .line-numbers {
cursor: pointer; cursor: pointer;
...@@ -84,6 +89,13 @@ ...@@ -84,6 +89,13 @@
} }
} }
.blob-no-preview {
.vertical-center {
justify-content: center;
width: 100%;
}
}
&.edit-mode { &.edit-mode {
.blob-viewer-container { .blob-viewer-container {
overflow: hidden; overflow: hidden;
...@@ -103,7 +115,7 @@ ...@@ -103,7 +115,7 @@
overflow: auto; overflow: auto;
> div, > div,
.file-content { .file-content:not(.wiki) {
display: flex; display: flex;
} }
......
...@@ -10,9 +10,8 @@ class Admin::DeployKeysController < Admin::ApplicationController ...@@ -10,9 +10,8 @@ class Admin::DeployKeysController < Admin::ApplicationController
end end
def create def create
@deploy_key = deploy_keys.new(create_params.merge(user: current_user)) @deploy_key = DeployKeys::CreateService.new(current_user, create_params.merge(public: true)).execute
if @deploy_key.persisted?
if @deploy_key.save
redirect_to admin_deploy_keys_path redirect_to admin_deploy_keys_path
else else
render 'new' render 'new'
......
...@@ -29,7 +29,7 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -29,7 +29,7 @@ class Admin::LabelsController < Admin::ApplicationController
@label = Labels::UpdateService.new(label_params).execute(@label) @label = Labels::UpdateService.new(label_params).execute(@label)
if @label.valid? if @label.valid?
redirect_to admin_labels_path, notice: 'label was successfully updated.' redirect_to admin_labels_path, notice: 'Label was successfully updated.'
else else
render :edit render :edit
end end
......
...@@ -2,6 +2,7 @@ class AutocompleteController < ApplicationController ...@@ -2,6 +2,7 @@ class AutocompleteController < ApplicationController
AWARD_EMOJI_MAX = 100 AWARD_EMOJI_MAX = 100
skip_before_action :authenticate_user!, only: [:users, :award_emojis] skip_before_action :authenticate_user!, only: [:users, :award_emojis]
<<<<<<< HEAD
before_action :load_project, only: [:users, :project_groups] before_action :load_project, only: [:users, :project_groups]
before_action :find_users, only: [:users] before_action :find_users, only: [:users]
...@@ -28,6 +29,13 @@ class AutocompleteController < ApplicationController ...@@ -28,6 +29,13 @@ class AutocompleteController < ApplicationController
@users = [author, *@users].uniq if author @users = [author, *@users].uniq if author
end end
end end
=======
before_action :load_project, only: [:users]
before_action :load_group, only: [:users]
def users
@users = AutocompleteUsersFinder.new(params: params, current_user: current_user, project: @project, group: @group).execute
>>>>>>> ce-com/master
render json: @users, only: [:name, :username, :id], methods: [:avatar_url] render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
end end
...@@ -64,6 +72,7 @@ class AutocompleteController < ApplicationController ...@@ -64,6 +72,7 @@ class AutocompleteController < ApplicationController
private private
<<<<<<< HEAD
def load_users_by_ability def load_users_by_ability
ability = :push_code_to_protected_branches if params[:push_code_to_protected_branches].present? ability = :push_code_to_protected_branches if params[:push_code_to_protected_branches].present?
ability = :push_code if params[:push_code].present? ability = :push_code if params[:push_code].present?
...@@ -87,14 +96,15 @@ class AutocompleteController < ApplicationController ...@@ -87,14 +96,15 @@ class AutocompleteController < ApplicationController
User.where(id: user_ids) User.where(id: user_ids)
elsif params[:group_id].present? elsif params[:group_id].present?
=======
def load_group
@group ||= begin
if @project.blank? && params[:group_id].present?
>>>>>>> ce-com/master
group = Group.find(params[:group_id]) group = Group.find(params[:group_id])
return render_404 unless can?(current_user, :read_group, group) return render_404 unless can?(current_user, :read_group, group)
group
group.users end
elsif current_user
User.all
else
User.none
end end
end end
......
...@@ -12,7 +12,11 @@ module Boards ...@@ -12,7 +12,11 @@ module Boards
def index def index
issues = Boards::Issues::ListService.new(board_parent, current_user, filter_params).execute issues = Boards::Issues::ListService.new(board_parent, current_user, filter_params).execute
issues = issues.page(params[:page]).per(params[:per] || 20) issues = issues.page(params[:page]).per(params[:per] || 20)
<<<<<<< HEAD
make_sure_position_is_set(issues) unless Gitlab::Geo.secondary? make_sure_position_is_set(issues) unless Gitlab::Geo.secondary?
=======
make_sure_position_is_set(issues)
>>>>>>> ce-com/master
issues = issues.preload(:project, issues = issues.preload(:project,
:milestone, :milestone,
:assignees, :assignees,
......
...@@ -7,9 +7,9 @@ class Profiles::GpgKeysController < Profiles::ApplicationController ...@@ -7,9 +7,9 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
end end
def create def create
@gpg_key = current_user.gpg_keys.new(gpg_key_params) @gpg_key = GpgKeys::CreateService.new(current_user, gpg_key_params).execute
if @gpg_key.save if @gpg_key.persisted?
redirect_to profile_gpg_keys_path redirect_to profile_gpg_keys_path
else else
@gpg_keys = current_user.gpg_keys.select(&:persisted?) @gpg_keys = current_user.gpg_keys.select(&:persisted?)
......
...@@ -11,9 +11,9 @@ class Profiles::KeysController < Profiles::ApplicationController ...@@ -11,9 +11,9 @@ class Profiles::KeysController < Profiles::ApplicationController
end end
def create def create
@key = current_user.keys.new(key_params) @key = Keys::CreateService.new(current_user, key_params).execute
if @key.save if @key.persisted?
redirect_to profile_key_path(@key) redirect_to profile_key_path(@key)
else else
@keys = current_user.keys.select(&:persisted?) @keys = current_user.keys.select(&:persisted?)
......
...@@ -38,7 +38,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController ...@@ -38,7 +38,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
end end
def set_index_vars def set_index_vars
@scopes = Gitlab::Auth::AVAILABLE_SCOPES @scopes = Gitlab::Auth.available_scopes
@personal_access_token = finder.build @personal_access_token = finder.build
@inactive_personal_access_tokens = finder(state: 'inactive').execute @inactive_personal_access_tokens = finder(state: 'inactive').execute
......
...@@ -27,7 +27,7 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -27,7 +27,7 @@ class Projects::CompareController < Projects::ApplicationController
def create def create
if params[:from].blank? || params[:to].blank? if params[:from].blank? || params[:to].blank?
flash[:alert] = "You must select from and to branches" flash[:alert] = "You must select a Source and a Target revision"
from_to_vars = { from_to_vars = {
from: params[:from].presence, from: params[:from].presence,
to: params[:to].presence to: params[:to].presence
......
...@@ -22,7 +22,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -22,7 +22,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def create def create
@key = DeployKey.new(create_params.merge(user: current_user)) @key = DeployKeys::CreateService.new(current_user, create_params).execute
unless @key.valid? && @project.deploy_keys << @key unless @key.valid? && @project.deploy_keys << @key
flash[:alert] = @key.errors.full_messages.join(', ').html_safe flash[:alert] = @key.errors.full_messages.join(', ').html_safe
......
...@@ -89,9 +89,9 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -89,9 +89,9 @@ class Projects::IssuesController < Projects::ApplicationController
.inc_relations_for_view .inc_relations_for_view
.includes(:noteable) .includes(:noteable)
.fresh .fresh
.reject { |n| n.cross_reference_not_visible_for?(current_user) }
prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
discussions = Discussion.build_collection(notes, @issue) discussions = Discussion.build_collection(notes, @issue)
......
class AutocompleteUsersFinder
attr_reader :current_user, :project, :group, :search, :skip_users,
:page, :per_page, :author_id, :params
def initialize(params:, current_user:, project:, group:)
@current_user = current_user
@project = project
@group = group
@search = params[:search]
@skip_users = params[:skip_users]
@page = params[:page]
@per_page = params[:per_page]
@author_id = params[:author_id]
@params = params
end
def execute
items = find_users
items = items.active
items = items.reorder(:name)
items = items.search(search) if search.present?
items = items.where.not(id: skip_users) if skip_users.present?
items = items.page(page).per(per_page)
if params[:todo_filter].present? && current_user
items = items.todo_authors(current_user.id, params[:todo_state_filter])
end
if search.blank?
# Include current user if available to filter by "Me"
if params[:current_user].present? && current_user
items = [current_user, *items].uniq
end
if author_id.present? && current_user
author = User.find_by_id(author_id)
items = [author, *items].uniq if author
end
end
items
end
private
def find_users
return users_from_project if project
return group.users if group
return User.all if current_user
User.none
end
def users_from_project
user_ids = project.team.users.pluck(:id)
user_ids << author_id if author_id.present?
User.where(id: user_ids)
end
end
...@@ -79,4 +79,8 @@ module BoardsHelper ...@@ -79,4 +79,8 @@ module BoardsHelper
'max-select': dropdown_options[:data][:'max-select'] 'max-select': dropdown_options[:data][:'max-select']
} }
end end
def boards_link_text
_("Board")
end
end end
...@@ -21,7 +21,7 @@ module GroupsHelper ...@@ -21,7 +21,7 @@ module GroupsHelper
group.ancestors.reverse.each_with_index do |parent, index| group.ancestors.reverse.each_with_index do |parent, index|
if index > 0 if index > 0
add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true), location: :before) add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true, for_dropdown: true), location: :before)
else else
full_title += breadcrumb_list_item group_title_link(parent, hidable: false) full_title += breadcrumb_list_item group_title_link(parent, hidable: false)
end end
...@@ -91,8 +91,8 @@ module GroupsHelper ...@@ -91,8 +91,8 @@ module GroupsHelper
private private
def group_title_link(group, hidable: false, show_avatar: false) def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false)
link_to(group_path(group), class: "group-path breadcrumb-item-text js-breadcrumb-item-text #{'hidable' if hidable}") do link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
output = output =
if (group.try(:avatar_url) || show_avatar) && !Rails.env.test? if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
image_tag(group_icon(group), class: "avatar-tile", width: 15, height: 15) image_tag(group_icon(group), class: "avatar-tile", width: 15, height: 15)
......
...@@ -466,10 +466,13 @@ module Ci ...@@ -466,10 +466,13 @@ module Ci
.fabricate! .fabricate!
end end
<<<<<<< HEAD
def codeclimate_artifact def codeclimate_artifact
artifacts.codequality.find(&:has_codeclimate_json?) artifacts.codequality.find(&:has_codeclimate_json?)
end end
=======
>>>>>>> ce-com/master
def latest_builds_with_artifacts def latest_builds_with_artifacts
@latest_builds_with_artifacts ||= builds.latest.with_artifacts @latest_builds_with_artifacts ||= builds.latest.with_artifacts
end end
......
...@@ -28,10 +28,4 @@ class DeployKey < Key ...@@ -28,10 +28,4 @@ class DeployKey < Key
def can_push_to?(project) def can_push_to?(project)
can_push? && has_access_to?(project) can_push? && has_access_to?(project)
end end
private
# we don't want to notify the user for deploy keys
def notify_user
end
end end
...@@ -6,7 +6,10 @@ class Environment < ActiveRecord::Base ...@@ -6,7 +6,10 @@ class Environment < ActiveRecord::Base
belongs_to :project, required: true, validate: true belongs_to :project, required: true, validate: true
has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :deployments,
-> (env) { where(project_id: env.project_id) },
dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment' has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment'
before_validation :nullify_external_url before_validation :nullify_external_url
......
...@@ -36,7 +36,6 @@ class GpgKey < ActiveRecord::Base ...@@ -36,7 +36,6 @@ class GpgKey < ActiveRecord::Base
before_validation :extract_fingerprint, :extract_primary_keyid before_validation :extract_fingerprint, :extract_primary_keyid
after_commit :update_invalid_gpg_signatures, on: :create after_commit :update_invalid_gpg_signatures, on: :create
after_commit :notify_user, on: :create
def primary_keyid def primary_keyid
super&.upcase super&.upcase
...@@ -107,8 +106,4 @@ class GpgKey < ActiveRecord::Base ...@@ -107,8 +106,4 @@ class GpgKey < ActiveRecord::Base
# only allows one key # only allows one key
self.primary_keyid = Gitlab::Gpg.primary_keyids_from_key(key).first self.primary_keyid = Gitlab::Gpg.primary_keyids_from_key(key).first
end end
def notify_user
NotificationService.new.new_gpg_key(self)
end
end end
...@@ -31,7 +31,6 @@ class Key < ActiveRecord::Base ...@@ -31,7 +31,6 @@ class Key < ActiveRecord::Base
delegate :name, :email, to: :user, prefix: true delegate :name, :email, to: :user, prefix: true
after_commit :add_to_shell, on: :create after_commit :add_to_shell, on: :create
after_commit :notify_user, on: :create
after_create :post_create_hook after_create :post_create_hook
after_commit :remove_from_shell, on: :destroy after_commit :remove_from_shell, on: :destroy
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
...@@ -121,8 +120,4 @@ class Key < ActiveRecord::Base ...@@ -121,8 +120,4 @@ class Key < ActiveRecord::Base
"type is forbidden. Must be #{allowed_types}" "type is forbidden. Must be #{allowed_types}"
end end
def notify_user
NotificationService.new.new_key(self)
end
end end
...@@ -244,6 +244,13 @@ class Namespace < ActiveRecord::Base ...@@ -244,6 +244,13 @@ class Namespace < ActiveRecord::Base
end end
def force_share_with_group_lock_on_descendants def force_share_with_group_lock_on_descendants
descendants.update_all(share_with_group_lock: true) return unless Group.supports_nested_groups?
# We can't use `descendants.update_all` since Rails will throw away the WITH
# RECURSIVE statement. We also can't use WHERE EXISTS since we can't use
# different table aliases, hence we're just using WHERE IN. Since we have a
# maximum of 20 nested groups this should be fine.
Namespace.where(id: descendants.select(:id))
.update_all(share_with_group_lock: true)
end end
end end
...@@ -28,7 +28,7 @@ class PersonalAccessToken < ActiveRecord::Base ...@@ -28,7 +28,7 @@ class PersonalAccessToken < ActiveRecord::Base
protected protected
def validate_scopes def validate_scopes
unless revoked || scopes.all? { |scope| Gitlab::Auth::AVAILABLE_SCOPES.include?(scope.to_sym) } unless revoked || scopes.all? { |scope| Gitlab::Auth.available_scopes.include?(scope.to_sym) }
errors.add :scopes, "can only contain available scopes" errors.add :scopes, "can only contain available scopes"
end end
end end
......
...@@ -165,7 +165,7 @@ class Project < ActiveRecord::Base ...@@ -165,7 +165,7 @@ class Project < ActiveRecord::Base
has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true
has_one :project_feature has_one :project_feature, inverse_of: :project
has_one :statistics, class_name: 'ProjectStatistics' has_one :statistics, class_name: 'ProjectStatistics'
# Container repositories need to remove data from the container registry, # Container repositories need to remove data from the container registry,
...@@ -194,7 +194,7 @@ class Project < ActiveRecord::Base ...@@ -194,7 +194,7 @@ class Project < ActiveRecord::Base
has_one :auto_devops, class_name: 'ProjectAutoDevops' has_one :auto_devops, class_name: 'ProjectAutoDevops'
accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :variables, allow_destroy: true
accepts_nested_attributes_for :project_feature accepts_nested_attributes_for :project_feature, update_only: true
accepts_nested_attributes_for :import_data accepts_nested_attributes_for :import_data
accepts_nested_attributes_for :auto_devops accepts_nested_attributes_for :auto_devops
......
...@@ -41,6 +41,8 @@ class ProjectFeature < ActiveRecord::Base ...@@ -41,6 +41,8 @@ class ProjectFeature < ActiveRecord::Base
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to # http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
belongs_to :project, -> { unscope(where: :pending_delete) } belongs_to :project, -> { unscope(where: :pending_delete) }
validates :project, presence: true
validate :repository_children_level validate :repository_children_level
default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :builds_access_level, value: ENABLED, allows_nil: false
......
...@@ -80,6 +80,6 @@ class PipelinesEmailService < Service ...@@ -80,6 +80,6 @@ class PipelinesEmailService < Service
end end
def retrieve_recipients(data) def retrieve_recipients(data)
recipients.to_s.split(',').reject(&:blank?) recipients.to_s.split(/[,(?:\r?\n) ]+/).reject(&:empty?)
end end
end end
...@@ -148,7 +148,7 @@ class ProjectTeam ...@@ -148,7 +148,7 @@ class ProjectTeam
def member?(user, min_access_level = Gitlab::Access::GUEST) def member?(user, min_access_level = Gitlab::Access::GUEST)
return false unless user return false unless user
user.authorized_project?(project, min_access_level) max_member_access(user.id) >= min_access_level
end end
def human_max_access(user_id) def human_max_access(user_id)
......
...@@ -97,6 +97,12 @@ class Repository ...@@ -97,6 +97,12 @@ class Repository
) )
end end
# we need to have this method here because it is not cached in ::Git and
# the method is called multiple times for every request
def has_visible_content?
branch_count > 0
end
def inspect def inspect
"#<#{self.class.name}:#{@disk_path}>" "#<#{self.class.name}:#{@disk_path}>"
end end
...@@ -771,6 +777,17 @@ class Repository ...@@ -771,6 +777,17 @@ class Repository
def with_cache_hooks def with_cache_hooks
result = yield result = yield
<<<<<<< HEAD
return unless result
after_create if result.repo_created?
after_create_branch if result.branch_created?
result.newrev
end
=======
return unless result return unless result
...@@ -780,6 +797,7 @@ class Repository ...@@ -780,6 +797,7 @@ class Repository
result.newrev result.newrev
end end
>>>>>>> ce-com/master
def with_branch(user, *args) def with_branch(user, *args)
with_cache_hooks do with_cache_hooks do
Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit| Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit|
...@@ -850,6 +868,7 @@ class Repository ...@@ -850,6 +868,7 @@ class Repository
end end
end end
<<<<<<< HEAD
def ff_merge(user, source, target_branch, merge_request: nil) def ff_merge(user, source, target_branch, merge_request: nil)
our_commit = rugged.branches[target_branch].target our_commit = rugged.branches[target_branch].target
their_commit = their_commit =
...@@ -869,6 +888,8 @@ class Repository ...@@ -869,6 +888,8 @@ class Repository
end end
end end
=======
>>>>>>> ce-com/master
def merge(user, source_sha, merge_request, message) def merge(user, source_sha, merge_request, message)
with_cache_hooks do with_cache_hooks do
raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id| raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id|
......
module DeployKeys
class CreateService < Keys::BaseService
def execute
DeployKey.create(params.merge(user: user))
end
end
end
module GpgKeys
class CreateService < Keys::BaseService
def execute
key = user.gpg_keys.create(params)
notification_service.new_gpg_key(key) if key.persisted?
key
end
end
end
module Keys
class BaseService
attr_accessor :user, :params
def initialize(user, params)
@user, @params = user, params
end
def notification_service
NotificationService.new
end
end
end
module Keys
class CreateService < ::Keys::BaseService
def execute
key = user.keys.create(params)
notification_service.new_key(key) if key.persisted?
key
end
end
end
...@@ -34,7 +34,10 @@ module Projects ...@@ -34,7 +34,10 @@ module Projects
success success
else else
error('Project could not be updated!') model_errors = project.errors.full_messages.to_sentence
error_message = model_errors.presence || 'Project could not be updated!'
error(error_message)
end end
end end
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
%span.light %span.light
- has_icon = provider_has_icon?(provider) - has_icon = provider_has_icon?(provider)
= link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
%fieldset.prepend-top-10 %fieldset.prepend-top-10.checkbox.remember-me
= check_box_tag :remember_me %label
= label_tag :remember_me, 'Remember me' = check_box_tag :remember_me, nil, false, class: 'remember-me-checkbox'
%span
Remember me
...@@ -52,7 +52,11 @@ ...@@ -52,7 +52,11 @@
%span %span
ConvDev Index ConvDev Index
<<<<<<< HEAD
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles audit_logs)) do = nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles audit_logs)) do
=======
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
>>>>>>> ce-com/master
= link_to admin_system_info_path do = link_to admin_system_info_path do
.nav-icon-container .nav-icon-container
= custom_icon('monitoring') = custom_icon('monitoring')
......
...@@ -14,7 +14,11 @@ ...@@ -14,7 +14,11 @@
.sidebar-context-title .sidebar-context-title
= @group.name = @group.name
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
<<<<<<< HEAD
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
=======
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
>>>>>>> ce-com/master
= link_to group_path(@group) do = link_to group_path(@group) do
.nav-icon-container .nav-icon-container
= custom_icon('project') = custom_icon('project')
...@@ -37,6 +41,7 @@ ...@@ -37,6 +41,7 @@
%span %span
Activity Activity
<<<<<<< HEAD
- if @group.feature_available?(:contribution_analytics) || show_promotions? - if @group.feature_available?(:contribution_analytics) || show_promotions?
= nav_link(path: 'analytics#show') do = nav_link(path: 'analytics#show') do
= link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
...@@ -44,6 +49,9 @@ ...@@ -44,6 +49,9 @@
Contribution Analytics Contribution Analytics
= nav_link(path: issues_sub_menu_items) do = nav_link(path: issues_sub_menu_items) do
=======
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
>>>>>>> ce-com/master
= link_to issues_group_path(@group) do = link_to issues_group_path(@group) do
.nav-icon-container .nav-icon-container
= custom_icon('issues') = custom_icon('issues')
...@@ -100,11 +108,15 @@ ...@@ -100,11 +108,15 @@
Members Members
%ul.sidebar-sub-level-items.is-fly-out-only %ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
= link_to merge_requests_group_path(@group) do = link_to group_group_members_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Members') } #{ _('Members') }
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
<<<<<<< HEAD
= nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do
=======
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
>>>>>>> ce-com/master
= link_to edit_group_path(@group) do = link_to edit_group_path(@group) do
.nav-icon-container .nav-icon-container
= custom_icon('settings') = custom_icon('settings')
......
...@@ -35,7 +35,11 @@ ...@@ -35,7 +35,11 @@
%span= _('Cycle Analytics') %span= _('Cycle Analytics')
- if project_nav_tab? :files - if project_nav_tab? :files
<<<<<<< HEAD
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network path_locks)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network path_locks)) do
=======
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
>>>>>>> ce-com/master
= link_to project_tree_path(@project), class: 'shortcuts-tree' do = link_to project_tree_path(@project), class: 'shortcuts-tree' do
.nav-icon-container .nav-icon-container
= custom_icon('doc_text') = custom_icon('doc_text')
...@@ -245,7 +249,11 @@ ...@@ -245,7 +249,11 @@
#{ _('Snippets') } #{ _('Snippets') }
- if project_nav_tab? :settings - if project_nav_tab? :settings
<<<<<<< HEAD
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show audit_events#index]) do = nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show audit_events#index]) do
=======
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
>>>>>>> ce-com/master
= link_to edit_project_path(@project), class: 'shortcuts-tree' do = link_to edit_project_path(@project), class: 'shortcuts-tree' do
.nav-icon-container .nav-icon-container
= custom_icon('settings') = custom_icon('settings')
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.preview-row .preview-row
.quadrant.three .quadrant.three
.quadrant.four .quadrant.four
= f.radio_button :theme_id, theme.id = f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id
= theme.name = theme.name
.col-sm-12 .col-sm-12
......
.file-content.blob_file.blob-no-preview .file-content.blob_file.blob-no-preview
.center .center.render-error.vertical-center
= link_to blob_raw_path do = link_to blob_raw_path do
%h1.light %h1.light
= icon('download') = icon('download')
......
...@@ -49,7 +49,8 @@ ...@@ -49,7 +49,8 @@
data: { toggle: "modal", data: { toggle: "modal",
target: "#modal-delete-branch", target: "#modal-delete-branch",
delete_path: project_branch_path(@project, branch.name), delete_path: project_branch_path(@project, branch.name),
branch_name: branch.name } } branch_name: branch.name,
is_merged: ("true" if @repository.merged_to_root_ref?(branch.name)) } }
= icon("trash-o") = icon("trash-o")
- else - else
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled", %button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
......
...@@ -6,13 +6,18 @@ ...@@ -6,13 +6,18 @@
%h3.page-title %h3.page-title
Delete protected branch Delete protected branch
= surround "'", "'?" do = surround "'", "'?" do
%span.js-branch-name>[branch name] %span.js-branch-name.ref-name>[branch name]
.modal-body .modal-body
%p %p
You’re about to permanently delete the protected branch You’re about to permanently delete the protected branch
= succeed '.' do = succeed '.' do
%strong.js-branch-name [branch name] %strong.js-branch-name.ref-name [branch name]
%p.js-not-merged
- default_branch = capture do
%span.ref-name= @repository.root_ref
= s_("Branches|This branch hasn’t been merged into %{default_branch}.").html_safe % { default_branch: default_branch }
= s_("Branches|To avoid data loss, consider merging this branch before deleting it.")
%p %p
Once you confirm and press Once you confirm and press
= succeed ',' do = succeed ',' do
......
...@@ -2,22 +2,22 @@ ...@@ -2,22 +2,22 @@
.clearfix .clearfix
- if params[:to] && params[:from] - if params[:to] && params[:from]
.compare-switch-container .compare-switch-container
= link_to icon('exchange'), {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Switch base of comparison'} = link_to icon('exchange'), { from: params[:to], to: params[:from] }, class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Swap revisions'
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
.input-group.inline-input-group
%span.input-group-addon from
= hidden_field_tag :from, params[:from]
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag'
= render 'shared/ref_dropdown'
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.to.js-compare-to-dropdown .form-group.dropdown.compare-form-group.to.js-compare-to-dropdown
.input-group.inline-input-group .input-group.inline-input-group
%span.input-group-addon to %span.input-group-addon Source
= hidden_field_tag :to, params[:to] = hidden_field_tag :to, params[:to]
= button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do = button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
.dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag' .dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag'
= render 'shared/ref_dropdown' = render 'shared/ref_dropdown'
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
.input-group.inline-input-group
%span.input-group-addon Target
= hidden_field_tag :from, params[:from]
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag'
= render 'shared/ref_dropdown'
&nbsp; &nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn" = button_tag "Compare", class: "btn btn-create commits-compare-btn"
- if @merge_request.present? - if @merge_request.present?
......
...@@ -7,13 +7,19 @@ ...@@ -7,13 +7,19 @@
.sub-header-block .sub-header-block
Compare Git revisions. Compare Git revisions.
%br %br
Fill input field with commit SHA like Choose a branch/tag (e.g.
%code.ref-name 4eedf23 = succeed ')' do
or branch/tag name like
%code.ref-name master %code.ref-name master
and press compare button for the commits list and a code diff. or enter a commit SHA (e.g.
= succeed ')' do
%code.ref-name 4eedf23
to see what's changed or to create a merge request.
%br %br
Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field. Changes are shown as if the
%b source
revision was being merged into the
%b target
revision.
.prepend-top-20 .prepend-top-20
= render "form" = render "form"
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
%ul %ul
- diff_files.each do |diff_file| - diff_files.each do |diff_file|
%li %li
%a{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path } %a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
= icon("#{diff_file_changed_icon(diff_file)} fw", class: "#{diff_file_changed_icon_color(diff_file)} append-right-5") = icon("#{diff_file_changed_icon(diff_file)} fw", class: "#{diff_file_changed_icon_color(diff_file)} append-right-5")
%span.diff-file-changes-path= diff_file.new_path %span.diff-file-changes-path.append-right-5= diff_file.new_path
.pull-right .pull-right
%span.cgreen< %span.cgreen<
+#{diff_file.added_lines} +#{diff_file.added_lines}
......
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
New project New project
- if import_sources_enabled? - if import_sources_enabled?
%p %p
Create or Import your project from popular Git services A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), #{link_to 'among other things', help_page_path("user/project/index.md", anchor: "projects-features"), target: '_blank'}.
%p
All features are enabled when you create a project, but you can disable the ones you don’t need in the project settings.
.col-lg-9.js-toggle-container .col-lg-9.js-toggle-container
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
.create-project-options .create-project-options
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
.col-sm-10 .col-sm-10
.checkbox .checkbox
= f.check_box :access_level, {}, 'ref_protected', 'not_protected' = f.check_box :access_level, {}, 'ref_protected', 'not_protected'
%span.light This runner will only run on pipelines trigged on protected branches %span.light This runner will only run on pipelines triggered on protected branches
.form-group .form-group
= label :run_untagged, 'Run untagged jobs', class: 'control-label' = label :run_untagged, 'Run untagged jobs', class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -12,13 +12,13 @@ ...@@ -12,13 +12,13 @@
- if params[:author_id].present? - if params[:author_id].present?
= hidden_field_tag(:author_id, params[:author_id]) = hidden_field_tag(:author_id, params[:author_id])
= dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit", = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user.try(:username), current_user: true, project_id: @project.try(:id), selected: params[:author_id], field_name: "author_id", default_label: "Author" } }) placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
.filter-item.inline .filter-item.inline
- if params[:assignee_id].present? - if params[:assignee_id].present?
= hidden_field_tag(:assignee_id, params[:assignee_id]) = hidden_field_tag(:assignee_id, params[:assignee_id])
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user.try(:username), null_user: true, current_user: true, project_id: @project.try(:id), group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, board: board, show_started: true = render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, board: board, show_started: true
......
...@@ -24,9 +24,9 @@ ...@@ -24,9 +24,9 @@
.block.milestone .block.milestone
.sidebar-collapsed-icon .sidebar-collapsed-icon
= icon('clock-o', 'aria-hidden': 'true') = icon('clock-o', 'aria-hidden': 'true')
%span %span.milestone-title
- if issuable.milestone - if issuable.milestone
%span.has-tooltip{ title: milestone_remaining_days(issuable.milestone), data: { container: 'body', html: 1, placement: 'left' } } %span.has-tooltip{ title: "#{issuable.milestone.title}<br>#{milestone_remaining_days(issuable.milestone)}", data: { container: 'body', html: 1, placement: 'left' } }
= issuable.milestone.title = issuable.milestone.title
- else - else
None None
......
...@@ -26,6 +26,6 @@ ...@@ -26,6 +26,6 @@
%span.assignee-icon %span.assignee-icon
- assignees.each do |assignee| - assignees.each do |assignee|
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }), = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }),
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
- image_tag(avatar_icon(assignee, 16), class: "avatar s16", alt: '') - image_tag(avatar_icon(assignee, 16), class: "avatar s16", alt: '')
---
title: Return only group's members in user dropdowns on issuables list pages
merge_request: 14249
author:
type: changed
---
title: Make the labels in the Compare form less confusing
merge_request: 14225
author:
type: changed
---
title: Extract AutocompleteController#users into finder
merge_request: 13778
author: Maxim Rydkin, Mayra Cabrera
type: other
---
title: creation of keys moved to services
merge_request: 13331
author: haseebeqx
---
title: Truncate milestone title if sidebar is collapsed
merge_request:
author:
type: fixed
---
title: Fix the diff file header from being html escaped for renamed files.
merge_request: 14121
author:
type: fixed
---
title: Fix mini graph pipeline breakin in merge request view
merge_request:
author:
type: fixed
---
title: Allow using newlines in pipeline email service recipients
merge_request: 14250
author:
type: fixed
---
title: changed dashed border button color to be darker
merge_request: !14041
author:
type: other
---
title: Constrain environment deployments to project IDs
merge_request:
author:
type: other
---
title: "Disallow NULL values for environments.project_id"
merge_request:
author:
type: other
---
title: Fix docs for lightweight tag creation via API
merge_request:
author:
type: other
---
title: Fixed the sidebar scrollbar overlapping links
merge_request:
author:
type: fixed
---
title: Fix project feature being deleted when updating project with invalid visibility
level
merge_request:
author:
type: fixed
---
title: Fixed milestone issuable assignee link URL
merge_request:
author:
type: fixed
---
title: Remove animate.js and label animation.
merge_request:
author:
type: removed
---
title: Replace the 'project/archived.feature' spinach test with an rspec analog
merge_request: 14322
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the 'project/commits/revert.feature' spinach test with an rspec analog
merge_request: 14325
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the 'project/snippets.feature' spinach test with an rspec analog
merge_request: 14326
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Replace the 'search.feature' spinach test with an rspec analog
merge_request: 14248
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Eliminate N+1 queries in loading discussions.json endpoint
merge_request:
author:
type: fixed
---
title: Made the "remember me" check boxes have consistent styles and alignment
merge_request:
author: Jedidiah Broadbent
type: fixed
---
title: Display whether branch has been merged when deleting protected branch
merge_request: 14220
author:
type: changed
...@@ -416,3 +416,39 @@ ...@@ -416,3 +416,39 @@
:why: https://gitlab.com/gitlab-com/organization/issues/117 :why: https://gitlab.com/gitlab-com/organization/issues/117
:versions: [] :versions: []
:when: 2017-09-04 12:59:51.150798717 Z :when: 2017-09-04 12:59:51.150798717 Z
- - :approve
- console-browserify
- :who: Mike Greiling
:why: https://github.com/Raynos/console-browserify/blob/f0a8898487e2a47b8a5dc8734b91059fa2825506/LICENCE
:versions: []
:when: 2017-09-16 05:13:07.073651000 Z
- - :approve
- duplexer
- :who: Mike Greiling
:why: https://github.com/Raynos/duplexer/blob/master/LICENCE
:versions: []
:when: 2017-09-16 05:14:15.774643000 Z
- - :approve
- json3
- :who: Mike Greiling
:why: https://github.com/bestiejs/json3/blob/v3.3.2/LICENSE
:versions: []
:when: 2017-09-16 05:15:16.273892000 Z
- - :approve
- mime
- :who: Mike Greiling
:why: https://github.com/broofa/node-mime/blob/v1.3.4/LICENSE
:versions: []
:when: 2017-09-16 05:16:21.135542000 Z
- - :approve
- querystring-es3
- :who: Mike Greiling
:why: https://github.com/mike-spainhower/querystring/blob/v0.2.0/License.md
:versions: []
:when: 2017-09-16 05:17:20.372089000 Z
- - :approve
- utils-merge
- :who: Mike Greiling
:why: https://github.com/jaredhanson/utils-merge/blob/v1.0.0/LICENSE
:versions: []
:when: 2017-09-16 05:18:26.193764000 Z
...@@ -58,7 +58,7 @@ Doorkeeper.configure do ...@@ -58,7 +58,7 @@ Doorkeeper.configure do
# For more information go to # For more information go to
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
default_scopes(*Gitlab::Auth::DEFAULT_SCOPES) default_scopes(*Gitlab::Auth::DEFAULT_SCOPES)
optional_scopes(*Gitlab::Auth::OPTIONAL_SCOPES) optional_scopes(*Gitlab::Auth.optional_scopes)
# Change the way client credentials are retrieved from the request object. # Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
......
...@@ -30,7 +30,7 @@ if app.config.serve_static_files ...@@ -30,7 +30,7 @@ if app.config.serve_static_files
settings.merge!( settings.merge!(
host: Gitlab.config.gitlab.host, host: Gitlab.config.gitlab.host,
port: Gitlab.config.gitlab.port, port: Gitlab.config.gitlab.port,
https: Gitlab.config.gitlab.https https: false
) )
app.config.middleware.insert_before( app.config.middleware.insert_before(
Gitlab::Middleware::Static, Gitlab::Middleware::Static,
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class MigrateUserExternalMailData < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
class User < ActiveRecord::Base
self.table_name = 'users'
include EachBatch
end
class UserSyncedAttributesMetadata < ActiveRecord::Base
self.table_name = 'user_synced_attributes_metadata'
include EachBatch
end
def up
User.each_batch do |batch|
start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
execute <<-EOF
INSERT INTO user_synced_attributes_metadata (user_id, provider, email_synced)
SELECT id, email_provider, external_email
FROM users
WHERE external_email = TRUE
AND NOT EXISTS (
SELECT true
FROM user_synced_attributes_metadata
WHERE user_id = users.id
AND provider = users.email_provider
)
AND id BETWEEN #{start_id} AND #{end_id}
EOF
end
end
def down
UserSyncedAttributesMetadata.each_batch do |batch|
start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
execute <<-EOF
UPDATE users
SET users.email_provider = metadata.provider, users.external_email = metadata.email_synced
FROM user_synced_attributes_metadata as metadata, users
WHERE metadata.email_synced = TRUE
AND metadata.user_id = users.id
AND id BETWEEN #{start_id} AND #{end_id}
EOF
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class EnvironmentsProjectIdNotNull < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
change_column_null :environments, :project_id, false
end
def down
change_column_null :environments, :project_id, true
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexForRecentPushEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index_if_not_present(
:merge_requests,
[:source_project_id, :source_branch]
)
remove_concurrent_index_if_present(:merge_requests, :source_project_id)
end
def down
add_concurrent_index_if_not_present(:merge_requests, :source_project_id)
remove_concurrent_index_if_present(
:merge_requests,
[:source_project_id, :source_branch]
)
end
def add_concurrent_index_if_not_present(table, columns)
return if index_exists?(table, columns)
add_concurrent_index(table, columns)
end
def remove_concurrent_index_if_present(table, columns)
return unless index_exists?(table, columns)
remove_concurrent_index(table, columns)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class DeleteConflictingRedirectRoutes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'DeleteConflictingRedirectRoutesRange'.freeze
BATCH_SIZE = 200 # At 200, I expect under 20s per batch, which is under our query timeout of 60s.
DELAY_INTERVAL = 12.seconds
disable_ddl_transaction!
class Route < ActiveRecord::Base
include EachBatch
self.table_name = 'routes'
end
def up
say opening_message
queue_background_migration_jobs_by_range_at_intervals(Route, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
def down
# nothing
end
def opening_message
<<~MSG
Clean up redirect routes that conflict with regular routes.
See initial bug fix:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13357
MSG
end
end
class FixProjectsWithoutProjectFeature < ActiveRecord::Migration
DOWNTIME = false
def up
# Deletes corrupted project features
sql = "DELETE FROM project_features WHERE project_id IS NULL"
execute(sql)
# Creates missing project features with private visibility
sql =
%Q{
INSERT INTO project_features(project_id, repository_access_level, issues_access_level, merge_requests_access_level, wiki_access_level,
builds_access_level, snippets_access_level, created_at, updated_at)
SELECT projects.id as project_id,
10 as repository_access_level,
10 as issues_access_level,
10 as merge_requests_access_level,
10 as wiki_access_level,
10 as builds_access_level ,
10 as snippets_access_level,
projects.created_at,
projects.updated_at
FROM projects
LEFT OUTER JOIN project_features ON project_features.project_id = projects.id
WHERE (project_features.id IS NULL)
}
execute(sql)
end
def down
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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