Skip to content
Snippets Groups Projects
Commit c833a09d authored by Jose Ivan Vargas Lopez's avatar Jose Ivan Vargas Lopez
Browse files

Merge branch 'master' into jivl-update-katex

parents 46ae0362 0a22ff26
No related branches found
No related tags found
No related merge requests found
Showing
with 354 additions and 125 deletions
Loading
@@ -2,6 +2,16 @@
Loading
@@ -2,6 +2,16 @@
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.4.3 (2018-02-05)
### Security (4 changes)
- Fix namespace access issue for GitHub, BitBucket, and GitLab.com project importers.
- Fix stored XSS in code blocks that ignore highlighting.
- Fix wilcard protected tags protecting all branches.
- Restrict Todo API mark_as_done endpoint to the user's todos only.
## 10.4.2 (2018-01-30) ## 10.4.2 (2018-01-30)
   
### Fixed (6 changes) ### Fixed (6 changes)
Loading
@@ -197,6 +207,16 @@ entry.
Loading
@@ -197,6 +207,16 @@ entry.
- Use a background migration for issues.closed_at. - Use a background migration for issues.closed_at.
   
   
## 10.3.7 (2018-02-05)
### Security (4 changes)
- Fix namespace access issue for GitHub, BitBucket, and GitLab.com project importers.
- Fix stored XSS in code blocks that ignore highlighting.
- Fix wilcard protected tags protecting all branches.
- Restrict Todo API mark_as_done endpoint to the user's todos only.
## 10.3.6 (2018-01-22) ## 10.3.6 (2018-01-22)
   
### Fixed (17 changes, 2 of them are from the community) ### Fixed (17 changes, 2 of them are from the community)
Loading
@@ -415,6 +435,16 @@ entry.
Loading
@@ -415,6 +435,16 @@ entry.
- Clean up schema of the "merge_requests" table. - Clean up schema of the "merge_requests" table.
   
   
## 10.2.8 (2018-02-07)
### Security (4 changes)
- Fix namespace access issue for GitHub, BitBucket, and GitLab.com project importers.
- Fix stored XSS in code blocks that ignore highlighting.
- Fix wilcard protected tags protecting all branches.
- Restrict Todo API mark_as_done endpoint to the user's todos only.
## 10.2.7 (2018-01-18) ## 10.2.7 (2018-01-18)
   
- No changes. - No changes.
Loading
Loading
0.78.0 0.81.0
6.0.2 6.0.3
3.5.1 3.6.0
Loading
@@ -294,7 +294,7 @@ group :metrics do
Loading
@@ -294,7 +294,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
   
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~> 0.7.0.beta44' gem 'prometheus-client-mmap', '~> 0.9.1'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
   
Loading
@@ -410,7 +410,9 @@ group :ed25519 do
Loading
@@ -410,7 +410,9 @@ group :ed25519 do
end end
   
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.83.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.84.0', require: 'gitaly'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
gem 'google-protobuf', '= 3.5.1'
   
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
   
