Commit 3130262e authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch 'master' into 38031-monitoring-hover-info-is-clipped

parents c1461770 18fee306
...@@ -8,4 +8,4 @@ ...@@ -8,4 +8,4 @@
karma.config.js karma.config.js
webpack.config.js webpack.config.js
svg.config.js svg.config.js
/app/assets/javascripts/locale/**/*.js /app/assets/javascripts/locale/**/app.js
...@@ -63,4 +63,5 @@ eslint-report.html ...@@ -63,4 +63,5 @@ eslint-report.html
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/webpack-report/ /webpack-report/
/locale/**/LC_MESSAGES /locale/**/LC_MESSAGES
/locale/**/*.time_stamp
/.rspec /.rspec
...@@ -404,6 +404,7 @@ docs lint: ...@@ -404,6 +404,7 @@ docs lint:
before_script: [] before_script: []
script: script:
- scripts/lint-doc.sh - scripts/lint-doc.sh
- scripts/lint-changelog-yaml
- mv doc/ /nanoc/content/ - mv doc/ /nanoc/content/
- cd /nanoc - cd /nanoc
# Build HTML from Markdown # Build HTML from Markdown
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.0.2 (2017-09-27)
- [FIXED] Notes will not show an empty bubble when the author isn't a member. !14450
- [FIXED] Some checks in `rake gitlab:check` were failling with 'undefined method `run_command`'. !14469
- [FIXED] Make locked setting of Runner to not affect jobs scheduling. !14483
- [FIXED] Re-allow `name` attribute on user-provided anchor HTML.
## 10.0.1 (2017-09-23) ## 10.0.1 (2017-09-23)
- [FIXED] Fix duplicate key errors in PostDeployMigrateUserExternalMailData migration. - [FIXED] Fix duplicate key errors in PostDeployMigrateUserExternalMailData migration.
...@@ -78,6 +85,8 @@ entry. ...@@ -78,6 +85,8 @@ entry.
- [FIXED] Fixed merge request changes bar jumping. - [FIXED] Fixed merge request changes bar jumping.
- [FIXED] Improve migrations using triggers. - [FIXED] Improve migrations using triggers.
- [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression. - [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression.
- [FIXED] disabling notifications globally now properly turns off group/project added
emails !13325
- [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930 - [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930
- [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi) - [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi)
- [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov) - [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov)
...@@ -186,6 +195,13 @@ entry. ...@@ -186,6 +195,13 @@ entry.
- Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi) - Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi)
- [BUGIFX] Improves subgroup creation permissions. !13418 - [BUGIFX] Improves subgroup creation permissions. !13418
## 9.5.6 (2017-09-29)
- [FIXED] Fix MR ready to merge buttons/controls at mobile breakpoint. !14242
- [FIXED] Fix errors thrown in merge request widget with external CI service/integration.
- [FIXED] Update x/x discussions resolved checkmark icon to be green when all discussions resolved.
- [FIXED] Fix 500 error on merged merge requests when GitLab is restored from a backup.
## 9.5.5 (2017-09-18) ## 9.5.5 (2017-09-18)
- [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller) - [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller)
......
...@@ -49,7 +49,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._ ...@@ -49,7 +49,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
Thank you for your interest in contributing to GitLab. This guide details how Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is efficient for everyone. to contribute to GitLab in a way that is efficient for everyone.
Looking for something to work on? Look for the label [Accepting Merge Requests](#i-want-to-contribute). Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute).
GitLab comes into two flavors, GitLab Community Edition (CE) our free and open GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
source edition, and GitLab Enterprise Edition (EE) which is our commercial source edition, and GitLab Enterprise Edition (EE) which is our commercial
...@@ -101,7 +101,7 @@ the remaining issues on the GitHub issue tracker. ...@@ -101,7 +101,7 @@ the remaining issues on the GitHub issue tracker.
## I want to contribute! ## I want to contribute!
If you want to contribute to GitLab, but are not sure where to start, If you want to contribute to GitLab, but are not sure where to start,
look for [issues with the label `Accepting Merge Requests` and weight < 5][accepting-mrs-weight]. look for [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight].
These issues will be of reasonable size and challenge, for anyone to start These issues will be of reasonable size and challenge, for anyone to start
contributing to GitLab. contributing to GitLab.
...@@ -209,8 +209,7 @@ We add the ~"Accepting Merge Requests" label to: ...@@ -209,8 +209,7 @@ We add the ~"Accepting Merge Requests" label to:
- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to - Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
solve in the ~"Next Patch Release") solve in the ~"Next Patch Release")
- Small ~"feature proposal" that do not need ~UX / ~"Product work", or for which - Small ~"feature proposal"
the ~UX / ~"Product work" is already done
- Small ~"technical debt" issues - Small ~"technical debt" issues
After adding the ~"Accepting Merge Requests" label, we try to estimate the After adding the ~"Accepting Merge Requests" label, we try to estimate the
...@@ -223,6 +222,17 @@ know how difficult the issue is. Additionally: ...@@ -223,6 +222,17 @@ know how difficult the issue is. Additionally:
- We encourage people that have never contributed to any open source project to - We encourage people that have never contributed to any open source project to
look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers] look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
If you've decided that you would like to work on an issue, please @-mention
the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
as soon as possible. The product manager will then pull in appropriate GitLab team
members to further discuss scope, design, and technical considerations. This will
ensure that that your contribution is aligned with the GitLab product and minimize
any rework and delay in getting it merged into master.
GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
should update the issue description with a responsible product manager, inviting
any potential community contributor to @-mention per above.
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1 [firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
......
...@@ -398,7 +398,7 @@ group :ed25519 do ...@@ -398,7 +398,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.33.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.39.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -275,7 +275,7 @@ GEM ...@@ -275,7 +275,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.33.0) gitaly-proto (0.39.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1025,7 +1025,7 @@ DEPENDENCIES ...@@ -1025,7 +1025,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.33.0) gitaly-proto (~> 0.39.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2) gitlab-markup (~> 1.6.2)
......
{"iconCount":134,"icons":["abuse","account","admin","angle-double-left","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","calendar","cancel","chevron-down","chevron-left","chevron-right","chevron-up","clock","code","comment-dots","comment-next","comment","comments","commit","credit-card","disk","doc_code","doc_image","doc_text","download","duplicate","earth","eye-slash","eye","file-additions","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","merge-request-close-m","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","star-o","star","stop","talic","task-done","template","thump-down","thump-up","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]} {"iconCount":135,"spriteSize":58718,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","calendar","cancel","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","comment-dots","comment-next","comment","comments","commit","credit-card","disk","doc_code","doc_image","doc_text","download","duplicate","earth","eye-slash","eye","file-additions","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","star-o","star","stop","talic","task-done","template","thump-down","thump-up","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file \ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -40,10 +40,10 @@ export default () => { ...@@ -40,10 +40,10 @@ export default () => {
class="text-center" class="text-center"
v-if="error"> v-if="error">
<span v-if="loadError"> <span v-if="loadError">
An error occured whilst loading the file. Please try again later. An error occurred whilst loading the file. Please try again later.
</span> </span>
<span v-else> <span v-else>
An error occured whilst parsing the file. An error occurred whilst parsing the file.
</span> </span>
</p> </p>
</div> </div>
......
...@@ -48,10 +48,10 @@ export default () => { ...@@ -48,10 +48,10 @@ export default () => {
class="text-center" class="text-center"
v-if="error"> v-if="error">
<span v-if="loadError"> <span v-if="loadError">
An error occured whilst loading the file. Please try again later. An error occurred whilst loading the file. Please try again later.
</span> </span>
<span v-else> <span v-else>
An error occured whilst decoding the file. An error occurred whilst decoding the file.
</span> </span>
</p> </p>
</div> </div>
......
...@@ -77,9 +77,6 @@ $(() => { ...@@ -77,9 +77,6 @@ $(() => {
}); });
Store.rootPath = this.boardsEndpoint; Store.rootPath = this.boardsEndpoint;
this.filterManager = new FilteredSearchBoards(Store.filter, true);
this.filterManager.setup();
// Listen for updateTokens event // Listen for updateTokens event
eventHub.$on('updateTokens', this.updateTokens); eventHub.$on('updateTokens', this.updateTokens);
}, },
...@@ -87,6 +84,9 @@ $(() => { ...@@ -87,6 +84,9 @@ $(() => {
eventHub.$off('updateTokens', this.updateTokens); eventHub.$off('updateTokens', this.updateTokens);
}, },
mounted () { mounted () {
this.filterManager = new FilteredSearchBoards(Store.filter, true);
this.filterManager.setup();
Store.disabled = this.disabled; Store.disabled = this.disabled;
gl.boardService.all() gl.boardService.all()
.then(response => response.json()) .then(response => response.json())
......
...@@ -68,7 +68,7 @@ export default { ...@@ -68,7 +68,7 @@ export default {
<div class="flash-container" <div class="flash-container"
v-if="error"> v-if="error">
<div class="flash-alert"> <div class="flash-alert">
An error occured. Please try again. An error occurred. Please try again.
</div> </div>
</div> </div>
<label class="label-light" <label class="label-light"
......
...@@ -167,7 +167,7 @@ window.Build = (function () { ...@@ -167,7 +167,7 @@ window.Build = (function () {
Build.prototype.getBuildTrace = function () { Build.prototype.getBuildTrace = function () {
return $.ajax({ return $.ajax({
url: `${this.pageUrl}/trace.json`, url: `${this.pageUrl}/trace.json`,
data: this.state, data: { state: this.state },
}) })
.done((log) => { .done((log) => {
setCiStatusFavicon(`${this.pageUrl}/status.json`); setCiStatusFavicon(`${this.pageUrl}/status.json`);
......
...@@ -298,7 +298,7 @@ class CopyAsGFM { ...@@ -298,7 +298,7 @@ class CopyAsGFM {
const documentFragment = getSelectedFragment(); const documentFragment = getSelectedFragment();
if (!documentFragment) return; if (!documentFragment) return;
const el = transformer(documentFragment.cloneNode(true)); const el = transformer(documentFragment.cloneNode(true), e.currentTarget);
if (!el) return; if (!el) return;
e.preventDefault(); e.preventDefault();
...@@ -338,55 +338,64 @@ class CopyAsGFM { ...@@ -338,55 +338,64 @@ class CopyAsGFM {
} }
static transformGFMSelection(documentFragment) { static transformGFMSelection(documentFragment) {
const gfmEls = documentFragment.querySelectorAll('.md, .wiki'); const gfmElements = documentFragment.querySelectorAll('.md, .wiki');
switch (gfmEls.length) { switch (gfmElements.length) {
case 0: { case 0: {
return documentFragment; return documentFragment;
} }
case 1: { case 1: {
return gfmEls[0]; return gfmElements[0];
} }
default: { default: {
const allGfmEl = document.createElement('div'); const allGfmElement = document.createElement('div');
for (let i = 0; i < gfmEls.length; i += 1) { for (let i = 0; i < gfmElements.length; i += 1) {
const lineEl = gfmEls[i]; const gfmElement = gfmElements[i];
allGfmEl.appendChild(lineEl); allGfmElement.appendChild(gfmElement);
allGfmEl.appendChild(document.createTextNode('\n\n')); allGfmElement.appendChild(document.createTextNode('\n\n'));
} }
return allGfmEl; return allGfmElement;
} }
} }
} }
static transformCodeSelection(documentFragment) { static transformCodeSelection(documentFragment, target) {
const lineEls = documentFragment.querySelectorAll('.line'); let lineSelector = '.line';
let codeEl; if (target) {
if (lineEls.length > 1) { const lineClass = ['left-side', 'right-side'].filter(name => target.classList.contains(name))[0];
codeEl = document.createElement('pre'); if (lineClass) {
codeEl.className = 'code highlight'; lineSelector = `.line_content.${lineClass} ${lineSelector}`;
}
}
const lineElements = documentFragment.querySelectorAll(lineSelector);
let codeElement;
if (lineElements.length > 1) {
codeElement = document.createElement('pre');
codeElement.className = 'code highlight';
const lang = lineEls[0].getAttribute('lang'); const lang = lineElements[0].getAttribute('lang');
if (lang) { if (lang) {
codeEl.setAttribute('lang', lang); codeElement.setAttribute('lang', lang);
} }
} else { } else {
codeEl = document.createElement('code'); codeElement = document.createElement('code');
} }
if (lineEls.length > 0) { if (lineElements.length > 0) {
for (let i = 0; i < lineEls.length; i += 1) { for (let i = 0; i < lineElements.length; i += 1) {
const lineEl = lineEls[i]; const lineElement = lineElements[i];
codeEl.appendChild(lineEl); codeElement.appendChild(lineElement);
codeEl.appendChild(document.createTextNode('\n')); codeElement.appendChild(document.createTextNode('\n'));
} }
} else { } else {
codeEl.appendChild(documentFragment); codeElement.appendChild(documentFragment);
} }
return codeEl; return codeElement;
} }
static nodeToGFM(node, respectWhitespaceParam = false) { static nodeToGFM(node, respectWhitespaceParam = false) {
......
<script> <script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
...@@ -8,6 +10,8 @@ ...@@ -8,6 +10,8 @@
}, },
components: { components: {
userAvatarImage, userAvatarImage,
limitWarning,
totalTime,
}, },
}; };
</script> </script>
......
<script> <script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
...@@ -8,6 +10,8 @@ ...@@ -8,6 +10,8 @@
}, },
components: { components: {
userAvatarImage, userAvatarImage,
limitWarning,
totalTime,
}, },
}; };
</script> </script>
......
<script> <script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import iconCommit from '../svg/icon_commit.svg'; import iconCommit from '../svg/icon_commit.svg';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
},
components: {
userAvatarImage,
},
computed: {
iconCommit() {
return iconCommit;
}, },
}, components: {
}; userAvatarImage,
totalTime,
limitWarning,
},
computed: {
iconCommit() {
return iconCommit;
},
},
};
</script> </script>
<template> <template>
<div> <div>
......
<script> <script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
...@@ -8,6 +10,8 @@ ...@@ -8,6 +10,8 @@
}, },
components: { components: {
userAvatarImage, userAvatarImage,
totalTime,
limitWarning,
}, },
}; };
</script> </script>
......
<script> <script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
...@@ -9,6 +11,8 @@ ...@@ -9,6 +11,8 @@
}, },
components: { components: {
userAvatarImage, userAvatarImage,
totalTime,
limitWarning,
}, },
computed: { computed: {
iconBranch() { iconBranch() {
......
<script> <script>
import iconBuildStatus from '../svg/icon_build_status.svg'; import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
import limitWarning from './limit_warning_component.vue';
import totalTime from './total_time_component.vue';
export default { export default {
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
},
computed: {
iconBuildStatus() {
return iconBuildStatus;
}, },
iconBranch() { components: {
return iconBranch; totalTime,
limitWarning,
}, },
}, computed: {
}; iconBuildStatus() {
return iconBuildStatus;
},
iconBranch() {
return iconBranch;
},
},
};
</script> </script>
<template> <template>
<div> <div>
......
...@@ -3,14 +3,12 @@ ...@@ -3,14 +3,12 @@
import Vue from 'vue'; import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
import limitWarningComponent from './components/limit_warning_component.vue';
import stageCodeComponent from './components/stage_code_component.vue'; import stageCodeComponent from './components/stage_code_component.vue';
import stagePlanComponent from './components/stage_plan_component.vue'; import stagePlanComponent from './components/stage_plan_component.vue';
import stageComponent from './components/stage_component.vue'; import stageComponent from './components/stage_component.vue';
import stageReviewComponent from './components/stage_review_component.vue'; import stageReviewComponent from './components/stage_review_component.vue';
import stageStagingComponent from './components/stage_staging_component.vue'; import stageStagingComponent from './components/stage_staging_component.vue';
import stageTestComponent from './components/stage_test_component.vue'; import stageTestComponent from './components/stage_test_component.vue';
import totalTime from './components/total_time_component.vue';
import CycleAnalyticsService from './cycle_analytics_service'; import CycleAnalyticsService from './cycle_analytics_service';
import CycleAnalyticsStore from './cycle_analytics_store'; import CycleAnalyticsStore from './cycle_analytics_store';
...@@ -133,8 +131,4 @@ $(() => { ...@@ -133,8 +131,4 @@ $(() => {
}, },
}, },
}); });
// Register global components
Vue.component('limit-warning', limitWarningComponent);
Vue.component('total-time', totalTime);
}); });
...@@ -24,7 +24,8 @@ class Diff { ...@@ -24,7 +24,8 @@ class Diff {
if (!isBound) { if (!isBound) {
$(document) $(document)
.on('click', '.js-unfold', this.handleClickUnfold.bind(this)) .on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)); .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this))
.on('mousedown', 'td.line_content.parallel', this.handleParallelLineDown.bind(this));
isBound = true; isBound = true;
} }
...@@ -100,6 +101,18 @@ class Diff { ...@@ -100,6 +101,18 @@ class Diff {
this.highlightSelectedLine(); this.highlightSelectedLine();
} }
handleParallelLineDown(e) {
const line = $(e.currentTarget);
const table = line.closest('table');
table.removeClass('left-side-selected right-side-selected');
const lineClass = ['left-side', 'right-side'].filter(name => line.hasClass(name))[0];
if (lineClass) {
table.addClass(`${lineClass}-selected`);
}
}
diffViewType() { diffViewType() {
return $('.inline-parallel-buttons a.active').data('view-type'); return $('.inline-parallel-buttons a.active').data('view-type');
} }
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
/* global NotificationsDropdown */ /* global NotificationsDropdown */
/* global GroupAvatar */ /* global GroupAvatar */
/* global LineHighlighter */ /* global LineHighlighter */
/* global ProjectFork */
/* global BuildArtifacts */ /* global BuildArtifacts */
/* global GroupsSelect */ /* global GroupsSelect */
/* global Search */ /* global Search */
...@@ -476,7 +475,9 @@ import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils'; ...@@ -476,7 +475,9 @@ import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:forks:new': case 'projects:forks:new':
new ProjectFork(); import(/* webpackChunkName: 'project_fork' */ './project_fork')
.then(fork => fork.default())
.catch(() => {});
break; break;
case 'projects:artifacts:browse': case 'projects:artifacts:browse':
new ShortcutsNavigation(); new ShortcutsNavigation();
......
...@@ -163,7 +163,7 @@ export default { ...@@ -163,7 +163,7 @@ export default {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => this.fetchEnvironments()) .then(() => this.fetchEnvironments())
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
} }
}, },
......
...@@ -158,7 +158,7 @@ export default { ...@@ -158,7 +158,7 @@ export default {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => this.fetchEnvironments()) .then(() => this.fetchEnvironments())
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
} }
}, },
}, },
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a
*/ */
import Cookies from 'js-cookie';
const LINE_NUMBER_CLASS = 'diff-line-num'; const LINE_NUMBER_CLASS = 'diff-line-num';
const UNFOLDABLE_LINE_CLASS = 'js-unfold'; const UNFOLDABLE_LINE_CLASS = 'js-unfold';
const NO_COMMENT_CLASS = 'no-comment-btn'; const NO_COMMENT_CLASS = 'no-comment-btn';
...@@ -27,9 +29,7 @@ export default { ...@@ -27,9 +29,7 @@ export default {
this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === ''; this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === '';
} }
if (typeof notes !== 'undefined' && !this.isParallelView) { this.isParallelView = Cookies.get('diff_view') === 'parallel';
this.isParallelView = notes.isParallelView && notes.isParallelView();
}
if (this.userCanCreateNote) { if (this.userCanCreateNote) {
$diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e)) $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e))
......
...@@ -14,7 +14,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { ...@@ -14,7 +14,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown {
loadingTemplate: this.loadingTemplate, loadingTemplate: this.loadingTemplate,
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -17,7 +17,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { ...@@ -17,7 +17,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
preprocessing, preprocessing,
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -26,7 +26,7 @@ class DropdownUser extends gl.FilteredSearchDropdown { ...@@ -26,7 +26,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
}, },
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -36,7 +36,7 @@ class FilteredSearchManager { ...@@ -36,7 +36,7 @@ class FilteredSearchManager {
.catch((error) => { .catch((error) => {
if (error.name === 'RecentSearchesServiceError') return undefined; if (error.name === 'RecentSearchesServiceError') return undefined;
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new window.Flash('An error occured while parsing recent searches'); new window.Flash('An error occurred while parsing recent searches');
// Gracefully fail to empty array // Gracefully fail to empty array
return []; return [];
}) })
......
...@@ -67,10 +67,13 @@ const PARTICIPANTS_ROW_COUNT = 7; ...@@ -67,10 +67,13 @@ const PARTICIPANTS_ROW_COUNT = 7;
originalText = $(this).data("original-text"); originalText = $(this).data("original-text");
if (currentText === originalText) { if (currentText === originalText) {
$(this).text(lessText); $(this).text(lessText);
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
} else { } else {
$(this).text(originalText); $(this).text(originalText);
} }
return $(".js-participants-hidden").toggle();
$(".js-participants-hidden").toggle();
}; };
return IssuableContext; return IssuableContext;
......
...@@ -71,6 +71,7 @@ export const handleLocationHash = () => { ...@@ -71,6 +71,7 @@ export const handleLocationHash = () => {
// This is required to handle non-unicode characters in hash // This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash); hash = decodeURIComponent(hash);
const target = document.getElementById(hash) || document.getElementById(`user-content-${hash}`);
const fixedTabs = document.querySelector('.js-tabs-affix'); const fixedTabs = document.querySelector('.js-tabs-affix');
const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck'); const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck');
const fixedNav = document.querySelector('.navbar-gitlab'); const fixedNav = document.querySelector('.navbar-gitlab');
...@@ -78,25 +79,19 @@ export const handleLocationHash = () => { ...@@ -78,25 +79,19 @@ export const handleLocationHash = () => {
let adjustment = 0; let adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight; if (fixedNav) adjustment -= fixedNav.offsetHeight;
// scroll to user-generated markdown anchor if we cannot find a match if (target && target.scrollIntoView) {
if (document.getElementById(hash) === null) { target.scrollIntoView(true);
const target = document.getElementById(`user-content-${hash}`); }
if (target && target.scrollIntoView) {
target.scrollIntoView(true);
window.scrollBy(0, adjustment);
}
} else {
// only adjust for fixedTabs when not targeting user-generated content
if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight;
}
if (fixedDiffStats) { if (fixedTabs) {
adjustment -= fixedDiffStats.offsetHeight; adjustment -= fixedTabs.offsetHeight;
} }
window.scrollBy(0, adjustment); if (fixedDiffStats) {
adjustment -= fixedDiffStats.offsetHeight;
} }
window.scrollBy(0, adjustment);
}; };
// Check if element scrolled into viewport from above or below // Check if element scrolled into viewport from above or below
......
...@@ -55,7 +55,7 @@ window.dateFormat = dateFormat; ...@@ -55,7 +55,7 @@ window.dateFormat = dateFormat;
if (!timeagoInstance) { if (!timeagoInstance) {
const localeRemaining = function(number, index) { const localeRemaining = function(number, index) {
return [ return [
[s__('Timeago|less than a minute ago'), s__('Timeago|a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
[s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
[s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
...@@ -73,7 +73,7 @@ window.dateFormat = dateFormat; ...@@ -73,7 +73,7 @@ window.dateFormat = dateFormat;
}; };
locale = function(number, index) { locale = function(number, index) {
return [ return [
[s__('Timeago|less than a minute ago'), s__('Timeago|a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
[s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
[s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
......
...@@ -28,148 +28,149 @@ ...@@ -28,148 +28,149 @@
// </div> // </div>
// </div> // </div>
// //
(function() {
this.LineHighlighter = (function() {
// CSS class applied to highlighted lines
LineHighlighter.prototype.highlightClass = 'hll';
// Internal copy of location.hash so we're not dependent on `location` in tests
LineHighlighter.prototype._hash = '';
function LineHighlighter(hash) {
if (hash == null) {
// Initialize a LineHighlighter object
//
// hash - String URL hash for dependency injection in tests
hash = location.hash;
}
this.setHash = this.setHash.bind(this);
this.highlightLine = this.highlightLine.bind(this);
this.clickHandler = this.clickHandler.bind(this);
this.highlightHash = this.highlightHash.bind(this);
this._hash = hash;
this.bindEvents();
this.highlightHash();
}
LineHighlighter.prototype.bindEvents = function() { const LineHighlighter = function(options = {}) {
const $fileHolder = $('.file-holder'); options.highlightLineClass = options.highlightLineClass || 'hll';
$fileHolder.on('click', 'a[data-line-number]', this.clickHandler); options.fileHolderSelector = options.fileHolderSelector || '.file-holder';
$fileHolder.on('highlight:line', this.highlightHash); options.scrollFileHolder = options.scrollFileHolder || false;
}; options.hash = options.hash || location.hash;
LineHighlighter.prototype.highlightHash = function() {
var range;
if (this._hash !== '') {
range = this.hashToRange(this._hash);
if (range[0]) {
this.highlightRange(range);
$.scrollTo("#L" + range[0], {
// Scroll to the first highlighted line on initial load
// Offset -50 for the sticky top bar, and another -100 for some context
offset: -150
});
}
}
};
LineHighlighter.prototype.clickHandler = function(event) {
var current, lineNumber, range;
event.preventDefault();
this.clearHighlight();
lineNumber = $(event.target).closest('a').data('line-number');
current = this.hashToRange(this._hash);
if (!(current[0] && event.shiftKey)) {
// If there's no current selection, or there is but Shift wasn't held,
// treat this like a single-line selection.
this.setHash(lineNumber);
return this.highlightLine(lineNumber);
} else if (event.shiftKey) {
if (lineNumber < current[0]) {
range = [lineNumber, current[0]];
} else {
range = [current[0], lineNumber];
}
this.setHash(range[0], range[1]);
return this.highlightRange(range);
}
};
LineHighlighter.prototype.clearHighlight = function() {
return $("." + this.highlightClass).removeClass(this.highlightClass);
// Unhighlight previously highlighted lines
};
// Convert a URL hash String into line numbers
//
// hash - Hash String
//
// Examples:
//
// hashToRange('#L5') # => [5, null]
// hashToRange('#L5-15') # => [5, 15]
// hashToRange('#foo') # => [null, null]
//
// Returns an Array
LineHighlighter.prototype.hashToRange = function(hash) {
var first, last, matches;
// ?L(\d+)(?:-(\d+))?$/)
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/);
if (matches && matches.length) {
first = parseInt(matches[1], 10);
last = matches[2] ? parseInt(matches[2], 10) : null;
return [first, last];
} else {
return [null, null];
}
};
// Highlight a single line
//
// lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) {
return $("#LC" + lineNumber).addClass(this.highlightClass);
};
// Highlight all lines within a range
//
// range - Array containing the starting and ending line numbers
LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results;
if (range[1]) {
results = [];
for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) {
results.push(this.highlightLine(lineNumber));
}
return results;
} else {
return this.highlightLine(range[0]);
}
};
// Set the URL hash string this.options = options;
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) { this._hash = options.hash;
var hash; this.highlightLineClass = options.highlightLineClass;
if (lastLineNumber) { this.setHash = this.setHash.bind(this);
hash = "#L" + firstLineNumber + "-" + lastLineNumber; this.highlightLine = this.highlightLine.bind(this);
this.clickHandler = this.clickHandler.bind(this);
this.highlightHash = this.highlightHash.bind(this);
this.bindEvents();
this.highlightHash();
};
LineHighlighter.prototype.bindEvents = function() {
const $fileHolder = $(this.options.fileHolderSelector);
$fileHolder.on('click', 'a[data-line-number]', this.clickHandler);
$fileHolder.on('highlight:line', this.highlightHash);
};
LineHighlighter.prototype.highlightHash = function() {
var range;
if (this._hash !== '') {
range = this.hashToRange(this._hash);
if (range[0]) {
this.highlightRange(range);
const lineSelector = `#L${range[0]}`;
const scrollOptions = {
// Scroll to the first highlighted line on initial load
// Offset -50 for the sticky top bar, and another -100 for some context
offset: -150
};
if (this.options.scrollFileHolder) {
$(this.options.fileHolderSelector).scrollTo(lineSelector, scrollOptions);
} else { } else {
hash = "#L" + firstLineNumber; $.scrollTo(lineSelector, scrollOptions);
} }
this._hash = hash; }
return this.__setLocationHash__(hash); }
}; };
// Make the actual hash change in the browser LineHighlighter.prototype.clickHandler = function(event) {
// var current, lineNumber, range;
// This method is stubbed in tests. event.preventDefault();
LineHighlighter.prototype.__setLocationHash__ = function(value) { this.clearHighlight();
return history.pushState({ lineNumber = $(event.target).closest('a').data('line-number');
url: value current = this.hashToRange(this._hash);
// We're using pushState instead of assigning location.hash directly to if (!(current[0] && event.shiftKey)) {
// prevent the page from scrolling on the hashchange event // If there's no current selection, or there is but Shift wasn't held,
}, document.title, value); // treat this like a single-line selection.
}; this.setHash(lineNumber);
return this.highlightLine(lineNumber);
return LineHighlighter; } else if (event.shiftKey) {
})(); if (lineNumber < current[0]) {
}).call(window); range = [lineNumber, current[0]];
} else {
range = [current[0], lineNumber];
}
this.setHash(range[0], range[1]);
return this.highlightRange(range);
}
};
LineHighlighter.prototype.clearHighlight = function() {
return $("." + this.highlightLineClass).removeClass(this.highlightLineClass);
};
// Convert a URL hash String into line numbers
//
// hash - Hash String
//
// Examples:
//
// hashToRange('#L5') # => [5, null]
// hashToRange('#L5-15') # => [5, 15]
// hashToRange('#foo') # => [null, null]
//
// Returns an Array
LineHighlighter.prototype.hashToRange = function(hash) {
var first, last, matches;
// ?L(\d+)(?:-(\d+))?$/)
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/);
if (matches && matches.length) {
first = parseInt(matches[1], 10);
last = matches[2] ? parseInt(matches[2], 10) : null;
return [first, last];
} else {
return [null, null];
}
};
// Highlight a single line
//
// lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) {
return $("#LC" + lineNumber).addClass(this.highlightLineClass);
};
// Highlight all lines within a range
//
// range - Array containing the starting and ending line numbers
LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results;
if (range[1]) {
results = [];
for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) {
results.push(this.highlightLine(lineNumber));
}
return results;
} else {
return this.highlightLine(range[0]);
}
};
// Set the URL hash string
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
var hash;
if (lastLineNumber) {
hash = "#L" + firstLineNumber + "-" + lastLineNumber;
} else {
hash = "#L" + firstLineNumber;
}
this._hash = hash;
return this.__setLocationHash__(hash);
};
// Make the actual hash change in the browser
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
return history.pushState({
url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
}, document.title, value);
};
window.LineHighlighter = LineHighlighter;
...@@ -16,9 +16,8 @@ const locales = allLocales.reduce((d, obj) => { ...@@ -16,9 +16,8 @@ const locales = allLocales.reduce((d, obj) => {
return data; return data;
}, {}); }, {});
let lang = document.querySelector('html').getAttribute('lang') || 'en'; const langAttribute = document.querySelector('html').getAttribute('lang');
lang = lang.replace(/-/g, '_'); const lang = (langAttribute || 'en').replace(/-/g, '_');
const locale = new Jed(locales[lang]); const locale = new Jed(locales[lang]);
/** /**
......
...@@ -124,7 +124,6 @@ import './preview_markdown'; ...@@ -124,7 +124,6 @@ import './preview_markdown';
import './project'; import './project';
import './project_avatar'; import './project_avatar';
import './project_find_file'; import './project_find_file';
import './project_fork';
import './project_import'; import './project_import';
import './project_label_subscription'; import './project_label_subscription';
import './project_new'; import './project_new';
...@@ -302,7 +301,10 @@ $(function () { ...@@ -302,7 +301,10 @@ $(function () {
return $container.remove(); return $container.remove();
// Commit show suppressed diff // Commit show suppressed diff
}); });
$('.navbar-toggle').on('click', () => $('.header-content').toggleClass('menu-expanded')); $('.navbar-toggle').on('click', () => {
$('.header-content').toggleClass('menu-expanded');
gl.lazyLoader.loadCheck();
});
// Show/hide comments on diff // Show/hide comments on diff
$body.on('click', '.js-toggle-diff-comments', function (e) { $body.on('click', '.js-toggle-diff-comments', function (e) {
var $this = $(this); var $this = $(this);
......
...@@ -352,7 +352,7 @@ import { ...@@ -352,7 +352,7 @@ import {
} }
expandViewContainer() { expandViewContainer() {
const $wrapper = $('.content-wrapper .container-fluid'); const $wrapper = $('.content-wrapper .container-fluid').not('.breadcrumbs');
if (this.fixedLayoutPref === null) { if (this.fixedLayoutPref === null) {
this.fixedLayoutPref = $wrapper.hasClass('container-limited'); this.fixedLayoutPref = $wrapper.hasClass('container-limited');
} }
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
}, },
deleteHandler() { deleteHandler() {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
if (confirm('Are you sure you want to delete this list?')) { if (confirm('Are you sure you want to delete this comment?')) {
this.isDeleting = true; this.isDeleting = true;
this.deleteNote(this.note) this.deleteNote(this.note)
......
...@@ -28,8 +28,7 @@ ...@@ -28,8 +28,7 @@
popoverOptions() { popoverOptions() {
return { return {
html: true, html: true,
delay: { hide: 600 }, trigger: 'focus',
trigger: 'hover',
placement: 'top', placement: 'top',
title: '<div class="autodevops-title">This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b></div>', title: '<div class="autodevops-title">This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b></div>',
content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow">Learn more about Auto DevOps</a>`, content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow">Learn more about Auto DevOps</a>`,
...@@ -75,6 +74,7 @@ ...@@ -75,6 +74,7 @@
</span> </span>
<a <a
v-if="pipeline.flags.auto_devops" v-if="pipeline.flags.auto_devops"
tabindex="0"
class="js-pipeline-url-autodevops label label-info autodevops-badge" class="js-pipeline-url-autodevops label label-info autodevops-badge"
v-popover="popoverOptions" v-popover="popoverOptions"
role="button"> role="button">
......
...@@ -97,7 +97,7 @@ export default { ...@@ -97,7 +97,7 @@ export default {
postAction(endpoint) { postAction(endpoint) {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => eventHub.$emit('refreshPipelines')) .then(() => eventHub.$emit('refreshPipelines'))
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
}, },
}, },
}; };
...@@ -73,7 +73,8 @@ import _ from 'underscore'; ...@@ -73,7 +73,8 @@ import _ from 'underscore';
aspectRatio: 1, aspectRatio: 1,
modal: true, modal: true,
scalable: false, scalable: false,
rotatable: false, rotatable: true,
checkOrientation: true,
zoomable: true, zoomable: true,
dragMode: 'move', dragMode: 'move',
guides: false, guides: false,
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */ export default () => {
(function() { $('.fork-thumbnail a').on('click', function forkThumbnailClicked() {
this.ProjectFork = (function() { if ($(this).hasClass('disabled')) return false;
function ProjectFork() {
$('.fork-thumbnail a').on('click', function() {
$('.fork-namespaces').hide();
return $('.save-project-loader').show();
});
}
return ProjectFork; $('.fork-namespaces').hide();
})(); return $('.save-project-loader').show();
}).call(window); });
};
...@@ -19,7 +19,7 @@ export default class ProjectsService { ...@@ -19,7 +19,7 @@ export default class ProjectsService {
getSearchedProjects(searchQuery) { getSearchedProjects(searchQuery) {
return this.projectsPath.get({ return this.projectsPath.get({
simple: false, simple: true,
per_page: 20, per_page: 20,
membership: !!gon.current_user_id, membership: !!gon.current_user_id,
order_by: 'last_activity_at', order_by: 'last_activity_at',
......
...@@ -37,14 +37,14 @@ export default { ...@@ -37,14 +37,14 @@ export default {
content: f.newContent, content: f.newContent,
})); }));
const payload = { const payload = {
branch: Store.targetBranch, branch: Store.currentBranch,
commit_message: commitMessage, commit_message: commitMessage,
actions, actions,
}; };
Store.submitCommitsLoading = true; Store.submitCommitsLoading = true;
Service.commitFiles(payload) Service.commitFiles(payload)
.then(this.resetCommitState) .then(this.resetCommitState)
.catch(() => Flash('An error occured while committing your changes')); .catch(() => Flash('An error occurred while committing your changes'));
}, },
resetCommitState() { resetCommitState() {
...@@ -105,7 +105,7 @@ export default { ...@@ -105,7 +105,7 @@ export default {
</label> </label>
<div class="col-md-6"> <div class="col-md-6">
<span class="help-block"> <span class="help-block">
{{targetBranch}} {{currentBranch}}
</span> </span>
</div> </div>
</div> </div>
......
...@@ -26,16 +26,6 @@ export default { ...@@ -26,16 +26,6 @@ export default {
this.editMode = !this.editMode; this.editMode = !this.editMode;
Store.toggleBlobView(); Store.toggleBlobView();
}, },
toggleProjectRefsForm() {
$('.project-refs-form').toggleClass('disabled', this.editMode);
$('.js-tree-ref-target-holder').toggle(this.editMode);
},
},
watch: {
editMode() {
this.toggleProjectRefsForm();
},
}, },
}; };
</script> </script>
......
...@@ -95,7 +95,7 @@ export default RepoFile; ...@@ -95,7 +95,7 @@ export default RepoFile;
</div> </div>
</td> </td>
<td class="hidden-xs"> <td class="hidden-xs text-right">
<span <span
class="commit-update" class="commit-update"
:title="tooltipTitle(file.lastCommitUpdate)"> :title="tooltipTitle(file.lastCommitUpdate)">
......
<script> <script>
/* global LineHighlighter */
import Store from '../stores/repo_store'; import Store from '../stores/repo_store';
export default { export default {
data: () => Store, data: () => Store,
mounted() {
this.highlightFile();
},
computed: { computed: {
html() { html() {
return this.activeFile.html; return this.activeFile.html;
}, },
}, },
methods: { methods: {
highlightFile() { highlightFile() {
$(this.$el).find('.file-content').syntaxHighlight(); $(this.$el).find('.file-content').syntaxHighlight();
}, },
}, },
mounted() {
this.highlightFile();
this.lineHighlighter = new LineHighlighter({
fileHolderSelector: '.blob-viewer-container',
scrollFileHolder: true,
});
},
watch: { watch: {
html() { html() {
this.$nextTick(() => { this.$nextTick(() => {
...@@ -45,7 +49,7 @@ export default { ...@@ -45,7 +49,7 @@ export default {
v-else v-else
class="vertical-center render-error"> class="vertical-center render-error">
<p class="text-center"> <p class="text-center">
The source could not be displayed because a rendering error occured. You can <a :href="activeFile.raw_path">download</a> it instead. The source could not be displayed because a rendering error occurred. You can <a :href="activeFile.raw_path">download</a> it instead.
</p> </p>
</div> </div>
</div> </div>
......
...@@ -37,17 +37,24 @@ export default { ...@@ -37,17 +37,24 @@ export default {
let file = clickedFile; let file = clickedFile;
if (file.loading) return; if (file.loading) return;
file.loading = true; file.loading = true;
if (file.type === 'tree' && file.opened) { if (file.type === 'tree' && file.opened) {
file = Store.removeChildFilesOfTree(file); file = Store.removeChildFilesOfTree(file);
file.loading = false; file.loading = false;
} else { } else {
Service.url = file.url; const openFile = Helper.getFileFromPath(file.url);
Helper.getContent(file) if (openFile) {
.then(() => { file.loading = false;
file.loading = false; Store.setActiveFiles(openFile);
Helper.scrollTabsRight(); } else {
}) Service.url = file.url;
.catch(Helper.loadingError); Helper.getContent(file)
.then(() => {
file.loading = false;
Helper.scrollTabsRight();
})
.catch(Helper.loadingError);
}
} }
}, },
...@@ -68,7 +75,7 @@ export default { ...@@ -68,7 +75,7 @@ export default {
<tr> <tr>
<th class="name">Name</th> <th class="name">Name</th>
<th class="hidden-sm hidden-xs last-commit">Last Commit</th> <th class="hidden-sm hidden-xs last-commit">Last Commit</th>
<th class="hidden-xs last-update">Last Update</th> <th class="hidden-xs last-update text-right">Last Update</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
......
...@@ -58,13 +58,13 @@ const RepoHelper = { ...@@ -58,13 +58,13 @@ const RepoHelper = {
return langs.find(lang => lang.extensions && lang.extensions.indexOf(`.${ext}`) > -1); return langs.find(lang => lang.extensions && lang.extensions.indexOf(`.${ext}`) > -1);
}, },
setDirectoryOpen(tree) { setDirectoryOpen(tree, title) {
const file = tree; const file = tree;
if (!file) return undefined; if (!file) return undefined;
file.opened = true; file.opened = true;
file.icon = 'fa-folder-open'; file.icon = 'fa-folder-open';
RepoHelper.updateHistoryEntry(file.url, file.name); RepoHelper.updateHistoryEntry(file.url, title);
return file; return file;
}, },
...@@ -135,6 +135,8 @@ const RepoHelper = { ...@@ -135,6 +135,8 @@ const RepoHelper = {
return Service.getContent() return Service.getContent()
.then((response) => { .then((response) => {
const data = response.data; const data = response.data;
if (response.headers && response.headers['page-title']) data.pageTitle = response.headers['page-title'];
Store.isTree = RepoHelper.isTree(data); Store.isTree = RepoHelper.isTree(data);
if (!Store.isTree) { if (!Store.isTree) {
if (!file) file = data; if (!file) file = data;
...@@ -168,7 +170,7 @@ const RepoHelper = { ...@@ -168,7 +170,7 @@ const RepoHelper = {
} else { } else {
// it's a tree // it's a tree
if (!file) Store.isRoot = RepoHelper.isRoot(Service.url); if (!file) Store.isRoot = RepoHelper.isRoot(Service.url);
file = RepoHelper.setDirectoryOpen(file); file = RepoHelper.setDirectoryOpen(file, data.pageTitle || data.name);
const newDirectory = RepoHelper.dataToListOfFiles(data); const newDirectory = RepoHelper.dataToListOfFiles(data);
Store.addFilesToDirectory(file, Store.files, newDirectory); Store.addFilesToDirectory(file, Store.files, newDirectory);
Store.prevURL = Service.blobURLtoParentTree(Service.url); Store.prevURL = Service.blobURLtoParentTree(Service.url);
...@@ -255,7 +257,7 @@ const RepoHelper = { ...@@ -255,7 +257,7 @@ const RepoHelper = {
history.pushState({ key: RepoHelper.key }, '', url); history.pushState({ key: RepoHelper.key }, '', url);
if (title) { if (title) {
document.title = `${title} · GitLab`; document.title = title;
} }
}, },
...@@ -263,6 +265,10 @@ const RepoHelper = { ...@@ -263,6 +265,10 @@ const RepoHelper = {
return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url); return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url);
}, },
getFileFromPath(path) {
return Store.openedFiles.find(file => file.url === path);
},
loadingError() { loadingError() {
Flash('Unable to load this content at this time.'); Flash('Unable to load this content at this time.');
}, },
......
...@@ -11,10 +11,6 @@ function initDropdowns() { ...@@ -11,10 +11,6 @@ function initDropdowns() {
} }
function addEventsForNonVueEls() { function addEventsForNonVueEls() {
$(document).on('change', '.dropdown', () => {
Store.targetBranch = $('.project-refs-target-form input[name="ref"]').val();
});
window.onbeforeunload = function confirmUnload(e) { window.onbeforeunload = function confirmUnload(e) {
const hasChanged = Store.openedFiles const hasChanged = Store.openedFiles
.some(file => file.changed); .some(file => file.changed);
......
...@@ -32,7 +32,6 @@ const RepoStore = { ...@@ -32,7 +32,6 @@ const RepoStore = {
isCommitable: false, isCommitable: false,
binary: false, binary: false,
currentBranch: '', currentBranch: '',
targetBranch: 'new-branch',
commitMessage: '', commitMessage: '',
binaryTypes: { binaryTypes: {
png: false, png: false,
...@@ -84,7 +83,7 @@ const RepoStore = { ...@@ -84,7 +83,7 @@ const RepoStore = {
}).catch(Helper.loadingError); }).catch(Helper.loadingError);
} }
if (!file.loading) Helper.updateHistoryEntry(file.url, file.name); if (!file.loading) Helper.updateHistoryEntry(file.url, file.pageTitle || file.name);
RepoStore.binary = file.binary; RepoStore.binary = file.binary;
}, },
......
...@@ -29,28 +29,32 @@ import Cookies from 'js-cookie'; ...@@ -29,28 +29,32 @@ import Cookies from 'js-cookie';
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
$document.on('click', '.js-sidebar-toggle', function(e, triggered) { $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
$this = $(this);
$thisIcon = $this.find('i');
$allGutterToggleIcons = $('.js-sidebar-toggle i');
if ($thisIcon.hasClass('fa-angle-double-right')) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
$('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
} else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
}
if (!triggered) {
return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
});
return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
}; };
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
$this = $(this);
$thisIcon = $this.find('i');
$allGutterToggleIcons = $('.js-sidebar-toggle i');
if ($thisIcon.hasClass('fa-angle-double-right')) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
$('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
} else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
}
if (!triggered) {
Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
};
Sidebar.prototype.toggleTodo = function(e) { Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url; var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget); $this = $(e.currentTarget);
......
...@@ -287,6 +287,7 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '. ...@@ -287,6 +287,7 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
onClearInputClick(e) { onClearInputClick(e) {
e.preventDefault(); e.preventDefault();
this.wrap.toggleClass('has-value', !!e.target.value);
return this.searchInput.val('').focus(); return this.searchInput.val('').focus();
} }
......
...@@ -38,7 +38,7 @@ class SidebarMoveIssue { ...@@ -38,7 +38,7 @@ class SidebarMoveIssue {
data: (searchTerm, callback) => { data: (searchTerm, callback) => {
this.mediator.fetchAutocompleteProjects(searchTerm) this.mediator.fetchAutocompleteProjects(searchTerm)
.then(callback) .then(callback)
.catch(() => new Flash('An error occured while fetching projects autocomplete.')); .catch(() => new Flash('An error occurred while fetching projects autocomplete.'));
}, },
renderRow: project => ` renderRow: project => `
<li> <li>
...@@ -73,7 +73,7 @@ class SidebarMoveIssue { ...@@ -73,7 +73,7 @@ class SidebarMoveIssue {
this.mediator.moveIssue() this.mediator.moveIssue()
.catch(() => { .catch(() => {
Flash('An error occured while moving the issue.'); Flash('An error occurred while moving the issue.');
this.$confirmButton this.$confirmButton
.enable() .enable()
.removeClass('is-loading'); .removeClass('is-loading');
......
...@@ -41,7 +41,7 @@ export default class SidebarMediator { ...@@ -41,7 +41,7 @@ export default class SidebarMediator {
this.store.setAssigneeData(data); this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data); this.store.setTimeTrackingData(data);
}) })
.catch(() => new Flash('Error occured when fetching sidebar data')); .catch(() => new Flash('Error occurred when fetching sidebar data'));
} }
fetchAutocompleteProjects(searchTerm) { fetchAutocompleteProjects(searchTerm) {
......
...@@ -27,7 +27,7 @@ export default { ...@@ -27,7 +27,7 @@ export default {
<button <button
v-if="showDisabledButton" v-if="showDisabledButton"
type="button" type="button"
class="btn btn-success btn-sm" class="js-disabled-merge-button btn btn-success btn-sm"
disabled="true"> disabled="true">
Merge Merge
</button> </button>
......
...@@ -16,9 +16,9 @@ export default { ...@@ -16,9 +16,9 @@ export default {
<div class="media-body"> <div class="media-body">
<mr-widget-author-and-time <mr-widget-author-and-time
actionText="Closed by" actionText="Closed by"
:author="mr.closedBy" :author="mr.closedEvent.author"
:dateTitle="mr.updatedAt" :dateTitle="mr.closedEvent.updatedAt"
:dateReadable="mr.closedAt" :dateReadable="mr.closedEvent.formattedUpdatedAt"
/> />
<section class="mr-info-list"> <section class="mr-info-list">
<p> <p>
......
...@@ -10,27 +10,37 @@ export default { ...@@ -10,27 +10,37 @@ export default {
}, },
template: ` template: `
<div class="mr-widget-body media"> <div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton /> <status-icon
status="failed"
showDisabledButton />
<div class="media-body space-children"> <div class="media-body space-children">
<span class="bold"> <span
There are merge conflicts<span v-if="!mr.canMerge">.</span> v-if="mr.shouldBeRebased"
<span v-if="!mr.canMerge"> class="bold">
Resolve these conflicts or ask someone with write access to this repository to merge it locally Fast-forward merge is not possible.
</span> To merge this request, first rebase locally.
</span> </span>
<a <template v-else>
v-if="mr.canMerge && mr.conflictResolutionPath" <span class="bold">
:href="mr.conflictResolutionPath" There are merge conflicts<span v-if="!mr.canMerge">.</span>
class="btn btn-default btn-xs js-resolve-conflicts-button"> <span v-if="!mr.canMerge">
Resolve conflicts Resolve these conflicts or ask someone with write access to this repository to merge it locally
</a> </span>
<a </span>
v-if="mr.canMerge" <a
class="btn btn-default btn-xs js-merge-locally-button" v-if="mr.canMerge && mr.conflictResolutionPath"
data-toggle="modal" :href="mr.conflictResolutionPath"
href="#modal_merge_info"> class="js-resolve-conflicts-button btn btn-default btn-xs">
Merge locally Resolve conflicts
</a> </a>
<a
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
</template>
</div> </div>
</div> </div>
`, `,
......
...@@ -69,9 +69,9 @@ export default { ...@@ -69,9 +69,9 @@ export default {
<div class="space-children"> <div class="space-children">
<mr-widget-author-and-time <mr-widget-author-and-time
actionText="Merged by" actionText="Merged by"
:author="mr.mergedBy" :author="mr.mergedEvent.author"
:dateTitle="mr.updatedAt" :date-title="mr.mergedEvent.updatedAt"
:dateReadable="mr.mergedAt" /> :date-readable="mr.mergedEvent.formattedUpdatedAt" />
<a <a
v-if="mr.canRevertInCurrentMR" v-if="mr.canRevertInCurrentMR"
v-tooltip v-tooltip
......
...@@ -284,10 +284,16 @@ export default { ...@@ -284,10 +284,16 @@ export default {
:mr="mr" :mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" /> :is-merge-button-disabled="isMergeButtonDisabled" />
<span
v-if="mr.ffOnlyEnabled"
class="js-fast-forward-message">
Fast-forward merge without a merge commit
</span>
<button <button
v-else
@click="toggleCommitMessageEditor" @click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled" :disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs" class="js-modify-commit-message-button btn btn-default btn-xs"
type="button"> type="button">
Modify commit message Modify commit message
</button> </button>
......
...@@ -37,10 +37,8 @@ export default class MergeRequestStore { ...@@ -37,10 +37,8 @@ export default class MergeRequestStore {
} }
this.updatedAt = data.updated_at; this.updatedAt = data.updated_at;
this.mergedAt = MergeRequestStore.getEventDate(data.merge_event); this.mergedEvent = MergeRequestStore.getEventObject(data.merge_event);
this.closedAt = MergeRequestStore.getEventDate(data.closed_event); this.closedEvent = MergeRequestStore.getEventObject(data.closed_event);
this.mergedBy = MergeRequestStore.getAuthorObject(data.merge_event);
this.closedBy = MergeRequestStore.getAuthorObject(data.closed_event);
this.setToMWPSBy = MergeRequestStore.getAuthorObject({ author: data.merge_user || {} }); this.setToMWPSBy = MergeRequestStore.getAuthorObject({ author: data.merge_user || {} });
this.mergeUserId = data.merge_user_id; this.mergeUserId = data.merge_user_id;
this.currentUserId = gon.current_user_id; this.currentUserId = gon.current_user_id;
...@@ -57,6 +55,8 @@ export default class MergeRequestStore { ...@@ -57,6 +55,8 @@ export default class MergeRequestStore {
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false; this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false; this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false;
this.mergePath = data.merge_path; this.mergePath = data.merge_path;
this.ffOnlyEnabled = data.ff_only_enabled;
this.shouldBeRebased = !!data.should_be_rebased;
this.statusPath = data.status_path; this.statusPath = data.status_path;
this.emailPatchesPath = data.email_patches_path; this.emailPatchesPath = data.email_patches_path;
this.plainDiffPath = data.plain_diff_path; this.plainDiffPath = data.plain_diff_path;
...@@ -118,6 +118,14 @@ export default class MergeRequestStore { ...@@ -118,6 +118,14 @@ export default class MergeRequestStore {
} }
} }
static getEventObject(event) {
return {
author: MergeRequestStore.getAuthorObject(event),
updatedAt: gl.utils.formatDate(MergeRequestStore.getEventUpdatedAtDate(event)),
formattedUpdatedAt: MergeRequestStore.getEventDate(event),
};
}
static getAuthorObject(event) { static getAuthorObject(event) {
if (!event) { if (!event) {
return {}; return {};
...@@ -131,6 +139,14 @@ export default class MergeRequestStore { ...@@ -131,6 +139,14 @@ export default class MergeRequestStore {
}; };
} }
static getEventUpdatedAtDate(event) {
if (!event) {
return '';
}
return event.updated_at;
}
static getEventDate(event) { static getEventDate(event) {
const timeagoInstance = new Timeago(); const timeagoInstance = new Timeago();
...@@ -138,7 +154,7 @@ export default class MergeRequestStore { ...@@ -138,7 +154,7 @@ export default class MergeRequestStore {
return ''; return '';
} }
return timeagoInstance.format(event.updated_at); return timeagoInstance.format(MergeRequestStore.getEventUpdatedAtDate(event));
} }
} }
...@@ -873,6 +873,13 @@ ...@@ -873,6 +873,13 @@
min-width: 100%; min-width: 100%;
} }
} }
header.navbar-gitlab-new .header-content .dropdown {
.dropdown-menu {
left: 0;
min-width: 100%;
}
}
} }
@include new-style-dropdown('.breadcrumbs-list .dropdown '); @include new-style-dropdown('.breadcrumbs-list .dropdown ');
......
...@@ -126,7 +126,7 @@ ...@@ -126,7 +126,7 @@
.search-input-wrap { .search-input-wrap {
.search-icon, .search-icon,
.clear-icon { .clear-icon {
color: rgba($color-200, .8); fill: rgba($color-200, .8);
} }
} }
...@@ -141,7 +141,7 @@ ...@@ -141,7 +141,7 @@
.search-input-wrap { .search-input-wrap {
.search-icon { .search-icon {
color: rgba($color-200, .8); fill: rgba($color-200, .8);
} }
} }
} }
...@@ -252,7 +252,7 @@ body { ...@@ -252,7 +252,7 @@ body {
.search-input-wrap { .search-input-wrap {
.search-icon { .search-icon {
color: $theme-gray-200; fill: $theme-gray-200;
} }
.search-input { .search-input {
......
...@@ -109,8 +109,7 @@ header { ...@@ -109,8 +109,7 @@ header {
.user-counter { .user-counter {
svg { svg {
height: 16px; margin-right: 3px;
width: 23px;
} }
} }
...@@ -133,16 +132,16 @@ header { ...@@ -133,16 +132,16 @@ header {
} }
&.navbar-gitlab-new { &.navbar-gitlab-new {
.fa-times { .close-icon {
display: none; display: none;
} }
.menu-expanded { .menu-expanded {
.fa-ellipsis-v { .more-icon {
display: none; display: none;
} }
.fa-times { .close-icon {
display: block; display: block;
} }
} }
......
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
} }
svg { svg {
&.s8 { @include svg-size(8px); }
&.s16 { @include svg-size(16px); } &.s16 { @include svg-size(16px); }
&.s18 { @include svg-size(18px); }
&.s24 { @include svg-size(24px); } &.s24 { @include svg-size(24px); }
&.s32 { @include svg-size(32px); } &.s32 { @include svg-size(32px); }
&.s48 { @include svg-size(48px); } &.s48 { @include svg-size(48px); }
......
...@@ -229,6 +229,10 @@ ul.content-list { ...@@ -229,6 +229,10 @@ ul.content-list {
.label-default { .label-default {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
} }
.avatar-cell {
align-self: flex-start;
}
} }
.panel > .content-list > li { .panel > .content-list > li {
......
...@@ -120,17 +120,24 @@ header.navbar-gitlab-new { ...@@ -120,17 +120,24 @@ header.navbar-gitlab-new {
.container-fluid { .container-fluid {
.navbar-toggle { .navbar-toggle {
min-width: 45px; min-width: 45px;
padding: 4px $gl-padding; padding: 0 $gl-padding;
margin-right: -7px; margin-right: -7px;
font-size: 14px;
text-align: center; text-align: center;
color: currentColor; color: currentColor;
svg {
fill: currentColor;
}
&:hover, &:hover,
&:focus, &:focus,
&.active { &.active {
color: currentColor; color: currentColor;
background-color: transparent; background-color: transparent;
svg {
fill: currentColor;
}
} }
} }
...@@ -279,10 +286,6 @@ header.navbar-gitlab-new { ...@@ -279,10 +286,6 @@ header.navbar-gitlab-new {
} }
} }
.admin-icon i {
font-size: 18px;
}
.caret-down { .caret-down {
height: 11px; height: 11px;
width: 11px; width: 11px;
...@@ -306,6 +309,8 @@ header.navbar-gitlab-new { ...@@ -306,6 +309,8 @@ header.navbar-gitlab-new {
display: flex; display: flex;
width: 100%; width: 100%;
position: relative; position: relative;
padding-top: $gl-padding / 2;
padding-bottom: $gl-padding / 2;
align-items: center; align-items: center;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
...@@ -346,6 +351,7 @@ header.navbar-gitlab-new { ...@@ -346,6 +351,7 @@ header.navbar-gitlab-new {
display: flex; display: flex;
align-items: center; align-items: center;
position: relative; position: relative;
padding: 2px 0;
&:not(:last-child) { &:not(:last-child) {
margin-right: 20px; margin-right: 20px;
...@@ -381,7 +387,7 @@ header.navbar-gitlab-new { ...@@ -381,7 +387,7 @@ header.navbar-gitlab-new {
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 16px;
a { a {
color: $gl-text-color; color: $gl-text-color;
......
...@@ -56,8 +56,8 @@ $new-sidebar-collapsed-width: 50px; ...@@ -56,8 +56,8 @@ $new-sidebar-collapsed-width: 50px;
color: $hover-color; color: $hover-color;
.settings-avatar { .settings-avatar {
i { svg {
color: $hover-color; fill: $hover-color;
} }
} }
} }
...@@ -76,12 +76,9 @@ $new-sidebar-collapsed-width: 50px; ...@@ -76,12 +76,9 @@ $new-sidebar-collapsed-width: 50px;
.settings-avatar { .settings-avatar {
background-color: $white-light; background-color: $white-light;
i { svg {
font-size: 20px; fill: $gl-text-color-secondary;
width: 100%; margin: auto;
color: $gl-text-color-secondary;
text-align: center;
align-self: center;
} }
} }
...@@ -177,16 +174,16 @@ $new-sidebar-collapsed-width: 50px; ...@@ -177,16 +174,16 @@ $new-sidebar-collapsed-width: 50px;
.nav-icon-container { .nav-icon-container {
display: flex; display: flex;
margin-right: 8px; margin-right: 8px;
svg {
height: 16px;
width: 16px;
}
} }
.fly-out-top-item { .fly-out-top-item {
display: none; display: none;
} }
svg {
height: 16px;
width: 16px;
}
} }
.nav-sidebar-inner-scroll { .nav-sidebar-inner-scroll {
...@@ -354,18 +351,22 @@ $new-sidebar-collapsed-width: 50px; ...@@ -354,18 +351,22 @@ $new-sidebar-collapsed-width: 50px;
display: flex; display: flex;
align-items: center; align-items: center;
i { svg {
font-size: 20px; fill: $gl-text-color-secondary;
margin-right: 8px; margin-right: 8px;
} }
.fa-angle-double-right { .icon-angle-double-right {
display: none; display: none;
} }
&:hover { &:hover {
background-color: $border-color; background-color: $border-color;
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
} }
...@@ -407,15 +408,16 @@ $new-sidebar-collapsed-width: 50px; ...@@ -407,15 +408,16 @@ $new-sidebar-collapsed-width: 50px;
.toggle-sidebar-button { .toggle-sidebar-button {
width: $new-sidebar-collapsed-width - 2px; width: $new-sidebar-collapsed-width - 2px;
padding: 16px 18px; padding: 16px;
.collapse-text, .collapse-text,
.fa-angle-double-left { .icon-angle-double-left {
display: none; display: none;
} }
.fa-angle-double-right { .icon-angle-double-right {
display: block; display: block;
margin: 0;
} }
} }
} }
......
...@@ -7,6 +7,7 @@ $gutter_inner_width: 250px; ...@@ -7,6 +7,7 @@ $gutter_inner_width: 250px;
$sidebar-transition-duration: .15s; $sidebar-transition-duration: .15s;
$sidebar-breakpoint: 1024px; $sidebar-breakpoint: 1024px;
$default-transition-duration: .15s; $default-transition-duration: .15s;
$right-sidebar-transition-duration: .3s;
/* /*
* Color schema * Color schema
......
...@@ -55,6 +55,15 @@ ...@@ -55,6 +55,15 @@
.boards-app { .boards-app {
position: relative; position: relative;
@media (min-width: $screen-sm-min) {
transition: width $right-sidebar-transition-duration;
width: 100%;
&.is-compact {
width: calc(100% - #{$gutter_width});
}
}
} }
.boards-app-loading { .boards-app-loading {
...@@ -78,11 +87,6 @@ ...@@ -78,11 +87,6 @@
height: calc(100vh - 222px); height: calc(100vh - 222px);
// scss-lint:enable DuplicateProperty // scss-lint:enable DuplicateProperty
min-height: 475px; min-height: 475px;
transition: width .2s;
&.is-compact {
width: calc(100% - 290px);
}
} }
} }
...@@ -412,14 +416,6 @@ ...@@ -412,14 +416,6 @@
.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar, .page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar,
.page-with-new-sidebar.page-with-sidebar .issue-boards-sidebar { .page-with-new-sidebar.page-with-sidebar .issue-boards-sidebar {
position: absolute;
&.right-sidebar {
top: 0;
bottom: 0;
height: 100%;
}
.issuable-sidebar-header { .issuable-sidebar-header {
position: relative; position: relative;
} }
...@@ -457,8 +453,8 @@ ...@@ -457,8 +453,8 @@
.right-sidebar.right-sidebar-expanded { .right-sidebar.right-sidebar-expanded {
&.boards-sidebar-slide-enter-active, &.boards-sidebar-slide-enter-active,
&.boards-sidebar-slide-leave-active { &.boards-sidebar-slide-leave-active {
transition: width .2s, transition: width $right-sidebar-transition-duration,
padding .2s; padding $right-sidebar-transition-duration;
} }
&.boards-sidebar-slide-enter, &.boards-sidebar-slide-enter,
......
...@@ -77,6 +77,18 @@ ...@@ -77,6 +77,18 @@
word-wrap: break-word; word-wrap: break-word;
} }
} }
&.left-side-selected {
td.line_content.parallel.right-side {
@include user-select(none);
}
}
&.right-side-selected {
td.line_content.parallel.left-side {
@include user-select(none);
}
}
} }
tr.line_holder.parallel { tr.line_holder.parallel {
...@@ -535,7 +547,6 @@ ...@@ -535,7 +547,6 @@
} }
.diff-notes-collapse { .diff-notes-collapse {
position: relative;
width: 19px; width: 19px;
height: 19px; height: 19px;
padding: 0; padding: 0;
...@@ -543,11 +554,7 @@ ...@@ -543,11 +554,7 @@
z-index: 100; z-index: 100;
svg { svg {
position: absolute; vertical-align: text-top;
left: 50%;
top: 50%;
margin-left: -5.5px;
margin-top: -5.5px;
} }
path { path {
......
...@@ -223,14 +223,14 @@ ...@@ -223,14 +223,14 @@
top: $new-navbar-height; top: $new-navbar-height;
bottom: 0; bottom: 0;
right: 0; right: 0;
transition: width .3s; transition: width $right-sidebar-transition-duration;
background: $gray-light; background: $gray-light;
z-index: 200; z-index: 200;
overflow: hidden; overflow: hidden;
.issuable-sidebar { .issuable-sidebar {
width: calc(100% + 100px); width: calc(100% + 100px);
height: calc(100% - #{$new-navbar-height}); height: 100%;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
......
...@@ -516,7 +516,7 @@ a.deploy-project-label { ...@@ -516,7 +516,7 @@ a.deploy-project-label {
text-align: center; text-align: center;
width: 169px; width: 169px;
&:hover, &:hover:not(.disabled),
&.forked { &.forked {
background-color: $row-hover; background-color: $row-hover;
border-color: $row-hover-border; border-color: $row-hover-border;
...@@ -543,6 +543,15 @@ a.deploy-project-label { ...@@ -543,6 +543,15 @@ a.deploy-project-label {
padding-top: $gl-padding; padding-top: $gl-padding;
color: $gl-text-color; color: $gl-text-color;
&.disabled {
opacity: .3;
cursor: not-allowed;
&:hover {
text-decoration: none;
}
}
.caption { .caption {
min-height: 30px; min-height: 30px;
padding: $gl-padding 0; padding: $gl-padding 0;
......
...@@ -54,6 +54,10 @@ ...@@ -54,6 +54,10 @@
border-radius: $border-radius-default; border-radius: $border-radius-default;
color: $almost-black; color: $almost-black;
.code.white pre .hll {
background-color: $well-light-border !important;
}
.tree-content-holder { .tree-content-holder {
display: flex; display: flex;
min-height: 300px; min-height: 300px;
......
...@@ -81,17 +81,10 @@ input[type="checkbox"]:hover { ...@@ -81,17 +81,10 @@ input[type="checkbox"]:hover {
.clear-icon { .clear-icon {
position: absolute; position: absolute;
right: 5px; right: 5px;
top: 0; top: 4px;
&::before {
font-family: FontAwesome;
font-weight: $gl-font-weight-normal;
font-style: normal;
}
} }
.search-icon { .search-icon {
@extend .fa-search;
transition: color $default-transition-duration; transition: color $default-transition-duration;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
...@@ -99,7 +92,6 @@ input[type="checkbox"]:hover { ...@@ -99,7 +92,6 @@ input[type="checkbox"]:hover {
} }
.clear-icon { .clear-icon {
@extend .fa-times;
display: none; display: none;
} }
......
...@@ -22,8 +22,7 @@ class Admin::ApplicationsController < Admin::ApplicationController ...@@ -22,8 +22,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
@application = Doorkeeper::Application.new(application_params) @application = Doorkeeper::Application.new(application_params)
if @application.save if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) redirect_to_admin_page
redirect_to admin_application_url(@application)
else else
render :new render :new
end end
...@@ -42,6 +41,13 @@ class Admin::ApplicationsController < Admin::ApplicationController ...@@ -42,6 +41,13 @@ class Admin::ApplicationsController < Admin::ApplicationController
redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.' redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.'
end end
protected
def redirect_to_admin_page
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to admin_application_url(@application)
end
private private
def set_application def set_application
......
...@@ -128,7 +128,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -128,7 +128,7 @@ class Admin::UsersController < Admin::ApplicationController
end end
respond_to do |format| respond_to do |format|
result = Users::UpdateService.new(user, user_params_with_pass).execute do |user| result = Users::UpdateService.new(current_user, user_params_with_pass.merge(user: user)).execute do |user|
user.skip_reconfirmation! user.skip_reconfirmation!
end end
...@@ -155,7 +155,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -155,7 +155,7 @@ class Admin::UsersController < Admin::ApplicationController
def remove_email def remove_email
email = user.emails.find(params[:email_id]) email = user.emails.find(params[:email_id])
success = Emails::DestroyService.new(user, email: email.email).execute success = Emails::DestroyService.new(current_user, user: user, email: email.email).execute
respond_to do |format| respond_to do |format|
if success if success
...@@ -219,7 +219,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -219,7 +219,7 @@ class Admin::UsersController < Admin::ApplicationController
end end
def update_user(&block) def update_user(&block)
result = Users::UpdateService.new(user).execute(&block) result = Users::UpdateService.new(current_user, user: user).execute(&block)
result[:status] == :success result[:status] == :success
end end
......
...@@ -25,6 +25,8 @@ class ApplicationController < ActionController::Base ...@@ -25,6 +25,8 @@ class ApplicationController < ActionController::Base
around_action :set_locale around_action :set_locale
after_action :set_page_title_header, if: -> { request.format == :json }
protect_from_forgery with: :exception protect_from_forgery with: :exception
helper_method :can?, :current_application_settings helper_method :can?, :current_application_settings
...@@ -335,4 +337,9 @@ class ApplicationController < ActionController::Base ...@@ -335,4 +337,9 @@ class ApplicationController < ActionController::Base
sign_in user, store: false sign_in user, store: false
end end
end end
def set_page_title_header
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
response.headers['Page-Title'] = page_title('GitLab').encode('ISO-8859-1')
end
end end
...@@ -59,6 +59,7 @@ module AuthenticatesWithTwoFactor ...@@ -59,6 +59,7 @@ module AuthenticatesWithTwoFactor
sign_in(user) sign_in(user)
else else
user.increment_failed_attempts! user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
flash.now[:alert] = 'Invalid two-factor code.' flash.now[:alert] = 'Invalid two-factor code.'
prompt_for_two_factor(user) prompt_for_two_factor(user)
end end
...@@ -75,6 +76,7 @@ module AuthenticatesWithTwoFactor ...@@ -75,6 +76,7 @@ module AuthenticatesWithTwoFactor
sign_in(user) sign_in(user)
else else
user.increment_failed_attempts! user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F")
flash.now[:alert] = 'Authentication via U2F device failed.' flash.now[:alert] = 'Authentication via U2F device failed.'
prompt_for_two_factor(user) prompt_for_two_factor(user)
end end
......
...@@ -106,7 +106,7 @@ module IssuableCollections ...@@ -106,7 +106,7 @@ module IssuableCollections
# @filter_params[:authorized_only] = true # @filter_params[:authorized_only] = true
end end
@filter_params @filter_params.permit(IssuableFinder::VALID_PARAMS)
end end
def set_default_state def set_default_state
......
...@@ -15,9 +15,9 @@ module NotesActions ...@@ -15,9 +15,9 @@ module NotesActions
notes = notes_finder.execute notes = notes_finder.execute
.inc_relations_for_view .inc_relations_for_view
.reject { |n| n.cross_reference_not_visible_for?(current_user) }
notes = prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
notes_json[:notes] = notes_json[:notes] =
if noteable.discussions_rendered_on_frontend? if noteable.discussions_rendered_on_frontend?
......
...@@ -12,10 +12,15 @@ class ConfirmationsController < Devise::ConfirmationsController ...@@ -12,10 +12,15 @@ class ConfirmationsController < Devise::ConfirmationsController
def after_confirmation_path_for(resource_name, resource) def after_confirmation_path_for(resource_name, resource)
if signed_in?(resource_name) if signed_in?(resource_name)
after_sign_in_path_for(resource) after_sign_in(resource)
else else
Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}")
flash[:notice] += " Please sign in." flash[:notice] += " Please sign in."
new_session_path(resource_name) new_session_path(resource_name)
end end
end end
def after_sign_in(resource)
after_sign_in_path_for(resource)
end
end end
...@@ -21,14 +21,20 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController ...@@ -21,14 +21,20 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
@application.owner = current_user @application.owner = current_user
if @application.save if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) redirect_to_oauth_application_page
redirect_to oauth_application_url(@application)
else else
set_index_vars set_index_vars
render :index render :index
end end
end end
protected
def redirect_to_oauth_application_page
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to oauth_application_url(@application)
end
private private
def verify_user_oauth_applications_enabled def verify_user_oauth_applications_enabled
......
...@@ -2,7 +2,7 @@ class Profiles::AvatarsController < Profiles::ApplicationController ...@@ -2,7 +2,7 @@ class Profiles::AvatarsController < Profiles::ApplicationController
def destroy def destroy
@user = current_user @user = current_user
Users::UpdateService.new(@user).execute { |user| user.remove_avatar! } Users::UpdateService.new(current_user, user: @user).execute { |user| user.remove_avatar! }
redirect_to profile_path, status: 302 redirect_to profile_path, status: 302
end end
......
...@@ -5,7 +5,7 @@ class Profiles::EmailsController < Profiles::ApplicationController ...@@ -5,7 +5,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
end end
def create def create
@email = Emails::CreateService.new(current_user, email_params).execute @email = Emails::CreateService.new(current_user, email_params.merge(user: current_user)).execute
if @email.errors.blank? if @email.errors.blank?
NotificationService.new.new_email(@email) NotificationService.new.new_email(@email)
...@@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController ...@@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
def destroy def destroy
@email = current_user.emails.find(params[:id]) @email = current_user.emails.find(params[:id])
Emails::DestroyService.new(current_user, email: @email.email).execute Emails::DestroyService.new(current_user, user: current_user, email: @email.email).execute
respond_to do |format| respond_to do |format|
format.html { redirect_to profile_emails_url, status: 302 } format.html { redirect_to profile_emails_url, status: 302 }
......
...@@ -14,7 +14,7 @@ class Profiles::KeysController < Profiles::ApplicationController ...@@ -14,7 +14,7 @@ class Profiles::KeysController < Profiles::ApplicationController
@key = Keys::CreateService.new(current_user, key_params).execute @key = Keys::CreateService.new(current_user, key_params).execute
if @key.persisted? if @key.persisted?
redirect_to profile_key_path(@key) redirect_to_profile_key_path
else else
@keys = current_user.keys.select(&:persisted?) @keys = current_user.keys.select(&:persisted?)
render :index render :index
...@@ -50,6 +50,12 @@ class Profiles::KeysController < Profiles::ApplicationController ...@@ -50,6 +50,12 @@ class Profiles::KeysController < Profiles::ApplicationController
end end
end end
protected
def redirect_to_profile_key_path
redirect_to profile_key_path(@key)
end
private private
def key_params def key_params
......
...@@ -7,7 +7,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController ...@@ -7,7 +7,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController
end end
def update def update
result = Users::UpdateService.new(current_user, user_params).execute result = Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Notification settings saved" flash[:notice] = "Notification settings saved"
......
...@@ -21,10 +21,10 @@ class Profiles::PasswordsController < Profiles::ApplicationController ...@@ -21,10 +21,10 @@ class Profiles::PasswordsController < Profiles::ApplicationController
password_automatically_set: false password_automatically_set: false
} }
result = Users::UpdateService.new(@user, password_attributes).execute result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
if result[:status] == :success if result[:status] == :success
Users::UpdateService.new(@user, password_expires_at: nil).execute Users::UpdateService.new(current_user, user: @user, password_expires_at: nil).execute
redirect_to root_path, notice: 'Password successfully changed' redirect_to root_path, notice: 'Password successfully changed'
else else
...@@ -46,7 +46,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController ...@@ -46,7 +46,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
return return
end end
result = Users::UpdateService.new(@user, password_attributes).execute result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Password was successfully updated. Please login with it" flash[:notice] = "Password was successfully updated. Please login with it"
......
...@@ -6,7 +6,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController ...@@ -6,7 +6,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
def update def update
begin begin
result = Users::UpdateService.new(user, preferences_params).execute result = Users::UpdateService.new(current_user, preferences_params.merge(user: user)).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = 'Preferences saved.' flash[:notice] = 'Preferences saved.'
......
...@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
current_user.otp_grace_period_started_at = Time.current current_user.otp_grace_period_started_at = Time.current
end end
Users::UpdateService.new(current_user).execute! Users::UpdateService.new(current_user, user: current_user).execute!
if two_factor_authentication_required? && !current_user.two_factor_enabled? if two_factor_authentication_required? && !current_user.two_factor_enabled?
two_factor_authentication_reason( two_factor_authentication_reason(
...@@ -41,7 +41,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -41,7 +41,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def create def create
if current_user.validate_and_consume_otp!(params[:pin_code]) if current_user.validate_and_consume_otp!(params[:pin_code])
Users::UpdateService.new(current_user, otp_required_for_login: true).execute! do |user| Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user|
@codes = user.generate_otp_backup_codes! @codes = user.generate_otp_backup_codes!
end end
...@@ -70,7 +70,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -70,7 +70,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
end end
def codes def codes
Users::UpdateService.new(current_user).execute! do |user| Users::UpdateService.new(current_user, user: current_user).execute! do |user|
@codes = user.generate_otp_backup_codes! @codes = user.generate_otp_backup_codes!
end end
end end
......
...@@ -10,7 +10,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -10,7 +10,7 @@ class ProfilesController < Profiles::ApplicationController
def update def update
respond_to do |format| respond_to do |format|
result = Users::UpdateService.new(@user, user_params).execute result = Users::UpdateService.new(current_user, user_params.merge(user: @user)).execute
if result[:status] == :success if result[:status] == :success
message = "Profile was successfully updated" message = "Profile was successfully updated"
...@@ -25,7 +25,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -25,7 +25,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def reset_private_token def reset_private_token
Users::UpdateService.new(@user).execute! do |user| Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_authentication_token! user.reset_authentication_token!
end end
...@@ -35,7 +35,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -35,7 +35,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def reset_incoming_email_token def reset_incoming_email_token
Users::UpdateService.new(@user).execute! do |user| Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_incoming_email_token! user.reset_incoming_email_token!
end end
...@@ -45,7 +45,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -45,7 +45,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def reset_rss_token def reset_rss_token
Users::UpdateService.new(@user).execute! do |user| Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_rss_token! user.reset_rss_token!
end end
...@@ -61,7 +61,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -61,7 +61,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def update_username def update_username
result = Users::UpdateService.new(@user, username: user_params[:username]).execute result = Users::UpdateService.new(current_user, user: @user, username: user_params[:username]).execute
options = if result[:status] == :success options = if result[:status] == :success
{ notice: "Username successfully changed" } { notice: "Username successfully changed" }
......
...@@ -41,6 +41,8 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -41,6 +41,8 @@ class Projects::BlobController < Projects::ApplicationController
end end
format.json do format.json do
page_title @blob.path, @ref, @project.name_with_namespace
show_json show_json
end end
end end
......
...@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_create_issue!, only: [:new, :create] before_action :authorize_create_issue!, only: [:new, :create]
# Allow modify issue # Allow modify issue
before_action :authorize_update_issue!, only: [:edit, :update, :move] before_action :authorize_update_issue!, only: [:update, :move]
# Allow create a new branch and empty WIP merge request from current issue # Allow create a new branch and empty WIP merge request from current issue
before_action :authorize_create_merge_request!, only: [:create_merge_request] before_action :authorize_create_merge_request!, only: [:create_merge_request]
...@@ -63,10 +63,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -63,10 +63,6 @@ class Projects::IssuesController < Projects::ApplicationController
respond_with(@issue) respond_with(@issue)
end end
def edit
respond_with(@issue)
end
def show def show
@noteable = @issue @noteable = @issue
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
...@@ -126,10 +122,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -126,10 +122,6 @@ class Projects::IssuesController < Projects::ApplicationController
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue) @issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
respond_to do |format| respond_to do |format|
format.html do
recaptcha_check_with_fallback { render :edit }
end
format.json do format.json do
render_issue_json render_issue_json
end end
......
...@@ -35,6 +35,8 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -35,6 +35,8 @@ class Projects::TreeController < Projects::ApplicationController
end end
format.json do format.json do
page_title @path.presence || _("Files"), @ref, @project.name_with_namespace
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38261 # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38261
Gitlab::GitalyClient.allow_n_plus_1_calls do Gitlab::GitalyClient.allow_n_plus_1_calls do
render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree) render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree)
......
...@@ -344,6 +344,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -344,6 +344,7 @@ class ProjectsController < Projects::ApplicationController
:tag_list, :tag_list,
:visibility_level, :visibility_level,
:template_name, :template_name,
:merge_method,
project_feature_attributes: %i[ project_feature_attributes: %i[
builds_access_level builds_access_level
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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