summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJacob Schatz <jschatz@gitlab.com>2018-02-28 17:55:11 +0000
committerJacob Schatz <jschatz@gitlab.com>2018-02-28 17:55:11 +0000
commit9cb3ababe752b5b27c097721cd35aa23dd11bcaa (patch)
treed12e1b0147a7fe986feedfb270399320f4415473 /app
parent54432de3b6574fd63da0723a2f2fc47b8e4037cc (diff)
parentccbce7af7e0e532ae5d19ea2af7aaff8933490a9 (diff)
downloadgitlab-ce-9cb3ababe752b5b27c097721cd35aa23dd11bcaa.tar.gz
Merge branch 'master' into 'dispatcher-cleanup'
# Conflicts: # config/webpack.config.js
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js6
-rw-r--r--app/assets/javascripts/ide/components/ide_context_bar.vue4
-rw-r--r--app/assets/javascripts/ide/components/ide_project_branches_tree.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_project_tree.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_repo_tree.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue6
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue6
-rw-r--r--app/assets/javascripts/ide/components/new_branch_form.vue4
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue6
-rw-r--r--app/assets/javascripts/ide/components/repo_edit_button.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_loading_file.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_preview.vue4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js4
-rw-r--r--app/assets/javascripts/labels_select.js40
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js6
-rw-r--r--app/assets/javascripts/pages/projects/cycle_analytics/show/index.js3
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.js224
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue232
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js6
-rw-r--r--app/assets/stylesheets/pages/issuable.scss7
-rw-r--r--app/controllers/projects/pages_domains_controller.rb29
-rw-r--r--app/models/ci/runner.rb4
-rw-r--r--app/services/projects/update_service.rb15
-rw-r--r--app/views/admin/runners/_runner.html.haml2
-rw-r--r--app/views/admin/runners/index.html.haml1
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml1
-rw-r--r--app/views/projects/pages_domains/_form.html.haml56
-rw-r--r--app/views/projects/pages_domains/edit.html.haml11
-rw-r--r--app/views/projects/pages_domains/new.html.haml6
-rw-r--r--app/views/projects/pages_domains/show.html.haml4
-rw-r--r--app/views/projects/runners/_form.html.haml5
-rw-r--r--app/views/projects/runners/show.html.haml3
34 files changed, 398 insertions, 311 deletions
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index add24303e7b..9501e35b178 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -6,7 +6,7 @@ import { __ } from '../../locale';
import Sidebar from '../../right_sidebar';
import eventHub from '../../sidebar/event_hub';
import assigneeTitle from '../../sidebar/components/assignees/assignee_title';
-import assignees from '../../sidebar/components/assignees/assignees';
+import assignees from '../../sidebar/components/assignees/assignees.vue';
import DueDateSelectors from '../../due_date_select';
import './sidebar/remove_issue';
import IssuableContext from '../../issuable_context';
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 034f2923b3b..46d89c825f9 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -14,10 +14,10 @@ import CycleAnalyticsStore from './cycle_analytics_store';
Vue.use(Translate);
-$(() => {
+export default () => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
- gl.cycleAnalyticsApp = new Vue({
+ new Vue({ // eslint-disable-line no-new
el: '#cycle-analytics',
name: 'CycleAnalytics',
components: {
@@ -132,4 +132,4 @@ $(() => {
},
},
});
-});
+};
diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue
index dd947f66969..9d933b8891d 100644
--- a/app/assets/javascripts/ide/components/ide_context_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_context_bar.vue
@@ -1,8 +1,8 @@
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
+ import icon from '~/vue_shared/components/icon.vue';
+ import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import repoCommitSection from './repo_commit_section.vue';
- import icon from '../../vue_shared/components/icon.vue';
- import panelResizer from '../../vue_shared/components/panel_resizer.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
index af2f7341a91..2fbff2bd789 100644
--- a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
@@ -1,6 +1,6 @@
<script>
+import icon from '~/vue_shared/components/icon.vue';
import repoTree from './ide_repo_tree.vue';
-import icon from '../../vue_shared/components/icon.vue';
import newDropdown from './new_dropdown/index.vue';
export default {
diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue
index ed49a0e72a2..32bf7175c88 100644
--- a/app/assets/javascripts/ide/components/ide_project_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_project_tree.vue
@@ -1,6 +1,6 @@
<script>
+import projectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
import branchesTree from './ide_project_branches_tree.vue';
-import projectAvatarImage from '../../vue_shared/components/project_avatar/image.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue
index 4651e345d75..4a324264992 100644
--- a/app/assets/javascripts/ide/components/ide_repo_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_repo_tree.vue
@@ -1,8 +1,8 @@
<script>
import { mapState } from 'vuex';
+import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import repoPreviousDirectory from './repo_prev_directory.vue';
import repoFile from './repo_file.vue';
-import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
import { treeList } from '../stores/utils';
export default {
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index a68f8ce0169..18b5059a17f 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -1,9 +1,9 @@
<script>
import { mapState, mapActions } from 'vuex';
+ import icon from '~/vue_shared/components/icon.vue';
+ import panelResizer from '~/vue_shared/components/panel_resizer.vue';
+ import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import projectTree from './ide_project_tree.vue';
- import icon from '../../vue_shared/components/icon.vue';
- import panelResizer from '../../vue_shared/components/panel_resizer.vue';
- import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index e48c446c4a4..97ae64b206d 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -1,8 +1,8 @@
<script>
import { mapState } from 'vuex';
- import icon from '../../vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
- import timeAgoMixin from '../../vue_shared/mixins/timeago';
+ import icon from '~/vue_shared/components/icon.vue';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import timeAgoMixin from '~/vue_shared/mixins/timeago';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/new_branch_form.vue b/app/assets/javascripts/ide/components/new_branch_form.vue
index 56e31256132..1e8d5bb6453 100644
--- a/app/assets/javascripts/ide/components/new_branch_form.vue
+++ b/app/assets/javascripts/ide/components/new_branch_form.vue
@@ -1,7 +1,7 @@
<script>
import { mapState, mapActions } from 'vuex';
- import flash, { hideFlash } from '../../flash';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+ import flash, { hideFlash } from '~/flash';
+ import loadingIcon from '~/vue_shared/components/loading_icon.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 96b1bb78c1d..37f2cf30a29 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -1,8 +1,8 @@
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
-import tooltip from '../../vue_shared/directives/tooltip';
-import icon from '../../vue_shared/components/icon.vue';
-import modal from '../../vue_shared/components/modal.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import icon from '~/vue_shared/components/icon.vue';
+import modal from '~/vue_shared/components/modal.vue';
import commitFilesList from './commit_sidebar/list.vue';
export default {
diff --git a/app/assets/javascripts/ide/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue
index c43e9163340..fe4320731d9 100644
--- a/app/assets/javascripts/ide/components/repo_edit_button.vue
+++ b/app/assets/javascripts/ide/components/repo_edit_button.vue
@@ -1,6 +1,6 @@
<script>
import { mapGetters, mapActions, mapState } from 'vuex';
-import modal from '../../vue_shared/components/modal.vue';
+import modal from '~/vue_shared/components/modal.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index f99228012f4..f31cc12339b 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -1,7 +1,7 @@
<script>
/* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex';
-import flash from '../../flash';
+import flash from '~/flash';
import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue
index 3aeb6f0b28f..79af8c0b0c7 100644
--- a/app/assets/javascripts/ide/components/repo_loading_file.vue
+++ b/app/assets/javascripts/ide/components/repo_loading_file.vue
@@ -1,6 +1,6 @@
<script>
import { mapState } from 'vuex';
- import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
+ import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/repo_preview.vue b/app/assets/javascripts/ide/components/repo_preview.vue
index e47270a9855..a216269e292 100644
--- a/app/assets/javascripts/ide/components/repo_preview.vue
+++ b/app/assets/javascripts/ide/components/repo_preview.vue
@@ -1,7 +1,7 @@
<script>
import { mapGetters } from 'vuex';
- import LineHighlighter from '../../line_highlighter';
- import syntaxHighlight from '../../syntax_highlight';
+ import LineHighlighter from '~/line_highlighter';
+ import syntaxHighlight from '~/syntax_highlight';
export default {
computed: {
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index d007d0ae78f..2c690b1f635 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import { visitUrl } from '../../lib/utils/url_utility';
-import flash from '../../flash';
+import { visitUrl } from '~/lib/utils/url_utility';
+import flash from '~/flash';
import service from '../services';
import * as types from './mutation_types';
import { stripHtml } from '../../lib/utils/text_utility';
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 89a246f56cf..9b46bbf83da 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -21,7 +21,7 @@ export default class LabelsSelect {
}
$els.each(function(i, dropdown) {
- var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
+ var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
$dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter');
$toggleText = $dropdown.find('.dropdown-toggle-text');
@@ -53,13 +53,6 @@ export default class LabelsSelect {
.map(function () {
return this.value;
}).get();
- if (issueUpdateURL != null) {
- issueURLSplit = issueUpdateURL.split('/');
- }
- if (issueUpdateURL) {
- labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>');
- labelNoneHTMLTemplate = '<span class="no-value">None</span>';
- }
const handleClick = options.handleClick;
$sidebarLabelTooltip.tooltip();
@@ -91,14 +84,17 @@ export default class LabelsSelect {
$loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide();
- data.issueURLSplit = issueURLSplit;
+ data.issueUpdateURL = issueUpdateURL;
labelCount = 0;
- if (data.labels.length) {
- template = labelHTMLTemplate(data);
+ if (data.labels.length && issueUpdateURL) {
+ template = LabelsSelect.getLabelTemplate({
+ labels: data.labels,
+ issueUpdateURL,
+ });
labelCount = data.labels.length;
}
else {
- template = labelNoneHTMLTemplate;
+ template = '<span class="no-value">None</span>';
}
$value.removeAttr('style').html(template);
$sidebarCollapsedValue.text(labelCount);
@@ -418,6 +414,26 @@ export default class LabelsSelect {
this.bindEvents();
}
+ static getLabelTemplate(tplData) {
+ // We could use ES6 template string here
+ // and properly indent markup for readability
+ // but that also introduces unintended white-space
+ // so best approach is to use traditional way of
+ // concatenation
+ // see: http://2ality.com/2016/05/template-literal-whitespace.html#joining-arrays
+ const tpl = _.template([
+ '<% _.each(labels, function(label){ %>',
+ '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
+ '<span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
+ '<%- label.title %>',
+ '</span>',
+ '</a>',
+ '<% }); %>',
+ ].join(''));
+
+ return tpl(tplData);
+ }
+
bindEvents() {
return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue);
}
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index b3f6a72fdcb..42f7460ad55 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -2,9 +2,9 @@
import { visitUrl } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select';
import { isMetaClick } from '~/lib/utils/common_utils';
-import { __ } from '../../../../locale';
-import flash from '../../../../flash';
-import axios from '../../../../lib/utils/axios_utils';
+import { __ } from '~/locale';
+import flash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
export default class Todos {
constructor() {
diff --git a/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js
new file mode 100644
index 00000000000..df58e9dd072
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js
@@ -0,0 +1,3 @@
+import initCycleAnalytics from '~/cycle_analytics/cycle_analytics_bundle';
+
+document.addEventListener('DOMContentLoaded', initCycleAnalytics);
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.js b/app/assets/javascripts/sidebar/components/assignees/assignees.js
deleted file mode 100644
index 643877b9d47..00000000000
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.js
+++ /dev/null
@@ -1,224 +0,0 @@
-export default {
- name: 'Assignees',
- data() {
- return {
- defaultRenderCount: 5,
- defaultMaxCounter: 99,
- showLess: true,
- };
- },
- props: {
- rootPath: {
- type: String,
- required: true,
- },
- users: {
- type: Array,
- required: true,
- },
- editable: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- firstUser() {
- return this.users[0];
- },
- hasMoreThanTwoAssignees() {
- return this.users.length > 2;
- },
- hasMoreThanOneAssignee() {
- return this.users.length > 1;
- },
- hasAssignees() {
- return this.users.length > 0;
- },
- hasNoUsers() {
- return !this.users.length;
- },
- hasOneUser() {
- return this.users.length === 1;
- },
- renderShowMoreSection() {
- return this.users.length > this.defaultRenderCount;
- },
- numberOfHiddenAssignees() {
- return this.users.length - this.defaultRenderCount;
- },
- isHiddenAssignees() {
- return this.numberOfHiddenAssignees > 0;
- },
- hiddenAssigneesLabel() {
- return `+ ${this.numberOfHiddenAssignees} more`;
- },
- collapsedTooltipTitle() {
- const maxRender = Math.min(this.defaultRenderCount, this.users.length);
- const renderUsers = this.users.slice(0, maxRender);
- const names = renderUsers.map(u => u.name);
-
- if (this.users.length > maxRender) {
- names.push(`+ ${this.users.length - maxRender} more`);
- }
-
- return names.join(', ');
- },
- sidebarAvatarCounter() {
- let counter = `+${this.users.length - 1}`;
-
- if (this.users.length > this.defaultMaxCounter) {
- counter = `${this.defaultMaxCounter}+`;
- }
-
- return counter;
- },
- },
- methods: {
- assignSelf() {
- this.$emit('assign-self');
- },
- toggleShowLess() {
- this.showLess = !this.showLess;
- },
- renderAssignee(index) {
- return !this.showLess || (index < this.defaultRenderCount && this.showLess);
- },
- avatarUrl(user) {
- return user.avatar || user.avatar_url || gon.default_avatar_url;
- },
- assigneeUrl(user) {
- return `${this.rootPath}${user.username}`;
- },
- assigneeAlt(user) {
- return `${user.name}'s avatar`;
- },
- assigneeUsername(user) {
- return `@${user.username}`;
- },
- shouldRenderCollapsedAssignee(index) {
- const firstTwo = this.users.length <= 2 && index <= 2;
-
- return index === 0 || firstTwo;
- },
- },
- template: `
- <div>
- <div
- class="sidebar-collapsed-icon sidebar-collapsed-user"
- :class="{ 'multiple-users': hasMoreThanOneAssignee, 'has-tooltip': hasAssignees }"
- data-container="body"
- data-placement="left"
- :title="collapsedTooltipTitle"
- >
- <i
- v-if="hasNoUsers"
- aria-label="No Assignee"
- class="fa fa-user"
- />
- <button
- type="button"
- class="btn-link"
- v-for="(user, index) in users"
- v-if="shouldRenderCollapsedAssignee(index)"
- >
- <img
- width="24"
- class="avatar avatar-inline s24"
- :alt="assigneeAlt(user)"
- :src="avatarUrl(user)"
- />
- <span class="author">
- {{ user.name }}
- </span>
- </button>
- <button
- v-if="hasMoreThanTwoAssignees"
- class="btn-link"
- type="button"
- >
- <span
- class="avatar-counter sidebar-avatar-counter"
- >
- {{ sidebarAvatarCounter }}
- </span>
- </button>
- </div>
- <div class="value hide-collapsed">
- <template v-if="hasNoUsers">
- <span class="assign-yourself no-value">
- No assignee
- <template v-if="editable">
- -
- <button
- type="button"
- class="btn-link"
- @click="assignSelf"
- >
- assign yourself
- </button>
- </template>
- </span>
- </template>
- <template v-else-if="hasOneUser">
- <a
- class="author_link bold"
- :href="assigneeUrl(firstUser)"
- >
- <img
- width="32"
- class="avatar avatar-inline s32"
- :alt="assigneeAlt(firstUser)"
- :src="avatarUrl(firstUser)"
- />
- <span class="author">
- {{ firstUser.name }}
- </span>
- <span class="username">
- {{ assigneeUsername(firstUser) }}
- </span>
- </a>
- </template>
- <template v-else>
- <div class="user-list">
- <div
- class="user-item"
- v-for="(user, index) in users"
- v-if="renderAssignee(index)"
- >
- <a
- class="user-link has-tooltip"
- data-placement="bottom"
- :href="assigneeUrl(user)"
- :data-title="user.name"
- >
- <img
- width="32"
- class="avatar avatar-inline s32"
- :alt="assigneeAlt(user)"
- :src="avatarUrl(user)"
- />
- </a>
- </div>
- </div>
- <div
- v-if="renderShowMoreSection"
- class="user-list-more"
- >
- <button
- type="button"
- class="btn-link"
- @click="toggleShowLess"
- >
- <template v-if="showLess">
- {{ hiddenAssigneesLabel }}
- </template>
- <template v-else>
- - show less
- </template>
- </button>
- </div>
- </template>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
new file mode 100644
index 00000000000..1e7f46454bf
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -0,0 +1,232 @@
+<script>
+export default {
+ name: 'Assignees',
+ props: {
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ users: {
+ type: Array,
+ required: true,
+ },
+ editable: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ defaultRenderCount: 5,
+ defaultMaxCounter: 99,
+ showLess: true,
+ };
+ },
+ computed: {
+ firstUser() {
+ return this.users[0];
+ },
+ hasMoreThanTwoAssignees() {
+ return this.users.length > 2;
+ },
+ hasMoreThanOneAssignee() {
+ return this.users.length > 1;
+ },
+ hasAssignees() {
+ return this.users.length > 0;
+ },
+ hasNoUsers() {
+ return !this.users.length;
+ },
+ hasOneUser() {
+ return this.users.length === 1;
+ },
+ renderShowMoreSection() {
+ return this.users.length > this.defaultRenderCount;
+ },
+ numberOfHiddenAssignees() {
+ return this.users.length - this.defaultRenderCount;
+ },
+ isHiddenAssignees() {
+ return this.numberOfHiddenAssignees > 0;
+ },
+ hiddenAssigneesLabel() {
+ return `+ ${this.numberOfHiddenAssignees} more`;
+ },
+ collapsedTooltipTitle() {
+ const maxRender = Math.min(this.defaultRenderCount, this.users.length);
+ const renderUsers = this.users.slice(0, maxRender);
+ const names = renderUsers.map(u => u.name);
+
+ if (this.users.length > maxRender) {
+ names.push(`+ ${this.users.length - maxRender} more`);
+ }
+
+ return names.join(', ');
+ },
+ sidebarAvatarCounter() {
+ let counter = `+${this.users.length - 1}`;
+
+ if (this.users.length > this.defaultMaxCounter) {
+ counter = `${this.defaultMaxCounter}+`;
+ }
+
+ return counter;
+ },
+ },
+ methods: {
+ assignSelf() {
+ this.$emit('assign-self');
+ },
+ toggleShowLess() {
+ this.showLess = !this.showLess;
+ },
+ renderAssignee(index) {
+ return !this.showLess || (index < this.defaultRenderCount && this.showLess);
+ },
+ avatarUrl(user) {
+ return user.avatar || user.avatar_url || gon.default_avatar_url;
+ },
+ assigneeUrl(user) {
+ return `${this.rootPath}${user.username}`;
+ },
+ assigneeAlt(user) {
+ return `${user.name}'s avatar`;
+ },
+ assigneeUsername(user) {
+ return `@${user.username}`;
+ },
+ shouldRenderCollapsedAssignee(index) {
+ const firstTwo = this.users.length <= 2 && index <= 2;
+
+ return index === 0 || firstTwo;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div
+ class="sidebar-collapsed-icon sidebar-collapsed-user"
+ :class="{ 'multiple-users': hasMoreThanOneAssignee, 'has-tooltip': hasAssignees }"
+ data-container="body"
+ data-placement="left"
+ :title="collapsedTooltipTitle"
+ >
+ <i
+ v-if="hasNoUsers"
+ aria-label="No Assignee"
+ class="fa fa-user"
+ >
+ </i>
+ <button
+ type="button"
+ class="btn-link"
+ v-for="(user, index) in users"
+ v-if="shouldRenderCollapsedAssignee(index)"
+ :key="user.id"
+ >
+ <img
+ width="24"
+ class="avatar avatar-inline s24"
+ :alt="assigneeAlt(user)"
+ :src="avatarUrl(user)"
+ />
+ <span class="author">
+ {{ user.name }}
+ </span>
+ </button>
+ <button
+ v-if="hasMoreThanTwoAssignees"
+ class="btn-link"
+ type="button"
+ >
+ <span
+ class="avatar-counter sidebar-avatar-counter"
+ >
+ {{ sidebarAvatarCounter }}
+ </span>
+ </button>
+ </div>
+ <div class="value hide-collapsed">
+ <template v-if="hasNoUsers">
+ <span class="assign-yourself no-value">
+ No assignee
+ <template v-if="editable">
+ -
+ <button
+ type="button"
+ class="btn-link"
+ @click="assignSelf"
+ >
+ assign yourself
+ </button>
+ </template>
+ </span>
+ </template>
+ <template v-else-if="hasOneUser">
+ <a
+ class="author_link bold"
+ :href="assigneeUrl(firstUser)"
+ >
+ <img
+ width="32"
+ class="avatar avatar-inline s32"
+ :alt="assigneeAlt(firstUser)"
+ :src="avatarUrl(firstUser)"
+ />
+ <span class="author">
+ {{ firstUser.name }}
+ </span>
+ <span class="username">
+ {{ assigneeUsername(firstUser) }}
+ </span>
+ </a>
+ </template>
+ <template v-else>
+ <div class="user-list">
+ <div
+ class="user-item"
+ v-for="(user, index) in users"
+ v-if="renderAssignee(index)"
+ :key="user.id"
+ >
+ <a
+ class="user-link has-tooltip"
+ data-container="body"
+ data-placement="bottom"
+ :href="assigneeUrl(user)"
+ :data-title="user.name"
+ >
+ <img
+ width="32"
+ class="avatar avatar-inline s32"
+ :alt="assigneeAlt(user)"
+ :src="avatarUrl(user)"
+ />
+ </a>
+ </div>
+ </div>
+ <div
+ v-if="renderShowMoreSection"
+ class="user-list-more"
+ >
+ <button
+ type="button"
+ class="btn-link"
+ @click="toggleShowLess"
+ >
+ <template v-if="showLess">
+ {{ hiddenAssigneesLabel }}
+ </template>
+ <template v-else>
+ - show less
+ </template>
+ </button>
+ </div>
+ </template>
+ </div>
+ </div>
+</template>
+
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
index 9e47039d920..8269fe1281d 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
@@ -1,6 +1,6 @@
import Flash from '../../../flash';
import AssigneeTitle from './assignee_title';
-import Assignees from './assignees';
+import Assignees from './assignees.vue';
import Store from '../../stores/sidebar_store';
import eventHub from '../../event_hub';
@@ -28,8 +28,8 @@ export default {
},
},
components: {
- 'assignee-title': AssigneeTitle,
- assignees: Assignees,
+ AssigneeTitle,
+ Assignees,
},
methods: {
assignSelf() {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 0cf67734237..4c9732c26d9 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -103,6 +103,7 @@
.issuable-show-labels {
a {
margin-bottom: 5px;
+ margin-right: 5px;
display: inline-block;
.color-label {
@@ -116,6 +117,12 @@
}
&.has-labels {
+ // this font size is a fix to
+ // prevent unintended spacing between labels
+ // which shows up when rendering markup has white-space
+ // characters present.
+ // see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3
+ font-size: 0;
margin-bottom: -5px;
}
}
diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb
index b71f1e5fef4..4856be61e88 100644
--- a/app/controllers/projects/pages_domains_controller.rb
+++ b/app/controllers/projects/pages_domains_controller.rb
@@ -3,7 +3,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :require_pages_enabled!
before_action :authorize_update_pages!, except: [:show]
- before_action :domain, only: [:show, :destroy, :verify]
+ before_action :domain, except: [:new, :create]
def show
end
@@ -24,8 +24,11 @@ class Projects::PagesDomainsController < Projects::ApplicationController
redirect_to project_pages_domain_path(@project, @domain)
end
+ def edit
+ end
+
def create
- @domain = @project.pages_domains.create(pages_domain_params)
+ @domain = @project.pages_domains.create(create_params)
if @domain.valid?
redirect_to project_pages_domain_path(@project, @domain)
@@ -34,6 +37,16 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
end
+ def update
+ if @domain.update(update_params)
+ redirect_to project_pages_path(@project),
+ status: 302,
+ notice: 'Domain was updated'
+ else
+ render 'edit'
+ end
+ end
+
def destroy
@domain.destroy
@@ -49,12 +62,12 @@ class Projects::PagesDomainsController < Projects::ApplicationController
private
- def pages_domain_params
- params.require(:pages_domain).permit(
- :certificate,
- :key,
- :domain
- )
+ def create_params
+ params.require(:pages_domain).permit(:key, :certificate, :domain)
+ end
+
+ def update_params
+ params.require(:pages_domain).permit(:key, :certificate)
end
def domain
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 13c784bea0d..609620a62bb 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -49,7 +49,7 @@ module Ci
ref_protected: 1
}
- cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at
+ cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at, :ip_address
# Searches for runners matching the given query.
#
@@ -157,7 +157,7 @@ module Ci
end
def update_cached_info(values)
- values = values&.slice(:version, :revision, :platform, :architecture) || {}
+ values = values&.slice(:version, :revision, :platform, :architecture, :ip_address) || {}
values[:contacted_at] = Time.now
cache_attributes(values)
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 0e235a6d2a0..379a8068023 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -15,6 +15,8 @@ module Projects
return error("Could not set the default branch") unless project.change_head(params[:default_branch])
end
+ ensure_wiki_exists if enabling_wiki?
+
if project.update_attributes(params.except(:default_branch))
if project.previous_changes.include?('path')
project.rename_repo
@@ -52,5 +54,18 @@ module Projects
project.repository.exists? &&
new_branch && new_branch != project.default_branch
end
+
+ def enabling_wiki?
+ return false if @project.wiki_enabled?
+
+ params[:project_feature_attributes][:wiki_access_level].to_i > ProjectFeature::DISABLED
+ end
+
+ def ensure_wiki_exists
+ ProjectWiki.new(project, project.owner).wiki
+ rescue ProjectWiki::CouldNotCreateWikiError
+ log_error("Could not create wiki for #{project.full_name}")
+ Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
+ end
end
end
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index 140688b52d3..e1cee584929 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -17,6 +17,8 @@
%td
= runner.version
%td
+ = runner.ip_address
+ %td
- if runner.shared?
n/a
- else
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index abec3607cab..9f13dbbbd82 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -60,6 +60,7 @@
%th Runner token
%th Description
%th Version
+ %th IP Address
%th Projects
%th Jobs
%th Tags
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index d98e0564da4..02395b6eb9b 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -2,7 +2,6 @@
- page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_vue')
- = webpack_bundle_tag('cycle_analytics')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml
index ca1b41b140a..d81b07832bb 100644
--- a/app/views/projects/pages_domains/_form.html.haml
+++ b/app/views/projects/pages_domains/_form.html.haml
@@ -1,34 +1,30 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
- - if @domain.errors.any?
- #error_explanation
- .alert.alert-danger
- - @domain.errors.full_messages.each do |msg|
- %p= msg
+- if @domain.errors.any?
+ #error_explanation
+ .alert.alert-danger
+ - @domain.errors.full_messages.each do |msg|
+ %p= msg
+.form-group
+ = f.label :domain, class: 'control-label' do
+ Domain
+ .col-sm-10
+ = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted?
+
+- if Gitlab.config.pages.external_https
.form-group
- = f.label :domain, class: 'control-label' do
- Domain
+ = f.label :certificate, class: 'control-label' do
+ Certificate (PEM)
.col-sm-10
- = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control'
-
- - if Gitlab.config.pages.external_https
- .form-group
- = f.label :certificate, class: 'control-label' do
- Certificate (PEM)
- .col-sm-10
- = f.text_area :certificate, rows: 5, class: 'form-control'
- %span.help-inline Upload a certificate for your domain with all intermediates
-
- .form-group
- = f.label :key, class: 'control-label' do
- Key (PEM)
- .col-sm-10
- = f.text_area :key, rows: 5, class: 'form-control'
- %span.help-inline Upload a private key for your certificate
- - else
- .nothing-here-block
- Support for custom certificates is disabled.
- Ask your system's administrator to enable it.
+ = f.text_area :certificate, rows: 5, class: 'form-control'
+ %span.help-inline Upload a certificate for your domain with all intermediates
- .form-actions
- = f.submit 'Create New Domain', class: "btn btn-save"
+ .form-group
+ = f.label :key, class: 'control-label' do
+ Key (PEM)
+ .col-sm-10
+ = f.text_area :key, rows: 5, class: 'form-control'
+ %span.help-inline Upload a private key for your certificate
+- else
+ .nothing-here-block
+ Support for custom certificates is disabled.
+ Ask your system's administrator to enable it.
diff --git a/app/views/projects/pages_domains/edit.html.haml b/app/views/projects/pages_domains/edit.html.haml
new file mode 100644
index 00000000000..5645a4604bf
--- /dev/null
+++ b/app/views/projects/pages_domains/edit.html.haml
@@ -0,0 +1,11 @@
+- add_to_breadcrumbs "Pages", project_pages_path(@project)
+- breadcrumb_title @domain.domain
+- page_title @domain.domain
+%h3.page_title
+ = @domain.domain
+%hr.clearfix
+%div
+ = form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
+ = render 'form', { f: f }
+ .form-actions
+ = f.submit 'Save Changes', class: "btn btn-save"
diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml
index e1477c71d06..5a397c9d3c7 100644
--- a/app/views/projects/pages_domains/new.html.haml
+++ b/app/views/projects/pages_domains/new.html.haml
@@ -1,6 +1,10 @@
+- add_to_breadcrumbs "Pages", project_pages_path(@project)
- page_title 'New Pages Domain'
%h3.page_title
New Pages Domain
%hr.clearfix
%div
- = render 'form'
+ = form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
+ = render 'form', { f: f }
+ .form-actions
+ = f.submit 'Create New Domain', class: "btn btn-save"
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 72e9203bdb0..ba0713daee9 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -1,4 +1,7 @@
+- add_to_breadcrumbs "Pages", project_pages_path(@project)
+- breadcrumb_title @domain.domain
- page_title "#{@domain.domain}", 'Pages Domains'
+
- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
- if verification_enabled && @domain.unverified?
%p.alert.alert-warning
@@ -8,6 +11,7 @@
%h3.page-title
Pages Domain
+ = link_to 'Edit', edit_project_pages_domain_path(@project, @domain), class: 'btn btn-success pull-right'
.table-holder
%table.table
diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml
index e660fce652f..49c90869146 100644
--- a/app/views/projects/runners/_form.html.haml
+++ b/app/views/projects/runners/_form.html.haml
@@ -30,6 +30,11 @@
.col-sm-10
= f.text_field :token, class: 'form-control', readonly: true
.form-group
+ = label_tag :ip_address, class: 'control-label' do
+ IP Address
+ .col-sm-10
+ = f.text_field :ip_address, class: 'form-control', readonly: true
+ .form-group
= label_tag :description, class: 'control-label' do
Description
.col-sm-10
diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml
index dfab04aa1fb..4e57f5f844d 100644
--- a/app/views/projects/runners/show.html.haml
+++ b/app/views/projects/runners/show.html.haml
@@ -41,6 +41,9 @@
%td Version
%td= @runner.version
%tr
+ %td IP Address
+ %td= @runner.ip_address
+ %tr
%td Revision
%td= @runner.revision
%tr