Loading
Loading
Loading
@@ -285,7 +285,7 @@ GEM
Loading
@@ -285,7 +285,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.83.0) gitaly-proto (0.84.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)
Loading
@@ -340,7 +340,7 @@ GEM
Loading
@@ -340,7 +340,7 @@ GEM
mime-types (~> 3.0) mime-types (~> 3.0)
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.0) retriable (>= 2.0, < 4.0)
google-protobuf (3.5.1.1) google-protobuf (3.5.1)
googleapis-common-protos-types (1.0.1) googleapis-common-protos-types (1.0.1)
google-protobuf (~> 3.0) google-protobuf (~> 3.0)
googleauth (0.5.3) googleauth (0.5.3)
Loading
@@ -636,7 +636,7 @@ GEM
Loading
@@ -636,7 +636,7 @@ GEM
parser parser
unparser unparser
procto (0.0.3) procto (0.0.3)
prometheus-client-mmap (0.7.0.beta44) prometheus-client-mmap (0.9.1)
pry (0.10.4) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
Loading
@@ -1056,7 +1056,7 @@ DEPENDENCIES
Loading
@@ -1056,7 +1056,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.83.0) gitaly-proto (~> 0.84.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)
Loading
@@ -1066,6 +1066,7 @@ DEPENDENCIES
Loading
@@ -1066,6 +1066,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.13.6) google-api-client (~> 0.13.6)
google-protobuf (= 3.5.1)
gpgme gpgme
grape (~> 1.0) grape (~> 1.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
Loading
@@ -1131,7 +1132,7 @@ DEPENDENCIES
Loading
@@ -1131,7 +1132,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3) peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2) pg (~> 0.18.2)
premailer-rails (~> 1.9.7) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta44) prometheus-client-mmap (~> 0.9.1)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
Loading
Loading
{"iconCount":189,"spriteSize":85900,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} {"iconCount":191,"spriteSize":86607,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","soft-unwrap","soft-wrap","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]}
\ No newline at end of file \ No newline at end of file
This diff is collapsed.
<svg xmlns="http://www.w3.org/2000/svg" width="142" height="104" viewBox="0 0 142 104"><g fill="none" fill-rule="evenodd"><g transform="translate(112 4)"><path fill="#FFF" d="M8 4a4 4 0 0 0-4 4v14a4 4 0 0 0 4 4h14a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4H8z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M8 4a4 4 0 0 0-4 4v14a4 4 0 0 0 4 4h14a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4H8zm0-4h14a8 8 0 0 1 8 8v14a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V8a8 8 0 0 1 8-8z"/><rect width="10" height="10" x="10" y="10" fill="#FC6D26" rx="5"/></g><g transform="translate(5 74)"><rect width="30" height="30" fill="#FFF" rx="8"/><path fill="#E1DBF1" fill-rule="nonzero" d="M8 4a4 4 0 0 0-4 4v14a4 4 0 0 0 4 4h14a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4H8zm0-4h14a8 8 0 0 1 8 8v14a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V8a8 8 0 0 1 8-8z"/><rect width="10" height="10" x="10" y="10" fill="#6B4FBB" rx="5"/></g><path fill="#FFF" d="M6 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M6 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6zm0-4h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/><rect width="8" height="8" x="8" y="8" fill="#FC6D26" rx="4"/><g transform="translate(112 77)"><rect width="24" height="24" fill="#FFF" rx="6"/><path fill="#E1DBF1" fill-rule="nonzero" d="M6 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6zm0-4h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/><rect width="8" height="8" x="8" y="8" fill="#6B4FBB" rx="4"/></g><g transform="translate(46 29)"><rect width="46" height="46" y="2" fill="#E1DBF1" rx="10"/><rect width="46" height="46" fill="#E1DBF1" rx="10"/><path fill="#C3B8E3" fill-rule="nonzero" d="M10 4a6 6 0 0 0-6 6v26a6 6 0 0 0 6 6h26a6 6 0 0 0 6-6V10a6 6 0 0 0-6-6H10zm0-4h26c5.523 0 10 4.477 10 10v26c0 5.523-4.477 10-10 10H10C4.477 46 0 41.523 0 36V10C0 4.477 4.477 0 10 0z"/><rect width="14" height="14" x="16" y="16" fill="#6B4FBB" rx="2"/></g><path fill="#E1DBF1" fill-rule="nonzero" d="M98.413 35.682a2 2 0 1 1-2.826-2.83l2.122-2.12a2 2 0 1 1 2.827 2.83l-2.123 2.12z"/><path fill="#C3B8E3" d="M104.78 29.32a2 2 0 0 1-2.826-2.829l2.122-2.12a2 2 0 0 1 2.827 2.83l-2.122 2.12z"/><path fill="#C3B8E3" fill-rule="nonzero" d="M42.413 89.682a2 2 0 1 1-2.826-2.83l2.122-2.12a2 2 0 1 1 2.827 2.83l-2.123 2.12z"/><path fill="#E1DBF1" d="M48.78 83.32a2 2 0 1 1-2.826-2.829l2.122-2.12a2 2 0 1 1 2.827 2.83l-2.122 2.12z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M27.713 26.531a2 2 0 1 1 2.574-3.062l2.296 1.93a2 2 0 1 1-2.573 3.062l-2.297-1.93z"/><path fill="#C3B8E3" d="M34.604 32.321a2 2 0 1 1 2.573-3.062l2.297 1.93A2 2 0 0 1 36.9 34.25l-2.297-1.93z"/><path fill="#C3B8E3" fill-rule="nonzero" d="M93.74 74.553a2 2 0 0 1 2.52-3.106l2.33 1.891a2 2 0 1 1-2.521 3.106l-2.33-1.891z"/><path fill="#E1DBF1" d="M100.727 80.225a2 2 0 1 1 2.521-3.105l2.33 1.89a2 2 0 1 1-2.522 3.106l-2.33-1.89z"/></g></svg>
\ No newline at end of file
Loading
@@ -2,7 +2,7 @@
Loading
@@ -2,7 +2,7 @@
import Sortable from 'vendor/Sortable'; import Sortable from 'vendor/Sortable';
import Vue from 'vue'; import Vue from 'vue';
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
import boardList from './board_list'; import boardList from './board_list.vue';
import boardBlankState from './board_blank_state'; import boardBlankState from './board_blank_state';
import './board_delete'; import './board_delete';
   
