summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-04-27 16:56:30 +0100
committerPhil Hughes <me@iamphill.com>2017-04-27 16:56:30 +0100
commit94b93e49d2d6299dd0f1bf2d06c089f65cc7b26f (patch)
treede4f860d9d728216b353c825ea314f7328b141a9
parent429a42b0cd2f7dd20e8e6bc924ecc27b55500410 (diff)
parent4d4aefc561bc5a9462dc68db8715b96c6c86bbca (diff)
downloadgitlab-ce-94b93e49d2d6299dd0f1bf2d06c089f65cc7b26f.tar.gz
Merge branch 'master' into emoji-button-titles
-rw-r--r--.gitlab/issue_templates/Bug.md4
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js9
-rw-r--r--app/assets/javascripts/environments/components/environment.vue (renamed from app/assets/javascripts/environments/components/environment.js)153
-rw-r--r--app/assets/javascripts/environments/environments_bundle.js21
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js21
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue (renamed from app/assets/javascripts/environments/folder/environments_folder_view.js)104
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.js101
-rw-r--r--app/assets/javascripts/pipelines/pipelines.js10
-rw-r--r--app/assets/javascripts/pipelines/stores/pipelines_store.js31
-rw-r--r--app/assets/javascripts/vue_realtime_listener/index.js9
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table_row.js31
-rw-r--r--app/helpers/application_helper.rb32
-rw-r--r--app/helpers/markup_helper.rb (renamed from app/helpers/gitlab_markdown_helper.rb)137
-rw-r--r--app/helpers/tree_helper.rb4
-rw-r--r--app/mailers/base_mailer.rb2
-rw-r--r--app/models/application_setting.rb9
-rw-r--r--app/models/network/graph.rb3
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/services/projects/create_service.rb3
-rw-r--r--app/views/projects/_readme.html.haml3
-rw-r--r--app/views/projects/blob/_markup.html.haml2
-rw-r--r--app/views/projects/blob/preview.html.haml8
-rw-r--r--app/views/projects/tree/_readme.html.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/shared/snippets/_blob.html.haml2
-rw-r--r--changelogs/unreleased/2246-uuid-is-nil-for-new-installation.yml4
-rw-r--r--changelogs/unreleased/26585-remove-readme-view-caching.yml4
-rw-r--r--changelogs/unreleased/29505-allow-admins-sudo-to-blocked-users.yml4
-rw-r--r--changelogs/unreleased/30973-fix-network-graph-ordering.yml4
-rw-r--r--changelogs/unreleased/tc-make-user-master-project-by-admin.yml4
-rw-r--r--db/migrate/20170426175636_fill_missing_uuid_on_application_settings.rb10
-rw-r--r--db/schema.rb2
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/gitlab/asciidoc.rb20
-rw-r--r--lib/gitlab/git/repository.rb23
-rw-r--r--lib/gitlab/other_markup.rb10
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb15
-rw-r--r--spec/features/copy_as_gfm_spec.rb2
-rw-r--r--spec/features/markdown_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb27
-rw-r--r--spec/helpers/markup_helper_spec.rb (renamed from spec/helpers/gitlab_markdown_helper_spec.rb)31
-rw-r--r--spec/javascripts/environments/environment_spec.js5
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js4
-rw-r--r--spec/javascripts/pipelines/time_ago_spec.js64
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb21
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb29
-rw-r--r--spec/lib/gitlab/other_markup_spec.rb2
-rw-r--r--spec/models/application_setting_spec.rb1
-rw-r--r--spec/models/network/graph_spec.rb21
-rw-r--r--spec/models/repository_spec.rb4
-rw-r--r--spec/requests/api/helpers_spec.rb28
-rw-r--r--spec/services/projects/create_service_spec.rb16
-rw-r--r--spec/services/system_note_service_spec.rb2
54 files changed, 642 insertions, 438 deletions
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md
index 6bb21e6a3af..241d90a204b 100644
--- a/.gitlab/issue_templates/Bug.md
+++ b/.gitlab/issue_templates/Bug.md
@@ -26,6 +26,7 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info
<details>
+<pre>
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
@@ -33,11 +34,13 @@ logs, and code as it's very hard to read otherwise.)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
+</pre>
</details>
#### Results of GitLab application Check
<details>
+<pre>
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`)
@@ -47,6 +50,7 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing)
+</pre>
</details>
### Possible fixes
diff --git a/Gemfile.lock b/Gemfile.lock
index 4c3b2480b54..52707628748 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -429,7 +429,7 @@ GEM
multi_json (~> 1.10)
loofah (2.0.3)
nokogiri (>= 1.5.9)
- mail (2.6.4)
+ mail (2.6.5)
mime-types (>= 1.16, < 4)
mail_room (0.9.1)
memoist (0.15.0)
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index 68a1c1de1df..e704be8b53e 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -106,15 +106,6 @@ export default Vue.component('pipelines-table', {
eventHub.$on('refreshPipelines', this.fetchPipelines);
},
- beforeUpdate() {
- if (this.state.pipelines.length &&
- this.$children &&
- !this.isMakingRequest &&
- !this.isLoading) {
- this.store.startTimeAgoLoops.call(this, Vue);
- }
- },
-
beforeDestroyed() {
eventHub.$off('refreshPipelines');
},
diff --git a/app/assets/javascripts/environments/components/environment.js b/app/assets/javascripts/environments/components/environment.vue
index f7175e412da..f319d6ca0c8 100644
--- a/app/assets/javascripts/environments/components/environment.js
+++ b/app/assets/javascripts/environments/components/environment.vue
@@ -1,6 +1,7 @@
+<script>
+
/* eslint-disable no-new */
/* global Flash */
-import Vue from 'vue';
import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from './environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
@@ -8,7 +9,7 @@ import TablePaginationComponent from '../../vue_shared/components/table_paginati
import '../../lib/utils/common_utils';
import eventHub from '../event_hub';
-export default Vue.component('environment-component', {
+export default {
components: {
'environment-table': EnvironmentTable,
@@ -140,76 +141,90 @@ export default Vue.component('environment-component', {
});
},
},
-
- template: `
- <div :class="cssContainerClass">
- <div class="top-area">
- <ul v-if="!isLoading" class="nav-links">
- <li v-bind:class="{ 'active': scope === null || scope === 'available' }">
- <a :href="projectEnvironmentsPath">
- Available
- <span class="badge js-available-environments-count">
- {{state.availableCounter}}
- </span>
- </a>
- </li>
- <li v-bind:class="{ 'active' : scope === 'stopped' }">
- <a :href="projectStoppedEnvironmentsPath">
- Stopped
- <span class="badge js-stopped-environments-count">
- {{state.stoppedCounter}}
- </span>
- </a>
- </li>
- </ul>
- <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
- <a :href="newEnvironmentPath" class="btn btn-create">
- New environment
+};
+</script>
+<template>
+ <div :class="cssContainerClass">
+ <div class="top-area">
+ <ul
+ v-if="!isLoading"
+ class="nav-links">
+ <li :class="{ active: scope === null || scope === 'available' }">
+ <a :href="projectEnvironmentsPath">
+ Available
+ <span class="badge js-available-environments-count">
+ {{state.availableCounter}}
+ </span>
+ </a>
+ </li>
+ <li :class="{ active : scope === 'stopped' }">
+ <a :href="projectStoppedEnvironmentsPath">
+ Stopped
+ <span class="badge js-stopped-environments-count">
+ {{state.stoppedCounter}}
+ </span>
</a>
- </div>
+ </li>
+ </ul>
+ <div
+ v-if="canCreateEnvironmentParsed && !isLoading"
+ class="nav-controls">
+ <a
+ :href="newEnvironmentPath"
+ class="btn btn-create">
+ New environment
+ </a>
</div>
+ </div>
+
+ <div class="content-list environments-container">
+ <div
+ class="environments-list-loading text-center"
+ v-if="isLoading">
- <div class="content-list environments-container">
- <div class="environments-list-loading text-center" v-if="isLoading">
- <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </div>
-
- <div class="blank-state blank-state-no-icon"
- v-if="!isLoading && state.environments.length === 0">
- <h2 class="blank-state-title js-blank-state-title">
- You don't have any environments right now.
- </h2>
- <p class="blank-state-text">
- Environments are places where code gets deployed, such as staging or production.
- <br />
- <a :href="helpPagePath">
- Read more about environments
- </a>
- </p>
-
- <a v-if="canCreateEnvironmentParsed"
- :href="newEnvironmentPath"
- class="btn btn-create js-new-environment-button">
- New Environment
+ <i
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true" />
+ </div>
+
+ <div
+ class="blank-state blank-state-no-icon"
+ v-if="!isLoading && state.environments.length === 0">
+ <h2 class="blank-state-title js-blank-state-title">
+ You don't have any environments right now.
+ </h2>
+ <p class="blank-state-text">
+ Environments are places where code gets deployed, such as staging or production.
+ <br />
+ <a :href="helpPagePath">
+ Read more about environments
</a>
- </div>
-
- <div class="table-holder"
- v-if="!isLoading && state.environments.length > 0">
-
- <environment-table
- :environments="state.environments"
- :can-create-deployment="canCreateDeploymentParsed"
- :can-read-environment="canReadEnvironmentParsed"
- :service="service"
- :is-loading-folder-content="isLoadingFolderContent" />
- </div>
-
- <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
- :change="changePage"
- :pageInfo="state.paginationInformation">
- </table-pagination>
+ </p>
+
+ <a
+ v-if="canCreateEnvironmentParsed"
+ :href="newEnvironmentPath"
+ class="btn btn-create js-new-environment-button">
+ New Environment
+ </a>
</div>
+
+ <div
+ class="table-holder"
+ v-if="!isLoading && state.environments.length > 0">
+
+ <environment-table
+ :environments="state.environments"
+ :can-create-deployment="canCreateDeploymentParsed"
+ :can-read-environment="canReadEnvironmentParsed"
+ :service="service"
+ :is-loading-folder-content="isLoadingFolderContent" />
+ </div>
+
+ <table-pagination
+ v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
+ :change="changePage"
+ :pageInfo="state.paginationInformation" />
</div>
- `,
-});
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/environments_bundle.js b/app/assets/javascripts/environments/environments_bundle.js
index 8d963b335cf..c0662125f28 100644
--- a/app/assets/javascripts/environments/environments_bundle.js
+++ b/app/assets/javascripts/environments/environments_bundle.js
@@ -1,13 +1,10 @@
-import EnvironmentsComponent from './components/environment';
+import Vue from 'vue';
+import EnvironmentsComponent from './components/environment.vue';
-$(() => {
- window.gl = window.gl || {};
-
- if (gl.EnvironmentsListApp) {
- gl.EnvironmentsListApp.$destroy(true);
- }
-
- gl.EnvironmentsListApp = new EnvironmentsComponent({
- el: document.querySelector('#environments-list-view'),
- });
-});
+document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#environments-list-view',
+ components: {
+ 'environments-table-app': EnvironmentsComponent,
+ },
+ render: createElement => createElement('environments-table-app'),
+}));
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index f939eccf246..9add8c3d721 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -1,13 +1,10 @@
-import EnvironmentsFolderComponent from './environments_folder_view';
+import Vue from 'vue';
+import EnvironmentsFolderComponent from './environments_folder_view.vue';
-$(() => {
- window.gl = window.gl || {};
-
- if (gl.EnvironmentsListFolderApp) {
- gl.EnvironmentsListFolderApp.$destroy(true);
- }
-
- gl.EnvironmentsListFolderApp = new EnvironmentsFolderComponent({
- el: document.querySelector('#environments-folder-list-view'),
- });
-});
+document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#environments-folder-list-view',
+ components: {
+ 'environments-folder-app': EnvironmentsFolderComponent,
+ },
+ render: createElement => createElement('environments-folder-app'),
+}));
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 05d44f77d1d..d27b2acfcdf 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,6 +1,6 @@
+<script>
/* eslint-disable no-new */
/* global Flash */
-import Vue from 'vue';
import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from '../components/environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
@@ -8,7 +8,7 @@ import TablePaginationComponent from '../../vue_shared/components/table_paginati
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
-export default Vue.component('environment-folder-view', {
+export default {
components: {
'environment-table': EnvironmentTable,
'table-pagination': TablePaginationComponent,
@@ -116,54 +116,66 @@ export default Vue.component('environment-folder-view', {
return param;
},
},
+};
+</script>
+<template>
+ <div :class="cssContainerClass">
+ <div
+ class="top-area"
+ v-if="!isLoading">
+
+ <h4 class="js-folder-name environments-folder-name">
+ Environments / <b>{{folderName}}</b>
+ </h4>
+
+ <ul class="nav-links">
+ <li :class="{ active: scope === null || scope === 'available' }">
+ <a
+ :href="availablePath"
+ class="js-available-environments-folder-tab">
+ Available
+ <span class="badge js-available-environments-count">
+ {{state.availableCounter}}
+ </span>
+ </a>
+ </li>
+ <li :class="{ active : scope === 'stopped' }">
+ <a
+ :href="stoppedPath"
+ class="js-stopped-environments-folder-tab">
+ Stopped
+ <span class="badge js-stopped-environments-count">
+ {{state.stoppedCounter}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ </div>
- template: `
- <div :class="cssContainerClass">
- <div class="top-area" v-if="!isLoading">
-
- <h4 class="js-folder-name environments-folder-name">
- Environments / <b>{{folderName}}</b>
- </h4>
-
- <ul class="nav-links">
- <li v-bind:class="{ 'active': scope === null || scope === 'available' }">
- <a :href="availablePath" class="js-available-environments-folder-tab">
- Available
- <span class="badge js-available-environments-count">
- {{state.availableCounter}}
- </span>
- </a>
- </li>
- <li v-bind:class="{ 'active' : scope === 'stopped' }">
- <a :href="stoppedPath" class="js-stopped-environments-folder-tab">
- Stopped
- <span class="badge js-stopped-environments-count">
- {{state.stoppedCounter}}
- </span>
- </a>
- </li>
- </ul>
+ <div class="environments-container">
+ <div
+ class="environments-list-loading text-center"
+ v-if="isLoading">
+ <i
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true"/>
</div>
- <div class="environments-container">
- <div class="environments-list-loading text-center" v-if="isLoading">
- <i class="fa fa-spinner fa-spin"></i>
- </div>
-
- <div class="table-holder"
- v-if="!isLoading && state.environments.length > 0">
+ <div
+ class="table-holder"
+ v-if="!isLoading && state.environments.length > 0">
- <environment-table
- :environments="state.environments"
- :can-create-deployment="canCreateDeploymentParsed"
- :can-read-environment="canReadEnvironmentParsed"
- :service="service"/>
+ <environment-table
+ :environments="state.environments"
+ :can-create-deployment="canCreateDeploymentParsed"
+ :can-read-environment="canReadEnvironmentParsed"
+ :service="service"/>
- <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
- :change="changePage"
- :pageInfo="state.paginationInformation"/>
- </div>
+ <table-pagination
+ v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
+ :change="changePage"
+ :pageInfo="state.paginationInformation"/>
</div>
</div>
- `,
-});
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/time_ago.js b/app/assets/javascripts/pipelines/components/time_ago.js
index 498d0715f54..188f74cc705 100644
--- a/app/assets/javascripts/pipelines/components/time_ago.js
+++ b/app/assets/javascripts/pipelines/components/time_ago.js
@@ -2,68 +2,95 @@ import iconTimerSvg from 'icons/_icon_timer.svg';
import '../../lib/utils/datetime_utility';
export default {
+ props: {
+ finishedTime: {
+ type: String,
+ required: true,
+ },
+
+ duration: {
+ type: Number,
+ required: true,
+ },
+ },
+
data() {
return {
- currentTime: new Date(),
iconTimerSvg,
};
},
- props: ['pipeline'],
+
+ updated() {
+ $(this.$refs.tooltip).tooltip('fixTitle');
+ },
+
computed: {
- timeAgo() {
- return gl.utils.getTimeago();
+ hasDuration() {
+ return this.duration > 0;
},
- localTimeFinished() {
- return gl.utils.formatDate(this.pipeline.details.finished_at);
+
+ hasFinishedTime() {
+ return this.finishedTime !== '';
},
- timeStopped() {
- const changeTime = this.currentTime;
- const options = {
- weekday: 'long',
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- };
- options.timeZoneName = 'short';
- const finished = this.pipeline.details.finished_at;
- if (!finished && changeTime) return false;
- return ({ words: this.timeAgo.format(finished) });
+
+ localTimeFinished() {
+ return gl.utils.formatDate(this.finishedTime);
},
- duration() {
- const { duration } = this.pipeline.details;
- const date = new Date(duration * 1000);
+
+ durationFormated() {
+ const date = new Date(this.duration * 1000);
let hh = date.getUTCHours();
let mm = date.getUTCMinutes();
let ss = date.getSeconds();
- if (hh < 10) hh = `0${hh}`;
- if (mm < 10) mm = `0${mm}`;
- if (ss < 10) ss = `0${ss}`;
+ // left pad
+ if (hh < 10) {
+ hh = `0${hh}`;
+ }
+ if (mm < 10) {
+ mm = `0${mm}`;
+ }
+ if (ss < 10) {
+ ss = `0${ss}`;
+ }
- if (duration !== null) return `${hh}:${mm}:${ss}`;
- return false;
+ return `${hh}:${mm}:${ss}`;
},
- },
- methods: {
- changeTime() {
- this.currentTime = new Date();
+
+ finishedTimeFormated() {
+ const timeAgo = gl.utils.getTimeago();
+
+ return timeAgo.format(this.finishedTime);
},
},
+
template: `
<td class="pipelines-time-ago">
- <p class="duration" v-if='duration'>
- <span v-html="iconTimerSvg"></span>
- {{duration}}
+ <p
+ class="duration"
+ v-if="hasDuration">
+ <span
+ v-html="iconTimerSvg">
+ </span>
+ {{durationFormated}}
</p>
- <p class="finished-at" v-if='timeStopped'>
- <i class="fa fa-calendar"></i>
+
+ <p
+ class="finished-at"
+ v-if="hasFinishedTime">
+
+ <i
+ class="fa fa-calendar"
+ aria-hidden="true" />
+
<time
+ ref="tooltip"
data-toggle="tooltip"
data-placement="top"
data-container="body"
- :data-original-title='localTimeFinished'>
- {{timeStopped.words}}
+ :title="localTimeFinished">
+ {{finishedTimeFormated}}
</time>
</p>
</td>
diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js
index 6eea4812f33..93d4818231f 100644
--- a/app/assets/javascripts/pipelines/pipelines.js
+++ b/app/assets/javascripts/pipelines/pipelines.js
@@ -1,4 +1,3 @@
-import Vue from 'vue';
import Visibility from 'visibilityjs';
import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub';
@@ -161,15 +160,6 @@ export default {
eventHub.$on('refreshPipelines', this.fetchPipelines);
},
- beforeUpdate() {
- if (this.state.pipelines.length &&
- this.$children &&
- !this.isMakingRequest &&
- !this.isLoading) {
- this.store.startTimeAgoLoops.call(this, Vue);
- }
- },
-
beforeDestroyed() {
eventHub.$off('refreshPipelines');
},
diff --git a/app/assets/javascripts/pipelines/stores/pipelines_store.js b/app/assets/javascripts/pipelines/stores/pipelines_store.js
index 377ec8ba2cc..ffefe0192f2 100644
--- a/app/assets/javascripts/pipelines/stores/pipelines_store.js
+++ b/app/assets/javascripts/pipelines/stores/pipelines_store.js
@@ -1,6 +1,3 @@
-/* eslint-disable no-underscore-dangle*/
-import VueRealtimeListener from '../../vue_realtime_listener';
-
export default class PipelinesStore {
constructor() {
this.state = {};
@@ -30,32 +27,4 @@ export default class PipelinesStore {
this.state.pageInfo = paginationInfo;
}
-
- /**
- * FIXME: Move this inside the component.
- *
- * Once the data is received we will start the time ago loops.
- *
- * Everytime a request is made like retry or cancel a pipeline, every 10 seconds we
- * update the time to show how long as passed.
- *
- */
- startTimeAgoLoops() {
- const startTimeLoops = () => {
- this.timeLoopInterval = setInterval(() => {
- this.$children[0].$children.reduce((acc, component) => {
- const timeAgoComponent = component.$children.filter(el => el.$options._componentTag === 'time-ago')[0];
- acc.push(timeAgoComponent);
- return acc;
- }, []).forEach(e => e.changeTime());
- }, 10000);
- };
-
- startTimeLoops();
-
- const removeIntervals = () => clearInterval(this.timeLoopInterval);
- const startIntervals = () => startTimeLoops();
-
- VueRealtimeListener(removeIntervals, startIntervals);
- }
}
diff --git a/app/assets/javascripts/vue_realtime_listener/index.js b/app/assets/javascripts/vue_realtime_listener/index.js
deleted file mode 100644
index 4ddb2f975b0..00000000000
--- a/app/assets/javascripts/vue_realtime_listener/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export default (removeIntervals, startIntervals) => {
- window.removeEventListener('focus', startIntervals);
- window.removeEventListener('blur', removeIntervals);
- window.removeEventListener('onbeforeload', removeIntervals);
-
- window.addEventListener('focus', startIntervals);
- window.addEventListener('blur', removeIntervals);
- window.addEventListener('onbeforeload', removeIntervals);
-};
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
index 62b7131de51..79806bc7204 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
@@ -1,5 +1,4 @@
/* eslint-disable no-param-reassign */
-
import AsyncButtonComponent from '../../pipelines/components/async_button.vue';
import PipelinesActionsComponent from '../../pipelines/components/pipelines_actions';
import PipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts';
@@ -166,6 +165,32 @@ export default {
}
return undefined;
},
+
+ /**
+ * Timeago components expects a number
+ *
+ * @return {type} description
+ */
+ pipelineDuration() {
+ if (this.pipeline.details && this.pipeline.details.duration) {
+ return this.pipeline.details.duration;
+ }
+
+ return 0;
+ },
+
+ /**
+ * Timeago component expects a String.
+ *
+ * @return {String}
+ */
+ pipelineFinishedAt() {
+ if (this.pipeline.details && this.pipeline.details.finished_at) {
+ return this.pipeline.details.finished_at;
+ }
+
+ return '';
+ },
},
template: `
@@ -192,7 +217,9 @@ export default {
</div>
</td>
- <time-ago :pipeline="pipeline"/>
+ <time-ago
+ :duration="pipelineDuration"
+ :finished-time="pipelineFinishedAt" />
<td class="pipeline-actions">
<div class="pull-right btn-group">
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e5b811f3300..fff57472a4f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -196,38 +196,6 @@ module ApplicationHelper
end
end
- def render_markup(file_name, file_content)
- if gitlab_markdown?(file_name)
- Hamlit::RailsHelpers.preserve(markdown(file_content))
- elsif asciidoc?(file_name)
- asciidoc(file_content)
- elsif plain?(file_name)
- content_tag :pre, class: 'plain-readme' do
- file_content
- end
- else
- other_markup(file_name, file_content)
- end
- rescue RuntimeError
- simple_format(file_content)
- end
-
- def plain?(filename)
- Gitlab::MarkupHelper.plain?(filename)
- end
-
- def markup?(filename)
- Gitlab::MarkupHelper.markup?(filename)
- end
-
- def gitlab_markdown?(filename)
- Gitlab::MarkupHelper.gitlab_markdown?(filename)
- end
-
- def asciidoc?(filename)
- Gitlab::MarkupHelper.asciidoc?(filename)
- end
-
def promo_host
'about.gitlab.com'
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/markup_helper.rb
index 106feb87398..0781874d7fc 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -1,6 +1,22 @@
require 'nokogiri'
-module GitlabMarkdownHelper
+module MarkupHelper
+ def plain?(filename)
+ Gitlab::MarkupHelper.plain?(filename)
+ end
+
+ def markup?(filename)
+ Gitlab::MarkupHelper.markup?(filename)
+ end
+
+ def gitlab_markdown?(filename)
+ Gitlab::MarkupHelper.gitlab_markdown?(filename)
+ end
+
+ def asciidoc?(filename)
+ Gitlab::MarkupHelper.asciidoc?(filename)
+ end
+
# Use this in places where you would normally use link_to(gfm(...), ...).
#
# It solves a problem occurring with nested links (i.e.
@@ -11,7 +27,7 @@ module GitlabMarkdownHelper
# explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
def link_to_gfm(body, url, html_options = {})
- return "" if body.blank?
+ return '' if body.blank?
context = {
project: @project,
@@ -43,71 +59,73 @@ module GitlabMarkdownHelper
fragment.to_html.html_safe
end
+ # Return the first line of +text+, up to +max_chars+, after parsing the line
+ # as Markdown. HTML tags in the parsed output are not counted toward the
+ # +max_chars+ limit. If the length limit falls within a tag's contents, then
+ # the tag contents are truncated without removing the closing tag.
+ def first_line_in_markdown(text, max_chars = nil, options = {})
+ md = markdown(text, options).strip
+
+ truncate_visible(md, max_chars || md.length) if md.present?
+ end
+
def markdown(text, context = {})
- return "" unless text.present?
+ return '' unless text.present?
context[:project] ||= @project
-
- html = Banzai.render(text, context)
+ html = markdown_unsafe(text, context)
banzai_postprocess(html, context)
end
def markdown_field(object, field)
object = object.for_display if object.respond_to?(:for_display)
- return "" unless object.present?
+ return '' unless object.present?
html = Banzai.render_field(object, field)
banzai_postprocess(html, object.banzai_render_context(field))
end
- def asciidoc(text)
- Gitlab::Asciidoc.render(
- text,
- project: @project,
- current_user: (current_user if defined?(current_user)),
-
- # RelativeLinkFilter
- project_wiki: @project_wiki,
- requested_path: @path,
- ref: @ref,
- commit: @commit
- )
+ def markup(file_name, text, context = {})
+ context[:project] ||= @project
+ html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
+ banzai_postprocess(html, context)
end
- def other_markup(file_name, text)
- Gitlab::OtherMarkup.render(
- file_name,
- text,
- project: @project,
- current_user: (current_user if defined?(current_user)),
+ def render_wiki_content(wiki_page)
+ text = wiki_page.content
+ return '' unless text.present?
+
+ context = { pipeline: :wiki, project: @project, project_wiki: @project_wiki, page_slug: wiki_page.slug }
+
+ html =
+ case wiki_page.format
+ when :markdown
+ markdown_unsafe(text, context)
+ when :asciidoc
+ asciidoc_unsafe(text)
+ else
+ wiki_page.formatted_content.html_safe
+ end
- # RelativeLinkFilter
- project_wiki: @project_wiki,
- requested_path: @path,
- ref: @ref,
- commit: @commit
- )
+ banzai_postprocess(html, context)
end
- # Return the first line of +text+, up to +max_chars+, after parsing the line
- # as Markdown. HTML tags in the parsed output are not counted toward the
- # +max_chars+ limit. If the length limit falls within a tag's contents, then
- # the tag contents are truncated without removing the closing tag.
- def first_line_in_markdown(text, max_chars = nil, options = {})
- md = markdown(text, options).strip
+ def markup_unsafe(file_name, text, context = {})
+ return '' unless text.present?
- truncate_visible(md, max_chars || md.length) if md.present?
- end
-
- def render_wiki_content(wiki_page)
- case wiki_page.format
- when :markdown
- markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki, page_slug: wiki_page.slug)
- when :asciidoc
- asciidoc(wiki_page.content)
+ if gitlab_markdown?(file_name)
+ Hamlit::RailsHelpers.preserve(markdown_unsafe(text, context))
+ elsif asciidoc?(file_name)
+ asciidoc_unsafe(text)
+ elsif plain?(file_name)
+ content_tag :pre, class: 'plain-readme' do
+ text
+ end
else
- wiki_page.formatted_content.html_safe
+ other_markup_unsafe(file_name, text)
end
+ rescue RuntimeError
+ simple_format(text)
end
# Returns the text necessary to reference `entity` across projects
@@ -183,10 +201,10 @@ module GitlabMarkdownHelper
end
def markdown_toolbar_button(options = {})
- data = options[:data].merge({ container: "body" })
+ data = options[:data].merge({ container: 'body' })
content_tag :button,
- type: "button",
- class: "toolbar-btn js-md has-tooltip hidden-xs",
+ type: 'button',
+ class: 'toolbar-btn js-md has-tooltip hidden-xs',
tabindex: -1,
data: data,
title: options[:title],
@@ -195,17 +213,34 @@ module GitlabMarkdownHelper
end
end
+ def markdown_unsafe(text, context = {})
+ Banzai.render(text, context)
+ end
+
+ def asciidoc_unsafe(text)
+ Gitlab::Asciidoc.render(text)
+ end
+
+ def other_markup_unsafe(file_name, text)
+ Gitlab::OtherMarkup.render(file_name, text)
+ end
+
# Calls Banzai.post_process with some common context options
def banzai_postprocess(html, context = {})
+ return '' unless html.present?
+
context.merge!(
current_user: (current_user if defined?(current_user)),
# RelativeLinkFilter
- requested_path: @path,
+ commit: @commit,
project_wiki: @project_wiki,
- ref: @ref
+ ref: @ref,
+ requested_path: @path
)
Banzai.post_process(html, context)
end
+
+ extend self
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index f1dab60524e..f7b5a5f4dfc 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -12,10 +12,6 @@ module TreeHelper
tree.html_safe
end
- def render_readme(readme)
- render_markup(readme.name, readme.data)
- end
-
# Return an image icon depending on the file type and mode
#
# type - String type of the tree item; either 'folder' or 'file'
diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb
index a9b6b33eb5c..d2980db218a 100644
--- a/app/mailers/base_mailer.rb
+++ b/app/mailers/base_mailer.rb
@@ -1,6 +1,6 @@
class BaseMailer < ActionMailer::Base
helper ApplicationHelper
- helper GitlabMarkdownHelper
+ helper MarkupHelper
attr_accessor :current_user
helper_method :current_user, :can?
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index dd1a6922968..cf042717c95 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -28,6 +28,8 @@ class ApplicationSetting < ActiveRecord::Base
attr_accessor :domain_whitelist_raw, :domain_blacklist_raw
+ validates :uuid, presence: true
+
validates :session_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
@@ -159,6 +161,7 @@ class ApplicationSetting < ActiveRecord::Base
end
end
+ before_validation :ensure_uuid!
before_save :ensure_runners_registration_token
before_save :ensure_health_check_access_token
@@ -344,6 +347,12 @@ class ApplicationSetting < ActiveRecord::Base
private
+ def ensure_uuid!
+ return if uuid?
+
+ self.uuid = SecureRandom.uuid
+ end
+
def check_repository_storages
invalid = repository_storages - Gitlab.config.repositories.storages.keys
errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 0bbc9451ffd..59737bb6085 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -107,7 +107,8 @@ module Network
def find_commits(skip = 0)
opts = {
max_count: self.class.max_count,
- skip: skip
+ skip: skip,
+ order: :date
}
opts[:ref] = @commit.id if @filter_ref
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7bb874d7744..e74edb8e6f7 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -17,9 +17,9 @@ class Repository
# same name. The cache key used by those methods must also match method's
# name.
#
- # For example, for entry `:readme` there's a method called `readme` which
- # stores its data in the `readme` cache key.
- CACHED_METHODS = %i(size commit_count readme contribution_guide
+ # For example, for entry `:commit_count` there's a method called `commit_count` which
+ # stores its data in the `commit_count` cache key.
+ CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide
changelog license_blob license_key gitignore koding_yml
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? empty? root_ref).freeze
@@ -28,7 +28,7 @@ class Repository
# changed. This Hash maps file types (as returned by Gitlab::FileDetector) to
# the corresponding methods to call for refreshing caches.
METHOD_CACHES_FOR_FILE_TYPES = {
- readme: :readme,
+ readme: :rendered_readme,
changelog: :changelog,
license: %i(license_blob license_key),
contributing: :contribution_guide,
@@ -527,7 +527,11 @@ class Repository
head.readme
end
end
- cache_method :readme
+
+ def rendered_readme
+ MarkupHelper.markup_unsafe(readme.name, readme.data, project: project) if readme
+ end
+ cache_method :rendered_readme
def contribution_guide
file_on_head(:contributing)
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 7828c5806b0..535d93385e6 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -97,7 +97,8 @@ module Projects
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group || @project.gitlab_project_import?
- @project.team << [current_user, :master, current_user]
+ owners = [current_user, @project.namespace.owner].compact.uniq
+ @project.add_master(owners, current_user: current_user)
end
@project.group&.refresh_members_authorized_projects
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index b6fb08b68e9..c0d12cbc66e 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -4,8 +4,7 @@
- if can?(current_user, :push_code, @project)
= link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme'
.file-content.wiki
- = cache(readme_cache_key) do
- = render_readme(readme)
+ = markup(readme.name, readme.data, rendered: @repository.rendered_readme)
- else
.row-content-block.second-block.center
%h3.page-title
diff --git a/app/views/projects/blob/_markup.html.haml b/app/views/projects/blob/_markup.html.haml
index 4ee4b03ff04..0090f7a11df 100644
--- a/app/views/projects/blob/_markup.html.haml
+++ b/app/views/projects/blob/_markup.html.haml
@@ -1,4 +1,4 @@
- blob.load_all_data!(@repository)
.file-content.wiki
- = render_markup(blob.name, blob.data)
+ = markup(blob.name, blob.data)
diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml
index 5cafb644b40..e87b73c9a34 100644
--- a/app/views/projects/blob/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
@@ -1,12 +1,8 @@
.diff-file
.diff-content
- - if gitlab_markdown?(@blob.name)
+ - if markup?(@blob.name)
.file-content.wiki
- = preserve do
- = markdown(@content)
- - elsif markup?(@blob.name)
- .file-content.wiki
- = raw render_markup(@blob.name, @content)
+ = markup(@blob.name, @content)
- else
.file-content.code.js-syntax-highlight
- unless @diff_lines.empty?
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index bdcc160a067..01599060844 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -5,4 +5,4 @@
%strong
= readme.name
.file-content.wiki
- = render_readme(readme)
+ = markup(readme.name, readme.data)
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index f84be600df8..f2fe5742c12 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -21,7 +21,7 @@
.file-content.wiki
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
- = render_markup(snippet.file_name, chunk[:data])
+ = markup(snippet.file_name, chunk[:data])
- else
.file-content.code
.nothing-here-block Empty file
diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml
index 74f71e6cbd1..895c3f1e99d 100644
--- a/app/views/shared/snippets/_blob.html.haml
+++ b/app/views/shared/snippets/_blob.html.haml
@@ -24,6 +24,6 @@
- if gitlab_markdown?(@snippet.file_name)
= preserve(markdown_field(@snippet, :content))
- else
- = render_markup(@snippet.file_name, @snippet.content)
+ = markup(@snippet.file_name, @snippet.content)
- else
= render 'shared/file_highlight', blob: @snippet
diff --git a/changelogs/unreleased/2246-uuid-is-nil-for-new-installation.yml b/changelogs/unreleased/2246-uuid-is-nil-for-new-installation.yml
new file mode 100644
index 00000000000..70d35f06af4
--- /dev/null
+++ b/changelogs/unreleased/2246-uuid-is-nil-for-new-installation.yml
@@ -0,0 +1,4 @@
+---
+title: Lazily sets UUID in ApplicationSetting for new installations
+merge_request:
+author:
diff --git a/changelogs/unreleased/26585-remove-readme-view-caching.yml b/changelogs/unreleased/26585-remove-readme-view-caching.yml
new file mode 100644
index 00000000000..6aefae982bf
--- /dev/null
+++ b/changelogs/unreleased/26585-remove-readme-view-caching.yml
@@ -0,0 +1,4 @@
+---
+title: 'Remove view fragment caching for project READMEs'
+merge_request: 8838
+author:
diff --git a/changelogs/unreleased/29505-allow-admins-sudo-to-blocked-users.yml b/changelogs/unreleased/29505-allow-admins-sudo-to-blocked-users.yml
new file mode 100644
index 00000000000..42fd71ccd5f
--- /dev/null
+++ b/changelogs/unreleased/29505-allow-admins-sudo-to-blocked-users.yml
@@ -0,0 +1,4 @@
+---
+title: Allow admins to sudo to blocked users via the API
+merge_request: 10842
+author:
diff --git a/changelogs/unreleased/30973-fix-network-graph-ordering.yml b/changelogs/unreleased/30973-fix-network-graph-ordering.yml
new file mode 100644
index 00000000000..420ec107842
--- /dev/null
+++ b/changelogs/unreleased/30973-fix-network-graph-ordering.yml
@@ -0,0 +1,4 @@
+---
+title: Fix ordering of commits in the network graph
+merge_request: 10936
+author:
diff --git a/changelogs/unreleased/tc-make-user-master-project-by-admin.yml b/changelogs/unreleased/tc-make-user-master-project-by-admin.yml
new file mode 100644
index 00000000000..459d6178bdd
--- /dev/null
+++ b/changelogs/unreleased/tc-make-user-master-project-by-admin.yml
@@ -0,0 +1,4 @@
+---
+title: Ensure namespace owner is Master of project upon creation
+merge_request: 10910
+author:
diff --git a/db/migrate/20170426175636_fill_missing_uuid_on_application_settings.rb b/db/migrate/20170426175636_fill_missing_uuid_on_application_settings.rb
new file mode 100644
index 00000000000..58ad2c64075
--- /dev/null
+++ b/db/migrate/20170426175636_fill_missing_uuid_on_application_settings.rb
@@ -0,0 +1,10 @@
+class FillMissingUuidOnApplicationSettings < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def up
+ execute("UPDATE application_settings SET uuid = #{quote(SecureRandom.uuid)} WHERE uuid is NULL")
+ end
+
+ def down
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ff00951d5f6..49d7c996661 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170424142900) do
+ActiveRecord::Schema.define(version: 20170426175636) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index ddff3c8c1e8..86bf567fe69 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -102,7 +102,7 @@ module API
end
def authenticate!
- unauthorized! unless current_user && can?(current_user, :access_api)
+ unauthorized! unless current_user && can?(initial_current_user, :access_api)
end
def authenticate_non_get!
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index d575367d81a..fba80c7132e 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -14,28 +14,16 @@ module Gitlab
# Public: Converts the provided Asciidoc markup into HTML.
#
# input - the source text in Asciidoc format
- # context - a Hash with the template context:
- # :commit
- # :project
- # :project_wiki
- # :requested_path
- # :ref
- # asciidoc_opts - a Hash of options to pass to the Asciidoctor converter
#
- def self.render(input, context, asciidoc_opts = {})
- asciidoc_opts.reverse_merge!(
- safe: :secure,
- backend: :gitlab_html5,
- attributes: []
- )
- asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
+ def self.render(input)
+ asciidoc_opts = { safe: :secure,
+ backend: :gitlab_html5,
+ attributes: DEFAULT_ADOC_ATTRS }
plantuml_setup
html = ::Asciidoctor.convert(input, asciidoc_opts)
- html = Banzai.post_process(html, context)
-
filter = Banzai::Filter::SanitizationFilter.new(html)
html = filter.call.to_s
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index d7dac9f6149..452dba7971d 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -494,7 +494,9 @@ module Gitlab
# :contains is the commit contained by the refs from which to begin (SHA1 or name)
# :max_count is the maximum number of commits to fetch
# :skip is the number of commits to skip
- # :order is the commits order and allowed value is :date(default) or :topo
+ # :order is the commits order and allowed value is :none (default), :date, or :topo
+ # commit ordering types are documented here:
+ # http://www.rubydoc.info/github/libgit2/rugged/Rugged#SORT_NONE-constant)
#
def find_commits(options = {})
actual_options = options.dup
@@ -522,11 +524,8 @@ module Gitlab
end
end
- if actual_options[:order] == :topo
- walker.sorting(Rugged::SORT_TOPO)
- else
- walker.sorting(Rugged::SORT_NONE)
- end
+ sort_type = rugged_sort_type(actual_options[:order])
+ walker.sorting(sort_type)
commits = []
offset = actual_options[:skip]
@@ -1273,6 +1272,18 @@ module Gitlab
def gitaly_ref_client
@gitaly_ref_client ||= Gitlab::GitalyClient::Ref.new(self)
end
+
+ # Returns the `Rugged` sorting type constant for a given
+ # sort type key. Valid keys are `:none`, `:topo`, and `:date`
+ def rugged_sort_type(key)
+ @rugged_sort_types ||= {
+ none: Rugged::SORT_NONE,
+ topo: Rugged::SORT_TOPO,
+ date: Rugged::SORT_DATE
+ }
+
+ @rugged_sort_types.fetch(key, Rugged::SORT_NONE)
+ end
end
end
end
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index e67acf28c94..c2adc9aa10b 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -4,19 +4,11 @@ module Gitlab
# Public: Converts the provided markup into HTML.
#
# input - the source text in a markup format
- # context - a Hash with the template context:
- # :commit
- # :project
- # :project_wiki
- # :requested_path
- # :ref
#
- def self.render(file_name, input, context)
+ def self.render(file_name, input)
html = GitHub::Markup.render(file_name, input).
force_encoding(input.encoding)
- html = Banzai.post_process(html, context)
-
filter = Banzai::Filter::SanitizationFilter.new(html)
html = filter.call.to_s
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
new file mode 100644
index 00000000000..dd14ffdb2ce
--- /dev/null
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+feature 'Admin cohorts page', feature: true do
+ before do
+ login_as :admin
+ end
+
+ scenario 'See users count per month' do
+ 2.times { create(:user) }
+
+ visit admin_cohorts_path
+
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
+ end
+end
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 64a6c70061b..344e31e5ef5 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'Copy as GFM', feature: true, js: true do
- include GitlabMarkdownHelper
+ include MarkupHelper
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 894df13a2dc..ba930de937d 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -26,7 +26,7 @@ require 'erb'
describe 'GitLab Markdown', feature: true do
include Capybara::Node::Matchers
- include GitlabMarkdownHelper
+ include MarkupHelper
include MarkdownMatchers
# Sometimes it can be useful to see the parsed output of the Markdown document
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 5c07ea8a872..01bdf01ad22 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -239,33 +239,6 @@ describe ApplicationHelper do
end
end
- describe 'render_markup' do
- let(:content) { 'Noël' }
- let(:user) { create(:user) }
- before do
- allow(helper).to receive(:current_user).and_return(user)
- end
-
- it 'preserves encoding' do
- expect(content.encoding.name).to eq('UTF-8')
- expect(helper.render_markup('foo.rst', content).encoding.name).to eq('UTF-8')
- end
-
- it "delegates to #markdown when file name corresponds to Markdown" do
- expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
- expect(helper).to receive(:markdown).and_return('NOEL')
-
- expect(helper.render_markup('foo.md', content)).to eq('NOEL')
- end
-
- it "delegates to #asciidoc when file name corresponds to AsciiDoc" do
- expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
- expect(helper).to receive(:asciidoc).and_return('NOEL')
-
- expect(helper.render_markup('foo.adoc', content)).to eq('NOEL')
- end
- end
-
describe '#active_when' do
it { expect(helper.active_when(true)).to eq('active') }
it { expect(helper.active_when(false)).to eq(nil) }
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 2b6cec38b9a..2a0de0b0656 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
-describe GitlabMarkdownHelper do
- include ApplicationHelper
-
+describe MarkupHelper do
let!(:project) { create(:project, :repository) }
let(:user) { create(:user, username: 'gfm') }
@@ -128,7 +126,7 @@ describe GitlabMarkdownHelper do
it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown)
- expect(helper).to receive(:markdown).with('wiki content', pipeline: :wiki, project_wiki: @wiki, page_slug: "nested/page")
+ expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page")
helper.render_wiki_content(@wiki)
end
@@ -136,7 +134,7 @@ describe GitlabMarkdownHelper do
it "uses Asciidoctor for asciidoc files" do
allow(@wiki).to receive(:format).and_return(:asciidoc)
- expect(helper).to receive(:asciidoc).with('wiki content')
+ expect(helper).to receive(:asciidoc_unsafe).with('wiki content')
helper.render_wiki_content(@wiki)
end
@@ -151,6 +149,29 @@ describe GitlabMarkdownHelper do
end
end
+ describe 'markup' do
+ let(:content) { 'Noël' }
+
+ it 'preserves encoding' do
+ expect(content.encoding.name).to eq('UTF-8')
+ expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
+ end
+
+ it "delegates to #markdown_unsafe when file name corresponds to Markdown" do
+ expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
+ expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
+
+ expect(helper.markup('foo.md', content)).to eq('NOEL')
+ end
+
+ it "delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc" do
+ expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
+ expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
+
+ expect(helper.markup('foo.adoc', content)).to eq('NOEL')
+ end
+ end
+
describe '#first_line_in_markdown' do
it 'truncates Markdown properly' do
text = "@#{user.username}, can you look at this?\nHello world\n"
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index 9762688af1a..1c54cc3054c 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -1,15 +1,18 @@
import Vue from 'vue';
import '~/flash';
-import EnvironmentsComponent from '~/environments/components/environment';
+import environmentsComponent from '~/environments/components/environment.vue';
import { environment, folder } from './mock_data';
describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw');
+ let EnvironmentsComponent;
let component;
beforeEach(() => {
loadFixtures('static/environments/environments.html.raw');
+
+ EnvironmentsComponent = Vue.extend(environmentsComponent);
});
describe('successfull request', () => {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 72f3db29a66..350078ad5f5 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -1,13 +1,15 @@
import Vue from 'vue';
import '~/flash';
-import EnvironmentsFolderViewComponent from '~/environments/folder/environments_folder_view';
+import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import { environmentsList } from '../mock_data';
describe('Environments Folder View', () => {
preloadFixtures('static/environments/environments_folder_view.html.raw');
+ let EnvironmentsFolderViewComponent;
beforeEach(() => {
loadFixtures('static/environments/environments_folder_view.html.raw');
+ EnvironmentsFolderViewComponent = Vue.extend(environmentsFolderViewComponent);
window.history.pushState({}, null, 'environments/folders/build');
});
diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js
new file mode 100644
index 00000000000..24581e8c672
--- /dev/null
+++ b/spec/javascripts/pipelines/time_ago_spec.js
@@ -0,0 +1,64 @@
+import Vue from 'vue';
+import timeAgo from '~/pipelines/components/time_ago';
+
+describe('Timeago component', () => {
+ let TimeAgo;
+ beforeEach(() => {
+ TimeAgo = Vue.extend(timeAgo);
+ });
+
+ describe('with duration', () => {
+ it('should render duration and timer svg', () => {
+ const component = new TimeAgo({
+ propsData: {
+ duration: 10,
+ finishedTime: '',
+ },
+ }).$mount();
+
+ expect(component.$el.querySelector('.duration')).toBeDefined();
+ expect(component.$el.querySelector('.duration svg')).toBeDefined();
+ });
+ });
+
+ describe('without duration', () => {
+ it('should not render duration and timer svg', () => {
+ const component = new TimeAgo({
+ propsData: {
+ duration: 0,
+ finishedTime: '',
+ },
+ }).$mount();
+
+ expect(component.$el.querySelector('.duration')).toBe(null);
+ });
+ });
+
+ describe('with finishedTime', () => {
+ it('should render time and calendar icon', () => {
+ const component = new TimeAgo({
+ propsData: {
+ duration: 0,
+ finishedTime: '2017-04-26T12:40:23.277Z',
+ },
+ }).$mount();
+
+ expect(component.$el.querySelector('.finished-at')).toBeDefined();
+ expect(component.$el.querySelector('.finished-at i.fa-calendar')).toBeDefined();
+ expect(component.$el.querySelector('.finished-at time')).toBeDefined();
+ });
+ });
+
+ describe('without finishedTime', () => {
+ it('should not render time and calendar icon', () => {
+ const component = new TimeAgo({
+ propsData: {
+ duration: 0,
+ finishedTime: '',
+ },
+ }).$mount();
+
+ expect(component.$el.querySelector('.finished-at')).toBe(null);
+ });
+ });
+});
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index bca57105d1d..0f47fb2fbd9 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -22,26 +22,9 @@ module Gitlab
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
- expect( render(input, context) ).to eql html
+ expect(render(input)).to eq(html)
end
- context "with asciidoc_opts" do
- let(:asciidoc_opts) { { safe: :safe, attributes: ['foo'] } }
-
- it "merges the options with default ones" do
- expected_asciidoc_opts = {
- safe: :safe,
- backend: :gitlab_html5,
- attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo']
- }
-
- expect(Asciidoctor).to receive(:convert)
- .with(input, expected_asciidoc_opts).and_return(html)
-
- render(input, context, asciidoc_opts)
- end
- end
-
context "XSS" do
links = {
'links' => {
@@ -60,7 +43,7 @@ module Gitlab
links.each do |name, data|
it "does not convert dangerous #{name} into HTML" do
- expect(render(data[:input], context)).to eql data[:output]
+ expect(render(data[:input])).to eq(data[:output])
end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 3d6d7292b42..f88653cb1fe 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1031,6 +1031,35 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#find_commits' do
+ it 'should return a return a collection of commits' do
+ commits = repository.find_commits
+
+ expect(commits).not_to be_empty
+ expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) )
+ end
+
+ context 'while applying a sort order based on the `order` option' do
+ it "allows ordering topologically (no parents shown before their children)" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO)
+
+ repository.find_commits(order: :topo)
+ end
+
+ it "allows ordering by date" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE)
+
+ repository.find_commits(order: :date)
+ end
+
+ it "applies no sorting by default" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE)
+
+ repository.find_commits
+ end
+ end
+ end
+
describe '#branches with deleted branch' do
before(:each) do
ref = double()
diff --git a/spec/lib/gitlab/other_markup_spec.rb b/spec/lib/gitlab/other_markup_spec.rb
index 22e80ec46be..d6d53e8586c 100644
--- a/spec/lib/gitlab/other_markup_spec.rb
+++ b/spec/lib/gitlab/other_markup_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::OtherMarkup, lib: true do
}
links.each do |name, data|
it "does not convert dangerous #{name} into HTML" do
- expect(render(data[:file], data[:input], context)).to eql data[:output]
+ expect(render(data[:file], data[:input])).to eq(data[:output])
end
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 01ca1584ed2..c2c19c62048 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -4,6 +4,7 @@ describe ApplicationSetting, models: true do
let(:setting) { ApplicationSetting.create_from_defaults }
it { expect(setting).to be_valid }
+ it { expect(setting.uuid).to be_present }
describe 'validations' do
let(:http) { 'http://example.com' }
diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb
index 492c4e01bd8..46b36e11c23 100644
--- a/spec/models/network/graph_spec.rb
+++ b/spec/models/network/graph_spec.rb
@@ -9,4 +9,25 @@ describe Network::Graph, models: true do
expect(graph.notes).to eq( { note_on_commit.commit_id => 1 } )
end
+
+ describe "#commits" do
+ let(:graph) { described_class.new(project, 'refs/heads/master', project.repository.commit, nil) }
+
+ it "returns a list of commits" do
+ commits = graph.commits
+
+ expect(commits).not_to be_empty
+ expect(commits).to all( be_kind_of(Network::Commit) )
+ end
+
+ it "sorts the commits by commit date (descending)" do
+ # Remove duplicate timestamps because they make it harder to
+ # assert that the commits are sorted as expected.
+ commits = graph.commits.uniq(&:date)
+ sorted_commits = commits.sort_by(&:date).reverse
+
+ expect(commits).not_to be_empty
+ expect(commits.map(&:id)).to eq(sorted_commits.map(&:id))
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 74d5ebc6db0..98d0641443e 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1803,9 +1803,9 @@ describe Repository, models: true do
describe '#refresh_method_caches' do
it 'refreshes the caches of the given types' do
expect(repository).to receive(:expire_method_caches).
- with(%i(readme license_blob license_key))
+ with(%i(rendered_readme license_blob license_key))
- expect(repository).to receive(:readme)
+ expect(repository).to receive(:rendered_readme)
expect(repository).to receive(:license_blob)
expect(repository).to receive(:license_key)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 4845ab1ae1f..06c8eb1d0b7 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -427,6 +427,7 @@ describe API::Helpers do
context 'current_user is nil' do
before do
expect_any_instance_of(self.class).to receive(:current_user).and_return(nil)
+ allow_any_instance_of(self.class).to receive(:initial_current_user).and_return(nil)
end
it 'returns a 401 response' do
@@ -435,13 +436,38 @@ describe API::Helpers do
end
context 'current_user is present' do
+ let(:user) { build(:user) }
+
before do
- expect_any_instance_of(self.class).to receive(:current_user).at_least(:once).and_return(User.new)
+ expect_any_instance_of(self.class).to receive(:current_user).at_least(:once).and_return(user)
+ expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(user)
end
it 'does not raise an error' do
expect { authenticate! }.not_to raise_error
end
end
+
+ context 'current_user is blocked' do
+ let(:user) { build(:user, :blocked) }
+
+ before do
+ expect_any_instance_of(self.class).to receive(:current_user).at_least(:once).and_return(user)
+ end
+
+ it 'raises an error' do
+ expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(user)
+
+ expect { authenticate! }.to raise_error '401 - {"message"=>"401 Unauthorized"}'
+ end
+
+ it "doesn't raise an error if an admin user is impersonating a blocked user (via sudo)" do
+ admin_user = build(:user, :admin)
+
+ expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(admin_user)
+
+ expect { authenticate! }.not_to raise_error
+ end
+ end
end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 7a07ea618c0..033e6ecd18c 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -27,6 +27,22 @@ describe Projects::CreateService, '#execute', services: true do
end
end
+ context "admin creates project with other user's namespace_id" do
+ it 'sets the correct permissions' do
+ admin = create(:admin)
+ opts = {
+ name: 'GitLab',
+ namespace_id: user.namespace.id
+ }
+ project = create_project(admin, opts)
+
+ expect(project).to be_persisted
+ expect(project.owner).to eq(user)
+ expect(project.team.masters).to include(user, admin)
+ expect(project.namespace).to eq(user.namespace)
+ end
+ end
+
context 'group namespace' do
let(:group) do
create(:group).tap do |group|
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 42d63a9f9ba..75d7caf2508 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -595,7 +595,7 @@ describe SystemNoteService, services: true do
end
shared_examples 'cross project mentionable' do
- include GitlabMarkdownHelper
+ include MarkupHelper
it 'contains cross reference to new noteable' do
expect(subject.note).to include cross_project_reference(new_project, new_noteable)