Loading
Loading
<script>
import Sortable from 'vendor/Sortable'; import Sortable from 'vendor/Sortable';
import boardNewIssue from './board_new_issue'; import boardNewIssue from './board_new_issue';
import boardCard from './board_card.vue'; import boardCard from './board_card.vue';
Loading
@@ -8,6 +9,11 @@ const Store = gl.issueBoards.BoardsStore;
Loading
@@ -8,6 +9,11 @@ const Store = gl.issueBoards.BoardsStore;
   
export default { export default {
name: 'BoardList', name: 'BoardList',
components: {
boardCard,
boardNewIssue,
loadingIcon,
},
props: { props: {
disabled: { disabled: {
type: Boolean, type: Boolean,
Loading
@@ -42,46 +48,6 @@ export default {
Loading
@@ -42,46 +48,6 @@ export default {
showIssueForm: false, showIssueForm: false,
}; };
}, },
components: {
boardCard,
boardNewIssue,
loadingIcon,
},
methods: {
listHeight() {
return this.$refs.list.getBoundingClientRect().height;
},
scrollHeight() {
return this.$refs.list.scrollHeight;
},
scrollTop() {
return this.$refs.list.scrollTop + this.listHeight();
},
scrollToTop() {
this.$refs.list.scrollTop = 0;
},
loadNextPage() {
const getIssues = this.list.nextPage();
const loadingDone = () => {
this.list.loadingMore = false;
};
if (getIssues) {
this.list.loadingMore = true;
getIssues
.then(loadingDone)
.catch(loadingDone);
}
},
toggleForm() {
this.showIssueForm = !this.showIssueForm;
},
onScroll() {
if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
this.loadNextPage();
}
},
},
watch: { watch: {
filters: { filters: {
handler() { handler() {
Loading
@@ -157,51 +123,90 @@ export default {
Loading
@@ -157,51 +123,90 @@ export default {
eventHub.$off(`scroll-board-list-${this.list.id}`, this.scrollToTop); eventHub.$off(`scroll-board-list-${this.list.id}`, this.scrollToTop);
this.$refs.list.removeEventListener('scroll', this.onScroll); this.$refs.list.removeEventListener('scroll', this.onScroll);
}, },
template: ` methods: {
<div class="board-list-component"> listHeight() {
<div return this.$refs.list.getBoundingClientRect().height;
class="board-list-loading text-center" },
aria-label="Loading issues" scrollHeight() {
v-if="loading"> return this.$refs.list.scrollHeight;
<loading-icon /> },
</div> scrollTop() {
<board-new-issue return this.$refs.list.scrollTop + this.listHeight();
:list="list" },
v-if="list.type !== 'closed' && showIssueForm"/> scrollToTop() {
<ul this.$refs.list.scrollTop = 0;
class="board-list" },
v-show="!loading" loadNextPage() {
ref="list" const getIssues = this.list.nextPage();
:data-board="list.id" const loadingDone = () => {
:class="{ 'is-smaller': showIssueForm }"> this.list.loadingMore = false;
<board-card };
v-for="(issue, index) in issues"
ref="issue"
:index="index"
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath"
:disabled="disabled"
:key="issue.id" />
<li
class="board-list-count text-center"
v-if="showCount"
data-issue-id="-1">
   
<loading-icon if (getIssues) {
v-show="list.loadingMore" this.list.loadingMore = true;
label="Loading more issues" getIssues
/> .then(loadingDone)
.catch(loadingDone);
}
},
toggleForm() {
this.showIssueForm = !this.showIssueForm;
},
onScroll() {
if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
this.loadNextPage();
}
},
},
};
</script>
   
<span v-if="list.issues.length === list.issuesSize"> <template>
Showing all issues <div class="board-list-component">
</span> <div
<span v-else> class="board-list-loading text-center"
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues aria-label="Loading issues"
</span> v-if="loading">
</li> <loading-icon />
</ul>
</div> </div>
`, <board-new-issue
}; :list="list"
v-if="list.type !== 'closed' && showIssueForm"/>
<ul
class="board-list"
v-show="!loading"
ref="list"
:data-board="list.id"
:class="{ 'is-smaller': showIssueForm }">
<board-card
v-for="(issue, index) in issues"
ref="issue"
:index="index"
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath"
:disabled="disabled"
:key="issue.id" />
<li
class="board-list-count text-center"
v-if="showCount"
data-issue-id="-1">
<loading-icon
v-show="list.loadingMore"
label="Loading more issues"
/>
<span
v-if="list.issues.length === list.issuesSize"
>
Showing all issues
</span>
<span
v-else
>
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
</span>
</li>
</ul>
</div>
</template>
/* eslint-disable func-names, no-new, space-before-function-paren, one-var, /* eslint-disable func-names, no-new, space-before-function-paren, one-var,
promise/catch-or-return */ promise/catch-or-return */
import axios from '~/lib/utils/axios_utils';
import _ from 'underscore'; import _ from 'underscore';
import CreateLabelDropdown from '../../create_label'; import CreateLabelDropdown from '../../create_label';
   
Loading
@@ -28,9 +29,9 @@ gl.issueBoards.newListDropdownInit = () => {
Loading
@@ -28,9 +29,9 @@ gl.issueBoards.newListDropdownInit = () => {
   
$this.glDropdown({ $this.glDropdown({
data(term, callback) { data(term, callback) {
$.get($this.attr('data-list-labels-path')) axios.get($this.attr('data-list-labels-path'))
.then((resp) => { .then(({ data }) => {
callback(resp); callback(data);
}); });
}, },
renderRow (label) { renderRow (label) {
Loading
Loading
import _ from 'underscore';
import axios from '../lib/utils/axios_utils';
import { s__ } from '../locale';
import Flash from '../flash';
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
import statusCodes from '../lib/utils/http_status';
import VariableList from './ci_variable_list';
function generateErrorBoxContent(errors) {
const errorList = [].concat(errors).map(errorString => `
<li>
${_.escape(errorString)}
</li>
`);
return `
<p>
${s__('CiVariable|Validation failed')}
</p>
<ul>
${errorList.join('')}
</ul>
`;
}
// Used for the variable list on CI/CD projects/groups settings page
export default class AjaxVariableList {
constructor({
container,
saveButton,
errorBox,
formField = 'variables',
saveEndpoint,
}) {
this.container = container;
this.saveButton = saveButton;
this.errorBox = errorBox;
this.saveEndpoint = saveEndpoint;
this.variableList = new VariableList({
container: this.container,
formField,
});
this.bindEvents();
this.variableList.init();
}
bindEvents() {
this.saveButton.addEventListener('click', this.onSaveClicked.bind(this));
}
onSaveClicked() {
const loadingIcon = this.saveButton.querySelector('.js-secret-variables-save-loading-icon');
loadingIcon.classList.toggle('hide', false);
this.errorBox.classList.toggle('hide', true);
// We use this to prevent a user from changing a key before we have a chance
// to match it up in `updateRowsWithPersistedVariables`
this.variableList.toggleEnableRow(false);
return axios.patch(this.saveEndpoint, {
variables_attributes: this.variableList.getAllData(),
}, {
// We want to be able to process the `res.data` from a 400 error response
// and print the validation messages such as duplicate variable keys
validateStatus: status => (
status >= statusCodes.OK &&
status < statusCodes.MULTIPLE_CHOICES
) ||
status === statusCodes.BAD_REQUEST,
})
.then((res) => {
loadingIcon.classList.toggle('hide', true);
this.variableList.toggleEnableRow(true);
if (res.status === statusCodes.OK && res.data) {
this.updateRowsWithPersistedVariables(res.data.variables);
} else if (res.status === statusCodes.BAD_REQUEST) {
// Validation failed
this.errorBox.innerHTML = generateErrorBoxContent(res.data);
this.errorBox.classList.toggle('hide', false);
}
})
.catch(() => {
loadingIcon.classList.toggle('hide', true);
this.variableList.toggleEnableRow(true);
Flash(s__('CiVariable|Error occured while saving variables'));
});
}
updateRowsWithPersistedVariables(persistedVariables = []) {
const persistedVariableMap = [].concat(persistedVariables).reduce((variableMap, variable) => ({
...variableMap,
[variable.key]: variable,
}), {});
this.container.querySelectorAll('.js-row').forEach((row) => {
// If we submitted a row that was destroyed, remove it so we don't try
// to destroy it again which would cause a BE error
const destroyInput = row.querySelector('.js-ci-variable-input-destroy');
if (convertPermissionToBoolean(destroyInput.value)) {
row.remove();
// Update the ID input so any future edits and `_destroy` will apply on the BE
} else {
const key = row.querySelector('.js-ci-variable-input-key').value;
const persistedVariable = persistedVariableMap[key];
if (persistedVariable) {
// eslint-disable-next-line no-param-reassign
row.querySelector('.js-ci-variable-input-id').value = persistedVariable.id;
row.setAttribute('data-is-persisted', 'true');
}
}
});
}
}
Loading
@@ -11,7 +11,7 @@ function createEnvironmentItem(value) {
Loading
@@ -11,7 +11,7 @@ function createEnvironmentItem(value) {
return { return {
title: value === '*' ? ALL_ENVIRONMENTS_STRING : value, title: value === '*' ? ALL_ENVIRONMENTS_STRING : value,
id: value, id: value,
text: value, text: value === '*' ? s__('CiVariable|* (All environments)') : value,
}; };
} }
   
Loading
@@ -39,13 +39,13 @@ export default class VariableList {
Loading
@@ -39,13 +39,13 @@ export default class VariableList {
}, },
protected: { protected: {
selector: '.js-ci-variable-input-protected', selector: '.js-ci-variable-input-protected',
default: 'true', default: 'false',
}, },
environment: { environment_scope: {
// We can't use a `.js-` class here because // We can't use a `.js-` class here because
// gl_dropdown replaces the <input> and doesn't copy over the class // gl_dropdown replaces the <input> and doesn't copy over the class
// See https://gitlab.com/gitlab-org/gitlab-ce/issues/42458 // See https://gitlab.com/gitlab-org/gitlab-ce/issues/42458
selector: `input[name="${this.formField}[variables_attributes][][environment]"]`, selector: `input[name="${this.formField}[variables_attributes][][environment_scope]"]`,
default: '*', default: '*',
}, },
_destroy: { _destroy: {
Loading
@@ -104,12 +104,15 @@ export default class VariableList {
Loading
@@ -104,12 +104,15 @@ export default class VariableList {
   
setupToggleButtons($row[0]); setupToggleButtons($row[0]);
   
// Reset the resizable textarea
$row.find(this.inputMap.value.selector).css('height', '');
const $environmentSelect = $row.find('.js-variable-environment-toggle'); const $environmentSelect = $row.find('.js-variable-environment-toggle');
if ($environmentSelect.length) { if ($environmentSelect.length) {
const createItemDropdown = new CreateItemDropdown({ const createItemDropdown = new CreateItemDropdown({
$dropdown: $environmentSelect, $dropdown: $environmentSelect,
defaultToggleLabel: ALL_ENVIRONMENTS_STRING, defaultToggleLabel: ALL_ENVIRONMENTS_STRING,
fieldName: `${this.formField}[variables_attributes][][environment]`, fieldName: `${this.formField}[variables_attributes][][environment_scope]`,
getData: (term, callback) => callback(this.getEnvironmentValues()), getData: (term, callback) => callback(this.getEnvironmentValues()),
createNewItemFromValue: createEnvironmentItem, createNewItemFromValue: createEnvironmentItem,
onSelect: () => { onSelect: () => {
Loading
@@ -117,7 +120,7 @@ export default class VariableList {
Loading
@@ -117,7 +120,7 @@ export default class VariableList {
// so they have the new value we just picked // so they have the new value we just picked
this.refreshDropdownData(); this.refreshDropdownData();
   
$row.find(this.inputMap.environment.selector).trigger('trigger-change'); $row.find(this.inputMap.environment_scope.selector).trigger('trigger-change');
}, },
}); });
   
Loading
@@ -143,7 +146,8 @@ export default class VariableList {
Loading
@@ -143,7 +146,8 @@ export default class VariableList {
$row.after($rowClone); $row.after($rowClone);
} }
   
removeRow($row) { removeRow(row) {
const $row = $(row);
const isPersisted = convertPermissionToBoolean($row.attr('data-is-persisted')); const isPersisted = convertPermissionToBoolean($row.attr('data-is-persisted'));
   
if (isPersisted) { if (isPersisted) {
Loading
@@ -155,6 +159,10 @@ export default class VariableList {
Loading
@@ -155,6 +159,10 @@ export default class VariableList {
} else { } else {
$row.remove(); $row.remove();
} }
// Refresh the other dropdowns in the variable list
// so any value with the variable deleted is gone
this.refreshDropdownData();
} }
   
checkIfRowTouched($row) { checkIfRowTouched($row) {
Loading
@@ -165,6 +173,11 @@ export default class VariableList {
Loading
@@ -165,6 +173,11 @@ export default class VariableList {
}); });
} }
   
toggleEnableRow(isEnabled = true) {
this.$container.find(this.inputMap.key.selector).attr('disabled', !isEnabled);
this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled);
}
getAllData() { getAllData() {
// Ignore the last empty row because we don't want to try persist // Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems. // a blank variable and run into validation problems.
Loading
@@ -185,7 +198,7 @@ export default class VariableList {
Loading
@@ -185,7 +198,7 @@ export default class VariableList {
} }
   
getEnvironmentValues() { getEnvironmentValues() {
const valueMap = this.$container.find(this.inputMap.environment.selector).toArray() const valueMap = this.$container.find(this.inputMap.environment_scope.selector).toArray()
.reduce((prevValueMap, envInput) => ({ .reduce((prevValueMap, envInput) => ({
...prevValueMap, ...prevValueMap,
[envInput.value]: envInput.value, [envInput.value]: envInput.value,
Loading
Loading
Loading
@@ -32,13 +32,16 @@ export default class Clusters {
Loading
@@ -32,13 +32,16 @@ export default class Clusters {
installIngressPath, installIngressPath,
installRunnerPath, installRunnerPath,
installPrometheusPath, installPrometheusPath,
managePrometheusPath,
clusterStatus, clusterStatus,
clusterStatusReason, clusterStatusReason,
helpPath, helpPath,
ingressHelpPath,
} = document.querySelector('.js-edit-cluster-form').dataset; } = document.querySelector('.js-edit-cluster-form').dataset;
   
this.store = new ClustersStore(); this.store = new ClustersStore();
this.store.setHelpPath(helpPath); this.store.setHelpPaths(helpPath, ingressHelpPath);
this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus); this.store.updateStatus(clusterStatus);
this.store.updateStatusReason(clusterStatusReason); this.store.updateStatusReason(clusterStatusReason);
this.service = new ClustersService({ this.service = new ClustersService({
Loading
@@ -93,6 +96,8 @@ export default class Clusters {
Loading
@@ -93,6 +96,8 @@ export default class Clusters {
props: { props: {
applications: this.state.applications, applications: this.state.applications,
helpPath: this.state.helpPath, helpPath: this.state.helpPath,
ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath,
}, },
}); });
}, },
Loading
@@ -172,7 +177,7 @@ export default class Clusters {
Loading
@@ -172,7 +177,7 @@ export default class Clusters {
.map(appId => newApplicationMap[appId].title); .map(appId => newApplicationMap[appId].title);
   
if (appTitles.length > 0) { if (appTitles.length > 0) {
const text = sprintf(s__('ClusterIntegration|%{appList} was successfully installed on your cluster'), { const text = sprintf(s__('ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster'), {
appList: appTitles.join(', '), appList: appTitles.join(', '),
}); });
Flash(text, 'notice', this.successApplicationContainer); Flash(text, 'notice', this.successApplicationContainer);
Loading
Loading
Loading
@@ -32,6 +32,10 @@
Loading
@@ -32,6 +32,10 @@
type: String, type: String,
required: false, required: false,
}, },
manageLink: {
type: String,
required: false,
},
description: { description: {
type: String, type: String,
required: true, required: true,
Loading
@@ -89,6 +93,12 @@
Loading
@@ -89,6 +93,12 @@
   
return label; return label;
}, },
showManageButton() {
return this.manageLink && this.status === APPLICATION_INSTALLED;
},
manageButtonLabel() {
return s__('ClusterIntegration|Manage');
},
hasError() { hasError() {
return this.status === APPLICATION_ERROR || return this.status === APPLICATION_ERROR ||
this.requestStatus === REQUEST_FAILURE; this.requestStatus === REQUEST_FAILURE;
Loading
@@ -141,9 +151,21 @@
Loading
@@ -141,9 +151,21 @@
<div v-html="description"></div> <div v-html="description"></div>
</div> </div>
<div <div
class="table-section table-button-footer section-15 section-align-top" class="table-section table-button-footer section-align-top"
:class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
role="gridcell" role="gridcell"
> >
<div
v-if="showManageButton"
class="btn-group table-action-buttons"
>
<a
class="btn"
:href="manageLink"
>
{{ manageButtonLabel }}
</a>
</div>
<div class="btn-group table-action-buttons"> <div class="btn-group table-action-buttons">
<loading-button <loading-button
class="js-cluster-application-install-button" class="js-cluster-application-install-button"
Loading
Loading
Loading
@@ -18,13 +18,24 @@
Loading
@@ -18,13 +18,24 @@
required: false, required: false,
default: '', default: '',
}, },
ingressHelpPath: {
type: String,
required: false,
default: '',
},
managePrometheusPath: {
type: String,
required: false,
default: '',
},
}, },
computed: { computed: {
generalApplicationDescription() { generalApplicationDescription() {
return sprintf( return sprintf(
_.escape(s__(`ClusterIntegration|Install applications on your cluster. _.escape(s__(
Read more about %{helpLink}`)), `ClusterIntegration|Install applications on your Kubernetes cluster.
{ Read more about %{helpLink}`,
)), {
helpLink: `<a href="${this.helpPath}"> helpLink: `<a href="${this.helpPath}">
${_.escape(s__('ClusterIntegration|installing applications'))} ${_.escape(s__('ClusterIntegration|installing applications'))}
</a>`, </a>`,
Loading
@@ -34,7 +45,7 @@
Loading
@@ -34,7 +45,7 @@
}, },
helmTillerDescription() { helmTillerDescription() {
return _.escape(s__( return _.escape(s__(
`ClusterIntegration|Helm streamlines installing and managing Kubernets applications. `ClusterIntegration|Helm streamlines installing and managing Kubernetes applications.
Tiller runs inside of your Kubernetes Cluster, and manages Tiller runs inside of your Kubernetes Cluster, and manages
releases of your charts.`, releases of your charts.`,
)); ));
Loading
@@ -49,7 +60,7 @@
Loading
@@ -49,7 +60,7 @@
_.escape(s__( _.escape(s__(
`ClusterIntegration|%{boldNotice} This will add some extra resources `ClusterIntegration|%{boldNotice} This will add some extra resources
like a load balancer, which may incur additional costs depending on like a load balancer, which may incur additional costs depending on
the hosting provider Kubernetes is installed on. If you are using GKE, the hosting provider your Kubernetes cluster is installed on. If you are using GKE,
you can %{pricingLink}.`, you can %{pricingLink}.`,
)), { )), {
boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`,
Loading
@@ -59,13 +70,28 @@
Loading
@@ -59,13 +70,28 @@
false, false,
); );
   
const externalIpParagraph = sprintf(
_.escape(s__(
`ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS
at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`,
)), {
ingressHelpLink: `<a href="${this.ingressHelpPath}">
${_.escape(s__('ClusterIntegration|More information'))}
</a>`,
},
false,
);
return ` return `
<p> <p>
${descriptionParagraph} ${descriptionParagraph}
</p> </p>
<p class="append-bottom-0"> <p>
${extraCostParagraph} ${extraCostParagraph}
</p> </p>
<p class="settings-message append-bottom-0">
${externalIpParagraph}
</p>
`; `;
}, },
gitlabRunnerDescription() { gitlabRunnerDescription() {
Loading
@@ -76,11 +102,12 @@
Loading
@@ -76,11 +102,12 @@
}, },
prometheusDescription() { prometheusDescription() {
return sprintf( return sprintf(
_.escape(s__(`ClusterIntegration|Prometheus is an open-source monitoring system _.escape(s__(
with %{gitlabIntegrationLink} to monitor deployed applications.`)), `ClusterIntegration|Prometheus is an open-source monitoring system
{ with %{gitlabIntegrationLink} to monitor deployed applications.`,
)), {
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, ${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`,
}, },
false, false,
Loading
@@ -129,6 +156,7 @@ target="_blank" rel="noopener noreferrer">
Loading
@@ -129,6 +156,7 @@ target="_blank" rel="noopener noreferrer">
id="prometheus" id="prometheus"
:title="applications.prometheus.title" :title="applications.prometheus.title"
title-link="https://prometheus.io/docs/introduction/overview/" title-link="https://prometheus.io/docs/introduction/overview/"
:manage-link="managePrometheusPath"
:description="prometheusDescription" :description="prometheusDescription"
:status="applications.prometheus.status" :status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason" :status-reason="applications.prometheus.statusReason"
Loading
Loading
Loading
@@ -4,6 +4,7 @@ export default class ClusterStore {
Loading
@@ -4,6 +4,7 @@ export default class ClusterStore {
constructor() { constructor() {
this.state = { this.state = {
helpPath: null, helpPath: null,
ingressHelpPath: null,
status: null, status: null,
statusReason: null, statusReason: null,
applications: { applications: {
Loading
@@ -39,8 +40,13 @@ export default class ClusterStore {
Loading
@@ -39,8 +40,13 @@ export default class ClusterStore {
}; };
} }
   
setHelpPath(helpPath) { setHelpPaths(helpPath, ingressHelpPath) {
this.state.helpPath = helpPath; this.state.helpPath = helpPath;
this.state.ingressHelpPath = ingressHelpPath;
}
setManagePrometheusPath(managePrometheusPath) {
this.state.managePrometheusPath = managePrometheusPath;
} }
   
updateStatus(status) { updateStatus(status) {
Loading
Loading
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
import 'vendor/jquery.waitforimages';
   
// Width where images must fits in, for 2-up this gets divided by 2 // Width where images must fits in, for 2-up this gets divided by 2
const availWidth = 900; const availWidth = 900;
Loading
Loading
Loading
@@ -6,5 +6,5 @@ import 'vendor/jquery.endless-scroll';
Loading
@@ -6,5 +6,5 @@ import 'vendor/jquery.endless-scroll';
import 'vendor/jquery.caret'; import 'vendor/jquery.caret';
import 'vendor/jquery.atwho'; import 'vendor/jquery.atwho';
import 'vendor/jquery.scrollTo'; import 'vendor/jquery.scrollTo';
import 'vendor/jquery.waitforimages'; import 'jquery.waitforimages';
import 'select2/select2'; import 'select2/select2';
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment